github.com/cilium/cilium@v1.16.2/pkg/ipam/allocator/multipool/pool.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package multipool
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net/netip"
    10  
    11  	"go4.org/netipx"
    12  
    13  	"github.com/cilium/cilium/pkg/ipam"
    14  	"github.com/cilium/cilium/pkg/ipam/allocator/clusterpool/cidralloc"
    15  )
    16  
    17  var (
    18  	errPoolEmpty = errors.New("pool empty")
    19  )
    20  
    21  func allocFirstFreeCIDR(allocators []cidralloc.CIDRAllocator) (netip.Prefix, error) {
    22  	for _, alloc := range allocators {
    23  		if alloc.IsFull() {
    24  			continue
    25  		}
    26  
    27  		ipnet, err := alloc.AllocateNext()
    28  		if err != nil {
    29  			return netip.Prefix{}, err
    30  		}
    31  
    32  		prefix, ok := netipx.FromStdIPNet(ipnet)
    33  		if !ok {
    34  			return netip.Prefix{}, fmt.Errorf("invalid cidr %s allocated", ipnet)
    35  		}
    36  		return prefix, nil
    37  	}
    38  
    39  	return netip.Prefix{}, errPoolEmpty
    40  }
    41  
    42  func occupyCIDR(allocators []cidralloc.CIDRAllocator, cidr netip.Prefix) error {
    43  	ipnet := netipx.PrefixIPNet(cidr)
    44  	for _, alloc := range allocators {
    45  		if !alloc.InRange(ipnet) {
    46  			continue
    47  		}
    48  		if alloc.IsFull() {
    49  			return errPoolEmpty
    50  		}
    51  		allocated, err := alloc.IsAllocated(ipnet)
    52  		if err != nil {
    53  			return err
    54  		}
    55  		if allocated {
    56  			return fmt.Errorf("cidr %s has already been allocated", cidr)
    57  		}
    58  
    59  		return alloc.Occupy(ipnet)
    60  	}
    61  
    62  	return fmt.Errorf("cidr %s is not part of the requested pool", cidr)
    63  }
    64  
    65  func releaseCIDR(allocators []cidralloc.CIDRAllocator, cidr netip.Prefix) error {
    66  	ipnet := netipx.PrefixIPNet(cidr)
    67  	for _, alloc := range allocators {
    68  		if !alloc.InRange(ipnet) {
    69  			continue
    70  		}
    71  
    72  		allocated, err := alloc.IsAllocated(ipnet)
    73  		if err != nil {
    74  			return err
    75  		}
    76  		if !allocated {
    77  			return nil // not an error to release a cidr twice
    78  		}
    79  
    80  		return alloc.Release(ipnet)
    81  	}
    82  
    83  	return fmt.Errorf("released cidr %s was not part the pool", cidr)
    84  }
    85  
    86  func hasCIDR(allocators []cidralloc.CIDRAllocator, cidr netip.Prefix) bool {
    87  	for _, alloc := range allocators {
    88  		if alloc.IsClusterCIDR(cidr) {
    89  			return true
    90  		}
    91  	}
    92  	return false
    93  }
    94  
    95  func (c *cidrPool) allocCIDR(family ipam.Family) (netip.Prefix, error) {
    96  	switch family {
    97  	case ipam.IPv4:
    98  		return allocFirstFreeCIDR(c.v4)
    99  	case ipam.IPv6:
   100  		return allocFirstFreeCIDR(c.v6)
   101  	default:
   102  		return netip.Prefix{}, fmt.Errorf("invalid cidr family: %s", family)
   103  	}
   104  }
   105  
   106  func (c *cidrPool) occupyCIDR(cidr netip.Prefix) error {
   107  	if cidr.Addr().Is4() {
   108  		return occupyCIDR(c.v4, cidr)
   109  	} else {
   110  		return occupyCIDR(c.v6, cidr)
   111  	}
   112  }
   113  
   114  func (c *cidrPool) releaseCIDR(cidr netip.Prefix) error {
   115  	if cidr.Addr().Is4() {
   116  		return releaseCIDR(c.v4, cidr)
   117  	} else {
   118  		return releaseCIDR(c.v6, cidr)
   119  	}
   120  }
   121  
   122  func (c *cidrPool) hasCIDR(cidr netip.Prefix) bool {
   123  	if cidr.Addr().Is4() {
   124  		return hasCIDR(c.v4, cidr)
   125  	} else {
   126  		return hasCIDR(c.v6, cidr)
   127  	}
   128  }