Explorar o código

libnet/ipam: put `addrSpace` into a separate file

`addrSpace` methods are currently scattered in two different files.
As upcoming work will rewrite some of these methods, better put them
into a separate file.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Albin Kerouanton hai 1 ano
pai
achega
7301b98502
Modificáronse 3 ficheiros con 253 adicións e 240 borrados
  1. 253 0
      libnetwork/ipam/address_space.go
  2. 0 141
      libnetwork/ipam/allocator.go
  3. 0 99
      libnetwork/ipam/structures.go

+ 253 - 0
libnetwork/ipam/address_space.go

@@ -0,0 +1,253 @@
+package ipam
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"net/netip"
+	"strings"
+	"sync"
+
+	"github.com/containerd/log"
+	"github.com/docker/docker/libnetwork/internal/netiputil"
+	"github.com/docker/docker/libnetwork/ipamapi"
+	"github.com/docker/docker/libnetwork/types"
+)
+
+// addrSpace contains the pool configurations for the address space
+type addrSpace struct {
+	// Master subnet pools, indexed by the value's stringified PoolData.Pool field.
+	subnets map[netip.Prefix]*PoolData
+
+	// Predefined pool for the address space
+	predefined           []netip.Prefix
+	predefinedStartIndex int
+
+	mu sync.Mutex
+}
+
+func newAddrSpace(predefined []*net.IPNet) (*addrSpace, error) {
+	pdf := make([]netip.Prefix, len(predefined))
+	for i, n := range predefined {
+		var ok bool
+		pdf[i], ok = netiputil.ToPrefix(n)
+		if !ok {
+			return nil, fmt.Errorf("network at index %d (%v) is not in canonical form", i, n)
+		}
+	}
+	return &addrSpace{
+		subnets:    map[netip.Prefix]*PoolData{},
+		predefined: pdf,
+	}, nil
+}
+
+// allocateSubnet adds the subnet k to the address space.
+func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error {
+	aSpace.mu.Lock()
+	defer aSpace.mu.Unlock()
+
+	// Check if already allocated
+	if pool, ok := aSpace.subnets[nw]; ok {
+		var childExists bool
+		if sub != (netip.Prefix{}) {
+			_, childExists = pool.children[sub]
+		}
+		if sub == (netip.Prefix{}) || childExists {
+			// This means the same pool is already allocated. allocateSubnet is called when there
+			// is request for a pool/subpool. It should ensure there is no overlap with existing pools
+			return ipamapi.ErrPoolOverlap
+		}
+	}
+
+	return aSpace.allocateSubnetL(nw, sub)
+}
+
+func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error {
+	// If master pool, check for overlap
+	if sub == (netip.Prefix{}) {
+		if aSpace.overlaps(nw) {
+			return ipamapi.ErrPoolOverlap
+		}
+		// This is a new master pool, add it along with corresponding bitmask
+		aSpace.subnets[nw] = newPoolData(nw)
+		return nil
+	}
+
+	// This is a new non-master pool (subPool)
+	if nw.Addr().BitLen() != sub.Addr().BitLen() {
+		return fmt.Errorf("pool and subpool are of incompatible address families")
+	}
+
+	// Look for parent pool
+	pp, ok := aSpace.subnets[nw]
+	if !ok {
+		// Parent pool does not exist, add it along with corresponding bitmask
+		pp = newPoolData(nw)
+		pp.autoRelease = true
+		aSpace.subnets[nw] = pp
+	}
+	pp.children[sub] = struct{}{}
+	return nil
+}
+
+// overlaps reports whether nw contains any IP addresses in common with any of
+// the existing subnets in this address space.
+func (aSpace *addrSpace) overlaps(nw netip.Prefix) bool {
+	for pool := range aSpace.subnets {
+		if pool.Overlaps(nw) {
+			return true
+		}
+	}
+	return false
+}
+
+// getPredefineds returns the predefined subnets for the address space.
+//
+// It should not be called concurrently with any other method on the addrSpace.
+func (aSpace *addrSpace) getPredefineds() []netip.Prefix {
+	i := aSpace.predefinedStartIndex
+	// defensive in case the list changed since last update
+	if i >= len(aSpace.predefined) {
+		i = 0
+	}
+	return append(aSpace.predefined[i:], aSpace.predefined[:i]...)
+}
+
+// updatePredefinedStartIndex rotates the predefined subnet list by amt.
+//
+// It should not be called concurrently with any other method on the addrSpace.
+func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
+	i := aSpace.predefinedStartIndex + amt
+	if i < 0 || i >= len(aSpace.predefined) {
+		i = 0
+	}
+	aSpace.predefinedStartIndex = i
+}
+
+func (aSpace *addrSpace) allocatePredefinedPool(ipV6 bool) (netip.Prefix, error) {
+	aSpace.mu.Lock()
+	defer aSpace.mu.Unlock()
+
+	for i, nw := range aSpace.getPredefineds() {
+		if ipV6 != nw.Addr().Is6() {
+			continue
+		}
+		// Checks whether pool has already been allocated
+		if _, ok := aSpace.subnets[nw]; ok {
+			continue
+		}
+		// Shouldn't be necessary, but check prevents IP collisions should
+		// predefined pools overlap for any reason.
+		if !aSpace.overlaps(nw) {
+			aSpace.updatePredefinedStartIndex(i + 1)
+			err := aSpace.allocateSubnetL(nw, netip.Prefix{})
+			if err != nil {
+				return netip.Prefix{}, err
+			}
+			return nw, nil
+		}
+	}
+
+	v := 4
+	if ipV6 {
+		v = 6
+	}
+	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)
+}
+
+func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error {
+	aSpace.mu.Lock()
+	defer aSpace.mu.Unlock()
+
+	p, ok := aSpace.subnets[nw]
+	if !ok {
+		return ipamapi.ErrBadPool
+	}
+
+	if sub != (netip.Prefix{}) {
+		if _, ok := p.children[sub]; !ok {
+			return ipamapi.ErrBadPool
+		}
+		delete(p.children, sub)
+	} else {
+		p.autoRelease = true
+	}
+
+	if len(p.children) == 0 && p.autoRelease {
+		delete(aSpace.subnets, nw)
+	}
+
+	return nil
+}
+
+func (aSpace *addrSpace) requestAddress(nw, sub netip.Prefix, prefAddress netip.Addr, opts map[string]string) (netip.Addr, error) {
+	aSpace.mu.Lock()
+	defer aSpace.mu.Unlock()
+
+	p, ok := aSpace.subnets[nw]
+	if !ok {
+		return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
+	}
+
+	if prefAddress != (netip.Addr{}) && !nw.Contains(prefAddress) {
+		return netip.Addr{}, ipamapi.ErrIPOutOfRange
+	}
+
+	if sub != (netip.Prefix{}) {
+		if _, ok := p.children[sub]; !ok {
+			return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
+		}
+	}
+
+	// In order to request for a serial ip address allocation, callers can pass in the option to request
+	// IP allocation serially or first available IP in the subnet
+	serial := opts[ipamapi.AllocSerialPrefix] == "true"
+	ip, err := getAddress(nw, p.addrs, prefAddress, sub, serial)
+	if err != nil {
+		return netip.Addr{}, err
+	}
+
+	return ip, nil
+}
+
+func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr) error {
+	aSpace.mu.Lock()
+	defer aSpace.mu.Unlock()
+
+	p, ok := aSpace.subnets[nw]
+	if !ok {
+		return types.NotFoundErrorf("cannot find address pool for %v/%v", nw, sub)
+	}
+	if sub != (netip.Prefix{}) {
+		if _, ok := p.children[sub]; !ok {
+			return types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
+		}
+	}
+
+	if !address.IsValid() {
+		return types.InvalidParameterErrorf("invalid address")
+	}
+
+	if !nw.Contains(address) {
+		return ipamapi.ErrIPOutOfRange
+	}
+
+	defer log.G(context.TODO()).Debugf("Released address Address:%v Sequence:%s", address, p.addrs)
+
+	return p.addrs.Unset(netiputil.HostID(address, uint(nw.Bits())))
+}
+
+func (aSpace *addrSpace) DumpDatabase() string {
+	aSpace.mu.Lock()
+	defer aSpace.mu.Unlock()
+
+	var b strings.Builder
+	for k, config := range aSpace.subnets {
+		fmt.Fprintf(&b, "%v: %v\n", k, config)
+		fmt.Fprintf(&b, "  Bitmap: %v\n", config.addrs)
+		for k := range config.children {
+			fmt.Fprintf(&b, "  - Subpool: %v\n", k)
+		}
+	}
+	return b.String()
+}

