libnet/ipam: split v4/v6 address spaces
Address spaces are a continuum of addresses that can be used for a specific purpose (ie. 'local' for unmanaged containers, 'global for Swarm). As such, combining v4 and v6 prefixes / addresses into a single address space doesn't make much sense. Also, the upcoming rewrite of `addrSpace` will benefit from that split. Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
parent
8cb7558aaf
commit
eb78246263
4 changed files with 73 additions and 33 deletions
|
@ -3,7 +3,6 @@ package ipam
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
|
@ -25,18 +24,10 @@ type addrSpace struct {
|
|||
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)
|
||||
}
|
||||
}
|
||||
func newAddrSpace(predefined []netip.Prefix) (*addrSpace, error) {
|
||||
return &addrSpace{
|
||||
subnets: map[netip.Prefix]*PoolData{},
|
||||
predefined: pdf,
|
||||
predefined: predefined,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -22,26 +22,65 @@ const (
|
|||
// Allocator provides per address space ipv4/ipv6 book keeping
|
||||
type Allocator struct {
|
||||
// The address spaces
|
||||
local, global *addrSpace
|
||||
local4, local6, global4, global6 *addrSpace
|
||||
}
|
||||
|
||||
// NewAllocator returns an instance of libnetwork ipam
|
||||
func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
|
||||
var (
|
||||
a Allocator
|
||||
err error
|
||||
a Allocator
|
||||
err error
|
||||
lcAs4, lcAs6, glAs4, glAs6 []netip.Prefix
|
||||
)
|
||||
a.local, err = newAddrSpace(lcAs)
|
||||
|
||||
lcAs4, lcAs6, err = splitByIPFamily(lcAs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct local address space: %w", err)
|
||||
}
|
||||
a.global, err = newAddrSpace(glAs)
|
||||
|
||||
glAs4, glAs6, err = splitByIPFamily(glAs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct global address space: %w", err)
|
||||
}
|
||||
|
||||
a.local4, err = newAddrSpace(lcAs4)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct local v4 address space: %w", err)
|
||||
}
|
||||
a.local6, err = newAddrSpace(lcAs6)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct local v6 address space: %w", err)
|
||||
}
|
||||
a.global4, err = newAddrSpace(glAs4)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct global v4 address space: %w", err)
|
||||
}
|
||||
a.global6, err = newAddrSpace(glAs6)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct global v6 address space: %w", err)
|
||||
}
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func splitByIPFamily(s []*net.IPNet) ([]netip.Prefix, []netip.Prefix, error) {
|
||||
var v4, v6 []netip.Prefix
|
||||
|
||||
for i, n := range s {
|
||||
p, ok := netiputil.ToPrefix(n)
|
||||
if !ok {
|
||||
return []netip.Prefix{}, []netip.Prefix{}, fmt.Errorf("network at index %d (%v) is not in canonical form", i, n)
|
||||
}
|
||||
|
||||
if p.Addr().Is4() {
|
||||
v4 = append(v4, p)
|
||||
} else {
|
||||
v6 = append(v6, p)
|
||||
}
|
||||
}
|
||||
|
||||
return v4, v6, nil
|
||||
}
|
||||
|
||||
// GetDefaultAddressSpaces returns the local and global default address spaces
|
||||
func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
return localAddressSpace, globalAddressSpace, nil
|
||||
|
@ -62,7 +101,7 @@ func (a *Allocator) RequestPool(addressSpace, requestedPool, requestedSubPool st
|
|||
if addressSpace == "" {
|
||||
return "", nil, nil, parseErr(ipamapi.ErrInvalidAddressSpace)
|
||||
}
|
||||
aSpace, err := a.getAddrSpace(addressSpace)
|
||||
aSpace, err := a.getAddrSpace(addressSpace, v6)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
@ -115,7 +154,7 @@ func (a *Allocator) ReleasePool(poolID string) error {
|
|||
return types.InvalidParameterErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace)
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace, k.Is6())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -125,12 +164,18 @@ func (a *Allocator) ReleasePool(poolID string) error {
|
|||
|
||||
// Given the address space, returns the local or global PoolConfig based on whether the
|
||||
// address space is local or global. AddressSpace locality is registered with IPAM out of band.
|
||||
func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
|
||||
func (a *Allocator) getAddrSpace(as string, v6 bool) (*addrSpace, error) {
|
||||
switch as {
|
||||
case localAddressSpace:
|
||||
return a.local, nil
|
||||
if v6 {
|
||||
return a.local6, nil
|
||||
}
|
||||
return a.local4, nil
|
||||
case globalAddressSpace:
|
||||
return a.global, nil
|
||||
if v6 {
|
||||
return a.global6, nil
|
||||
}
|
||||
return a.global4, nil
|
||||
}
|
||||
return nil, types.InvalidParameterErrorf("cannot find address space %s", as)
|
||||
}
|
||||
|
@ -170,7 +215,7 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
|
|||
return nil, nil, types.InvalidParameterErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace)
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace, k.Is6())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -200,7 +245,7 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
|||
return types.InvalidParameterErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace)
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace, k.Is6())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = a.getAddrSpace(localAddressSpace)
|
||||
_, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err := a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Fatalf("Incorrect poolIDs returned %s, %s", pid0, pid1)
|
||||
}
|
||||
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Fatalf("Expected failure in adding sub pool: %v", err)
|
||||
}
|
||||
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Errorf("main pool should still exist. Got poolID %q, want %q", pid00, pid0)
|
||||
}
|
||||
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
t.Errorf("Unexpected failure in adding pool: %v", err)
|
||||
}
|
||||
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace)
|
||||
aSpace, err = a.getAddrSpace(localAddressSpace, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ func TestRemoveSubnet(t *testing.T) {
|
|||
{localAddressSpace, "192.168.0.0/16", false},
|
||||
{localAddressSpace, "172.17.0.0/16", false},
|
||||
{localAddressSpace, "10.0.0.0/8", false},
|
||||
{localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false},
|
||||
{localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", true},
|
||||
{globalAddressSpace, "172.17.0.0/16", false},
|
||||
{globalAddressSpace, "10.0.0.0/8", false},
|
||||
{globalAddressSpace, "2001:db8:1:2:3:4:5::/112", true},
|
||||
|
@ -929,7 +929,7 @@ func TestRelease(t *testing.T) {
|
|||
for i, inp := range toRelease {
|
||||
ip0 := net.ParseIP(inp.address)
|
||||
a.ReleaseAddress(pid, ip0)
|
||||
bm := a.local.subnets[netip.MustParsePrefix(subnet)].addrs
|
||||
bm := a.local4.subnets[netip.MustParsePrefix(subnet)].addrs
|
||||
if bm.Unselected() != 1 {
|
||||
t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected())
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ type SubnetKey struct {
|
|||
Subnet, ChildSubnet netip.Prefix
|
||||
}
|
||||
|
||||
func (k SubnetKey) Is6() bool {
|
||||
return k.Subnet.Addr().Is6()
|
||||
}
|
||||
|
||||
// PoolIDFromString creates a new PoolID and populates the SubnetKey object
|
||||
// reading it from the given string.
|
||||
func PoolIDFromString(str string) (pID PoolID, err error) {
|
||||
|
|
Loading…
Add table
Reference in a new issue