github.com/cilium/cilium@v1.16.2/pkg/ipam/types/types.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/netip" 9 10 "github.com/cilium/cilium/pkg/cidr" 11 "github.com/cilium/cilium/pkg/lock" 12 ) 13 14 // Limits specifies the IPAM relevant instance limits 15 type Limits struct { 16 // Adapters specifies the maximum number of interfaces that can be 17 // attached to the instance 18 Adapters int 19 20 // IPv4 is the maximum number of IPv4 addresses per adapter/interface 21 IPv4 int 22 23 // IPv6 is the maximum number of IPv6 addresses per adapter/interface 24 IPv6 int 25 26 // HypervisorType tracks the instance's hypervisor type if available. Used to determine if features like prefix 27 // delegation are supported on an instance. Bare metal instances would have empty string. 28 HypervisorType string 29 } 30 31 // AllocationIP is an IP which is available for allocation, or already 32 // has been allocated 33 type AllocationIP struct { 34 // Owner is the owner of the IP. This field is set if the IP has been 35 // allocated. It will be set to the pod name or another identifier 36 // representing the usage of the IP 37 // 38 // The owner field is left blank for an entry in Spec.IPAM.Pool and 39 // filled out as the IP is used and also added to Status.IPAM.Used. 40 // 41 // +optional 42 Owner string `json:"owner,omitempty"` 43 44 // Resource is set for both available and allocated IPs, it represents 45 // what resource the IP is associated with, e.g. in combination with 46 // AWS ENI, this will refer to the ID of the ENI 47 // 48 // +optional 49 Resource string `json:"resource,omitempty"` 50 } 51 52 // AllocationMap is a map of allocated IPs indexed by IP 53 type AllocationMap map[string]AllocationIP 54 55 // IPAMPodCIDR is a pod CIDR 56 // 57 // +kubebuilder:validation:Format=cidr 58 type IPAMPodCIDR string 59 60 func (c *IPAMPodCIDR) ToPrefix() (*netip.Prefix, error) { 61 if c == nil { 62 return nil, fmt.Errorf("nil ipam cidr") 63 } 64 65 prefix, err := netip.ParsePrefix(string(*c)) 66 if err != nil { 67 return nil, fmt.Errorf("failed to parse ipam cidr %v: %w", c, err) 68 } 69 70 return &prefix, nil 71 } 72 73 // IPAMPoolAllocation describes an allocation of an IPAM pool from the operator to the 74 // node. It contains the assigned PodCIDRs allocated from this pool 75 type IPAMPoolAllocation struct { 76 // Pool is the name of the IPAM pool backing this allocation 77 // 78 // +kubebuilder:validation:MinLength=1 79 Pool string `json:"pool"` 80 81 // CIDRs contains a list of pod CIDRs currently allocated from this pool 82 // 83 // +optional 84 CIDRs []IPAMPodCIDR `json:"cidrs,omitempty"` 85 } 86 87 type IPAMPoolRequest struct { 88 // Pool is the name of the IPAM pool backing this request 89 // 90 // +kubebuilder:validation:MinLength=1 91 Pool string `json:"pool"` 92 93 // Needed indicates how many IPs out of the above Pool this node requests 94 // from the operator. The operator runs a reconciliation loop to ensure each 95 // node always has enough PodCIDRs allocated in each pool to fulfill the 96 // requested number of IPs here. 97 // 98 // +optional 99 Needed IPAMPoolDemand `json:"needed,omitempty"` 100 } 101 102 type IPAMPoolSpec struct { 103 // Requested contains a list of IPAM pool requests, i.e. indicates how many 104 // addresses this node requests out of each pool listed here. This field 105 // is owned and written to by cilium-agent and read by the operator. 106 // 107 // +optional 108 Requested []IPAMPoolRequest `json:"requested,omitempty"` 109 110 // Allocated contains the list of pooled CIDR assigned to this node. The 111 // operator will add new pod CIDRs to this field, whereas the agent will 112 // remove CIDRs it has released. 113 // 114 // +optional 115 Allocated []IPAMPoolAllocation `json:"allocated,omitempty"` 116 } 117 118 // IPAMSpec is the IPAM specification of the node 119 // 120 // This structure is embedded into v2.CiliumNode 121 type IPAMSpec struct { 122 // Pool is the list of IPv4 addresses available to the node for allocation. 123 // When an IPv4 address is used, it will remain on this list but will be added to 124 // Status.IPAM.Used 125 // 126 // +optional 127 Pool AllocationMap `json:"pool,omitempty"` 128 129 // IPv6Pool is the list of IPv6 addresses available to the node for allocation. 130 // When an IPv6 address is used, it will remain on this list but will be added to 131 // Status.IPAM.IPv6Used 132 // 133 // +optional 134 IPv6Pool AllocationMap `json:"ipv6-pool,omitempty"` 135 136 // Pools contains the list of assigned IPAM pools for this node. 137 // 138 // +optional 139 Pools IPAMPoolSpec `json:"pools,omitempty"` 140 141 // PodCIDRs is the list of CIDRs available to the node for allocation. 142 // When an IP is used, the IP will be added to Status.IPAM.Used 143 // 144 // +optional 145 PodCIDRs []string `json:"podCIDRs,omitempty"` 146 147 // MinAllocate is the minimum number of IPs that must be allocated when 148 // the node is first bootstrapped. It defines the minimum base socket 149 // of addresses that must be available. After reaching this watermark, 150 // the PreAllocate and MaxAboveWatermark logic takes over to continue 151 // allocating IPs. 152 // 153 // +kubebuilder:validation:Minimum=0 154 MinAllocate int `json:"min-allocate,omitempty"` 155 156 // MaxAllocate is the maximum number of IPs that can be allocated to the 157 // node. When the current amount of allocated IPs will approach this value, 158 // the considered value for PreAllocate will decrease down to 0 in order to 159 // not attempt to allocate more addresses than defined. 160 // 161 // +kubebuilder:validation:Minimum=0 162 MaxAllocate int `json:"max-allocate,omitempty"` 163 164 // PreAllocate defines the number of IP addresses that must be 165 // available for allocation in the IPAMspec. It defines the buffer of 166 // addresses available immediately without requiring cilium-operator to 167 // get involved. 168 // 169 // +kubebuilder:validation:Minimum=0 170 PreAllocate int `json:"pre-allocate,omitempty"` 171 172 // MaxAboveWatermark is the maximum number of addresses to allocate 173 // beyond the addresses needed to reach the PreAllocate watermark. 174 // Going above the watermark can help reduce the number of API calls to 175 // allocate IPs, e.g. when a new ENI is allocated, as many secondary 176 // IPs as possible are allocated. Limiting the amount can help reduce 177 // waste of IPs. 178 // 179 // +kubebuilder:validation:Minimum=0 180 MaxAboveWatermark int `json:"max-above-watermark,omitempty"` 181 } 182 183 // IPReleaseStatus defines the valid states in IP release handshake 184 // 185 // +kubebuilder:validation:Enum=marked-for-release;ready-for-release;do-not-release;released 186 type IPReleaseStatus string 187 188 // IPAMStatus is the IPAM status of a node 189 // 190 // This structure is embedded into v2.CiliumNode 191 type IPAMStatus struct { 192 // Used lists all IPv4 addresses out of Spec.IPAM.Pool which have been allocated 193 // and are in use. 194 // 195 // +optional 196 Used AllocationMap `json:"used,omitempty"` 197 198 // IPv6Used lists all IPv6 addresses out of Spec.IPAM.IPv6Pool which have been 199 // allocated and are in use. 200 // 201 // +optional 202 IPv6Used AllocationMap `json:"ipv6-used,omitempty"` 203 204 // PodCIDRs lists the status of each pod CIDR allocated to this node. 205 // 206 // +optional 207 PodCIDRs PodCIDRMap `json:"pod-cidrs,omitempty"` 208 209 // Operator is the Operator status of the node 210 // 211 // +optional 212 OperatorStatus OperatorStatus `json:"operator-status,omitempty"` 213 214 // ReleaseIPs tracks the state for every IPv4 address considered for release. 215 // The value can be one of the following strings: 216 // * marked-for-release : Set by operator as possible candidate for IP 217 // * ready-for-release : Acknowledged as safe to release by agent 218 // * do-not-release : IP already in use / not owned by the node. Set by agent 219 // * released : IP successfully released. Set by operator 220 // 221 // +optional 222 ReleaseIPs map[string]IPReleaseStatus `json:"release-ips,omitempty"` 223 224 // ReleaseIPv6s tracks the state for every IPv6 address considered for release. 225 // The value can be one of the following strings: 226 // * marked-for-release : Set by operator as possible candidate for IP 227 // * ready-for-release : Acknowledged as safe to release by agent 228 // * do-not-release : IP already in use / not owned by the node. Set by agent 229 // * released : IP successfully released. Set by operator 230 // 231 // +optional 232 ReleaseIPv6s map[string]IPReleaseStatus `json:"release-ipv6s,omitempty"` 233 } 234 235 // IPAMPoolRequest is a request from the agent to the operator, indicating how 236 // may IPs it requires from a given pool 237 type IPAMPoolDemand struct { 238 // IPv4Addrs contains the number of requested IPv4 addresses out of a given 239 // pool 240 // 241 // +optional 242 IPv4Addrs int `json:"ipv4-addrs,omitempty"` 243 244 // IPv6Addrs contains the number of requested IPv6 addresses out of a given 245 // pool 246 // 247 // +optional 248 IPv6Addrs int `json:"ipv6-addrs,omitempty"` 249 } 250 251 type PodCIDRMap map[string]PodCIDRMapEntry 252 253 // +kubebuilder:validation:Enum=released;depleted;in-use 254 type PodCIDRStatus string 255 256 const ( 257 PodCIDRStatusReleased PodCIDRStatus = "released" 258 PodCIDRStatusDepleted PodCIDRStatus = "depleted" 259 PodCIDRStatusInUse PodCIDRStatus = "in-use" 260 ) 261 262 type PodCIDRMapEntry struct { 263 // Status describes the status of a pod CIDR 264 // 265 // +optional 266 Status PodCIDRStatus `json:"status,omitempty"` 267 } 268 269 // OperatorStatus is the status used by cilium-operator to report 270 // errors in case the allocation CIDR failed. 271 type OperatorStatus struct { 272 // Error is the error message set by cilium-operator. 273 // 274 // +optional 275 Error string `json:"error,omitempty"` 276 } 277 278 // Tags implements generic key value tags 279 type Tags map[string]string 280 281 // Match returns true if the required tags are all found 282 func (t Tags) Match(required Tags) bool { 283 for k, neededvalue := range required { 284 haveValue, ok := t[k] 285 if !ok || (ok && neededvalue != haveValue) { 286 return false 287 } 288 } 289 return true 290 } 291 292 // Subnet is a representation of a subnet 293 type Subnet struct { 294 // ID is the subnet ID 295 ID string 296 297 // Name is the subnet name 298 Name string 299 300 // CIDR is the IPv4 CIDR associated with the subnet 301 CIDR *cidr.CIDR 302 303 // IPv6CIDR is the IPv6 CIDR associated with the subnet 304 IPv6CIDR *cidr.CIDR 305 306 // AvailabilityZone is the availability zone of the subnet 307 AvailabilityZone string 308 309 // VirtualNetworkID is the virtual network the subnet is in 310 VirtualNetworkID string 311 312 // AvailableAddresses is the number of IPv4 addresses available for 313 // allocation 314 AvailableAddresses int 315 316 // AvailableIPv6Addresses is the number of IPv6 addresses available for 317 // allocation 318 AvailableIPv6Addresses int 319 320 // Tags is the tags of the subnet 321 Tags Tags 322 } 323 324 // SubnetMap indexes subnets by subnet ID 325 type SubnetMap map[string]*Subnet 326 327 // FirstSubnetWithAvailableAddresses returns the first pool ID in the list of 328 // subnets with available addresses. If any of the preferred pool IDs have 329 // available addresses, the first pool ID with available addresses is returned. 330 func (m SubnetMap) FirstSubnetWithAvailableAddresses(preferredPoolIDs []PoolID) (PoolID, int) { 331 for _, p := range preferredPoolIDs { 332 if s := m[string(p)]; s != nil { 333 if s.AvailableAddresses > 0 { 334 return p, s.AvailableAddresses 335 } 336 } 337 } 338 339 for poolID, s := range m { 340 if s.AvailableAddresses > 0 { 341 return PoolID(poolID), s.AvailableAddresses 342 } 343 } 344 345 return PoolNotExists, 0 346 } 347 348 // VirtualNetwork is the representation of a virtual network 349 type VirtualNetwork struct { 350 // ID is the ID of the virtual network 351 ID string 352 353 // PrimaryCIDR is the primary IPv4 CIDR 354 PrimaryCIDR string 355 356 // CIDRs is the list of secondary IPv4 CIDR ranges associated with the VPC 357 CIDRs []string 358 359 // IPv6CIDRs is the list of IPv6 CIDR ranges associated with the VPC 360 IPv6CIDRs []string 361 } 362 363 // VirtualNetworkMap indexes virtual networks by their ID 364 type VirtualNetworkMap map[string]*VirtualNetwork 365 366 // PoolNotExists indicate that no such pool ID exists 367 const PoolNotExists = PoolID("") 368 369 // PoolUnspec indicates that the pool ID is unspecified 370 const PoolUnspec = PoolNotExists 371 372 // PoolID is the type used to identify an IPAM pool 373 type PoolID string 374 375 // PoolQuota defines the limits of an IPAM pool 376 type PoolQuota struct { 377 // AvailabilityZone is the availability zone in which the IPAM pool resides in 378 AvailabilityZone string 379 380 // AvailableIPs is the number of available IPs in the pool 381 AvailableIPs int 382 383 // AvailableIPv6s is the number of available IPv6 addresses in the pool 384 AvailableIPv6s int 385 } 386 387 // PoolQuotaMap is a map of pool quotas indexes by pool identifier 388 type PoolQuotaMap map[PoolID]PoolQuota 389 390 // Interface is the implementation of a IPAM relevant network interface 391 // +k8s:deepcopy-gen=false 392 // +deepequal-gen=false 393 type Interface interface { 394 // InterfaceID must return the identifier of the interface 395 InterfaceID() string 396 397 // ForeachAddress must iterate over all addresses of the interface and 398 // call fn for each address 399 ForeachAddress(instanceID string, fn AddressIterator) error 400 401 // DeepCopyInterface returns a deep copy of the underlying interface type. 402 DeepCopyInterface() Interface 403 } 404 405 // InterfaceRevision is the configurationr revision of a network interface. It 406 // consists of a revision hash representing the current configuration version 407 // and the resource itself. 408 // 409 // +k8s:deepcopy-gen=false 410 // +deepequal-gen=false 411 type InterfaceRevision struct { 412 // Resource is the interface resource 413 Resource Interface 414 415 // Fingerprint is the fingerprint reprsenting the network interface 416 // configuration. It is typically implemented as the result of a hash 417 // function calculated off the resource. This field is optional, not 418 // all IPAM backends make use of fingerprints. 419 Fingerprint string 420 } 421 422 // Instance is the representation of an instance, typically a VM, subject to 423 // per-node IPAM logic 424 // 425 // +k8s:deepcopy-gen=false 426 // +deepequal-gen=false 427 type Instance struct { 428 // interfaces is a map of all interfaces attached to the instance 429 // indexed by the interface ID 430 Interfaces map[string]InterfaceRevision 431 } 432 433 // InstanceMap is the list of all instances indexed by instance ID 434 // 435 // +k8s:deepcopy-gen=false 436 // +deepequal-gen=false 437 type InstanceMap struct { 438 mutex lock.RWMutex 439 data map[string]*Instance 440 } 441 442 // NewInstanceMap returns a new InstanceMap 443 func NewInstanceMap() *InstanceMap { 444 return &InstanceMap{data: map[string]*Instance{}} 445 } 446 447 // UpdateInstance updates the interfaces map for a particular instance. 448 func (m *InstanceMap) UpdateInstance(instanceID string, instance *Instance) { 449 m.mutex.Lock() 450 m.data[instanceID] = instance 451 m.mutex.Unlock() 452 } 453 454 // Update updates the definition of an interface for a particular instance. If 455 // the interface is already known, the definition is updated, otherwise the 456 // interface is added to the instance. 457 func (m *InstanceMap) Update(instanceID string, iface InterfaceRevision) { 458 m.mutex.Lock() 459 m.updateLocked(instanceID, iface) 460 m.mutex.Unlock() 461 } 462 463 func (m *InstanceMap) updateLocked(instanceID string, iface InterfaceRevision) { 464 if iface.Resource == nil { 465 return 466 } 467 468 i, ok := m.data[instanceID] 469 if !ok { 470 i = &Instance{} 471 m.data[instanceID] = i 472 } 473 474 if i.Interfaces == nil { 475 i.Interfaces = map[string]InterfaceRevision{} 476 } 477 478 i.Interfaces[iface.Resource.InterfaceID()] = iface 479 } 480 481 type Address interface{} 482 483 // AddressIterator is the function called by the ForeachAddress iterator 484 type AddressIterator func(instanceID, interfaceID, ip, poolID string, address Address) error 485 486 func foreachAddress(instanceID string, instance *Instance, fn AddressIterator) error { 487 for _, rev := range instance.Interfaces { 488 if err := rev.Resource.ForeachAddress(instanceID, fn); err != nil { 489 return err 490 } 491 } 492 493 return nil 494 } 495 496 // ForeachAddress calls fn for each address on each interface attached to each 497 // instance. If an instanceID is specified, the only the interfaces and 498 // addresses of the specified instance are considered. 499 // 500 // The InstanceMap is read-locked throughout the iteration process, i.e., no 501 // updates will occur. However, the address object given to the AddressIterator 502 // will point to live data and must be deep copied if used outside of the 503 // context of the iterator function. 504 func (m *InstanceMap) ForeachAddress(instanceID string, fn AddressIterator) error { 505 m.mutex.RLock() 506 defer m.mutex.RUnlock() 507 508 if instanceID != "" { 509 if instance := m.data[instanceID]; instance != nil { 510 return foreachAddress(instanceID, instance, fn) 511 } 512 return fmt.Errorf("instance does not exist: %q", instanceID) 513 } 514 515 for instanceID, instance := range m.data { 516 if err := foreachAddress(instanceID, instance, fn); err != nil { 517 return err 518 } 519 } 520 521 return nil 522 } 523 524 // InterfaceIterator is the function called by the ForeachInterface iterator 525 type InterfaceIterator func(instanceID, interfaceID string, iface InterfaceRevision) error 526 527 func foreachInterface(instanceID string, instance *Instance, fn InterfaceIterator) error { 528 for _, rev := range instance.Interfaces { 529 if err := fn(instanceID, rev.Resource.InterfaceID(), rev); err != nil { 530 return err 531 } 532 } 533 534 return nil 535 } 536 537 // ForeachInterface calls fn for each interface on each interface attached to 538 // each instance. If an instanceID is specified, the only the interfaces and 539 // addresses of the specified instance are considered. 540 // 541 // The InstanceMap is read-locked throughout the iteration process, i.e., no 542 // updates will occur. However, the address object given to the InterfaceIterator 543 // will point to live data and must be deep copied if used outside of the 544 // context of the iterator function. 545 func (m *InstanceMap) ForeachInterface(instanceID string, fn InterfaceIterator) error { 546 m.mutex.RLock() 547 defer m.mutex.RUnlock() 548 549 if instanceID != "" { 550 if instance := m.data[instanceID]; instance != nil { 551 return foreachInterface(instanceID, instance, fn) 552 } 553 return fmt.Errorf("instance does not exist: %q", instanceID) 554 } 555 for instanceID, instance := range m.data { 556 if err := foreachInterface(instanceID, instance, fn); err != nil { 557 return err 558 } 559 } 560 561 return nil 562 } 563 564 // GetInterface returns returns a particular interface of an instance. The 565 // boolean indicates whether the interface was found or not. 566 func (m *InstanceMap) GetInterface(instanceID, interfaceID string) (InterfaceRevision, bool) { 567 m.mutex.RLock() 568 defer m.mutex.RUnlock() 569 570 if instance := m.data[instanceID]; instance != nil { 571 if rev, ok := instance.Interfaces[interfaceID]; ok { 572 return rev, true 573 } 574 } 575 576 return InterfaceRevision{}, false 577 } 578 579 // DeepCopy returns a deep copy 580 func (m *InstanceMap) DeepCopy() *InstanceMap { 581 c := NewInstanceMap() 582 m.ForeachInterface("", func(instanceID, interfaceID string, rev InterfaceRevision) error { 583 // c is not exposed yet, we can access it without locking it 584 rev.Resource = rev.Resource.DeepCopyInterface() 585 c.updateLocked(instanceID, rev) 586 return nil 587 }) 588 return c 589 } 590 591 // NumInstances returns the number of instances in the instance map 592 func (m *InstanceMap) NumInstances() (size int) { 593 m.mutex.RLock() 594 size = len(m.data) 595 m.mutex.RUnlock() 596 return 597 } 598 599 // Exists returns whether the instance ID is in the instanceMap 600 func (m *InstanceMap) Exists(instanceID string) (exists bool) { 601 m.mutex.RLock() 602 defer m.mutex.RUnlock() 603 if instance := m.data[instanceID]; instance != nil { 604 return true 605 } 606 return false 607 } 608 609 // Delete instance from m.data 610 func (m *InstanceMap) Delete(instanceID string) { 611 m.mutex.Lock() 612 defer m.mutex.Unlock() 613 delete(m.data, instanceID) 614 }