// SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium package ipam import ( "net" "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/cidr" "github.com/cilium/cilium/pkg/datapath/types" ipamOption "github.com/cilium/cilium/pkg/ipam/option" "github.com/cilium/cilium/pkg/k8s/watchers/subscriber" "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" ) var ( log = logging.DefaultLogger.WithField(logfields.LogSubsys, "ipam") ) type ErrAllocation error // Family is the type describing all address families support by the IP // allocation manager type Family string const ( IPv6 Family = "ipv6" IPv4 Family = "ipv4" ) // DeriveFamily derives the address family of an IP func DeriveFamily(ip net.IP) Family { if ip.To4() == nil { return IPv6 } return IPv4 } // Configuration is the configuration passed into the IPAM subsystem type Configuration interface { // IPv4Enabled must return true when IPv4 is enabled IPv4Enabled() bool // IPv6 must return true when IPv6 is enabled IPv6Enabled() bool // IPAMMode returns the IPAM mode IPAMMode() string // HealthCheckingEnabled must return true when health-checking is // enabled HealthCheckingEnabled() bool // UnreachableRoutesEnabled returns true when unreachable-routes is // enabled UnreachableRoutesEnabled() bool // SetIPv4NativeRoutingCIDR is called by the IPAM module to announce // the native IPv4 routing CIDR if it exists SetIPv4NativeRoutingCIDR(cidr *cidr.CIDR) // IPv4NativeRoutingCIDR is called by the IPAM module retrieve // the native IPv4 routing CIDR if it exists GetIPv4NativeRoutingCIDR() *cidr.CIDR } // Owner is the interface the owner of an IPAM allocator has to implement type Owner interface { // UpdateCiliumNodeResource is called to create/update the CiliumNode // resource. The function must block until the custom resource has been // created. UpdateCiliumNodeResource() // LocalAllocCIDRsUpdated informs the agent that the local allocation CIDRs have // changed. LocalAllocCIDRsUpdated(ipv4AllocCIDRs, ipv6AllocCIDRs []*cidr.CIDR) } // K8sEventRegister is used to register and handle events as they are processed // by K8s controllers. type K8sEventRegister interface { // K8sEventReceived is called to do metrics accounting for received // Kubernetes events, as well as calculating timeouts for k8s watcher // cache sync. K8sEventReceived(apiGroupResourceName string, scope string, action string, valid, equal bool) // K8sEventProcessed is called to do metrics accounting for each processed // Kubernetes event. K8sEventProcessed(scope string, action string, status bool) // RegisterCiliumNodeSubscriber allows registration of subscriber.CiliumNode // implementations. Events for all CiliumNode events (not just the local one) // will be sent to the subscriber. RegisterCiliumNodeSubscriber(s subscriber.CiliumNode) } type MtuConfiguration interface { GetDeviceMTU() int } // NewIPAM returns a new IP address manager func NewIPAM(nodeAddressing types.NodeAddressing, c Configuration, owner Owner, k8sEventReg K8sEventRegister, mtuConfig MtuConfiguration) *IPAM { ipam := &IPAM{ nodeAddressing: nodeAddressing, config: c, owner: map[string]string{}, expirationTimers: map[string]string{}, blacklist: IPBlacklist{ ips: map[string]string{}, }, } switch c.IPAMMode() { case ipamOption.IPAMKubernetes, ipamOption.IPAMClusterPool: log.WithFields(logrus.Fields{ logfields.V4Prefix: nodeAddressing.IPv4().AllocationCIDR(), logfields.V6Prefix: nodeAddressing.IPv6().AllocationCIDR(), }).Infof("Initializing %s IPAM", c.IPAMMode()) if c.IPv6Enabled() { ipam.IPv6Allocator = newHostScopeAllocator(nodeAddressing.IPv6().AllocationCIDR().IPNet) } if c.IPv4Enabled() { ipam.IPv4Allocator = newHostScopeAllocator(nodeAddressing.IPv4().AllocationCIDR().IPNet) } case ipamOption.IPAMClusterPoolV2: log.Info("Initializing ClusterPool v2 IPAM") if c.IPv6Enabled() { ipam.IPv6Allocator = newClusterPoolAllocator(IPv6, c, owner, k8sEventReg) } if c.IPv4Enabled() { ipam.IPv4Allocator = newClusterPoolAllocator(IPv4, c, owner, k8sEventReg) } case ipamOption.IPAMCRD, ipamOption.IPAMENI, ipamOption.IPAMAzure, ipamOption.IPAMAlibabaCloud: log.Info("Initializing CRD-based IPAM") if c.IPv6Enabled() { ipam.IPv6Allocator = newCRDAllocator(IPv6, c, owner, k8sEventReg, mtuConfig) } if c.IPv4Enabled() { ipam.IPv4Allocator = newCRDAllocator(IPv4, c, owner, k8sEventReg, mtuConfig) } case ipamOption.IPAMDelegatedPlugin: log.Info("Initializing no-op IPAM since we're using a CNI delegated plugin") if c.IPv6Enabled() { ipam.IPv6Allocator = &noOpAllocator{} } if c.IPv4Enabled() { ipam.IPv4Allocator = &noOpAllocator{} } default: log.Fatalf("Unknown IPAM backend %s", c.IPAMMode()) } return ipam } func nextIP(ip net.IP) { for j := len(ip) - 1; j >= 0; j-- { ip[j]++ if ip[j] > 0 { break } } } // BlacklistIP ensures that a certain IP is never allocated. It is preferred to // use BlacklistIP() instead of allocating the IP as the allocation block can // change and suddenly cover the IP to be blacklisted. func (ipam *IPAM) BlacklistIP(ip net.IP, owner string) { ipam.allocatorMutex.Lock() ipam.blacklist.ips[ip.String()] = owner ipam.allocatorMutex.Unlock() }