+ 0 - 141
libnetwork/ipam/allocator.go

@@ -43,21 +43,6 @@ func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
 	return &a, nil
 }
 
-func newAddrSpace(predefined []*net.IPNet) (*addrSpace, error) {
-	pdf := make([]netip.Prefix, len(predefined))
-	for i, n := range predefined {
-		var ok bool
-		pdf[i], ok = netiputil.ToPrefix(n)
-		if !ok {
-			return nil, fmt.Errorf("network at index %d (%v) is not in canonical form", i, n)
-		}
-	}
-	return &addrSpace{
-		subnets:    map[netip.Prefix]*PoolData{},
-		predefined: pdf,
-	}, nil
-}
-
 // GetDefaultAddressSpaces returns the local and global default address spaces
 func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
 	return localAddressSpace, globalAddressSpace, nil
@@ -178,60 +163,6 @@ func newPoolData(pool netip.Prefix) *PoolData {
 	return &PoolData{addrs: h, children: map[netip.Prefix]struct{}{}}
 }
 
-// getPredefineds returns the predefined subnets for the address space.
-//
-// It should not be called concurrently with any other method on the addrSpace.
-func (aSpace *addrSpace) getPredefineds() []netip.Prefix {
-	i := aSpace.predefinedStartIndex
-	// defensive in case the list changed since last update
-	if i >= len(aSpace.predefined) {
-		i = 0
-	}
-	return append(aSpace.predefined[i:], aSpace.predefined[:i]...)
-}
-
-// updatePredefinedStartIndex rotates the predefined subnet list by amt.
-//
-// It should not be called concurrently with any other method on the addrSpace.
-func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
-	i := aSpace.predefinedStartIndex + amt
-	if i < 0 || i >= len(aSpace.predefined) {
-		i = 0
-	}
-	aSpace.predefinedStartIndex = i
-}
-
-func (aSpace *addrSpace) allocatePredefinedPool(ipV6 bool) (netip.Prefix, error) {
-	aSpace.mu.Lock()
-	defer aSpace.mu.Unlock()
-
-	for i, nw := range aSpace.getPredefineds() {
-		if ipV6 != nw.Addr().Is6() {
-			continue
-		}
-		// Checks whether pool has already been allocated
-		if _, ok := aSpace.subnets[nw]; ok {
-			continue
-		}
-		// Shouldn't be necessary, but check prevents IP collisions should
-		// predefined pools overlap for any reason.
-		if !aSpace.overlaps(nw) {
-			aSpace.updatePredefinedStartIndex(i + 1)
-			err := aSpace.allocateSubnetL(nw, netip.Prefix{})
-			if err != nil {
-				return netip.Prefix{}, err
-			}
-			return nw, nil
-		}
-	}
-
-	v := 4
-	if ipV6 {
-		v = 6
-	}
-	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)
-}
-
 // RequestAddress returns an address from the specified pool ID
 func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
 	log.G(context.TODO()).Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts)
