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  }