22 Commits

Author SHA1 Message Date
Matthieu MOREL
6d737371b8 fix comparison rule from errorlint
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2025-06-13 08:26:56 +00:00
Sebastiaan van Stijn
7c52c4d92e update go:build tags to go1.23 to align with vendor.mod
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>
2025-04-17 15:43:19 +02:00
Rob Murray
366f2b563d Clarify code/comments in defaultipam.newPoolData
The first address in an IPv6 range was reserved, but that wasn't
clear from comments or the code.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2025-01-20 16:48:46 +00:00
Rob Murray
bc93bba099 Use addrset.AddrSet instead of bitmap.Bitmap in IPAM
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>
2025-01-20 16:48:46 +00:00
Sebastiaan van Stijn
d885d097ef libnetwork/ipams/defaultipam: remove redundant capturing of loop vars (copyloopvar)
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>
2024-11-12 14:02:15 +01:00
Sebastiaan van Stijn
b453aa65fa update go:build tags to use go1.22
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>
2024-11-12 14:02:09 +01:00
Rob Murray
496b457ad8 Allow --ip-range ending on a 64-bit boundary
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>
2024-08-12 15:36:32 +01:00
Rob Murray
4de54ee14c Fix duplicate subnet allocations
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>
2024-06-28 13:34:35 +01:00
Sebastiaan van Stijn
cf376170ed Add more go:build statements to prevent downgrading Go language version
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>
2024-06-13 14:59:54 +02:00
Albin Kerouanton
1243f9da6d Revert "libnet/i/defaultipam: Disambiguate PoolID string format"
This reverts commit 9369132879.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-06-07 15:32:13 +02:00
Albin Kerouanton
6af0646236 Revert "libnet/i/defaultipam: Use InternalErrorf instead of InvalidParameterErrof"
This reverts commit 5a2fa59688.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-06-07 15:31:54 +02:00
Albin Kerouanton
32418e9753 daemon: set the default local addr pool if none configured
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>
2024-05-29 10:47:59 +02:00
Albin Kerouanton
d16a425f0f Merge pull request #47768 from akerouanton/libnet-ipam-linear-allocator
libnet/ipams/default: introduce a linear allocator
2024-05-23 11:07:12 +02:00
Albin Kerouanton
500eff0ae9 libnet/i/defaultipam: improve address pools validation
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>
2024-05-23 08:26:07 +02:00
Albin Kerouanton
0c022307e9 libnet/i/defaultipam: Unmap IPv4-mapped IPv6 addrs
This ensures such address pools are part of the IPv4 address space.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-05-23 08:26:00 +02:00
Albin Kerouanton
9d288b5b43 libnet/i/defaultipam: introduce a linear allocator
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>
2024-05-23 08:24:51 +02:00
Albin Kerouanton
5a2fa59688 libnet/i/defaultipam: Use InternalErrorf instead of InvalidParameterErrof
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>
2024-05-22 10:02:20 +02:00
Albin Kerouanton
9369132879 libnet/i/defaultipam: Disambiguate PoolID string format
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>
2024-05-22 10:02:19 +02:00
Albin Kerouanton
f2387f3632 libnet/ipams/defaultipam: move driver name to its pkg
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-04-26 17:28:29 +02:00
Albin Kerouanton
0db56de78e libnet/ipamutils: no more global state
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>
2024-04-26 17:28:29 +02:00
Albin Kerouanton
8cec9f0dca libnet/ipams/defaultipam: add a Register fn
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>
2024-04-26 17:28:29 +02:00
Albin Kerouanton
29f2ca04e0 libnet: move ipam pkg to ipam/defaultipam
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>
2024-04-26 17:28:29 +02:00