Browse code

libnet/ipam: use netip types internally

The netip types can be used as map keys, unlike net.IP and friends,
which is a very useful property to have for this application.

Signed-off-by: Cory Snider <csnider@mirantis.com>

Cory Snider authored on 2023/02/10 06:43:46
Showing 7 changed files
... ...
@@ -3,10 +3,12 @@ package ipam
3 3
 import (
4 4
 	"fmt"
5 5
 	"net"
6
+	"net/netip"
6 7
 	"strings"
7 8
 
8 9
 	"github.com/docker/docker/libnetwork/bitmap"
9 10
 	"github.com/docker/docker/libnetwork/ipamapi"
11
+	"github.com/docker/docker/libnetwork/ipbits"
10 12
 	"github.com/docker/docker/libnetwork/types"
11 13
 	"github.com/sirupsen/logrus"
12 14
 )
... ...
@@ -24,17 +26,34 @@ type Allocator struct {
24 24
 
25 25
 // NewAllocator returns an instance of libnetwork ipam
26 26
 func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
27
-	return &Allocator{
28
-		local:  newAddrSpace(lcAs),
29
-		global: newAddrSpace(glAs),
30
-	}, nil
27
+	var (
28
+		a   Allocator
29
+		err error
30
+	)
31
+	a.local, err = newAddrSpace(lcAs)
32
+	if err != nil {
33
+		return nil, fmt.Errorf("could not construct local address space: %w", err)
34
+	}
35
+	a.global, err = newAddrSpace(glAs)
36
+	if err != nil {
37
+		return nil, fmt.Errorf("could not construct global address space: %w", err)
38
+	}
39
+	return &a, nil
31 40
 }
32 41
 
33
-func newAddrSpace(predefined []*net.IPNet) *addrSpace {
34
-	return &addrSpace{
35
-		subnets:    map[string]*PoolData{},
36
-		predefined: predefined,
42
+func newAddrSpace(predefined []*net.IPNet) (*addrSpace, error) {
43
+	pdf := make([]netip.Prefix, len(predefined))
44
+	for i, n := range predefined {
45
+		var ok bool
46
+		pdf[i], ok = toPrefix(n)
47
+		if !ok {
48
+			return nil, fmt.Errorf("network at index %d (%v) is not in canonical form", i, n)
49
+		}
37 50
 	}
51
+	return &addrSpace{
52
+		subnets:    map[netip.Prefix]*PoolData{},
53
+		predefined: pdf,
54
+	}, nil
38 55
 }
39 56
 
40 57
 // GetDefaultAddressSpaces returns the local and global default address spaces
... ...
@@ -67,36 +86,32 @@ func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[
67 67
 		if subPool != "" {
68 68
 			return parseErr(ipamapi.ErrInvalidSubPool)
69 69
 		}
70
-		var nw *net.IPNet
71
-		nw, k.SubnetKey, err = aSpace.allocatePredefinedPool(v6)
70
+		k.Subnet, err = aSpace.allocatePredefinedPool(v6)
72 71
 		if err != nil {
73 72
 			return "", nil, nil, err
74 73
 		}
75
-		return k.String(), nw, nil, nil
74
+		return k.String(), toIPNet(k.Subnet), nil, nil
76 75
 	}
77 76
 
78
-	var (
79
-		nw, sub *net.IPNet
80
-	)
81
-	if _, nw, err = net.ParseCIDR(pool); err != nil {
77
+	if k.Subnet, err = netip.ParsePrefix(pool); err != nil {
82 78
 		return parseErr(ipamapi.ErrInvalidPool)
83 79
 	}
84 80
 
85 81
 	if subPool != "" {
86 82
 		var err error
87
-		_, sub, err = net.ParseCIDR(subPool)
83
+		k.ChildSubnet, err = netip.ParsePrefix(subPool)
88 84
 		if err != nil {
89 85
 			return parseErr(ipamapi.ErrInvalidSubPool)
90 86
 		}
91
-		k.ChildSubnet = subPool
92 87
 	}
93 88
 
94
-	k.SubnetKey, err = aSpace.allocateSubnet(nw, sub)
89
+	k.Subnet, k.ChildSubnet = k.Subnet.Masked(), k.ChildSubnet.Masked()
90
+	err = aSpace.allocateSubnet(k.Subnet, k.ChildSubnet)
95 91
 	if err != nil {
96 92
 		return "", nil, nil, err
97 93
 	}
98 94
 
99
-	return k.String(), nw, nil, nil
95
+	return k.String(), toIPNet(k.Subnet), nil, nil
100 96
 }
101 97
 
102 98
 // ReleasePool releases the address pool identified by the passed id
... ...
@@ -112,7 +127,7 @@ func (a *Allocator) ReleasePool(poolID string) error {
112 112
 		return err
113 113
 	}
114 114
 
115
-	return aSpace.releaseSubnet(k.SubnetKey)
115
+	return aSpace.releaseSubnet(k.Subnet, k.ChildSubnet)
116 116
 }
117 117
 
118 118
 // Given the address space, returns the local or global PoolConfig based on whether the
... ...
@@ -127,13 +142,12 @@ func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
127 127
 	return nil, types.BadRequestErrorf("cannot find address space %s", as)
128 128
 }
129 129
 
