Unverified Commit 82eeda08 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #60089 from rpothier/allocator-for-ipv6

Automatic merge from submit-queue (batch tested with PRs 57550, 60089). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Remove subnet size restriction for IPv6 RangeSize was restricting IPv6 subnets to a /66 due to the logic using a uint64. This is not practical for IPv6. This change removes the /64 restriction, but also sets a limit on the range that can be allocated, so that the bitmap will not grow too large. **What this PR does / why we need it**: This PR removes the /66 restriction in ipallocator for IPv6. It is not practical to restrict IPv6 to /66. Currently a /64 or /48 is not allowed. The problem with removing the restriction is the bitmap that tracks the subnets can grow really large, so a limit on the max size of the subnet was set to 65536. Setting the max size will have a side-effect with larger subnets that the allocator will allocate in a smaller section of IP's, this will need to be addressed in a follow-on PR. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #60081 **Special notes for your reviewer**: **Release note**: ```release-note-none ```
parents be2880d6 ad16986c
...@@ -262,11 +262,18 @@ func calculateIPOffset(base *big.Int, ip net.IP) int { ...@@ -262,11 +262,18 @@ func calculateIPOffset(base *big.Int, ip net.IP) int {
// RangeSize returns the size of a range in valid addresses. // RangeSize returns the size of a range in valid addresses.
func RangeSize(subnet *net.IPNet) int64 { func RangeSize(subnet *net.IPNet) int64 {
ones, bits := subnet.Mask.Size() ones, bits := subnet.Mask.Size()
if bits == 32 && (bits-ones) >= 31 || bits == 128 && (bits-ones) >= 63 { if bits == 32 && (bits-ones) >= 31 || bits == 128 && (bits-ones) >= 127 {
return 0 return 0
} }
max := int64(1) << uint(bits-ones) // For IPv6, the max size will be limited to 65536
return max // This is due to the allocator keeping track of all the
// allocated IP's in a bitmap. This will keep the size of
// the bitmap to 64k.
if bits == 128 && (bits-ones) >= 16 {
return int64(1) << uint(16)
} else {
return int64(1) << uint(bits-ones)
}
} }
// GetIndexedIP returns a net.IP that is subnet.IP + index in the contiguous IP space. // GetIndexedIP returns a net.IP that is subnet.IP + index in the contiguous IP space.
......
...@@ -25,88 +25,121 @@ import ( ...@@ -25,88 +25,121 @@ import (
) )
func TestAllocate(t *testing.T) { func TestAllocate(t *testing.T) {
_, cidr, err := net.ParseCIDR("192.168.1.0/24") testCases := []struct {
if err != nil { name string
t.Fatal(err) cidr string
} free int
r := NewCIDRRange(cidr) released string
t.Logf("base: %v", r.base.Bytes()) outOfRange1 string
if f := r.Free(); f != 254 { outOfRange2 string
t.Errorf("unexpected free %d", f) outOfRange3 string
} alreadyAllocated string
if f := r.Used(); f != 0 { }{
t.Errorf("unexpected used %d", f) {
name: "IPv4",
cidr: "192.168.1.0/24",
free: 254,
released: "192.168.1.5",
outOfRange1: "192.168.0.1",
outOfRange2: "192.168.1.0",
outOfRange3: "192.168.1.255",
alreadyAllocated: "192.168.1.1",
},
{
name: "IPv6",
cidr: "2001:db8:1::/48",
free: 65534,
released: "2001:db8:1::5",
outOfRange1: "2001:db8::1",
outOfRange2: "2001:db8:1::",
outOfRange3: "2001:db8:1::ffff",
alreadyAllocated: "2001:db8:1::1",
},
} }
found := sets.NewString() for _, tc := range testCases {
count := 0 _, cidr, err := net.ParseCIDR(tc.cidr)
for r.Free() > 0 {
ip, err := r.AllocateNext()
if err != nil { if err != nil {
t.Fatalf("error @ %d: %v", count, err) t.Fatal(err)
} }
count++ r := NewCIDRRange(cidr)
if !cidr.Contains(ip) { t.Logf("base: %v", r.base.Bytes())
t.Fatalf("allocated %s which is outside of %s", ip, cidr) if f := r.Free(); f != tc.free {
t.Errorf("Test %s unexpected free %d", tc.name, f)
} }
if found.Has(ip.String()) { if f := r.Used(); f != 0 {
t.Fatalf("allocated %s twice @ %d", ip, count) t.Errorf("Test %s unexpected used %d", tc.name, f)
}
found := sets.NewString()
count := 0
for r.Free() > 0 {
ip, err := r.AllocateNext()
if err != nil {
t.Fatalf("Test %s error @ %d: %v", tc.name, count, err)
}
count++
if !cidr.Contains(ip) {
t.Fatalf("Test %s allocated %s which is outside of %s", tc.name, ip, cidr)
}
if found.Has(ip.String()) {
t.Fatalf("Test %s allocated %s twice @ %d", tc.name, ip, count)
}
found.Insert(ip.String())
}
if _, err := r.AllocateNext(); err != ErrFull {
t.Fatal(err)
} }
found.Insert(ip.String())
}
if _, err := r.AllocateNext(); err != ErrFull {
t.Fatal(err)
}
released := net.ParseIP("192.168.1.5") released := net.ParseIP(tc.released)
if err := r.Release(released); err != nil { if err := r.Release(released); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if f := r.Free(); f != 1 { if f := r.Free(); f != 1 {
t.Errorf("unexpected free %d", f) t.Errorf("Test %s unexpected free %d", tc.name, f)
} }
if f := r.Used(); f != 253 { if f := r.Used(); f != (tc.free - 1) {
t.Errorf("unexpected free %d", f) t.Errorf("Test %s unexpected free %d", tc.name, f)
} }
ip, err := r.AllocateNext() ip, err := r.AllocateNext()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !released.Equal(ip) { if !released.Equal(ip) {
t.Errorf("unexpected %s : %s", ip, released) t.Errorf("Test %s unexpected %s : %s", tc.name, ip, released)
} }
if err := r.Release(released); err != nil { if err := r.Release(released); err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = r.Allocate(net.ParseIP("192.168.0.1")) err = r.Allocate(net.ParseIP(tc.outOfRange1))
if _, ok := err.(*ErrNotInRange); !ok { if _, ok := err.(*ErrNotInRange); !ok {
t.Fatal(err) t.Fatal(err)
} }
if err := r.Allocate(net.ParseIP("192.168.1.1")); err != ErrAllocated { if err := r.Allocate(net.ParseIP(tc.alreadyAllocated)); err != ErrAllocated {
t.Fatal(err) t.Fatal(err)
} }
err = r.Allocate(net.ParseIP("192.168.1.0")) err = r.Allocate(net.ParseIP(tc.outOfRange2))
if _, ok := err.(*ErrNotInRange); !ok { if _, ok := err.(*ErrNotInRange); !ok {
t.Fatal(err) t.Fatal(err)
} }
err = r.Allocate(net.ParseIP("192.168.1.255")) err = r.Allocate(net.ParseIP(tc.outOfRange3))
if _, ok := err.(*ErrNotInRange); !ok { if _, ok := err.(*ErrNotInRange); !ok {
t.Fatal(err) t.Fatal(err)
} }
if f := r.Free(); f != 1 { if f := r.Free(); f != 1 {
t.Errorf("unexpected free %d", f) t.Errorf("Test %s unexpected free %d", tc.name, f)
} }
if f := r.Used(); f != 253 { if f := r.Used(); f != (tc.free - 1) {
t.Errorf("unexpected free %d", f) t.Errorf("Test %s unexpected free %d", tc.name, f)
} }
if err := r.Allocate(released); err != nil { if err := r.Allocate(released); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if f := r.Free(); f != 0 { if f := r.Free(); f != 0 {
t.Errorf("unexpected free %d", f) t.Errorf("Test %s unexpected free %d", tc.name, f)
} }
if f := r.Used(); f != 254 { if f := r.Used(); f != tc.free {
t.Errorf("unexpected free %d", f) t.Errorf("Test %s unexpected free %d", tc.name, f)
}
} }
} }
...@@ -183,12 +216,12 @@ func TestRangeSize(t *testing.T) { ...@@ -183,12 +216,12 @@ func TestRangeSize(t *testing.T) {
}, },
{ {
name: "supported IPv6 cidr", name: "supported IPv6 cidr",
cidr: "2001:db8::/98", cidr: "2001:db8::/48",
addrs: 1073741824, addrs: 65536,
}, },
{ {
name: "unsupported IPv6 mask", name: "unsupported IPv6 mask",
cidr: "2001:db8::/65", cidr: "2001:db8::/1",
addrs: 0, addrs: 0,
}, },
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment