diff --git a/libnetwork/ipam/address_space.go b/libnetwork/ipam/address_space.go index 372b0af925..ac50a995b9 100644 --- a/libnetwork/ipam/address_space.go +++ b/libnetwork/ipam/address_space.go @@ -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 } diff --git a/libnetwork/ipam/allocator.go b/libnetwork/ipam/allocator.go index 3217ffceae..9faca4e0ad 100644 --- a/libnetwork/ipam/allocator.go +++ b/libnetwork/ipam/allocator.go @@ -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 } diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go index 5339f79941..c44ffbcfab 100644 --- a/libnetwork/ipam/allocator_test.go +++ b/libnetwork/ipam/allocator_test.go @@ -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()) } diff --git a/libnetwork/ipam/structures.go b/libnetwork/ipam/structures.go index fd47243c91..3a00d1cef5 100644 --- a/libnetwork/ipam/structures.go +++ b/libnetwork/ipam/structures.go @@ -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) {