github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/ipam/allocator.go (about)

     1  package ipam
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"net/netip"
     8  	"strings"
     9  
    10  	"github.com/containerd/log"
    11  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/bitmap"
    12  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/ipamapi"
    13  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/ipbits"
    14  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/types"
    15  )
    16  
    17  const (
    18  	localAddressSpace  = "LocalDefault"
    19  	globalAddressSpace = "GlobalDefault"
    20  )
    21  
    22  // Allocator provides per address space ipv4/ipv6 book keeping
    23  type Allocator struct {
    24  	// The address spaces
    25  	local, global *addrSpace
    26  }
    27  
    28  // NewAllocator returns an instance of libnetwork ipam
    29  func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
    30  	var (
    31  		a   Allocator
    32  		err error
    33  	)
    34  	a.local, err = newAddrSpace(lcAs)
    35  	if err != nil {
    36  		return nil, fmt.Errorf("could not construct local address space: %w", err)
    37  	}
    38  	a.global, err = newAddrSpace(glAs)
    39  	if err != nil {
    40  		return nil, fmt.Errorf("could not construct global address space: %w", err)
    41  	}
    42  	return &a, nil
    43  }
    44  
    45  func newAddrSpace(predefined []*net.IPNet) (*addrSpace, error) {
    46  	pdf := make([]netip.Prefix, len(predefined))
    47  	for i, n := range predefined {
    48  		var ok bool
    49  		pdf[i], ok = toPrefix(n)
    50  		if !ok {
    51  			return nil, fmt.Errorf("network at index %d (%v) is not in canonical form", i, n)
    52  		}
    53  	}
    54  	return &addrSpace{
    55  		subnets:    map[netip.Prefix]*PoolData{},
    56  		predefined: pdf,
    57  	}, nil
    58  }
    59  
    60  // GetDefaultAddressSpaces returns the local and global default address spaces
    61  func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
    62  	return localAddressSpace, globalAddressSpace, nil
    63  }
    64  
    65  // RequestPool returns an address pool along with its unique id.
    66  // addressSpace must be a valid address space name and must not be the empty string.
    67  // If requestedPool is the empty string then the default predefined pool for addressSpace will be used, otherwise pool must be a valid IP address and length in CIDR notation.
    68  // If requestedSubPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of requestedPool.
    69  // requestedSubPool must be empty if requestedPool is empty.
    70  func (a *Allocator) RequestPool(addressSpace, requestedPool, requestedSubPool string, _ map[string]string, v6 bool) (poolID string, pool *net.IPNet, meta map[string]string, err error) {
    71  	log.G(context.TODO()).Debugf("RequestPool(%s, %s, %s, _, %t)", addressSpace, requestedPool, requestedSubPool, v6)
    72  
    73  	parseErr := func(err error) error {
    74  		return types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, requestedPool, requestedSubPool, err)
    75  	}
    76  
    77  	if addressSpace == "" {
    78  		return "", nil, nil, parseErr(ipamapi.ErrInvalidAddressSpace)
    79  	}
    80  	aSpace, err := a.getAddrSpace(addressSpace)
    81  	if err != nil {
    82  		return "", nil, nil, err
    83  	}
    84  	if requestedPool == "" && requestedSubPool != "" {
    85  		return "", nil, nil, parseErr(ipamapi.ErrInvalidSubPool)
    86  	}
    87  
    88  	k := PoolID{AddressSpace: addressSpace}
    89  	if requestedPool == "" {
    90  		k.Subnet, err = aSpace.allocatePredefinedPool(v6)
    91  		if err != nil {
    92  			return "", nil, nil, err
    93  		}
    94  		return k.String(), toIPNet(k.Subnet), nil, nil
    95  	}
    96  
    97  	if k.Subnet, err = netip.ParsePrefix(requestedPool); err != nil {
    98  		return "", nil, nil, parseErr(ipamapi.ErrInvalidPool)
    99  	}
   100  
   101  	if requestedSubPool != "" {
   102  		k.ChildSubnet, err = netip.ParsePrefix(requestedSubPool)
   103  		if err != nil {
   104  			return "", nil, nil, parseErr(ipamapi.ErrInvalidSubPool)
   105  		}
   106  	}
   107  
   108  	k.Subnet, k.ChildSubnet = k.Subnet.Masked(), k.ChildSubnet.Masked()
   109  	// Prior to https://github.com/moby/moby/pull/44968, libnetwork would happily accept a ChildSubnet with a bigger
   110  	// mask than its parent subnet. In such case, it was producing IP addresses based on the parent subnet, and the
   111  	// child subnet was not allocated from the address pool. Following condition take care of restoring this behavior
   112  	// for networks created before upgrading to v24.0.
   113  	if k.ChildSubnet.IsValid() && k.ChildSubnet.Bits() < k.Subnet.Bits() {
   114  		k.ChildSubnet = k.Subnet
   115  	}
   116  
   117  	err = aSpace.allocateSubnet(k.Subnet, k.ChildSubnet)
   118  	if err != nil {
   119  		return "", nil, nil, err
   120  	}
   121  
   122  	return k.String(), toIPNet(k.Subnet), nil, nil
   123  }
   124  
   125  // ReleasePool releases the address pool identified by the passed id
   126  func (a *Allocator) ReleasePool(poolID string) error {
   127  	log.G(context.TODO()).Debugf("ReleasePool(%s)", poolID)
   128  	k, err := PoolIDFromString(poolID)
   129  	if err != nil {
   130  		return types.InvalidParameterErrorf("invalid pool id: %s", poolID)
   131  	}
   132  
   133  	aSpace, err := a.getAddrSpace(k.AddressSpace)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	return aSpace.releaseSubnet(k.Subnet, k.ChildSubnet)
   139  }
   140  
   141  // Given the address space, returns the local or global PoolConfig based on whether the
   142  // address space is local or global. AddressSpace locality is registered with IPAM out of band.
   143  func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
   144  	switch as {
   145  	case localAddressSpace:
   146  		return a.local, nil
   147  	case globalAddressSpace:
   148  		return a.global, nil
   149  	}
   150  	return nil, types.InvalidParameterErrorf("cannot find address space %s", as)
   151  }
   152  
   153  func newPoolData(pool netip.Prefix) *PoolData {
   154  	ones, bits := pool.Bits(), pool.Addr().BitLen()
   155  	numAddresses := uint64(1 << uint(bits-ones))
   156  
   157  	// Allow /64 subnet
   158  	if pool.Addr().Is6() && numAddresses == 0 {
   159  		numAddresses--
   160  	}
   161  
   162  	// Generate the new address masks.
   163  	h := bitmap.New(numAddresses)
   164  
   165  	// Pre-reserve the network address on IPv4 networks large
   166  	// enough to have one (i.e., anything bigger than a /31.
   167  	if !(pool.Addr().Is4() && numAddresses <= 2) {
   168  		h.Set(0)
   169  	}
   170  
   171  	// Pre-reserve the broadcast address on IPv4 networks large
   172  	// enough to have one (i.e., anything bigger than a /31).
   173  	if pool.Addr().Is4() && numAddresses > 2 {
   174  		h.Set(numAddresses - 1)
   175  	}
   176  
   177  	return &PoolData{addrs: h, children: map[netip.Prefix]struct{}{}}
   178  }
   179  
   180  // getPredefineds returns the predefined subnets for the address space.
   181  //
   182  // It should not be called concurrently with any other method on the addrSpace.
   183  func (aSpace *addrSpace) getPredefineds() []netip.Prefix {
   184  	i := aSpace.predefinedStartIndex
   185  	// defensive in case the list changed since last update
   186  	if i >= len(aSpace.predefined) {
   187  		i = 0
   188  	}
   189  	return append(aSpace.predefined[i:], aSpace.predefined[:i]...)
   190  }
   191  
   192  // updatePredefinedStartIndex rotates the predefined subnet list by amt.
   193  //
   194  // It should not be called concurrently with any other method on the addrSpace.
   195  func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
   196  	i := aSpace.predefinedStartIndex + amt
   197  	if i < 0 || i >= len(aSpace.predefined) {
   198  		i = 0
   199  	}
   200  	aSpace.predefinedStartIndex = i
   201  }
   202  
   203  func (aSpace *addrSpace) allocatePredefinedPool(ipV6 bool) (netip.Prefix, error) {
   204  	aSpace.Lock()
   205  	defer aSpace.Unlock()
   206  
   207  	for i, nw := range aSpace.getPredefineds() {
   208  		if ipV6 != nw.Addr().Is6() {
   209  			continue
   210  		}
   211  		// Checks whether pool has already been allocated
   212  		if _, ok := aSpace.subnets[nw]; ok {
   213  			continue
   214  		}
   215  		// Shouldn't be necessary, but check prevents IP collisions should
   216  		// predefined pools overlap for any reason.
   217  		if !aSpace.contains(nw) {
   218  			aSpace.updatePredefinedStartIndex(i + 1)
   219  			err := aSpace.allocateSubnetL(nw, netip.Prefix{})
   220  			if err != nil {
   221  				return netip.Prefix{}, err
   222  			}
   223  			return nw, nil
   224  		}
   225  	}
   226  
   227  	v := 4
   228  	if ipV6 {
   229  		v = 6
   230  	}
   231  	return netip.Prefix{}, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
   232  }
   233  
   234  // RequestAddress returns an address from the specified pool ID
   235  func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
   236  	log.G(context.TODO()).Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts)
   237  	k, err := PoolIDFromString(poolID)
   238  	if err != nil {
   239  		return nil, nil, types.InvalidParameterErrorf("invalid pool id: %s", poolID)
   240  	}
   241  
   242  	aSpace, err := a.getAddrSpace(k.AddressSpace)
   243  	if err != nil {
   244  		return nil, nil, err
   245  	}
   246  	var pref netip.Addr
   247  	if prefAddress != nil {
   248  		var ok bool
   249  		pref, ok = netip.AddrFromSlice(prefAddress)
   250  		if !ok {
   251  			return nil, nil, types.InvalidParameterErrorf("invalid preferred address: %v", prefAddress)
   252  		}
   253  	}
   254  	p, err := aSpace.requestAddress(k.Subnet, k.ChildSubnet, pref.Unmap(), opts)
   255  	if err != nil {
   256  		return nil, nil, err
   257  	}
   258  	return &net.IPNet{
   259  		IP:   p.AsSlice(),
   260  		Mask: net.CIDRMask(k.Subnet.Bits(), k.Subnet.Addr().BitLen()),
   261  	}, nil, nil
   262  }
   263  
   264  func (aSpace *addrSpace) requestAddress(nw, sub netip.Prefix, prefAddress netip.Addr, opts map[string]string) (netip.Addr, error) {
   265  	aSpace.Lock()
   266  	defer aSpace.Unlock()
   267  
   268  	p, ok := aSpace.subnets[nw]
   269  	if !ok {
   270  		return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
   271  	}
   272  
   273  	if prefAddress != (netip.Addr{}) && !nw.Contains(prefAddress) {
   274  		return netip.Addr{}, ipamapi.ErrIPOutOfRange
   275  	}
   276  
   277  	if sub != (netip.Prefix{}) {
   278  		if _, ok := p.children[sub]; !ok {
   279  			return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
   280  		}
   281  	}
   282  
   283  	// In order to request for a serial ip address allocation, callers can pass in the option to request
   284  	// IP allocation serially or first available IP in the subnet
   285  	serial := opts[ipamapi.AllocSerialPrefix] == "true"
   286  	ip, err := getAddress(nw, p.addrs, prefAddress, sub, serial)
   287  	if err != nil {
   288  		return netip.Addr{}, err
   289  	}
   290  
   291  	return ip, nil
   292  }
   293  
   294  // ReleaseAddress releases the address from the specified pool ID
   295  func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
   296  	log.G(context.TODO()).Debugf("ReleaseAddress(%s, %v)", poolID, address)
   297  	k, err := PoolIDFromString(poolID)
   298  	if err != nil {
   299  		return types.InvalidParameterErrorf("invalid pool id: %s", poolID)
   300  	}
   301  
   302  	aSpace, err := a.getAddrSpace(k.AddressSpace)
   303  	if err != nil {
   304  		return err
   305  	}
   306  
   307  	addr, ok := netip.AddrFromSlice(address)
   308  	if !ok {
   309  		return types.InvalidParameterErrorf("invalid address: %v", address)
   310  	}
   311  
   312  	return aSpace.releaseAddress(k.Subnet, k.ChildSubnet, addr.Unmap())
   313  }
   314  
   315  func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr) error {
   316  	aSpace.Lock()
   317  	defer aSpace.Unlock()
   318  
   319  	p, ok := aSpace.subnets[nw]
   320  	if !ok {
   321  		return types.NotFoundErrorf("cannot find address pool for %v/%v", nw, sub)
   322  	}
   323  	if sub != (netip.Prefix{}) {
   324  		if _, ok := p.children[sub]; !ok {
   325  			return types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
   326  		}
   327  	}
   328  
   329  	if !address.IsValid() {
   330  		return types.InvalidParameterErrorf("invalid address")
   331  	}
   332  
   333  	if !nw.Contains(address) {
   334  		return ipamapi.ErrIPOutOfRange
   335  	}
   336  
   337  	defer log.G(context.TODO()).Debugf("Released address Address:%v Sequence:%s", address, p.addrs)
   338  
   339  	return p.addrs.Unset(hostID(address, uint(nw.Bits())))
   340  }
   341  
   342  func getAddress(base netip.Prefix, bitmask *bitmap.Bitmap, prefAddress netip.Addr, ipr netip.Prefix, serial bool) (netip.Addr, error) {
   343  	var (
   344  		ordinal uint64
   345  		err     error
   346  	)
   347  
   348  	log.G(context.TODO()).Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", base, bitmask, serial, prefAddress)
   349  
   350  	if bitmask.Unselected() == 0 {
   351  		return netip.Addr{}, ipamapi.ErrNoAvailableIPs
   352  	}
   353  	if ipr == (netip.Prefix{}) && prefAddress == (netip.Addr{}) {
   354  		ordinal, err = bitmask.SetAny(serial)
   355  	} else if prefAddress != (netip.Addr{}) {
   356  		ordinal = hostID(prefAddress, uint(base.Bits()))
   357  		err = bitmask.Set(ordinal)
   358  	} else {
   359  		start, end := subnetRange(base, ipr)
   360  		ordinal, err = bitmask.SetAnyInRange(start, end, serial)
   361  	}
   362  
   363  	switch err {
   364  	case nil:
   365  		// Convert IP ordinal for this subnet into IP address
   366  		return ipbits.Add(base.Addr(), ordinal, 0), nil
   367  	case bitmap.ErrBitAllocated:
   368  		return netip.Addr{}, ipamapi.ErrIPAlreadyAllocated
   369  	case bitmap.ErrNoBitAvailable:
   370  		return netip.Addr{}, ipamapi.ErrNoAvailableIPs
   371  	default:
   372  		return netip.Addr{}, err
   373  	}
   374  }
   375  
   376  // DumpDatabase dumps the internal info
   377  func (a *Allocator) DumpDatabase() string {
   378  	aspaces := map[string]*addrSpace{
   379  		localAddressSpace:  a.local,
   380  		globalAddressSpace: a.global,
   381  	}
   382  
   383  	var b strings.Builder
   384  	for _, as := range []string{localAddressSpace, globalAddressSpace} {
   385  		fmt.Fprintf(&b, "\n### %s\n", as)
   386  		b.WriteString(aspaces[as].DumpDatabase())
   387  	}
   388  	return b.String()
   389  }
   390  
   391  func (aSpace *addrSpace) DumpDatabase() string {
   392  	aSpace.Lock()
   393  	defer aSpace.Unlock()
   394  
   395  	var b strings.Builder
   396  	for k, config := range aSpace.subnets {
   397  		fmt.Fprintf(&b, "%v: %v\n", k, config)
   398  		fmt.Fprintf(&b, "  Bitmap: %v\n", config.addrs)
   399  		for k := range config.children {
   400  			fmt.Fprintf(&b, "  - Subpool: %v\n", k)
   401  		}
   402  	}
   403  	return b.String()
   404  }
   405  
   406  // IsBuiltIn returns true for builtin drivers
   407  func (a *Allocator) IsBuiltIn() bool {
   408  	return true
   409  }