Go maintainers started to unconditionally update the minimum go version
for golang.org/x/ dependencies to go1.23, which means that we'll no longer
be able to support any version below that when updating those dependencies;
> all: upgrade go directive to at least 1.23.0 [generated]
>
> By now Go 1.24.0 has been released, and Go 1.22 is no longer supported
> per the Go Release Policy (https://go.dev/doc/devel/release#policy).
>
> For golang/go#69095.
This updates our minimum version to go1.23, as we won't be able to maintain
compatibility with older versions because of the above.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
The default IPAM allocator is unable to represent subnets larger than
64-bits (subnets with a smaller prefix), because it uses a Bitmap
that's limited to 64-bits.
When it's used to represent a 64-bit subnet, the top address can't
be allocated (because bitmap.Bitmap is initialised with the number
of bits it needs to represent in a uint64, so it's one short).
The rest of the daemon doesn't know about these limitations, so
strange things happen when a large IPv6 subnet is used.
No errors are reported, addresses/subnets are just set up incorrectly.
The IPAM code calculates offsets into the bitmap itself, details it
shouldn't need to understand and, because it's working on offsets
into a range it doesn't always notice when it's asked to set a bit
outside the range.
It's unusual to need a big subnet but, for example, it may be useful
for modelling an ISP network, or an ISP's gateway may be in a "/56"
subnet that's outside a 64-bit range used by hosts.
So, use addrset.AddrSet instead of bitmap.Bitmap.
Signed-off-by: Rob Murray <rob.murray@docker.com>
libnetwork/ipams/defaultipam/parallel_test.go:231:3: The copy of the 'for' variable "index" can be deleted (Go 1.22+) (copyloopvar)
index := index
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
commit a0807e7cfe configured golangci-lint
to use go1.23 semantics, which alowed linters like `copyloopvar` to lint
using thee correct semantics.
go1.22 now creates a copy of variables when assigned in a loop; make sure we
don't have files that may downgrade semantics to go1.21 in case that also means
disabling that feature; https://go.dev/ref/spec#Go_1.22
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
When defaultipam.newPoolData is asked for a pool of 64-bits
or more, it ends up with an overflowed u64 - so, it just
subtracts one to get a nearly-big-enough range (for a 64-bit
subnet).
When defaultipam.getAddress is called with an ipr (sub-pool
range), the range it calls bitmask.SetAnyInRange with is
exclusive of end. So, its end param can't be MaxUint64,
because that's the max value for the top end of the range
and, when checking the range, SetAnyInRange fails.
When fixed-cidr-v6 behaves more like fixed-cidr, it will ask
for a 64-bit range if that's what fixed-cidr-v6 needs. So,
it hits the bug when allocating an address for, for example:
docker network create --ipv6 --subnet fddd::/64 --ip-range fddd::/64 b46
The additional check for "ipr == base" avoids the issue in
this case, by ignoring the ipr/sub-pool range if ipr is the
same as the pool itself (not really a sub-pool).
But, it still fails when ipr!=base. For example:
docker network create --ipv6 --subnet fddd::/56 --ip-range fddd::/64 b46
So, also subtract one from 'end' if it's going to hit the max
value allowed by the Bitmap.
Signed-off-by: Rob Murray <rob.murray@docker.com>
Keep allocated subnets in-order, so that they're not mistakenly
reallocated due to a gap in the list where misplaced subnets should
have been.
Introduced in 9d288b5.
The iterator over allocated subnets was incremented too early, this
change moves it past three clauses in addrSpace.allocatePredefinedPool().
The three new unit tests correspond to a separate failure caused by
incrementing before each of them.
Signed-off-by: Rob Murray <rob.murray@docker.com>
Looks like some packages fail in go module mode, because they require
recent Go versions:
GO111MODULE=on go test -v
# github.com/docker/docker/libnetwork/ipamutils
../../libnetwork/ipamutils/utils.go:46:9: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipamutils/utils.go:51:9: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
# github.com/docker/docker/libnetwork/portallocator
../../libnetwork/portallocator/portallocator.go:179:7: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
# github.com/docker/docker/libnetwork/netutils
../../libnetwork/netutils/utils_linux.go:66:14: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/netutils/utils_linux.go:75:2: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
# github.com/docker/docker/api/server/router/grpc
../../api/server/router/grpc/grpc.go:56:48: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
# github.com/docker/docker/container
../../container/view.go:335:47: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
# github.com/docker/docker/libnetwork/ipams/defaultipam
../../libnetwork/ipams/defaultipam/address_space.go:33:2: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipams/defaultipam/address_space.go:53:2: clear requires go1.21 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipams/defaultipam/address_space.go:124:10: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipams/defaultipam/address_space.go:125:21: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipams/defaultipam/address_space.go:146:22: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipams/defaultipam/address_space.go:310:14: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/ipams/defaultipam/address_space.go:311:22: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
# github.com/docker/docker/libnetwork/drivers/bridge
../../libnetwork/drivers/bridge/port_mapping_linux.go:76:15: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
../../libnetwork/drivers/bridge/port_mapping_linux.go:201:2: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Until this commit, the default local address pool was initialized by the
defaultipam driver if none was provided by libnet / the daemon.
Now, defaultipam errors out if none is passed and instead the daemon is
made responsible for initializing it with the default values if the user
don'te set the related config parameter.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Nothing was validating whether address pools' `base` prefix
were larger than the target subnet `size` they're associated to. As
such invalid address pools would yield no subnet, the error could go
unnoticed.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
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>
InvalidParameterErrorf was used whenever an invalid value was found
during PoolID unmarshaling. This error is converted to a 400 HTTP code
by the HTTP server.
However, users never provide PoolIDs directly -- these are constructed
from user-supplied values which are already validated when the PoolID is
marshaled. Hence, if such erroneous value is found, it's an internal
error and should be converted to a 500.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Prior to this change PoolID microformat was using slashes to separate
fields. Those fields include subnet prefixes in CIDR notation, which
also include a slash. This makes future evolution harder than it should
be.
This change introduces a 'v2' microformat based on JSON. This has two
advantages:
1. Fields are clearly named to ensure each value is associated to the
right field.
2. Field values and separators are clearly distinguished to remove any
ambiguity.
The 'v1' encoding will be kept until the next major MCR LTS is released.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Prior to this change, cnmallocator would call
`ConfigGlobalScopeDefaultNetworks` right before initializing its
IPAM drivers. This function was mutating some global state used
during drivers init.
This change just remove the global state, and adds an arg to
ipams.Register and defaultipam.Register to pass the global pools
by arguments instead.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
All drivers except the default have a Register function. Before this
change, default's registration was handled by another package. Move
this logic into the driver pkg.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
All drivers except the default ipam driver are stored in ipams/.
Since `default` isn't a valid Go pkg name, this package is
renamed to `defaultipam`, following `windowsipam` example.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>