github.com/cilium/cilium@v1.16.2/pkg/clustermesh/types/addressing.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package types 5 6 import ( 7 "fmt" 8 "net" 9 "net/netip" 10 "strconv" 11 "strings" 12 13 "go4.org/netipx" 14 15 "github.com/cilium/cilium/pkg/cidr" 16 ippkg "github.com/cilium/cilium/pkg/ip" 17 ) 18 19 // 20 // In this file, we define types and utilities for cluster-aware 21 // addressing which identifies network endpoints using IP address 22 // and an optional ClusterID. With this special addressing scheme, 23 // we can distinguish network endpoints (e.g. Pods) that have the 24 // same IP address, but belong to the different cluster. 25 // 26 // A "bare" IP address is still a valid identifier because there 27 // are cases that endpoints can be identified without ClusterID 28 // (e.g. network endpoint has a unique IP address). We can consider 29 // this as a special case that ClusterID "doesn't matter". ClusterID 30 // 0 is reserved for indicating that. 31 // 32 33 // AddrCluster is a type that holds a pair of IP and ClusterID. 34 // We should use this type as much as possible when we implement 35 // IP + Cluster addressing. We should avoid managing IP and ClusterID 36 // separately. Otherwise, it is very hard for code readers to see 37 // where we are using cluster-aware addressing. 38 type AddrCluster struct { 39 addr netip.Addr 40 clusterID uint32 41 } 42 43 const AddrClusterLen = 20 44 45 // ParseAddrCluster parses s as an IP + ClusterID and returns AddrCluster. 46 // The string s can be a bare IP string (any IP address format allowed in 47 // netip.ParseAddr()) or IP string + @ + ClusterID with decimal. Bare IP 48 // string is considered as IP string + @ + ClusterID = 0. 49 func ParseAddrCluster(s string) (AddrCluster, error) { 50 atIndex := strings.LastIndex(s, "@") 51 52 var ( 53 addrStr string 54 clusterIDStr string 55 ) 56 57 if atIndex == -1 { 58 // s may be a bare IP address string, still valid 59 addrStr = s 60 clusterIDStr = "" 61 } else { 62 // s may be a IP + ClusterID string 63 addrStr = s[:atIndex] 64 clusterIDStr = s[atIndex+1:] 65 } 66 67 addr, err := netip.ParseAddr(addrStr) 68 if err != nil { 69 return AddrCluster{}, err 70 } 71 72 if clusterIDStr == "" { 73 if atIndex != len(s)-1 { 74 return AddrCluster{addr: addr, clusterID: 0}, nil 75 } else { 76 // handle the invalid case like "10.0.0.0@" 77 return AddrCluster{}, fmt.Errorf("empty cluster ID") 78 } 79 } 80 81 clusterID64, err := strconv.ParseUint(clusterIDStr, 10, 32) 82 if err != nil { 83 return AddrCluster{}, err 84 } 85 86 return AddrCluster{addr: addr, clusterID: uint32(clusterID64)}, nil 87 } 88 89 // MustParseAddrCluster calls ParseAddr(s) and panics on error. It is 90 // intended for use in tests with hard-coded strings. 91 func MustParseAddrCluster(s string) AddrCluster { 92 addrCluster, err := ParseAddrCluster(s) 93 if err != nil { 94 panic(err) 95 } 96 return addrCluster 97 } 98 99 // AddrClusterFromIP parses the given net.IP using ip.AddrFromIP and returns 100 // AddrCluster with ClusterID = 0. 101 func AddrClusterFromIP(ip net.IP) (AddrCluster, bool) { 102 addr, ok := ippkg.AddrFromIP(ip) 103 if !ok { 104 return AddrCluster{}, false 105 } 106 return AddrCluster{addr: addr, clusterID: 0}, true 107 } 108 109 func MustAddrClusterFromIP(ip net.IP) AddrCluster { 110 addr, ok := AddrClusterFromIP(ip) 111 if !ok { 112 panic("cannot convert net.IP to AddrCluster") 113 } 114 return addr 115 } 116 117 // AddrClusterFrom creates AddrCluster from netip.Addr and ClusterID 118 func AddrClusterFrom(addr netip.Addr, clusterID uint32) AddrCluster { 119 return AddrCluster{addr: addr, clusterID: clusterID} 120 } 121 122 // Addr returns IP address part of AddrCluster as netip.Addr. This function 123 // exists for keeping backward compatibility between the existing components 124 // which are not aware of the cluster-aware addressing. Calling this function 125 // against the AddrCluster which has non-zero clusterID will lose the ClusterID 126 // information. It should be used with an extra care. 127 func (ac AddrCluster) Addr() netip.Addr { 128 return ac.addr 129 } 130 131 // ClusterID returns ClusterID part of AddrCluster as uint32. We should avoid 132 // using this function as much as possible and treat IP address and ClusterID 133 // together. 134 func (ac AddrCluster) ClusterID() uint32 { 135 return ac.clusterID 136 } 137 138 // Equal returns true when given AddrCluster has a same IP address and ClusterID 139 func (ac0 AddrCluster) Equal(ac1 AddrCluster) bool { 140 return ac0.addr == ac1.addr && ac0.clusterID == ac1.clusterID 141 } 142 143 // Less compares ac0 and ac1 and returns true if ac0 is lesser than ac1 144 func (ac0 AddrCluster) Less(ac1 AddrCluster) bool { 145 // First, compare the IP address part 146 if ret := ac0.addr.Compare(ac1.addr); ret == -1 { 147 return true 148 } else if ret == 1 { 149 return false 150 } else { 151 // If IP address is the same, compare ClusterID 152 return ac0.clusterID < ac1.clusterID 153 } 154 } 155 156 // This is an alias of Equal which only exists for satisfying deepequal-gen 157 func (ac0 *AddrCluster) DeepEqual(ac1 *AddrCluster) bool { 158 return ac0.Equal(*ac1) 159 } 160 161 // DeepCopyInto copies in to out 162 func (in *AddrCluster) DeepCopyInto(out *AddrCluster) { 163 if out == nil { 164 return 165 } 166 out.addr = in.addr 167 out.clusterID = in.clusterID 168 } 169 170 // DeepCopy returns a new copy of AddrCluster 171 func (in *AddrCluster) DeepCopy() *AddrCluster { 172 out := new(AddrCluster) 173 in.DeepCopyInto(out) 174 return out 175 } 176 177 // String returns the string representation of the AddrCluster. If 178 // AddrCluster.clusterID = 0, it returns bare IP address string. Otherwise, it 179 // returns IP string + "@" + ClusterID (e.g. 10.0.0.1@1) 180 func (ac AddrCluster) String() string { 181 if ac.clusterID == 0 { 182 return ac.addr.String() 183 } 184 return ac.addr.String() + "@" + strconv.FormatUint(uint64(ac.clusterID), 10) 185 } 186 187 // Is4 reports whether IP address part of AddrCluster is an IPv4 address. 188 func (ac AddrCluster) Is4() bool { 189 return ac.addr.Is4() 190 } 191 192 // Is6 reports whether IP address part of AddrCluster is an IPv6 address. 193 func (ac AddrCluster) Is6() bool { 194 return ac.addr.Is6() 195 } 196 197 // IsUnspecified reports whether IP address part of the AddrCluster is an 198 // unspecified address, either the IPv4 address "0.0.0.0" or the IPv6 199 // address "::". 200 func (ac AddrCluster) IsUnspecified() bool { 201 return ac.addr.IsUnspecified() 202 } 203 204 // As20 returns the AddrCluster in its 20-byte representation which consists 205 // of 16-byte IP address part from netip.Addr.As16 and 4-byte ClusterID part. 206 func (ac AddrCluster) As20() (ac20 [20]byte) { 207 addr16 := ac.addr.As16() 208 copy(ac20[:16], addr16[:]) 209 ac20[16] = byte(ac.clusterID >> 24) 210 ac20[17] = byte(ac.clusterID >> 16) 211 ac20[18] = byte(ac.clusterID >> 8) 212 ac20[19] = byte(ac.clusterID) 213 return ac20 214 } 215 216 // AsNetIP returns the IP address part of AddCluster as a net.IP type. This 217 // function exists for keeping backward compatibility between the existing 218 // components which are not aware of the cluster-aware addressing. Calling 219 // this function against the AddrCluster which has non-zero clusterID will 220 // lose the ClusterID information. It should be used with an extra care. 221 func (ac AddrCluster) AsNetIP() net.IP { 222 return ac.addr.AsSlice() 223 } 224 225 func (ac AddrCluster) AsPrefixCluster() PrefixCluster { 226 return PrefixClusterFrom(ac.addr, ac.addr.BitLen(), WithClusterID(ac.clusterID)) 227 } 228 229 // PrefixCluster is a type that holds a pair of prefix and ClusterID. 230 // We should use this type as much as possible when we implement 231 // prefix + Cluster addressing. We should avoid managing prefix and 232 // ClusterID separately. Otherwise, it is very hard for code readers 233 // to see where we are using cluster-aware addressing. 234 type PrefixCluster struct { 235 prefix netip.Prefix 236 clusterID uint32 237 } 238 239 // ParsePrefixCluster parses s as an Prefix + ClusterID and returns PrefixCluster. 240 // The string s can be a bare IP prefix string (any prefix format allowed in 241 // netip.ParsePrefix()) or prefix string + @ + ClusterID with decimal. Bare prefix 242 // string is considered as prefix string + @ + ClusterID = 0. 243 func ParsePrefixCluster(s string) (PrefixCluster, error) { 244 atIndex := strings.LastIndex(s, "@") 245 246 var ( 247 prefixStr string 248 clusterIDStr string 249 ) 250 251 if atIndex == -1 { 252 // s may be a bare IP prefix string, still valid 253 prefixStr = s 254 clusterIDStr = "" 255 } else { 256 // s may be a prefix + ClusterID string 257 prefixStr = s[:atIndex] 258 clusterIDStr = s[atIndex+1:] 259 } 260 261 prefix, err := netip.ParsePrefix(prefixStr) 262 if err != nil { 263 return PrefixCluster{}, err 264 } 265 266 if clusterIDStr == "" { 267 if atIndex != len(s)-1 { 268 return PrefixCluster{prefix: prefix, clusterID: 0}, nil 269 } else { 270 // handle the invalid case like "10.0.0.0/24@" 271 return PrefixCluster{}, fmt.Errorf("empty cluster ID") 272 } 273 } 274 275 clusterID64, err := strconv.ParseUint(clusterIDStr, 10, 32) 276 if err != nil { 277 return PrefixCluster{}, err 278 } 279 280 return PrefixCluster{prefix: prefix, clusterID: uint32(clusterID64)}, nil 281 } 282 283 // MustParsePrefixCluster calls ParsePrefixCluster(s) and panics on error. 284 // It is intended for use in tests with hard-coded strings. 285 func MustParsePrefixCluster(s string) PrefixCluster { 286 prefixCluster, err := ParsePrefixCluster(s) 287 if err != nil { 288 panic(err) 289 } 290 return prefixCluster 291 } 292 293 func (pc PrefixCluster) IsSingleIP() bool { 294 return pc.prefix.IsSingleIP() 295 } 296 297 type PrefixClusterOpts func(*PrefixCluster) 298 299 func WithClusterID(id uint32) PrefixClusterOpts { 300 return func(pc *PrefixCluster) { pc.clusterID = id } 301 } 302 303 func PrefixClusterFrom(addr netip.Addr, bits int, opts ...PrefixClusterOpts) PrefixCluster { 304 pc := PrefixCluster{prefix: netip.PrefixFrom(addr, bits)} 305 for _, opt := range opts { 306 opt(&pc) 307 } 308 return pc 309 } 310 311 func PrefixClusterFromCIDR(c *cidr.CIDR, opts ...PrefixClusterOpts) PrefixCluster { 312 if c == nil { 313 return PrefixCluster{} 314 } 315 316 addr, ok := ippkg.AddrFromIP(c.IP) 317 if !ok { 318 return PrefixCluster{} 319 } 320 ones, _ := c.Mask.Size() 321 322 return PrefixClusterFrom(addr, ones, opts...) 323 } 324 325 func (pc0 PrefixCluster) Equal(pc1 PrefixCluster) bool { 326 return pc0.prefix == pc1.prefix && pc0.clusterID == pc1.clusterID 327 } 328 329 func (pc PrefixCluster) IsValid() bool { 330 return pc.prefix.IsValid() 331 } 332 333 func (pc PrefixCluster) AddrCluster() AddrCluster { 334 return AddrClusterFrom(pc.prefix.Addr(), pc.clusterID) 335 } 336 337 func (pc PrefixCluster) ClusterID() uint32 { 338 return pc.clusterID 339 } 340 341 func (pc PrefixCluster) String() string { 342 if pc.clusterID == 0 { 343 return pc.prefix.String() 344 } 345 return pc.prefix.String() + "@" + strconv.FormatUint(uint64(pc.clusterID), 10) 346 } 347 348 // AsPrefix returns the IP prefix part of PrefixCluster as a netip.Prefix type. 349 // This function exists for keeping backward compatibility between the existing 350 // components which are not aware of the cluster-aware addressing. Calling 351 // this function against the PrefixCluster which has non-zero clusterID will 352 // lose the ClusterID information. It should be used with an extra care. 353 func (pc PrefixCluster) AsPrefix() netip.Prefix { 354 return netip.PrefixFrom(pc.prefix.Addr(), pc.prefix.Bits()) 355 } 356 357 // AsIPNet returns the IP prefix part of PrefixCluster as a net.IPNet type. This 358 // function exists for keeping backward compatibility between the existing 359 // components which are not aware of the cluster-aware addressing. Calling 360 // this function against the PrefixCluster which has non-zero clusterID will 361 // lose the ClusterID information. It should be used with an extra care. 362 func (pc PrefixCluster) AsIPNet() net.IPNet { 363 return *netipx.PrefixIPNet(pc.AsPrefix()) 364 } 365 366 // This function is solely exists for annotating IPCache's key string with ClusterID. 367 // IPCache's key string is IP address or Prefix string (10.0.0.1 and 10.0.0.0/32 are 368 // different entry). This function assumes given string is one of those format and 369 // just put @<ClusterID> suffix and there's no format check for performance reason. 370 // User must make sure the input is a valid IP or Prefix string. 371 // 372 // We should eventually remove this function once we finish refactoring IPCache and 373 // stop using string as a key. At that point, we should consider using PrefixCluster 374 // type for IPCache's key. 375 func AnnotateIPCacheKeyWithClusterID(key string, clusterID uint32) string { 376 return key + "@" + strconv.FormatUint(uint64(clusterID), 10) 377 }