// Package addrset implements a set of IP addresses. package addrset import ( "errors" "fmt" "math/bits" "net" "net/netip" "strings" "github.com/moby/moby/v2/daemon/internal/netiputil" "github.com/moby/moby/v2/daemon/libnetwork/bitmap" "github.com/moby/moby/v2/daemon/libnetwork/ipbits" ) var ( // ErrNotAvailable is returned when no more addresses are available to set ErrNotAvailable = errors.New("address not available") // ErrAllocated is returned when the specific address requested is already allocated ErrAllocated = errors.New("address already allocated") ) const ( // maxBitsPerBitmap is the max size for a single bitmap in the address set. // // [bitmap.Bitmap] is initialised with a uint64 num-bits. So, it can't contain // enough bits for a 64-bit range (it's one bit short, the last address in the // range can't be represented). If that's fixed, this max can be increased, but // addrsPerBitmap() will need updating to deal with the overflow. // // A max of 63-bits means a 64-bit address range (the norm for IPv6) is // represented by up-to two bitmaps. maxBitsPerBitmap = 63 // minPrefixLen is the prefix length corresponding to maxBitsPerBitmap minPrefixLen = (net.IPv6len * 8) - maxBitsPerBitmap ) // AddrSet is a set of IP addresses. type AddrSet struct { pool netip.Prefix bitmaps map[netip.Prefix]*bitmap.Bitmap } // New returns an AddrSet for the range of addresses in pool. func New(pool netip.Prefix) *AddrSet { return &AddrSet{ pool: pool.Masked(), bitmaps: map[netip.Prefix]*bitmap.Bitmap{}, } } // Add adds address addr to the set. If addr is already in the set, it returns a // wrapped [ErrAllocated]. If addr is not in the set's address range, it returns // an error. func (as *AddrSet) Add(addr netip.Addr) error { if !as.pool.Contains(addr) { return fmt.Errorf("cannot add %s to '%s'", addr, as.pool) } bm, _, err := as.getBitmap(addr) if err != nil { return fmt.Errorf("finding bitmap for %s in '%s': %w", addr, as.pool, err) } bit := netiputil.HostID(addr, as.prefixLenPerBitmap()) if err := bm.Set(bit); err != nil { return fmt.Errorf("setting bit %d for %s in pool '%s': %w", bit, addr, as.pool, mapErr(err)) } return nil } // AddAny adds an arbitrary address to the set, and returns that address. Or, if // no addresses are available, it returns a wrapped [ErrNotAvailable]. // // If the address set's pool contains fewer than 1< maxBitsPerBitmap { bits = maxBitsPerBitmap } bmKey, err := addr.Prefix(as.pool.Addr().BitLen() - bits) if err != nil { return nil, netip.Prefix{}, err } bm, ok := as.bitmaps[bmKey] if !ok { bm = bitmap.New(as.addrsPerBitmap()) as.bitmaps[bmKey] = bm } return bm, bmKey, nil } func (as *AddrSet) addrsPerBitmap() uint64 { bits := as.pool.Addr().BitLen() - as.pool.Bits() if bits > maxBitsPerBitmap { bits = maxBitsPerBitmap } return uint64(1) << bits } func (as *AddrSet) prefixLenPerBitmap() uint { bits := as.pool.Bits() if as.pool.Addr().Is6() && bits < minPrefixLen { return minPrefixLen } return uint(bits) } func mapErr(err error) error { if errors.Is(err, bitmap.ErrBitAllocated) { return ErrAllocated } if errors.Is(err, bitmap.ErrNoBitAvailable) { return ErrNotAvailable } return err }