@@ -262,36 +193,6 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
 	}, nil, nil
 }
 
-func (aSpace *addrSpace) requestAddress(nw, sub netip.Prefix, prefAddress netip.Addr, opts map[string]string) (netip.Addr, error) {
-	aSpace.mu.Lock()
-	defer aSpace.mu.Unlock()
-
-	p, ok := aSpace.subnets[nw]
-	if !ok {
-		return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
-	}
-
-	if prefAddress != (netip.Addr{}) && !nw.Contains(prefAddress) {
-		return netip.Addr{}, ipamapi.ErrIPOutOfRange
-	}
-
-	if sub != (netip.Prefix{}) {
-		if _, ok := p.children[sub]; !ok {
-			return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
-		}
-	}
-
-	// In order to request for a serial ip address allocation, callers can pass in the option to request
-	// IP allocation serially or first available IP in the subnet
-	serial := opts[ipamapi.AllocSerialPrefix] == "true"
-	ip, err := getAddress(nw, p.addrs, prefAddress, sub, serial)
-	if err != nil {
-		return netip.Addr{}, err
-	}
-
-	return ip, nil
-}
-
 // ReleaseAddress releases the address from the specified pool ID
 func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
 	log.G(context.TODO()).Debugf("ReleaseAddress(%s, %v)", poolID, address)
@@ -313,33 +214,6 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
 	return aSpace.releaseAddress(k.Subnet, k.ChildSubnet, addr.Unmap())
 }
 
