mirror of https://github.com/k3s-io/k3s
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.pull/6/head
parent
aded0d9225
commit
ad16986cd8
|
@ -262,11 +262,18 @@ func calculateIPOffset(base *big.Int, ip net.IP) int {
|
|||
// RangeSize returns the size of a range in valid addresses.
|
||||
func RangeSize(subnet *net.IPNet) int64 {
|
||||
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
|
||||
}
|
||||
max := int64(1) << uint(bits-ones)
|
||||
return max
|
||||
// For IPv6, the max size will be limited to 65536
|
||||
// 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.
|
||||
|
|
|
@ -25,88 +25,121 @@ import (
|
|||
)
|
||||
|
||||
func TestAllocate(t *testing.T) {
|
||||
_, cidr, err := net.ParseCIDR("192.168.1.0/24")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
testCases := []struct {
|
||||
name string
|
||||
cidr string
|
||||
free int
|
||||
released string
|
||||
outOfRange1 string
|
||||
outOfRange2 string
|
||||
outOfRange3 string
|
||||
alreadyAllocated string
|
||||
}{
|
||||
{
|
||||
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",
|
||||
},
|
||||
}
|
||||
r := NewCIDRRange(cidr)
|
||||
t.Logf("base: %v", r.base.Bytes())
|
||||
if f := r.Free(); f != 254 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
}
|
||||
if f := r.Used(); f != 0 {
|
||||
t.Errorf("unexpected used %d", f)
|
||||
}
|
||||
found := sets.NewString()
|
||||
count := 0
|
||||
for r.Free() > 0 {
|
||||
for _, tc := range testCases {
|
||||
_, cidr, err := net.ParseCIDR(tc.cidr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := NewCIDRRange(cidr)
|
||||
t.Logf("base: %v", r.base.Bytes())
|
||||
if f := r.Free(); f != tc.free {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
if f := r.Used(); f != 0 {
|
||||
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)
|
||||
}
|
||||
|
||||
released := net.ParseIP(tc.released)
|
||||
if err := r.Release(released); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f := r.Free(); f != 1 {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
if f := r.Used(); f != (tc.free - 1) {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
ip, err := r.AllocateNext()
|
||||
if err != nil {
|
||||
t.Fatalf("error @ %d: %v", count, err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
count++
|
||||
if !cidr.Contains(ip) {
|
||||
t.Fatalf("allocated %s which is outside of %s", ip, cidr)
|
||||
if !released.Equal(ip) {
|
||||
t.Errorf("Test %s unexpected %s : %s", tc.name, ip, released)
|
||||
}
|
||||
if found.Has(ip.String()) {
|
||||
t.Fatalf("allocated %s twice @ %d", ip, count)
|
||||
}
|
||||
found.Insert(ip.String())
|
||||
}
|
||||
if _, err := r.AllocateNext(); err != ErrFull {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
released := net.ParseIP("192.168.1.5")
|
||||
if err := r.Release(released); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f := r.Free(); f != 1 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
}
|
||||
if f := r.Used(); f != 253 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
}
|
||||
ip, err := r.AllocateNext()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !released.Equal(ip) {
|
||||
t.Errorf("unexpected %s : %s", ip, released)
|
||||
}
|
||||
|
||||
if err := r.Release(released); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.Allocate(net.ParseIP("192.168.0.1"))
|
||||
if _, ok := err.(*ErrNotInRange); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r.Allocate(net.ParseIP("192.168.1.1")); err != ErrAllocated {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.Allocate(net.ParseIP("192.168.1.0"))
|
||||
if _, ok := err.(*ErrNotInRange); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.Allocate(net.ParseIP("192.168.1.255"))
|
||||
if _, ok := err.(*ErrNotInRange); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f := r.Free(); f != 1 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
}
|
||||
if f := r.Used(); f != 253 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
}
|
||||
if err := r.Allocate(released); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f := r.Free(); f != 0 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
}
|
||||
if f := r.Used(); f != 254 {
|
||||
t.Errorf("unexpected free %d", f)
|
||||
if err := r.Release(released); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.Allocate(net.ParseIP(tc.outOfRange1))
|
||||
if _, ok := err.(*ErrNotInRange); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r.Allocate(net.ParseIP(tc.alreadyAllocated)); err != ErrAllocated {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.Allocate(net.ParseIP(tc.outOfRange2))
|
||||
if _, ok := err.(*ErrNotInRange); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.Allocate(net.ParseIP(tc.outOfRange3))
|
||||
if _, ok := err.(*ErrNotInRange); !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f := r.Free(); f != 1 {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
if f := r.Used(); f != (tc.free - 1) {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
if err := r.Allocate(released); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f := r.Free(); f != 0 {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
if f := r.Used(); f != tc.free {
|
||||
t.Errorf("Test %s unexpected free %d", tc.name, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,12 +216,12 @@ func TestRangeSize(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "supported IPv6 cidr",
|
||||
cidr: "2001:db8::/98",
|
||||
addrs: 1073741824,
|
||||
cidr: "2001:db8::/48",
|
||||
addrs: 65536,
|
||||
},
|
||||
{
|
||||
name: "unsupported IPv6 mask",
|
||||
cidr: "2001:db8::/65",
|
||||
cidr: "2001:db8::/1",
|
||||
addrs: 0,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue