mirror of
https://github.com/moby/moby.git
synced 2026-07-13 19:12:11 +00:00
The previous allocator was subnetting address pools eagerly when the daemon started, and would then just iterate over that list whenever RequestPool was called. This was leading to high memory usage whenever IPv6 pools were configured with a target subnet size too different from the pools prefix size. For instance: pool = fd00::/8, target size = /64 -- 2 ^ (64-8) subnets would be generated upfront. This would take approx. 9 * 10^18 bits -- way too much for any human computer in 2024. Another noteworthy issue, the previous implementation was allocating a subnet, and then in another layer was checking whether the allocation was conflicting with some 'reserved networks'. If so, the allocation would be retried, etc... To make it worse, 'reserved networks' would be recomputed on every iteration. This is totally ineffective as there could be 'reserved networks' that fully overlap a given address pool (or many!). To fix this issue, a new field `Exclude` is added to `RequestPool`. It's up to each driver to take it into account. Since we don't know whether this retry loop is useful for some remote IPAM driver, it's reimplemented bug-for-bug directly in the remote driver. The new allocator uses a linear-search algorithm. It takes advantage of all lists (predefined pools, allocated subnets and reserved networks) being sorted and logically combines 'allocated' and 'reserved' through a 'double cursor' to iterate on both lists at the same time while preserving the total order. At the same time, it iterates over 'predefined' pools and looks for the first empty space that would be a good fit. Currently, the size of the allocated subnet is still dictated by each 'predefined' pools. We should consider hardcoding that size instead, and let users specify what subnet size they want. This wasn't possible before as the subnets were generated upfront. This new allocator should be able to deal with this easily. The method used for static allocation has been updated to make sure the ascending order of 'allocated' is preserved. It's bug-for-bug compatible with the previous implementation. One consequence of this new algorithm is that we don't keep track of where the last allocation happened, we just allocate the first free subnet we find. Before: - Allocate: 10.0.1.0/24, 10.0.2.0/24 ; Deallocate: 10.0.1.0/24 ; Allocate 10.0.3.0/24. Now, the 3rd allocation would yield 10.0.1.0/24 once again. As it doesn't change the semantics of the allocator, there's no reason to worry about that. Finally, about 'reserved networks'. The heuristics we use are now properly documented. It was discovered that we don't check routes for IPv6 allocations -- this can't be changed because there's no such thing as on-link routes for IPv6. (Kudos to Rob Murray for coming up with the linear-search idea.) Signed-off-by: Albin Kerouanton <albinker@gmail.com>
108 lines
4.8 KiB
Go
108 lines
4.8 KiB
Go
// Package ipamapi specifies the contract the IPAM service (built-in or remote) needs to satisfy.
|
|
package ipamapi
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
|
|
"github.com/docker/docker/libnetwork/types"
|
|
)
|
|
|
|
// IPAM plugin types
|
|
const (
|
|
// PluginEndpointType represents the Endpoint Type used by Plugin system
|
|
PluginEndpointType = "IpamDriver"
|
|
// RequestAddressType represents the Address Type used when requesting an address
|
|
RequestAddressType = "RequestAddressType"
|
|
)
|
|
|
|
// Registerer provides a callback interface for registering IPAM instances into libnetwork.
|
|
type Registerer interface {
|
|
// RegisterIpamDriver provides a way for drivers to dynamically register with libnetwork
|
|
RegisterIpamDriver(name string, driver Ipam) error
|
|
// RegisterIpamDriverWithCapabilities provides a way for drivers to dynamically register with libnetwork and specify capabilities
|
|
RegisterIpamDriverWithCapabilities(name string, driver Ipam, capability *Capability) error
|
|
}
|
|
|
|
// Well-known errors returned by IPAM
|
|
var (
|
|
ErrInvalidAddressSpace = types.InvalidParameterErrorf("invalid address space")
|
|
ErrInvalidPool = types.InvalidParameterErrorf("invalid address pool")
|
|
ErrInvalidSubPool = types.InvalidParameterErrorf("invalid address subpool")
|
|
ErrNoAvailableIPs = types.UnavailableErrorf("no available addresses on this pool")
|
|
ErrNoIPReturned = types.UnavailableErrorf("no address returned")
|
|
ErrIPAlreadyAllocated = types.ForbiddenErrorf("Address already in use")
|
|
ErrIPOutOfRange = types.InvalidParameterErrorf("requested address is out of range")
|
|
ErrPoolOverlap = types.ForbiddenErrorf("Pool overlaps with other one on this address space")
|
|
ErrBadPool = types.InvalidParameterErrorf("address space does not contain specified address pool")
|
|
ErrNoMoreSubnets = types.InvalidParameterErrorf("all predefined address pools have been fully subnetted")
|
|
)
|
|
|
|
// Ipam represents the interface the IPAM service plugins must implement
|
|
// in order to allow injection/modification of IPAM database.
|
|
type Ipam interface {
|
|
// GetDefaultAddressSpaces returns the default local and global address spaces for this ipam
|
|
GetDefaultAddressSpaces() (string, string, error)
|
|
// RequestPool allocate an address pool either statically or dynamically
|
|
// based on req.
|
|
RequestPool(req PoolRequest) (AllocatedPool, error)
|
|
// ReleasePool releases the address pool identified by the passed id
|
|
ReleasePool(poolID string) error
|
|
// RequestAddress request an address from the specified pool ID. Input options or required IP can be passed.
|
|
RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error)
|
|
// ReleaseAddress releases the address from the specified pool ID.
|
|
ReleaseAddress(string, net.IP) error
|
|
|
|
// IsBuiltIn returns true if it is a built-in driver.
|
|
IsBuiltIn() bool
|
|
}
|
|
|
|
type PoolRequest struct {
|
|
// AddressSpace is a mandatory field which denotes which block of pools
|
|
// should be used to make the allocation. This value is opaque, and only
|
|
// the IPAM driver can interpret it. Each driver might support a different
|
|
// set of AddressSpace.
|
|
AddressSpace string
|
|
// Pool is a prefix in CIDR notation. It's non-mandatory. When specified
|
|
// the Pool will be statically allocated. The Pool is used for dynamic
|
|
// address allocation -- except when SubPool is specified.
|
|
Pool string
|
|
// SubPool is a subnet from Pool, in CIDR notation too. It's non-mandatory.
|
|
// When specified, it represents the subnet where addresses will be
|
|
// dynamically allocated. It can't be specified if Pool isn't specified.
|
|
SubPool string
|
|
// Options is a map of opaque k/v passed to the driver. It's non-mandatory.
|
|
// Drivers are free to ignore it.
|
|
Options map[string]string
|
|
// Exclude is a list of prefixes the requester wish to not be dynamically
|
|
// allocated (ie. when Pool isn't specified). It's up to the IPAM driver to
|
|
// take it into account, or totally ignore it. It's required to be sorted.
|
|
Exclude []netip.Prefix
|
|
// V6 indicates which address family should be used to dynamically allocate
|
|
// a prefix (ie. when Pool isn't specified).
|
|
V6 bool
|
|
}
|
|
|
|
type AllocatedPool struct {
|
|
// PoolID represents the ID of the allocated pool. Its value is opaque and
|
|
// shouldn't be interpreted by anything but the IPAM driver that generated
|
|
// it.
|
|
PoolID string
|
|
// Pool is the allocated prefix.
|
|
Pool netip.Prefix
|
|
// Meta represents a list of extra IP addresses automatically reserved
|
|
// during the pool allocation. These are generally keyed by well-known
|
|
// strings defined in the netlabel package.
|
|
Meta map[string]string
|
|
}
|
|
|
|
// Capability represents the requirements and capabilities of the IPAM driver
|
|
type Capability struct {
|
|
// Whether on address request, libnetwork must
|
|
// specify the endpoint MAC address
|
|
RequiresMACAddress bool
|
|
// Whether of daemon start, libnetwork must replay the pool
|
|
// request and the address request for current local networks
|
|
RequiresRequestReplay bool
|
|
}
|