github.com/rawahars/moby@v24.0.4+incompatible/libnetwork/ipam/structures.go (about)

     1  package ipam
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/docker/docker/libnetwork/bitmap"
    10  	"github.com/docker/docker/libnetwork/ipamapi"
    11  	"github.com/docker/docker/libnetwork/types"
    12  )
    13  
    14  // PoolID is the pointer to the configured pools in each address space
    15  type PoolID struct {
    16  	AddressSpace string
    17  	SubnetKey
    18  }
    19  
    20  // PoolData contains the configured pool data
    21  type PoolData struct {
    22  	addrs    *bitmap.Bitmap
    23  	children map[netip.Prefix]struct{}
    24  
    25  	// Whether to implicitly release the pool once it no longer has any children.
    26  	autoRelease bool
    27  }
    28  
    29  // SubnetKey is the composite key to an address pool within an address space.
    30  type SubnetKey struct {
    31  	Subnet, ChildSubnet netip.Prefix
    32  }
    33  
    34  // addrSpace contains the pool configurations for the address space
    35  type addrSpace struct {
    36  	// Master subnet pools, indexed by the value's stringified PoolData.Pool field.
    37  	subnets map[netip.Prefix]*PoolData
    38  
    39  	// Predefined pool for the address space
    40  	predefined           []netip.Prefix
    41  	predefinedStartIndex int
    42  
    43  	sync.Mutex
    44  }
    45  
    46  // String returns the string form of the SubnetKey object
    47  func (s *PoolID) String() string {
    48  	k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
    49  	if s.ChildSubnet != (netip.Prefix{}) {
    50  		k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
    51  	}
    52  	return k
    53  }
    54  
    55  // FromString populates the SubnetKey object reading it from string
    56  func (s *PoolID) FromString(str string) error {
    57  	if str == "" || !strings.Contains(str, "/") {
    58  		return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
    59  	}
    60  
    61  	p := strings.Split(str, "/")
    62  	if len(p) != 3 && len(p) != 5 {
    63  		return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
    64  	}
    65  	sub, err := netip.ParsePrefix(p[1] + "/" + p[2])
    66  	if err != nil {
    67  		return types.BadRequestErrorf("%v", err)
    68  	}
    69  	var child netip.Prefix
    70  	if len(p) == 5 {
    71  		child, err = netip.ParsePrefix(p[3] + "/" + p[4])
    72  		if err != nil {
    73  			return types.BadRequestErrorf("%v", err)
    74  		}
    75  	}
    76  
    77  	*s = PoolID{
    78  		AddressSpace: p[0],
    79  		SubnetKey: SubnetKey{
    80  			Subnet:      sub,
    81  			ChildSubnet: child,
    82  		},
    83  	}
    84  	return nil
    85  }
    86  
    87  // String returns the string form of the PoolData object
    88  func (p *PoolData) String() string {
    89  	return fmt.Sprintf("PoolData[Children: %d]", len(p.children))
    90  }
    91  
    92  // allocateSubnet adds the subnet k to the address space.
    93  func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error {
    94  	aSpace.Lock()
    95  	defer aSpace.Unlock()
    96  
    97  	// Check if already allocated
    98  	if pool, ok := aSpace.subnets[nw]; ok {
    99  		var childExists bool
   100  		if sub != (netip.Prefix{}) {
   101  			_, childExists = pool.children[sub]
   102  		}
   103  		if sub == (netip.Prefix{}) || childExists {
   104  			// This means the same pool is already allocated. allocateSubnet is called when there
   105  			// is request for a pool/subpool. It should ensure there is no overlap with existing pools
   106  			return ipamapi.ErrPoolOverlap
   107  		}
   108  	}
   109  
   110  	return aSpace.allocateSubnetL(nw, sub)
   111  }
   112  
   113  func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error {
   114  	// If master pool, check for overlap
   115  	if sub == (netip.Prefix{}) {
   116  		if aSpace.contains(nw) {
   117  			return ipamapi.ErrPoolOverlap
   118  		}
   119  		// This is a new master pool, add it along with corresponding bitmask
   120  		aSpace.subnets[nw] = newPoolData(nw)
   121  		return nil
   122  	}
   123  
   124  	// This is a new non-master pool (subPool)
   125  	if nw.Addr().BitLen() != sub.Addr().BitLen() {
   126  		return fmt.Errorf("pool and subpool are of incompatible address families")
   127  	}
   128  
   129  	// Look for parent pool
   130  	pp, ok := aSpace.subnets[nw]
   131  	if !ok {
   132  		// Parent pool does not exist, add it along with corresponding bitmask
   133  		pp = newPoolData(nw)
   134  		pp.autoRelease = true
   135  		aSpace.subnets[nw] = pp
   136  	}
   137  	pp.children[sub] = struct{}{}
   138  	return nil
   139  }
   140  
   141  func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error {
   142  	aSpace.Lock()
   143  	defer aSpace.Unlock()
   144  
   145  	p, ok := aSpace.subnets[nw]
   146  	if !ok {
   147  		return ipamapi.ErrBadPool
   148  	}
   149  
   150  	if sub != (netip.Prefix{}) {
   151  		if _, ok := p.children[sub]; !ok {
   152  			return ipamapi.ErrBadPool
   153  		}
   154  		delete(p.children, sub)
   155  	} else {
   156  		p.autoRelease = true
   157  	}
   158  
   159  	if len(p.children) == 0 && p.autoRelease {
   160  		delete(aSpace.subnets, nw)
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  // contains checks whether nw is a superset or subset of any of the existing subnets in this address space.
   167  func (aSpace *addrSpace) contains(nw netip.Prefix) bool {
   168  	for pool := range aSpace.subnets {
   169  		if nw.Contains(pool.Addr()) || pool.Contains(nw.Addr()) {
   170  			return true
   171  		}
   172  	}
   173  	return false
   174  }