-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
1 parent
a047d4b
commit df88857
Showing
3 changed files
with
253 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |
Oops, something went wrong.