github.com/cilium/cilium@v1.16.2/pkg/maps/nat/per_cluster_nat.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package nat
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"strconv"
    10  	"unsafe"
    11  
    12  	"github.com/cilium/ebpf"
    13  
    14  	"github.com/cilium/cilium/pkg/bpf"
    15  	cmtypes "github.com/cilium/cilium/pkg/clustermesh/types"
    16  	"github.com/cilium/cilium/pkg/lock"
    17  )
    18  
    19  const (
    20  	perClusterOuterMapPrefix = "cilium_per_cluster_snat_"
    21  	perClusterIPv4OuterMap   = perClusterOuterMapPrefix + "v4_external"
    22  	perClusterIPv6OuterMap   = perClusterOuterMapPrefix + "v6_external"
    23  )
    24  
    25  // ClusterOuterMapName returns the name of the outer per-cluster NAT map
    26  // for the given IP family. It can be overwritten for testing purposes.
    27  var ClusterOuterMapName = clusterOuterMapName
    28  
    29  func clusterOuterMapName(family IPFamily) string {
    30  	if family == IPv4 {
    31  		return perClusterIPv4OuterMap
    32  	}
    33  	return perClusterIPv6OuterMap
    34  }
    35  
    36  func ClusterOuterMapNameTestOverride(prefix string) {
    37  	ClusterOuterMapName = func(family IPFamily) string {
    38  		return prefix + "_" + clusterOuterMapName(family)
    39  	}
    40  }
    41  
    42  // ClusterInnerMapName returns the name of the inner per-cluster NAT map
    43  // for the given IP family and cluster ID.
    44  func ClusterInnerMapName(family IPFamily, clusterID uint32) string {
    45  	return ClusterOuterMapName(family) + "_" + strconv.FormatUint(uint64(clusterID), 10)
    46  }
    47  
    48  var _ PerClusterNATMapper = (*perClusterNATMaps)(nil)
    49  
    50  // An interface to manage the per-cluster NAT maps.
    51  type PerClusterNATMapper interface {
    52  	// Create enforces the presence of the outer per-cluster NAT maps.
    53  	OpenOrCreate() error
    54  	// Close closes the outer per-cluster NAT maps handlers.
    55  	Close() error
    56  
    57  	// CreateClusterNATMaps enforces the presence of the inner maps for
    58  	// the given cluster ID. It must be called after that OpenOrCreate()
    59  	// has returned successfully.
    60  	CreateClusterNATMaps(clusterID uint32) error
    61  	// DeleteClusterNATMaps deletes the inner maps for the given cluster ID.
    62  	// It must be called after that OpenOrCreate() has returned successfully.
    63  	DeleteClusterNATMaps(clusterID uint32) error
    64  }
    65  
    66  // NewPerClusterNATMaps returns a new instance of the per-cluster NAT maps manager.
    67  func NewPerClusterNATMaps(ipv4, ipv6 bool) *perClusterNATMaps {
    68  	return newPerClusterNATMaps(ipv4, ipv6, maxEntries())
    69  }
    70  
    71  // GetClusterNATMap returns the per-cluster map for the given cluster ID. The
    72  // returned map needs to be opened by the caller, and it is not guaranteed to exist.
    73  func GetClusterNATMap(clusterID uint32, family IPFamily) (*Map, error) {
    74  	maps := NewPerClusterNATMaps(family == IPv4, family == IPv6)
    75  	return maps.getClusterNATMap(clusterID, family)
    76  }
    77  
    78  // CleanupPerClusterNATMaps deletes the per-cluster NAT maps, including the inner ones.
    79  func CleanupPerClusterNATMaps(ipv4, ipv6 bool) error {
    80  	maps := NewPerClusterNATMaps(ipv4, ipv6)
    81  	return maps.cleanup()
    82  }
    83  
    84  // A structure that holds per-cluster IPv4 and v6 NAT maps. It implements
    85  // PerClusterNATMapper.
    86  type perClusterNATMaps struct {
    87  	lock.RWMutex
    88  	v4Map *perClusterNATMap
    89  	v6Map *perClusterNATMap
    90  }
    91  
    92  // A map-in-map that holds per-cluster NAT maps.
    93  type perClusterNATMap struct {
    94  	*bpf.Map
    95  	family          IPFamily
    96  	innerMapEntries int
    97  }
    98  
    99  type PerClusterNATMapKey struct {
   100  	ClusterID uint32
   101  }
   102  
   103  func (k *PerClusterNATMapKey) String() string  { return strconv.FormatUint(uint64(k.ClusterID), 10) }
   104  func (n *PerClusterNATMapKey) New() bpf.MapKey { return &PerClusterNATMapKey{} }
   105  
   106  type PerClusterNATMapVal struct {
   107  	Fd uint32
   108  }
   109  
   110  func (v *PerClusterNATMapVal) String() string    { return fmt.Sprintf("fd=%d", v.Fd) }
   111  func (n *PerClusterNATMapVal) New() bpf.MapValue { return &PerClusterNATMapVal{} }
   112  
   113  func newPerClusterNATMap(family IPFamily, innerMapEntries int) *perClusterNATMap {
   114  	var (
   115  		keySize uint32
   116  		valSize uint32
   117  	)
   118  
   119  	if family == IPv4 {
   120  		keySize = uint32(unsafe.Sizeof(NatKey4{}))
   121  		valSize = uint32(unsafe.Sizeof(NatEntry4{}))
   122  	} else {
   123  		keySize = uint32(unsafe.Sizeof(NatKey6{}))
   124  		valSize = uint32(unsafe.Sizeof(NatEntry6{}))
   125  	}
   126  
   127  	inner := &ebpf.MapSpec{
   128  		Type:       ebpf.LRUHash,
   129  		KeySize:    keySize,
   130  		ValueSize:  valSize,
   131  		MaxEntries: uint32(innerMapEntries),
   132  	}
   133  
   134  	om := bpf.NewMapWithInnerSpec(
   135  		ClusterOuterMapName(family),
   136  		ebpf.ArrayOfMaps,
   137  		&PerClusterNATMapKey{},
   138  		&PerClusterNATMapVal{},
   139  		int(cmtypes.ClusterIDMax+1),
   140  		0,
   141  		inner,
   142  	)
   143  
   144  	return &perClusterNATMap{
   145  		Map:             om,
   146  		family:          family,
   147  		innerMapEntries: innerMapEntries,
   148  	}
   149  }
   150  
   151  func (om *perClusterNATMap) newInnerMap(clusterID uint32) *Map {
   152  	return NewMap(ClusterInnerMapName(om.family, clusterID), om.family, om.innerMapEntries)
   153  }
   154  
   155  func (om *perClusterNATMap) createClusterNATMap(clusterID uint32) error {
   156  	im := om.newInnerMap(clusterID)
   157  	if err := im.OpenOrCreate(); err != nil {
   158  		return fmt.Errorf("create inner map: %w", err)
   159  	}
   160  
   161  	defer im.Close()
   162  
   163  	if err := om.Update(
   164  		&PerClusterNATMapKey{clusterID},
   165  		&PerClusterNATMapVal{uint32(im.FD())},
   166  	); err != nil {
   167  		return fmt.Errorf("update outer map: %w", err)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func (om *perClusterNATMap) deleteClusterNATMap(clusterID uint32) error {
   174  	im := om.newInnerMap(clusterID)
   175  	if err := im.Unpin(); err != nil {
   176  		return fmt.Errorf("delete inner map: %w", err)
   177  	}
   178  
   179  	// Detach inner map from outer map. At this point, no
   180  	// one should have the reference of the inner map after
   181  	// this call.
   182  	if _, err := om.SilentDelete(&PerClusterNATMapKey{clusterID}); err != nil {
   183  		return fmt.Errorf("update outer map: %w", err)
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  func (om *perClusterNATMap) cleanup() error {
   190  	var errs []error
   191  
   192  	for id := uint32(1); id <= cmtypes.ClusterIDMax; id++ {
   193  		im := om.newInnerMap(id)
   194  		if err := im.Unpin(); err != nil {
   195  			errs = append(errs, fmt.Errorf("delete inner map for cluster ID %v: %w", id, err))
   196  		}
   197  	}
   198  
   199  	if err := om.Unpin(); err != nil {
   200  		errs = append(errs, fmt.Errorf("delete outer map: %w", err))
   201  	}
   202  
   203  	return errors.Join(errs...)
   204  }
   205  
   206  func newPerClusterNATMaps(ipv4, ipv6 bool, innerMapEntries int) *perClusterNATMaps {
   207  	var gm perClusterNATMaps
   208  
   209  	if ipv4 {
   210  		gm.v4Map = newPerClusterNATMap(IPv4, innerMapEntries)
   211  	}
   212  
   213  	if ipv6 {
   214  		gm.v6Map = newPerClusterNATMap(IPv6, innerMapEntries)
   215  	}
   216  
   217  	return &gm
   218  }
   219  
   220  func (gm *perClusterNATMaps) OpenOrCreate() (err error) {
   221  	return gm.foreach(
   222  		func(om *perClusterNATMap) error { return om.OpenOrCreate() },
   223  	)
   224  }
   225  
   226  func (gm *perClusterNATMaps) Close() (err error) {
   227  	return gm.foreach(
   228  		func(om *perClusterNATMap) error { return om.Close() },
   229  	)
   230  }
   231  
   232  func (gm *perClusterNATMaps) CreateClusterNATMaps(clusterID uint32) error {
   233  	if err := cmtypes.ValidateClusterID(clusterID); err != nil {
   234  		return err
   235  	}
   236  
   237  	return gm.foreach(
   238  		func(om *perClusterNATMap) error { return om.createClusterNATMap(clusterID) },
   239  	)
   240  }
   241  
   242  func (gm *perClusterNATMaps) DeleteClusterNATMaps(clusterID uint32) error {
   243  	if err := cmtypes.ValidateClusterID(clusterID); err != nil {
   244  		return err
   245  	}
   246  
   247  	return gm.foreach(func(om *perClusterNATMap) error {
   248  		return om.deleteClusterNATMap(clusterID)
   249  	})
   250  }
   251  
   252  func (gm *perClusterNATMaps) getClusterNATMap(clusterID uint32, family IPFamily) (*Map, error) {
   253  	if err := cmtypes.ValidateClusterID(clusterID); err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	gm.RLock()
   258  	defer gm.RUnlock()
   259  
   260  	if family == IPv4 && gm.v4Map == nil || family == IPv6 && gm.v6Map == nil {
   261  		return nil, fmt.Errorf("IP family %s not enabled", family)
   262  	}
   263  
   264  	if family == IPv4 {
   265  		return gm.v4Map.newInnerMap(clusterID), nil
   266  	}
   267  
   268  	return gm.v6Map.newInnerMap(clusterID), nil
   269  }
   270  
   271  func (gm *perClusterNATMaps) cleanup() error {
   272  	return gm.foreach(func(om *perClusterNATMap) error {
   273  		return om.cleanup()
   274  	})
   275  }
   276  
   277  func (gm *perClusterNATMaps) foreach(fn func(om *perClusterNATMap) error) error {
   278  	gm.Lock()
   279  	defer gm.Unlock()
   280  
   281  	var errs []error
   282  
   283  	// Attempt to perform the given operation on all maps, and collect all
   284  	// errors that are encountered. We do not implement a rollback mechanism
   285  	// in case of failures to keep the overall logic simple, as it is likely
   286  	// that the consumer of the different methods will nonetheless retry again
   287  	// the same operation on error. Hence, the rollback would only introduce
   288  	// additional churn, and it might not be even possible in certain cases
   289  	// (e.g., for deletion operations, to restore the previous state).
   290  	for _, om := range []*perClusterNATMap{gm.v4Map, gm.v6Map} {
   291  		if om != nil {
   292  			if err := fn(om); err != nil {
   293  				errs = append(errs, fmt.Errorf("%s: %w", om.family, err))
   294  			}
   295  		}
   296  	}
   297  
   298  	return errors.Join(errs...)
   299  }