Revision 470b3207efe07c66126db88e682aced94df450f5 authored by Nicolas Busseneau on 26 April 2023, 17:13:46 UTC, committed by Nicolas Busseneau on 26 April 2023, 17:13:46 UTC
Signed-off-by: Nicolas Busseneau <nicolas@isovalent.com>
1 parent b016532
Raw File
ipam.go
// 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()
}
back to top