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>
| ... | ... |
@@ -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 |
+} |