From da7c39f84931bce191e7afd225ba3db4d1407ca0 Mon Sep 17 00:00:00 2001 From: Abhijit Hoskeri Date: Sun, 21 Sep 2025 19:48:52 -0700 Subject: [PATCH] Fix IPv6 allocator for /64 CIDRs An ipAllocator with a 64 bit IPv6 CIDR can allocate addresses outside the CIDR range, due to an improper uint64 to int64 cast in the addOffsetAddress function. Replace the cast with a call to `math/big.Int.SetUint64()`. --- .../core/service/ipallocator/ipallocator.go | 10 ++-- .../service/ipallocator/ipallocator_test.go | 52 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/pkg/registry/core/service/ipallocator/ipallocator.go b/pkg/registry/core/service/ipallocator/ipallocator.go index 6613523f4df..1b47f6247e8 100644 --- a/pkg/registry/core/service/ipallocator/ipallocator.go +++ b/pkg/registry/core/service/ipallocator/ipallocator.go @@ -73,7 +73,7 @@ type Allocator struct { var _ Interface = &Allocator{} // NewIPAllocator returns an IP allocator associated to a network range -// that use the IPAddress objectto track the assigned IP addresses, +// that use the IPAddress object to track the assigned IP addresses, // using an informer cache as storage. func NewIPAllocator( cidr *net.IPNet, @@ -253,7 +253,11 @@ func (a *Allocator) allocateNextService(svc *api.Service, dryRun bool) (net.IP, var offset uint64 switch { case rangeSize >= math.MaxInt64: - offset = rand.Uint64() + offset = a.rand.Uint64() + // a.offsetAddress + offset should not overflow a 64 bit CIDR. + if math.MaxUint64-offset < uint64(a.rangeOffset) { + offset -= uint64(a.rangeOffset) + } case rangeSize == 0: return net.IP{}, ErrFull default: @@ -493,7 +497,7 @@ func (dry dryRunAllocator) EnableMetrics() { func addOffsetAddress(address netip.Addr, offset uint64) (netip.Addr, error) { addressBytes := address.AsSlice() addressBig := big.NewInt(0).SetBytes(addressBytes) - r := big.NewInt(0).Add(addressBig, big.NewInt(int64(offset))).Bytes() + r := big.NewInt(0).Add(addressBig, big.NewInt(0).SetUint64(offset)).Bytes() // r must be 4 or 16 bytes depending of the ip family // bigInt conversion to bytes will not take this into consideration // and drop the leading zeros, so we have to take this into account. diff --git a/pkg/registry/core/service/ipallocator/ipallocator_test.go b/pkg/registry/core/service/ipallocator/ipallocator_test.go index 62e5409b54f..9a3d5bfd7bf 100644 --- a/pkg/registry/core/service/ipallocator/ipallocator_test.go +++ b/pkg/registry/core/service/ipallocator/ipallocator_test.go @@ -198,6 +198,58 @@ func TestAllocateIPAllocator(t *testing.T) { } } +func TestAddOffsetAddress(t *testing.T) { + baseAddr := netip.MustParseAddr("fd54:ceea:51ad:2f00::0") + for _, tc := range []struct { + offset uint64 + wantAddr netip.Addr + }{ + { + offset: 0, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00::0"), + }, + { + offset: 1, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00::1"), + }, + { + offset: math.MaxInt64, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00:7fff:ffff:ffff:ffff"), + }, + { + offset: math.MaxInt64 + 1, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00:8000::"), + }, + { + offset: math.MaxUint64, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00:ffff:ffff:ffff:ffff"), + }, + { + offset: math.MaxUint64 - 1, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00:ffff:ffff:ffff:fffe"), + }, + { + offset: math.MaxUint64>>13 - 1, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00:7:ffff:ffff:fffe"), + }, + { + offset: math.MaxUint64 >> 59, + wantAddr: netip.MustParseAddr("fd54:ceea:51ad:2f00::1f"), + }, + } { + t.Run(fmt.Sprintf("base:%s,offset:%x,want:%s", baseAddr, tc.offset, tc.wantAddr), func(t *testing.T) { + gotAddr, err := addOffsetAddress(baseAddr, tc.offset) + if err != nil { + t.Fatalf("err: %v", err) + } + + if gotAddr.Compare(tc.wantAddr) != 0 { + t.Fatalf("compareAddr: got %s, want %s", gotAddr, tc.wantAddr) + } + }) + } +} + func TestAllocateTinyIPAllocator(t *testing.T) { _, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/32") if err != nil {