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  }