130
-func newPoolData(pool *net.IPNet) *PoolData {
131
-	ipVer := getAddressVersion(pool.IP)
132
-	ones, bits := pool.Mask.Size()
130
+func newPoolData(pool netip.Prefix) *PoolData {
131
+	ones, bits := pool.Bits(), pool.Addr().BitLen()
133 132
 	numAddresses := uint64(1 << uint(bits-ones))
134 133
 
135 134
 	// Allow /64 subnet
136
-	if ipVer == v6 && numAddresses == 0 {
135
+	if pool.Addr().Is6() && numAddresses == 0 {
137 136
 		numAddresses--
138 137
 	}
139 138
 
... ...
@@ -142,23 +156,23 @@ func newPoolData(pool *net.IPNet) *PoolData {
142 142
 
143 143
 	// Pre-reserve the network address on IPv4 networks large
144 144
 	// enough to have one (i.e., anything bigger than a /31.
145
-	if !(ipVer == v4 && numAddresses <= 2) {
145
+	if !(pool.Addr().Is4() && numAddresses <= 2) {
146 146
 		h.Set(0)
147 147
 	}
148 148
 
149 149
 	// Pre-reserve the broadcast address on IPv4 networks large
150 150
 	// enough to have one (i.e., anything bigger than a /31).
151
-	if ipVer == v4 && numAddresses > 2 {
151
+	if pool.Addr().Is4() && numAddresses > 2 {
152 152
 		h.Set(numAddresses - 1)
153 153
 	}
154 154
 
155
-	return &PoolData{Pool: pool, addrs: h, children: map[string]struct{}{}}
155
+	return &PoolData{addrs: h, children: map[netip.Prefix]struct{}{}}
156 156
 }
157 157
 
158 158
 // getPredefineds returns the predefined subnets for the address space.
159 159
 //
160 160
 // It should not be called concurrently with any other method on the addrSpace.
161
-func (aSpace *addrSpace) getPredefineds() []*net.IPNet {
161
+func (aSpace *addrSpace) getPredefineds() []netip.Prefix {
162 162
 	i := aSpace.predefinedStartIndex
163 163
 	// defensive in case the list changed since last update
164 164
 	if i >= len(aSpace.predefined) {
... ...
@@ -178,37 +192,35 @@ func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
178 178
 	aSpace.predefinedStartIndex = i
179 179
 }
180 180
 
181
-func (aSpace *addrSpace) allocatePredefinedPool(ipV6 bool) (*net.IPNet, SubnetKey, error) {
182
-	var v ipVersion
183
-	v = v4
184
-	if ipV6 {
185
-		v = v6
186
-	}
187
-
181
+func (aSpace *addrSpace) allocatePredefinedPool(ipV6 bool) (netip.Prefix, error) {
188 182
 	aSpace.Lock()
189 183
 	defer aSpace.Unlock()
190 184
 
191 185
 	for i, nw := range aSpace.getPredefineds() {
192
-		if v != getAddressVersion(nw.IP) {
186
+		if ipV6 != nw.Addr().Is6() {
193 187
 			continue
194 188
 		}
195 189
 		// Checks whether pool has already been allocated
196
-		if _, ok := aSpace.subnets[nw.String()]; ok {
190
+		if _, ok := aSpace.subnets[nw]; ok {
197 191
 			continue
198 192
 		}
199 193
 		// Shouldn't be necessary, but check prevents IP collisions should
200 194
 		// predefined pools overlap for any reason.
201 195
 		if !aSpace.contains(nw) {
202 196
 			aSpace.updatePredefinedStartIndex(i + 1)
203
-			k, err := aSpace.allocateSubnetL(nw, nil)
197
+			err := aSpace.allocateSubnetL(nw, netip.Prefix{})
204 198
 			if err != nil {
205
-				return nil, SubnetKey{}, err
199
+				return netip.Prefix{}, err
206 200
 			}
207
-			return nw, k, nil
201
+			return nw, nil
208 202
 		}
209 203
 	}
210 204
 
211
-	return nil, SubnetKey{}, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
205
+	v := 4
206
+	if ipV6 {
207
+		v = 6
208
+	}
209
+	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)
212 210
 }
213 211
 
214 212
 // RequestAddress returns an address from the specified pool ID
... ...
@@ -223,46 +235,52 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
223 223
 	if err != nil {
224 224
 		return nil, nil, err
225 225
 	}
226
-	return aSpace.requestAddress(k.SubnetKey, prefAddress, opts)
226
+	var pref netip.Addr
227
+	if prefAddress != nil {
228
+		var ok bool
229
+		pref, ok = netip.AddrFromSlice(prefAddress)
230
+		if !ok {
231
+			return nil, nil, types.BadRequestErrorf("invalid preferred address: %v", prefAddress)
232
+		}
233
+	}
234
+	p, err := aSpace.requestAddress(k.Subnet, k.ChildSubnet, pref.Unmap(), opts)
235
+	if err != nil {
236
+		return nil, nil, err
237
+	}
238
+	return &net.IPNet{
239
+		IP:   p.AsSlice(),
240
+		Mask: net.CIDRMask(k.Subnet.Bits(), k.Subnet.Addr().BitLen()),
241
+	}, nil, nil
227 242
 }
228 243
 
229
-func (aSpace *addrSpace) requestAddress(k SubnetKey, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
244
+func (aSpace *addrSpace) requestAddress(nw, sub netip.Prefix, prefAddress netip.Addr, opts map[string]string) (netip.Addr, error) {
230 245
 	aSpace.Lock()
231 246
 	defer aSpace.Unlock()
232 247
 
233
-	p, ok := aSpace.subnets[k.Subnet]
248
+	p, ok := aSpace.subnets[nw]
234 249
 	if !ok {
235
-		return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%+v", k)
250
+		return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
236 251
 	}
237 252
 
238
-	if prefAddress != nil && !p.Pool.Contains(prefAddress) {
239
-		return nil, nil, ipamapi.ErrIPOutOfRange
253
+	if prefAddress != (netip.Addr{}) && !nw.Contains(prefAddress) {
254
+		return netip.Addr{}, ipamapi.ErrIPOutOfRange
240 255
 	}
241 256
 
242
-	var ipr *AddressRange
243
-	if k.ChildSubnet != "" {
244
-		if _, ok := p.children[k.ChildSubnet]; !ok {
245
-			return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%+v", k)
246
-		}
247
-		_, sub, err := net.ParseCIDR(k.ChildSubnet)
248
-		if err != nil {
249
-			return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%+v: %v", k, err)
250
-		}
251
-		ipr, err = getAddressRange(sub, p.Pool)
252
-		if err != nil {
253
-			return nil, nil, err
257
+	if sub != (netip.Prefix{}) {
258
+		if _, ok := p.children[sub]; !ok {
259
+			return netip.Addr{}, types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
254 260
 		}
255 261
 	}
256 262
 
257 263
 	// In order to request for a serial ip address allocation, callers can pass in the option to request
258 264
 	// IP allocation serially or first available IP in the subnet
259 265
 	serial := opts[ipamapi.AllocSerialPrefix] == "true"
260
-	ip, err := getAddress(p.Pool, p.addrs, prefAddress, ipr, serial)
266
+	ip, err := getAddress(nw, p.addrs, prefAddress, sub, serial)
261 267
 	if err != nil {
262
-		return nil, nil, err
268
+		return netip.Addr{}, err
263 269
 	}
264 270
 
265
-	return &net.IPNet{IP: ip, Mask: p.Pool.Mask}, nil, nil
271
+	return ip, nil
266 272
 }
267 273
 
268 274
 // ReleaseAddress releases the address from the specified pool ID
... ...
@@ -278,79 +296,72 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
278 278
 		return err
279 279
 	}
280 280
 
281
-	return aSpace.releaseAddress(k.SubnetKey, address)
281
+	addr, ok := netip.AddrFromSlice(address)
282
+	if !ok {
283
+		return types.BadRequestErrorf("invalid address: %v", address)
284
+	}
285
+
286
+	return aSpace.releaseAddress(k.Subnet, k.ChildSubnet, addr.Unmap())
282 287
 }
283 288
 
284
-func (aSpace *addrSpace) releaseAddress(k SubnetKey, address net.IP) error {
289
+func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr) error {
285 290
 	aSpace.Lock()
286 291
 	defer aSpace.Unlock()
287 292
 
288
-	p, ok := aSpace.subnets[k.Subnet]
293
+	p, ok := aSpace.subnets[nw]
289 294
 	if !ok {
290
-		return types.NotFoundErrorf("cannot find address pool for %+v", k)
295
+		return types.NotFoundErrorf("cannot find address pool for %v/%v", nw, sub)
291 296
 	}
292
-	if k.ChildSubnet != "" {
293
-		if _, ok := p.children[k.ChildSubnet]; !ok {
294
-			return types.NotFoundErrorf("cannot find address pool for poolID:%+v", k)
297
+	if sub != (netip.Prefix{}) {
298
+		if _, ok := p.children[sub]; !ok {
299
+			return types.NotFoundErrorf("cannot find address pool for poolID:%v/%v", nw, sub)
295 300
 		}
296 301
 	}
297 302
 
298
-	if address == nil {
299
-		return types.BadRequestErrorf("invalid address: nil")
303
+	if !address.IsValid() {
304
+		return types.BadRequestErrorf("invalid address")
300 305
 	}
301 306
 
302
-	if !p.Pool.Contains(address) {
307
+	if !nw.Contains(address) {
303 308
 		return ipamapi.ErrIPOutOfRange
304 309
 	}
305 310
 
306
-	mask := p.Pool.Mask
307
-
308
-	h, err := types.GetHostPartIP(address, mask)
309
-	if err != nil {
310
-		return types.InternalErrorf("failed to release address %s: %v", address, err)
311
-	}
312
-
313 311
 	defer logrus.Debugf("Released address Address:%v Sequence:%s", address, p.addrs)
314 312
 
315
-	return p.addrs.Unset(ipToUint64(h))
313
+	return p.addrs.Unset(hostID(address, uint(nw.Bits())))
316 314
 }
317 315
 
318
-func getAddress(nw *net.IPNet, bitmask *bitmap.Bitmap, prefAddress net.IP, ipr *AddressRange, serial bool) (net.IP, error) {
316
+func getAddress(base netip.Prefix, bitmask *bitmap.Bitmap, prefAddress netip.Addr, ipr netip.Prefix, serial bool) (netip.Addr, error) {
319 317
 	var (
320 318
 		ordinal uint64
321 319
 		err     error
322
-		base    *net.IPNet
323 320
 	)
324 321
 
325
-	logrus.Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", nw, bitmask, serial, prefAddress)
326
-	base = types.GetIPNetCopy(nw)
322
+	logrus.Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", base, bitmask, serial, prefAddress)
327 323
 
328 324
 	if bitmask.Unselected() == 0 {
329
-		return nil, ipamapi.ErrNoAvailableIPs
325
+		return netip.Addr{}, ipamapi.ErrNoAvailableIPs
330 326
 	}
331
-	if ipr == nil && prefAddress == nil {
327
+	if ipr == (netip.Prefix{}) && prefAddress == (netip.Addr{}) {
332 328
 		ordinal, err = bitmask.SetAny(serial)
333
-	} else if prefAddress != nil {
334
-		hostPart, e := types.GetHostPartIP(prefAddress, base.Mask)
335
-		if e != nil {
336
-			return nil, types.InternalErrorf("failed to allocate requested address %s: %v", prefAddress.String(), e)
337
-		}
338
-		ordinal = ipToUint64(types.GetMinimalIP(hostPart))
329
+	} else if prefAddress != (netip.Addr{}) {
330
+		ordinal = hostID(prefAddress, uint(base.Bits()))
339 331
 		err = bitmask.Set(ordinal)
340 332
 	} else {
341
-		ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End, serial)
333
+		start, end := subnetRange(base, ipr)
334
+		ordinal, err = bitmask.SetAnyInRange(start, end, serial)
342 335
 	}
343 336
 
344 337
 	switch err {
345 338
 	case nil:
346 339
 		// Convert IP ordinal for this subnet into IP address
347
-		return generateAddress(ordinal, base), nil
340
+		return ipbits.Add(base.Addr(), ordinal, 0), nil
348 341
 	case bitmap.ErrBitAllocated:
349
-		return nil, ipamapi.ErrIPAlreadyAllocated
342
+		return netip.Addr{}, ipamapi.ErrIPAlreadyAllocated
350 343
 	case bitmap.ErrNoBitAvailable:
351
-		return nil, ipamapi.ErrNoAvailableIPs
344
+		return netip.Addr{}, ipamapi.ErrNoAvailableIPs
352 345
 	default:
353
-		return nil, err
346
+		return netip.Addr{}, err
354 347
 	}
355 348
 }
356 349
 
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"fmt"
7 7
 	"math/rand"
8 8
 	"net"
9
+	"net/netip"
9 10
 	"runtime"
10 11
 	"strconv"
11 12
 	"sync"
... ...
@@ -21,34 +22,8 @@ import (
21 21
 	is "gotest.tools/v3/assert/cmp"
22 22
 )
23 23
 
24
-func TestInt2IP2IntConversion(t *testing.T) {
25
-	for i := uint64(0); i < 256*256*256; i++ {
26
-		var array [4]byte // new array at each cycle
27
-		addIntToIP(array[:], i)
28
-		j := ipToUint64(array[:])
29
-		if j != i {
30
-			t.Fatalf("Failed to convert ordinal %d to IP % x and back to ordinal. Got %d", i, array, j)
31
-		}
32
-	}
33
-}
34
-
35
-func TestGetAddressVersion(t *testing.T) {
36
-	if v4 != getAddressVersion(net.ParseIP("172.28.30.112")) {
37
-		t.Fatal("Failed to detect IPv4 version")
38
-	}
39
-	if v4 != getAddressVersion(net.ParseIP("0.0.0.1")) {
40
-		t.Fatal("Failed to detect IPv4 version")
41
-	}
42
-	if v6 != getAddressVersion(net.ParseIP("ff01::1")) {
43
-		t.Fatal("Failed to detect IPv6 version")
44
-	}
45
-	if v6 != getAddressVersion(net.ParseIP("2001:db8::76:51")) {
46
-		t.Fatal("Failed to detect IPv6 version")
47
-	}
48
-}
49
-
50 24
 func TestKeyString(t *testing.T) {
51
-	k := &PoolID{AddressSpace: "default", SubnetKey: SubnetKey{Subnet: "172.27.0.0/16"}}
25
+	k := &PoolID{AddressSpace: "default", SubnetKey: SubnetKey{Subnet: netip.MustParsePrefix("172.27.0.0/16")}}
52 26
 	expected := "default/172.27.0.0/16"
53 27
 	if expected != k.String() {
54 28
 		t.Fatalf("Unexpected key string: %s", k.String())
... ...
@@ -64,7 +39,7 @@ func TestKeyString(t *testing.T) {
64 64
 	}
65 65
 
66 66
 	expected = fmt.Sprintf("%s/%s", expected, "172.27.3.0/24")
67
-	k.ChildSubnet = "172.27.3.0/24"
67
+	k.ChildSubnet = netip.MustParsePrefix("172.27.3.0/24")
68 68
 	if expected != k.String() {
69 69
 		t.Fatalf("Unexpected key string: %s", k.String())
70 70
 	}
... ...
@@ -495,7 +470,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
495 495
 		t.Fatal(err)
496 496
 	}
497 497
 	if !types.CompareIPNet(tre, treExp) {
498
-		t.Fatalf("Unexpected address: %v", tre)
498
+		t.Fatalf("Unexpected address: want %v, got %v", treExp, tre)
499 499
 	}
500 500
 
501 501
 	uno, _, err := a.RequestAddress(poolID, nil, nil)
... ...
@@ -619,7 +594,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
619 619
 		t.Fatal(err)
620 620
 	}
621 621
 	if !types.CompareIPNet(tre, treExp) {
622
-		t.Fatalf("Unexpected address: %v", tre)
622
+		t.Fatalf("Unexpected address: want %v, got %v", treExp, tre)
623 623
 	}
624 624
 
625 625
 	uno, _, err := a.RequestAddress(poolID, nil, opts)
... ...
@@ -954,7 +929,7 @@ func TestRelease(t *testing.T) {
954 954
 	for i, inp := range toRelease {
955 955
 		ip0 := net.ParseIP(inp.address)
956 956
 		a.ReleaseAddress(pid, ip0)
957
-		bm := a.local.subnets[subnet].addrs
957
+		bm := a.local.subnets[netip.MustParsePrefix(subnet)].addrs
958 958
 		if bm.Unselected() != 1 {
959 959
 			t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected())
960 960
 		}
... ...
@@ -976,8 +951,8 @@ func assertGetAddress(t *testing.T, subnet string) {
976 976
 		printTime = false
977 977
 	)
978 978
 
979
-	_, sub, _ := net.ParseCIDR(subnet)
980
-	ones, bits := sub.Mask.Size()
979
+	sub := netip.MustParsePrefix(subnet)
980
+	ones, bits := sub.Bits(), sub.Addr().BitLen()
981 981
 	zeroes := bits - ones
982 982
 	numAddresses := 1 << uint(zeroes)
983 983
 
... ...
@@ -986,7 +961,7 @@ func assertGetAddress(t *testing.T, subnet string) {
986 986
 	start := time.Now()
987 987
 	run := 0
988 988
 	for err != ipamapi.ErrNoAvailableIPs {
989
-		_, err = getAddress(sub, bm, nil, nil, false)
989
+		_, err = getAddress(sub, bm, netip.Addr{}, netip.Prefix{}, false)
990 990
 		run++
991 991
 	}
992 992
 	if printTime {
... ...
@@ -2,7 +2,7 @@ package ipam
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"net"
5
+	"net/netip"
6 6
 	"strings"
7 7
 	"sync"
8 8
 
... ...
@@ -19,9 +19,8 @@ type PoolID struct {
19 19
 
20 20
 // PoolData contains the configured pool data
21 21
 type PoolData struct {
22
-	Pool     *net.IPNet
23 22
 	addrs    *bitmap.Bitmap
24
-	children map[string]struct{}
23
+	children map[netip.Prefix]struct{}
25 24
 
26 25
 	// Whether to implicitly release the pool once it no longer has any children.
27 26
 	autoRelease bool
... ...
@@ -29,37 +28,25 @@ type PoolData struct {
29 29
 
30 30
 // SubnetKey is the composite key to an address pool within an address space.
31 31
 type SubnetKey struct {
32
-	Subnet, ChildSubnet string
32
+	Subnet, ChildSubnet netip.Prefix
33 33
 }
34 34
 
35 35
 // addrSpace contains the pool configurations for the address space
36 36
 type addrSpace struct {
37 37
 	// Master subnet pools, indexed by the value's stringified PoolData.Pool field.
38
-	subnets map[string]*PoolData
38
+	subnets map[netip.Prefix]*PoolData
39 39
 
40 40
 	// Predefined pool for the address space
41
-	predefined           []*net.IPNet
41
+	predefined           []netip.Prefix
42 42
 	predefinedStartIndex int
43 43
 
44 44
 	sync.Mutex
45 45
 }
46 46
 
47
-// AddressRange specifies first and last ip ordinal which
48
-// identifies a range in a pool of addresses
49
-type AddressRange struct {
50
-	Sub        *net.IPNet
51
-	Start, End uint64
52
-}
53
-
54
-// String returns the string form of the AddressRange object
55
-func (r *AddressRange) String() string {
56
-	return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
57
-}
58
-
59 47
 // String returns the string form of the SubnetKey object
60 48
 func (s *PoolID) String() string {
61 49
 	k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
62
-	if s.ChildSubnet != "" {
50
+	if s.ChildSubnet != (netip.Prefix{}) {
63 51
 		k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
64 52
 	}
65 53
 	return k
... ...
@@ -75,104 +62,111 @@ func (s *PoolID) FromString(str string) error {
75 75
 	if len(p) != 3 && len(p) != 5 {
76 76
 		return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
77 77
 	}
78
-	s.AddressSpace = p[0]
79
-	s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
78
+	sub, err := netip.ParsePrefix(p[1] + "/" + p[2])
79
+	if err != nil {
80
+		return types.BadRequestErrorf("%v", err)
81
+	}
82
+	var child netip.Prefix
80 83
 	if len(p) == 5 {
81
-		s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
84
+		child, err = netip.ParsePrefix(p[3] + "/" + p[4])
85
+		if err != nil {
86
+			return types.BadRequestErrorf("%v", err)
87
+		}
82 88
 	}
83 89
 
90
+	*s = PoolID{
91
+		AddressSpace: p[0],
92
+		SubnetKey: SubnetKey{
93
+			Subnet:      sub,
94
+			ChildSubnet: child,
95
+		},
96
+	}
84 97
 	return nil
85 98
 }
86 99
 
87 100
 // String returns the string form of the PoolData object
88 101
 func (p *PoolData) String() string {
89
-	return fmt.Sprintf("Pool: %s, Children: %d",
90
-		p.Pool.String(), len(p.children))
102
+	return fmt.Sprintf("PoolData[Children: %d]", len(p.children))
91 103
 }
92 104
 
93 105
 // allocateSubnet adds the subnet k to the address space.
94
-func (aSpace *addrSpace) allocateSubnet(nw, sub *net.IPNet) (SubnetKey, error) {
106
+func (aSpace *addrSpace) allocateSubnet(nw, sub netip.Prefix) error {
95 107
 	aSpace.Lock()
96 108
 	defer aSpace.Unlock()
97 109
 
98 110
 	// Check if already allocated
99
-	if pool, ok := aSpace.subnets[nw.String()]; ok {
111
+	if pool, ok := aSpace.subnets[nw]; ok {
100 112
 		var childExists bool
101
-		if sub != nil {
102
-			_, childExists = pool.children[sub.String()]
113
+		if sub != (netip.Prefix{}) {
114
+			_, childExists = pool.children[sub]
103 115
 		}
104
-		if sub == nil || childExists {
116
+		if sub == (netip.Prefix{}) || childExists {
105 117
 			// This means the same pool is already allocated. allocateSubnet is called when there
106 118
 			// is request for a pool/subpool. It should ensure there is no overlap with existing pools
107
-			return SubnetKey{}, ipamapi.ErrPoolOverlap
119
+			return ipamapi.ErrPoolOverlap
108 120
 		}
109 121
 	}
110 122
 
111 123
 	return aSpace.allocateSubnetL(nw, sub)
112 124
 }
113 125
 
114
-func (aSpace *addrSpace) allocateSubnetL(nw, sub *net.IPNet) (SubnetKey, error) {
126
+func (aSpace *addrSpace) allocateSubnetL(nw, sub netip.Prefix) error {
115 127
 	// If master pool, check for overlap
116
-	if sub == nil {
128
+	if sub == (netip.Prefix{}) {
117 129
 		if aSpace.contains(nw) {
118
-			return SubnetKey{}, ipamapi.ErrPoolOverlap
130
+			return ipamapi.ErrPoolOverlap
119 131
 		}
120
-		k := SubnetKey{Subnet: nw.String()}
121 132
 		// This is a new master pool, add it along with corresponding bitmask
122
-		aSpace.subnets[k.Subnet] = newPoolData(nw)
123
-		return k, nil
133
+		aSpace.subnets[nw] = newPoolData(nw)
134
+		return nil
124 135
 	}
125 136
 
126 137
 	// This is a new non-master pool (subPool)
127
-
128
-	_, err := getAddressRange(sub, nw)
129
-	if err != nil {
130
-		return SubnetKey{}, err
138
+	if nw.Addr().BitLen() != sub.Addr().BitLen() {
139
+		return fmt.Errorf("pool and subpool are of incompatible address families")
131 140
 	}
132 141
 
133
-	k := SubnetKey{Subnet: nw.String(), ChildSubnet: sub.String()}
134
-
135 142
 	// Look for parent pool
136
-	pp, ok := aSpace.subnets[k.Subnet]
143
+	pp, ok := aSpace.subnets[nw]
137 144
 	if !ok {
138 145
 		// Parent pool does not exist, add it along with corresponding bitmask
139 146
 		pp = newPoolData(nw)
140 147
 		pp.autoRelease = true
141
-		aSpace.subnets[k.Subnet] = pp
148
+		aSpace.subnets[nw] = pp
142 149
 	}
143
-	pp.children[k.ChildSubnet] = struct{}{}
144
-	return k, nil
150
+	pp.children[sub] = struct{}{}
151
+	return nil
145 152
 }
146 153
 
147
-func (aSpace *addrSpace) releaseSubnet(k SubnetKey) error {
154
+func (aSpace *addrSpace) releaseSubnet(nw, sub netip.Prefix) error {
148 155
 	aSpace.Lock()
149 156
 	defer aSpace.Unlock()
150 157
 
151
-	p, ok := aSpace.subnets[k.Subnet]
158
+	p, ok := aSpace.subnets[nw]
152 159
 	if !ok {
153 160
 		return ipamapi.ErrBadPool
154 161
 	}
155 162
 
156
-	if k.ChildSubnet != "" {
157
-		if _, ok := p.children[k.ChildSubnet]; !ok {
163
+	if sub != (netip.Prefix{}) {
164
+		if _, ok := p.children[sub]; !ok {
158 165
 			return ipamapi.ErrBadPool
159 166
 		}
160
-		delete(p.children, k.ChildSubnet)
167
+		delete(p.children, sub)
161 168
 	} else {
162 169
 		p.autoRelease = true
163 170
 	}
164 171
 
165 172
 	if len(p.children) == 0 && p.autoRelease {
166
-		delete(aSpace.subnets, k.Subnet)
173
+		delete(aSpace.subnets, nw)
167 174
 	}
168 175
 
169 176
 	return nil
170 177
 }
171 178
 
172 179
 // contains checks whether nw is a superset or subset of any of the existing subnets in this address space.
173
-func (aSpace *addrSpace) contains(nw *net.IPNet) bool {
174
-	for _, v := range aSpace.subnets {
175
-		if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
180
+func (aSpace *addrSpace) contains(nw netip.Prefix) bool {
181
+	for pool := range aSpace.subnets {
182
+		if nw.Contains(pool.Addr()) || pool.Contains(nw.Addr()) {
176 183
 			return true
177 184
 		}
178 185
 	}
... ...
@@ -1,75 +1,48 @@
1 1
 package ipam
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	"net"
5
+	"net/netip"
6 6
 
7
-	"github.com/docker/docker/libnetwork/types"
7
+	"github.com/docker/docker/libnetwork/ipbits"
8 8
 )
9 9
 
10
-type ipVersion int
11
-
12
-const (
13
-	v4 = 4
14
-	v6 = 6
15
-)
16
-
17
-func getAddressRange(nw, masterNw *net.IPNet) (*AddressRange, error) {
18
-	lIP, e := types.GetHostPartIP(nw.IP, masterNw.Mask)
19
-	if e != nil {
20
-		return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
10
+func toIPNet(p netip.Prefix) *net.IPNet {
11
+	if !p.IsValid() {
12
+		return nil
21 13
 	}
22
-	bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask)
23
-	if e != nil {
24
-		return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
14
+	return &net.IPNet{
15
+		IP:   p.Addr().AsSlice(),
16
+		Mask: net.CIDRMask(p.Bits(), p.Addr().BitLen()),
25 17
 	}
26
-	hIP, e := types.GetHostPartIP(bIP, masterNw.Mask)
27
-	if e != nil {
28
-		return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
29
-	}
30
-	return &AddressRange{nw, ipToUint64(types.GetMinimalIP(lIP)), ipToUint64(types.GetMinimalIP(hIP))}, nil
31 18
 }
32 19
 
33
-// It generates the ip address in the passed subnet specified by
34
-// the passed host address ordinal
35
-func generateAddress(ordinal uint64, network *net.IPNet) net.IP {
36
-	var address [16]byte
37
-
38
-	// Get network portion of IP
39
-	if getAddressVersion(network.IP) == v4 {
40
-		copy(address[:], network.IP.To4())
41
-	} else {
42
-		copy(address[:], network.IP)
20
+func toPrefix(n *net.IPNet) (netip.Prefix, bool) {
21
+	if ll := len(n.Mask); ll != net.IPv4len && ll != net.IPv6len {
22
+		return netip.Prefix{}, false
43 23
 	}
44 24
 
45
-	end := len(network.Mask)
46
-	addIntToIP(address[:end], ordinal)
47
-
48
-	return net.IP(address[:end])
49
-}
25
+	addr, ok := netip.AddrFromSlice(n.IP)
26
+	if !ok {
27
+		return netip.Prefix{}, false
28
+	}
50 29
 
51
-func getAddressVersion(ip net.IP) ipVersion {
52
-	if ip.To4() == nil {
53
-		return v6
30
+	ones, bits := n.Mask.Size()
31
+	if ones == 0 && bits == 0 {
32
+		return netip.Prefix{}, false
54 33
 	}
55
-	return v4
34
+
35
+	return netip.PrefixFrom(addr.Unmap(), ones), true
56 36
 }
57 37
 
58
-// Adds the ordinal IP to the current array
59
-// 192.168.0.0 + 53 => 192.168.0.53
60
-func addIntToIP(array []byte, ordinal uint64) {
61
-	for i := len(array) - 1; i >= 0; i-- {
62
-		array[i] |= (byte)(ordinal & 0xff)
63
-		ordinal >>= 8
64
-	}
38
+func hostID(addr netip.Addr, bits uint) uint64 {
39
+	return ipbits.Field(addr, bits, uint(addr.BitLen()))
65 40
 }
66 41
 
67
-// Convert an ordinal to the respective IP address
68
-func ipToUint64(ip []byte) (value uint64) {
69
-	cip := types.GetMinimalIP(ip)
70
-	for i := 0; i < len(cip); i++ {
71
-		j := len(cip) - 1 - i
72
-		value += uint64(cip[i]) << uint(j*8)
73
-	}
74
-	return value
42
+// subnetRange returns the amount to add to network.Addr() in order to yield the
43
+// first and last addresses in subnet, respectively.
44
+func subnetRange(network, subnet netip.Prefix) (start, end uint64) {
45
+	start = hostID(subnet.Addr(), uint(network.Bits()))
46
+	end = start + (1 << uint64(subnet.Addr().BitLen()-subnet.Bits())) - 1
47
+	return start, end
75 48
 }
76 49
new file mode 100644
... ...
@@ -0,0 +1,41 @@
0
+// Package ipbits contains utilities for manipulating [netip.Addr] values as
1
+// numbers or bitfields.
2
+package ipbits
3
+
4
+import (
5
+	"encoding/binary"
6
+	"net/netip"
7
+)
8
+
9
+// Add returns ip + (x << shift).
10
+func Add(ip netip.Addr, x uint64, shift uint) netip.Addr {
11
+	if ip.Is4() {
12
+		a := ip.As4()
13
+		addr := binary.BigEndian.Uint32(a[:])
14
+		addr += uint32(x) << shift
15
+		binary.BigEndian.PutUint32(a[:], addr)
16
+		return netip.AddrFrom4(a)
17
+	} else {
18
+		a := ip.As16()
19
+		addr := uint128From16(a)
20
+		addr = addr.add(uint128From(x).lsh(shift))
21
+		addr.fill16(&a)
22
+		return netip.AddrFrom16(a)
23
+	}
24
+}
25
+
26
+// Field returns the value of the bitfield [u, v] in ip as an integer,
27
+// where bit 0 is the most-significant bit of ip.
28
+//
29
+// The result is undefined if u > v, if v-u > 64, or if u or v is larger than
30
+// ip.BitLen().
31
+func Field(ip netip.Addr, u, v uint) uint64 {
32
+	if ip.Is4() {
33
+		mask := ^uint32(0) >> u
34
+		a := ip.As4()
35
+		return uint64((binary.BigEndian.Uint32(a[:]) & mask) >> (32 - v))
36
+	} else {
37
+		mask := uint128From(0).not().rsh(u)
38
+		return uint128From16(ip.As16()).and(mask).rsh(128 - v).uint64()
39
+	}
40
+}
0 41
new file mode 100644
... ...
@@ -0,0 +1,70 @@
0
+package ipbits
1
+
2
+import (
3
+	"net/netip"
4
+	"testing"
5
+)
6
+
7
+func TestAdd(t *testing.T) {
8
+	tests := []struct {
9
+		in    netip.Addr
10
+		x     uint64
11
+		shift uint
12
+		want  netip.Addr
13
+	}{
14
+		{netip.MustParseAddr("10.0.0.1"), 0, 0, netip.MustParseAddr("10.0.0.1")},
15
+		{netip.MustParseAddr("10.0.0.1"), 41, 0, netip.MustParseAddr("10.0.0.42")},
16
+		{netip.MustParseAddr("10.0.0.1"), 42, 16, netip.MustParseAddr("10.42.0.1")},
17
+		{netip.MustParseAddr("10.0.0.1"), 1, 7, netip.MustParseAddr("10.0.0.129")},
18
+		{netip.MustParseAddr("10.0.0.1"), 1, 24, netip.MustParseAddr("11.0.0.1")},
19
+		{netip.MustParseAddr("2001::1"), 0, 0, netip.MustParseAddr("2001::1")},
20
+		{netip.MustParseAddr("2001::1"), 0x41, 0, netip.MustParseAddr("2001::42")},
21
+		{netip.MustParseAddr("2001::1"), 1, 7, netip.MustParseAddr("2001::81")},
22
+		{netip.MustParseAddr("2001::1"), 0xcafe, 96, netip.MustParseAddr("2001:cafe::1")},
23
+		{netip.MustParseAddr("2001::1"), 1, 112, netip.MustParseAddr("2002::1")},
24
+	}
25
+
26
+	for _, tt := range tests {
27
+		if got := Add(tt.in, tt.x, tt.shift); tt.want != got {
28
+			t.Errorf("%v + (%v << %v) = %v; want %v", tt.in, tt.x, tt.shift, got, tt.want)
29
+		}
30
+	}
31
+}
32
+
33
+func BenchmarkAdd(b *testing.B) {
34
+	do := func(b *testing.B, addr netip.Addr) {
35
+		b.ReportAllocs()
36
+		for i := 0; i < b.N; i++ {
37
+			_ = Add(addr, uint64(i), 0)
38
+		}
39
+	}
40
+
41
+	b.Run("IPv4", func(b *testing.B) { do(b, netip.IPv4Unspecified()) })
42
+	b.Run("IPv6", func(b *testing.B) { do(b, netip.IPv6Unspecified()) })
43
+}
44
+
45
+func TestField(t *testing.T) {
46
+	tests := []struct {
47
+		in   netip.Addr
48
+		u, v uint
49
+		want uint64
50
+	}{
51
+		{netip.MustParseAddr("1.2.3.4"), 0, 8, 1},
52
+		{netip.MustParseAddr("1.2.3.4"), 8, 16, 2},
53
+		{netip.MustParseAddr("1.2.3.4"), 16, 24, 3},
54
+		{netip.MustParseAddr("1.2.3.4"), 24, 32, 4},
55
+		{netip.MustParseAddr("1.2.3.4"), 0, 32, 0x01020304},
56
+		{netip.MustParseAddr("1.2.3.4"), 0, 28, 0x102030},
57
+		{netip.MustParseAddr("1234:5678:9abc:def0::7654:3210"), 0, 8, 0x12},
58
+		{netip.MustParseAddr("1234:5678:9abc:def0::7654:3210"), 8, 16, 0x34},
59
+		{netip.MustParseAddr("1234:5678:9abc:def0::7654:3210"), 16, 24, 0x56},
60
+		{netip.MustParseAddr("1234:5678:9abc:def0::7654:3210"), 64, 128, 0x76543210},
61
+		{netip.MustParseAddr("1234:5678:9abc:def0:beef::7654:3210"), 48, 80, 0xdef0beef},
62
+	}
63
+
64
+	for _, tt := range tests {
65
+		if got := Field(tt.in, tt.u, tt.v); got != tt.want {
66
+			t.Errorf("Field(%v, %v, %v) = %v (0x%[4]x); want %v (0x%[5]x)", tt.in, tt.u, tt.v, got, tt.want)
67
+		}
68
+	}
69
+}
0 70
new file mode 100644
... ...
@@ -0,0 +1,62 @@
0
+package ipbits
1
+
2
+import (
3
+	"encoding/binary"
4
+	"math/bits"
5
+)
6
+
7
+type uint128 struct{ hi, lo uint64 }
8
+
9
+func uint128From16(b [16]byte) uint128 {
10
+	return uint128{
11
+		hi: binary.BigEndian.Uint64(b[:8]),
12
+		lo: binary.BigEndian.Uint64(b[8:]),
13
+	}
14
+}
15
+
16
+func uint128From(x uint64) uint128 {
17
+	return uint128{lo: x}
18
+}
19
+
20
+func (x uint128) add(y uint128) uint128 {
21
+	lo, carry := bits.Add64(x.lo, y.lo, 0)
22
+	hi, _ := bits.Add64(x.hi, y.hi, carry)
23
+	return uint128{hi: hi, lo: lo}
24
+}
25
+
26
+func (x uint128) lsh(n uint) uint128 {
27
+	if n > 64 {
28
+		return uint128{hi: x.lo << (n - 64)}
29
+	}
30
+	return uint128{
31
+		hi: x.hi<<n | x.lo>>(64-n),
32
+		lo: x.lo << n,
33
+	}
34
+}
35
+
36
+func (x uint128) rsh(n uint) uint128 {
37
+	if n > 64 {
38
+		return uint128{lo: x.hi >> (n - 64)}
39
+	}
40
+	return uint128{
41
+		hi: x.hi >> n,
42
+		lo: x.lo>>n | x.hi<<(64-n),
43
+	}
44
+}
45
+
46
+func (x uint128) and(y uint128) uint128 {
47
+	return uint128{hi: x.hi & y.hi, lo: x.lo & y.lo}
48
+}
49
+
50
+func (x uint128) not() uint128 {
51
+	return uint128{hi: ^x.hi, lo: ^x.lo}
52
+}
53
+
54
+func (x uint128) fill16(a *[16]byte) {
55
+	binary.BigEndian.PutUint64(a[:8], x.hi)
56
+	binary.BigEndian.PutUint64(a[8:], x.lo)
57
+}
58
+
59
+func (x uint128) uint64() uint64 {
60
+	return x.lo
61
+}