github.com/cilium/cilium@v1.16.2/pkg/ipam/ipam.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ipam 5 6 import ( 7 "net" 8 9 "github.com/sirupsen/logrus" 10 11 agentK8s "github.com/cilium/cilium/daemon/k8s" 12 "github.com/cilium/cilium/pkg/datapath/types" 13 ipamOption "github.com/cilium/cilium/pkg/ipam/option" 14 "github.com/cilium/cilium/pkg/k8s/client" 15 "github.com/cilium/cilium/pkg/logging" 16 "github.com/cilium/cilium/pkg/logging/logfields" 17 "github.com/cilium/cilium/pkg/node" 18 "github.com/cilium/cilium/pkg/option" 19 ) 20 21 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "ipam") 22 23 // Family is the type describing all address families support by the IP 24 // allocation manager 25 type Family string 26 27 const ( 28 IPv6 Family = "ipv6" 29 IPv4 Family = "ipv4" 30 ) 31 32 // DeriveFamily derives the address family of an IP 33 func DeriveFamily(ip net.IP) Family { 34 if ip.To4() == nil { 35 return IPv6 36 } 37 return IPv4 38 } 39 40 // Owner is the interface the owner of an IPAM allocator has to implement 41 type Owner interface { 42 // UpdateCiliumNodeResource is called to create/update the CiliumNode 43 // resource. The function must block until the custom resource has been 44 // created. 45 UpdateCiliumNodeResource() 46 } 47 48 // K8sEventRegister is used to register and handle events as they are processed 49 // by K8s controllers. 50 type K8sEventRegister interface { 51 // K8sEventReceived is called to do metrics accounting for received 52 // Kubernetes events, as well as calculating timeouts for k8s watcher 53 // cache sync. 54 K8sEventReceived(apiGroupResourceName string, scope string, action string, valid, equal bool) 55 56 // K8sEventProcessed is called to do metrics accounting for each processed 57 // Kubernetes event. 58 K8sEventProcessed(scope string, action string, status bool) 59 } 60 61 type MtuConfiguration interface { 62 GetDeviceMTU() int 63 } 64 65 type Metadata interface { 66 GetIPPoolForPod(owner string, family Family) (pool string, err error) 67 } 68 69 // NewIPAM returns a new IP address manager 70 func NewIPAM(nodeAddressing types.NodeAddressing, c *option.DaemonConfig, nodeDiscovery Owner, localNodeStore *node.LocalNodeStore, k8sEventReg K8sEventRegister, node agentK8s.LocalCiliumNodeResource, mtuConfig MtuConfiguration, clientset client.Clientset, metadata Metadata) *IPAM { 71 return &IPAM{ 72 nodeAddressing: nodeAddressing, 73 config: c, 74 owner: map[Pool]map[string]string{}, 75 expirationTimers: map[timerKey]expirationTimer{}, 76 excludedIPs: map[string]string{}, 77 78 k8sEventReg: k8sEventReg, 79 localNodeStore: localNodeStore, 80 nodeResource: node, 81 mtuConfig: mtuConfig, 82 clientset: clientset, 83 nodeDiscovery: nodeDiscovery, 84 metadata: metadata, 85 } 86 } 87 88 // ConfigureAllocator initializes the IPAM allocator according to the configuration. 89 // As a precondition, the NodeAddressing must be fully initialized - therefore the method 90 // must be called after Daemon.WaitForNodeInformation. 91 func (ipam *IPAM) ConfigureAllocator() { 92 switch ipam.config.IPAMMode() { 93 case ipamOption.IPAMKubernetes, ipamOption.IPAMClusterPool: 94 log.WithFields(logrus.Fields{ 95 logfields.V4Prefix: ipam.nodeAddressing.IPv4().AllocationCIDR(), 96 logfields.V6Prefix: ipam.nodeAddressing.IPv6().AllocationCIDR(), 97 }).Infof("Initializing %s IPAM", ipam.config.IPAMMode()) 98 99 if ipam.config.IPv6Enabled() { 100 ipam.IPv6Allocator = newHostScopeAllocator(ipam.nodeAddressing.IPv6().AllocationCIDR().IPNet) 101 } 102 103 if ipam.config.IPv4Enabled() { 104 ipam.IPv4Allocator = newHostScopeAllocator(ipam.nodeAddressing.IPv4().AllocationCIDR().IPNet) 105 } 106 case ipamOption.IPAMMultiPool: 107 log.Info("Initializing MultiPool IPAM") 108 manager := newMultiPoolManager(ipam.config, ipam.nodeResource, ipam.nodeDiscovery, ipam.clientset.CiliumV2().CiliumNodes()) 109 110 if ipam.config.IPv6Enabled() { 111 ipam.IPv6Allocator = manager.Allocator(IPv6) 112 } 113 if ipam.config.IPv4Enabled() { 114 ipam.IPv4Allocator = manager.Allocator(IPv4) 115 } 116 case ipamOption.IPAMCRD, ipamOption.IPAMENI, ipamOption.IPAMAzure, ipamOption.IPAMAlibabaCloud: 117 log.Info("Initializing CRD-based IPAM") 118 if ipam.config.IPv6Enabled() { 119 ipam.IPv6Allocator = newCRDAllocator(IPv6, ipam.config, ipam.nodeDiscovery, ipam.localNodeStore, ipam.clientset, ipam.k8sEventReg, ipam.mtuConfig) 120 } 121 122 if ipam.config.IPv4Enabled() { 123 ipam.IPv4Allocator = newCRDAllocator(IPv4, ipam.config, ipam.nodeDiscovery, ipam.localNodeStore, ipam.clientset, ipam.k8sEventReg, ipam.mtuConfig) 124 } 125 case ipamOption.IPAMDelegatedPlugin: 126 log.Info("Initializing no-op IPAM since we're using a CNI delegated plugin") 127 if ipam.config.IPv6Enabled() { 128 ipam.IPv6Allocator = &noOpAllocator{} 129 } 130 if ipam.config.IPv4Enabled() { 131 ipam.IPv4Allocator = &noOpAllocator{} 132 } 133 default: 134 log.Fatalf("Unknown IPAM backend %s", ipam.config.IPAMMode()) 135 } 136 } 137 138 // getIPOwner returns the owner for an IP in a particular pool or the empty 139 // string in case the pool or IP is not registered. 140 func (ipam *IPAM) getIPOwner(ip string, pool Pool) string { 141 if p, ok := ipam.owner[pool]; ok { 142 return p[ip] 143 } 144 return "" 145 } 146 147 // registerIPOwner registers a new owner for an IP in a particular pool. 148 func (ipam *IPAM) registerIPOwner(ip net.IP, owner string, pool Pool) { 149 if _, ok := ipam.owner[pool]; !ok { 150 ipam.owner[pool] = make(map[string]string) 151 } 152 ipam.owner[pool][ip.String()] = owner 153 } 154 155 // releaseIPOwner releases ip from pool and returns the previous owner. 156 func (ipam *IPAM) releaseIPOwner(ip net.IP, pool Pool) string { 157 var owner string 158 if m, ok := ipam.owner[pool]; ok { 159 ipStr := ip.String() 160 owner = m[ipStr] 161 delete(m, ipStr) 162 if len(m) == 0 { 163 delete(ipam.owner, pool) 164 } 165 } 166 return owner 167 } 168 169 // ExcludeIP ensures that a certain IP is never allocated. It is preferred to 170 // use this method instead of allocating the IP as the allocation block can 171 // change and suddenly cover the IP to be excluded. 172 func (ipam *IPAM) ExcludeIP(ip net.IP, owner string, pool Pool) { 173 ipam.allocatorMutex.Lock() 174 ipam.excludedIPs[pool.String()+":"+ip.String()] = owner 175 ipam.allocatorMutex.Unlock() 176 } 177 178 // isIPExcluded is used to check if a particular IP is excluded from being allocated. 179 func (ipam *IPAM) isIPExcluded(ip net.IP, pool Pool) (string, bool) { 180 owner, ok := ipam.excludedIPs[pool.String()+":"+ip.String()] 181 return owner, ok 182 } 183 184 // PoolOrDefault returns the default pool if no pool is specified. 185 func PoolOrDefault(pool string) Pool { 186 if pool == "" { 187 return PoolDefault() 188 } 189 return Pool(pool) 190 } 191 192 // PoolDefault returns the default pool 193 func PoolDefault() Pool { 194 return Pool(option.Config.IPAMDefaultIPPool) 195 }