-
Notifications
You must be signed in to change notification settings - Fork 18.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IPAM: handle subnets bigger than "/64" #49223
Conversation
93754fa
to
cc0af4d
Compare
70ee756
to
8924145
Compare
// 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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although technically we can get away with value receivers while mutating the map, Go consensus is to use a pointer receiver on methods which mutate the receiver for clarity of API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to pointers.
// no addresses are available, it returns a wrapped [ErrNotAvailable]. | ||
// | ||
// When serial=true, the set is scanned starting from the address following | ||
// the address most recently set by [AddrSet.SetAny] or [AddrSet.SetAnyInRange]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// the address most recently set by [AddrSet.SetAny] or [AddrSet.SetAnyInRange]. | |
// the address most recently set by [AddrSet.AddAny] or [AddrSet.AddAnyInRange]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
// If the pool has maxBitsPerBitmap or fewer, the key for the only bitmap is the pool's address. | ||
// If there's more than one bitmap, they all have maxBitsPerBitmap. So, assume there's a free | ||
// address in the bitmap keyed on the pool's address (no need to search other bitmaps, or work | ||
// out if more bitmaps could be created). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify that this assumption is unsound, with rationale
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "unsound" assumption would be that anything in libnetwork is capable of tracking, or needs to track, 2^63 different IP addresses (9,223,372,036,854,775,808 addresses). Let alone more than that.
But, for the overly ambitious or concerned, I've reworded and included it in the godoc comment.
} | ||
|
||
// AddAny adds an arbitrary address to the set, and returns that address. Or, if | ||
// no addresses are available, it returns a wrapped [ErrNotAvailable]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document the limitations of this function. An ErrNotAvailable returned from this function does not necessarily mean that all host IDs in the prefix have been allocated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above.
// If ipr is not fully contained within the set's range, it returns an error. | ||
// | ||
// When serial=true, the set is scanned starting from the address following | ||
// the address most recently set by [AddrSet.SetAny] or [AddrSet.SetAnyInRange]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// the address most recently set by [AddrSet.SetAny] or [AddrSet.SetAnyInRange]. | |
// the address most recently set by [AddrSet.AddAny] or [AddrSet.AddAnyInRange]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
daa5440
to
9a2d5df
Compare
I've added an extra commit, just to clarify the code that reserves network/broadcast addresses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small nit, otherwise LGTM
// Add an address that's already present. Expect an error. | ||
err = as.Add(netip.MustParseAddr("10.20.255.255")) | ||
assert.Check(t, is.ErrorIs(err, ErrAllocated)) | ||
assert.Check(t, is.ErrorIs(err, ErrAllocated)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert.Check(t, is.ErrorIs(err, ErrAllocated)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, thank you - fixed.
Signed-off-by: Rob Murray <rob.murray@docker.com>
Signed-off-by: Rob Murray <rob.murray@docker.com>
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>
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>
9a2d5df
to
366f2b5
Compare
- What I did
docker run --ip6
does not work properly when IPv6 prefix is shorter (larger) than /64 #21199The 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 (see the notes about "offset" in #48319).
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.
- How I did it
bitmap.Bitmap
for each range of up to 63-bits that has a bit set, within the subnet it's asked to represent.- How to verify it
New unit tests for
AddrSet
, plus existing tests (including the two "XFAIL" tests that now pass).- Description for the changelog
- IPAM now handles subnets bigger than "/64".