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 }