7257c77e19
I am finally convinced that, given two netip.Prefix values a and b, the expression a.Contains(b.Addr()) || b.Contains(a.Addr()) is functionally equivalent to a.Overlaps(b) The (netip.Prefix).Contains method works by masking the address with the prefix's mask and testing whether the remaining most-significant bits are equal to the same bits in the prefix. The (netip.Prefix).Overlaps method works by masking the longer prefix to the length of the shorter prefix and testing whether the remaining most-significant bits are equal. This is equivalent to shorterPrefix.Contains(longerPrefix.Addr()), therefore applying Contains symmetrically to two prefixes will always yield the same result as applying Overlaps to the two prefixes in either order. Signed-off-by: Cory Snider <csnider@mirantis.com>
169 lines
4.3 KiB
Go
169 lines
4.3 KiB
Go
package ipam
|
|
|
|
import (
|
|
"fmt"
|
|
"net/netip"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/docker/docker/libnetwork/bitmap"
|
|
"github.com/docker/docker/libnetwork/ipamapi"
|
|
"github.com/docker/docker/libnetwork/types"
|
|
)
|
|
|
|
// PoolID is the pointer to the configured pools in each address space
|
|
type PoolID struct {
|
|
AddressSpace string
|
|
SubnetKey
|
|
}
|
|
|
|
// PoolData contains the configured pool data
|
|
type PoolData struct {
|
|
addrs *bitmap.Bitmap
|
|
children map[netip.Prefix]struct{}
|
|
|
|
// Whether to implicitly release the pool once it no longer has any children.
|
|
autoRelease bool
|
|
}
|
|
|
|
// SubnetKey is the composite key to an address pool within an address space.
|
|
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
|
|
|
|
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) {
|
|
if str == "" {
|
|
return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
|
|
}
|
|
|
|
p := strings.Split(str, "/")
|
|
if len(p) != 3 && len(p) != 5 {
|
|
return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
|
|
}
|
|
pID.AddressSpace = p[0]
|
|
pID.Subnet, err = netip.ParsePrefix(p[1] + "/" + p[2])
|
|
if err != nil {
|
|
return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
|
|
}
|
|
if len(p) == 5 {
|
|
pID.ChildSubnet, err = netip.ParsePrefix(p[3] + "/" + p[4])
|
|
if err != nil {
|
|
return pID, types.InvalidParameterErrorf("invalid string form for subnetkey: %s", str)
|
|
}
|
|
}
|
|
|
|
return pID, nil
|
|
}
|
|
|
|
// String returns the string form of the SubnetKey object
|
|
func (s *PoolID) String() string {
|
|
if s.ChildSubnet == (netip.Prefix{}) {
|
|
return s.AddressSpace + "/" + s.Subnet.String()
|
|
} else {
|
|
return s.AddressSpace + "/" + s.Subnet.String() + "/" + s.ChildSubnet.String()
|
|
}
|
|
}
|
|
|
|
// String returns the string form of the PoolData object
|
|
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.Lock()
|
|
defer aSpace.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.Lock()
|
|
defer aSpace.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
|
|
}
|