-func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr) error {
-	aSpace.mu.Lock()
-	defer aSpace.mu.Unlock()
-
-	p, ok := aSpace.subnets[nw]
-	if !ok {
-		return types.NotFoundErrorf("cannot find address pool for %v/%v", nw, sub)
-	}
-	if sub != (netip.Prefix{}) {
-		if _, ok := p.children[sub]; !ok {
-			return types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
-		}
-	}
-
-	if !address.IsValid() {
-		return types.InvalidParameterErrorf("invalid address")
-	}
-
-	if !nw.Contains(address) {
-		return ipamapi.ErrIPOutOfRange
-	}
-
-	defer log.G(context.TODO()).Debugf("Released address Address:%v Sequence:%s", address, p.addrs)
-
-	return p.addrs.Unset(netiputil.HostID(address, uint(nw.Bits())))
-}
-
 func getAddress(base netip.Prefix, bitmask *bitmap.Bitmap, prefAddress netip.Addr, ipr netip.Prefix, serial bool) (netip.Addr, error) {
 	var (
 		ordinal uint64
@@ -389,21 +263,6 @@ func (a *Allocator) DumpDatabase() string {
 	return b.String()
 }
 
-func (aSpace *addrSpace) DumpDatabase() string {
-	aSpace.mu.Lock()
-	defer aSpace.mu.Unlock()
-
-	var b strings.Builder
-	for k, config := range aSpace.subnets {
-		fmt.Fprintf(&b, "%v: %v\n", k, config)
-		fmt.Fprintf(&b, "  Bitmap: %v\n", config.addrs)
-		for k := range config.children {
-			fmt.Fprintf(&b, "  - Subpool: %v\n", k)
-		}
-	}
-	return b.String()
-}
-
 // IsBuiltIn returns true for builtin drivers
 func (a *Allocator) IsBuiltIn() bool {
 	return true

+ 0 - 99
libnetwork/ipam/structures.go

@@ -4,10 +4,8 @@ import (
 	"fmt"
 	"net/netip"
 	"strings"
-	"sync"
 
 	"github.com/docker/docker/libnetwork/bitmap"
-	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/types"
 )
 
@@ -31,18 +29,6 @@ type SubnetKey struct {
 	Subnet, ChildSubnet netip.Prefix
 }
 
-// addrSpace contains the pool configurations for the address space
-type addrSpace struct {
-	// Master subnet pools, indexed by the value's stringified PoolData.Pool field.
-	subnets map[netip.Prefix]*PoolData
-
-	// Predefined pool for the address space
-	predefined           []netip.Prefix
-	predefinedStartIndex int
-
-	mu sync.Mutex
-}
-
 // PoolIDFromString creates a new PoolID and populates the SubnetKey object
 // reading it from the given string.
 func PoolIDFromString(str string) (pID PoolID, err error) {
@@ -82,88 +68,3 @@ func (s *PoolID) String() string {
 func (p *PoolData) String() string {
 	return fmt.Sprintf("PoolData[Children: %d]", len(p.children))
 }
-
-// allocateSubnet adds the subnet k to the address space.
-func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error {
-	aSpace.mu.Lock()
-	defer aSpace.mu.Unlock()
-
-	// Check if already allocated
-	if pool, ok := aSpace.subnets[nw]; ok {
-		var childExists bool
-		if sub != (netip.Prefix{}) {
-			_, childExists = pool.children[sub]
-		}
-		if sub == (netip.Prefix{}) || childExists {
-			// This means the same pool is already allocated. allocateSubnet is called when there
-			// is request for a pool/subpool. It should ensure there is no overlap with existing pools
-			return ipamapi.ErrPoolOverlap
-		}
-	}
-
-	return aSpace.allocateSubnetL(nw, sub)
-}
-
-func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error {
-	// If master pool, check for overlap
-	if sub == (netip.Prefix{}) {
-		if aSpace.overlaps(nw) {
-			return ipamapi.ErrPoolOverlap
-		}
-		// This is a new master pool, add it along with corresponding bitmask
-		aSpace.subnets[nw] = newPoolData(nw)
-		return nil
-	}
-
-	// This is a new non-master pool (subPool)
-	if nw.Addr().BitLen() != sub.Addr().BitLen() {
-		return fmt.Errorf("pool and subpool are of incompatible address families")
-	}
-
-	// Look for parent pool
-	pp, ok := aSpace.subnets[nw]
-	if !ok {
-		// Parent pool does not exist, add it along with corresponding bitmask
-		pp = newPoolData(nw)
-		pp.autoRelease = true
-		aSpace.subnets[nw] = pp
-	}
-	pp.children[sub] = struct{}{}
-	return nil
-}
-
-func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error {
-	aSpace.mu.Lock()
-	defer aSpace.mu.Unlock()
-
-	p, ok := aSpace.subnets[nw]
-	if !ok {
-		return ipamapi.ErrBadPool
-	}
-
-	if sub != (netip.Prefix{}) {
-		if _, ok := p.children[sub]; !ok {
-			return ipamapi.ErrBadPool
-		}
-		delete(p.children, sub)
-	} else {
-		p.autoRelease = true
-	}
-
-	if len(p.children) == 0 && p.autoRelease {
-		delete(aSpace.subnets, nw)
-	}
-
-	return nil
-}
-
-// overlaps reports whether nw contains any IP addresses in common with any of
-// the existing subnets in this address space.
-func (aSpace *addrSpace) overlaps(nw netip.Prefix) bool {
-	for pool := range aSpace.subnets {
-		if pool.Overlaps(nw) {
-			return true
-		}
-	}
-	return false
-}