From 5e96523913d265a3472922032a18fb0987db0bd2 Mon Sep 17 00:00:00 2001 From: Robert Pothier Date: Mon, 20 Mar 2017 16:39:09 -0400 Subject: [PATCH] Adding IPv6 to cidr_set and cidr_set_test --- pkg/controller/node/cidr_set.go | 103 ++++-- pkg/controller/node/cidr_set_test.go | 527 +++++++++++++++++++++------ 2 files changed, 504 insertions(+), 126 deletions(-) diff --git a/pkg/controller/node/cidr_set.go b/pkg/controller/node/cidr_set.go index 3ceec242e7d..6da63d4281d 100644 --- a/pkg/controller/node/cidr_set.go +++ b/pkg/controller/node/cidr_set.go @@ -35,19 +35,62 @@ type cidrSet struct { subNetMaskSize int } +const ( + // The subnet mask size cannot be greater than 16 more than the cluster mask size + // TODO: https://github.com/kubernetes/kubernetes/issues/44918 + // clusterSubnetMaxDiff limited to 16 due to the uncompressed bitmap + clusterSubnetMaxDiff = 16 + // maximum 64 bits of prefix + maxPrefixLength = 64 +) + func newCIDRSet(clusterCIDR *net.IPNet, subNetMaskSize int) *cidrSet { clusterMask := clusterCIDR.Mask clusterMaskSize, _ := clusterMask.Size() - maxCIDRs := 1 << uint32(subNetMaskSize-clusterMaskSize) + + var maxCIDRs int + if ((clusterCIDR.IP.To4() == nil) && (subNetMaskSize-clusterMaskSize > clusterSubnetMaxDiff)) || (subNetMaskSize > maxPrefixLength) { + maxCIDRs = 0 + } else { + maxCIDRs = 1 << uint32(subNetMaskSize-clusterMaskSize) + } return &cidrSet{ clusterCIDR: clusterCIDR, - clusterIP: clusterCIDR.IP.To4(), + clusterIP: clusterCIDR.IP, clusterMaskSize: clusterMaskSize, maxCIDRs: maxCIDRs, subNetMaskSize: subNetMaskSize, } } +func (s *cidrSet) indexToCIDRBlock(index int) *net.IPNet { + var ip []byte + var mask int + switch /*v4 or v6*/ { + case s.clusterIP.To4() != nil: + { + j := uint32(index) << uint32(32-s.subNetMaskSize) + ipInt := (binary.BigEndian.Uint32(s.clusterIP)) | j + ip = make([]byte, 4) + binary.BigEndian.PutUint32(ip, ipInt) + mask = 32 + + } + case s.clusterIP.To16() != nil: + { + j := uint64(index) << uint64(64-s.subNetMaskSize) + ipInt := (binary.BigEndian.Uint64(s.clusterIP)) | j + ip = make([]byte, 16) + binary.BigEndian.PutUint64(ip, ipInt) + mask = 128 + } + } + return &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(s.subNetMaskSize, mask), + } +} + func (s *cidrSet) allocateNext() (*net.IPNet, error) { s.Lock() defer s.Unlock() @@ -67,41 +110,47 @@ func (s *cidrSet) allocateNext() (*net.IPNet, error) { s.used.SetBit(&s.used, nextUnused, 1) - j := uint32(nextUnused) << uint32(32-s.subNetMaskSize) - ipInt := (binary.BigEndian.Uint32(s.clusterIP)) | j - ip := make([]byte, 4) - binary.BigEndian.PutUint32(ip, ipInt) - - return &net.IPNet{ - IP: ip, - Mask: net.CIDRMask(s.subNetMaskSize, 32), - }, nil + return s.indexToCIDRBlock(nextUnused), nil } func (s *cidrSet) getBeginingAndEndIndices(cidr *net.IPNet) (begin, end int, err error) { begin, end = 0, s.maxCIDRs-1 cidrMask := cidr.Mask maskSize, _ := cidrMask.Size() + var ipSize int + + if cidr == nil { + return -1, -1, fmt.Errorf("Error getting indices for cluster cidr %v, cidr is nil", s.clusterCIDR) + } if !s.clusterCIDR.Contains(cidr.IP.Mask(s.clusterCIDR.Mask)) && !cidr.Contains(s.clusterCIDR.IP.Mask(cidr.Mask)) { return -1, -1, fmt.Errorf("cidr %v is out the range of cluster cidr %v", cidr, s.clusterCIDR) } if s.clusterMaskSize < maskSize { - subNetMask := net.CIDRMask(s.subNetMaskSize, 32) + + ipSize = net.IPv4len + if cidr.IP.To4() == nil { + ipSize = net.IPv6len + } + subNetMask := net.CIDRMask(s.subNetMaskSize, ipSize*8) begin, err = s.getIndexForCIDR(&net.IPNet{ - IP: cidr.IP.To4().Mask(subNetMask), + IP: cidr.IP.Mask(subNetMask), Mask: subNetMask, }) if err != nil { return -1, -1, err } - - ip := make([]byte, 4) - ipInt := binary.BigEndian.Uint32(cidr.IP) | (^binary.BigEndian.Uint32(cidr.Mask)) - binary.BigEndian.PutUint32(ip, ipInt) + ip := make([]byte, ipSize) + if cidr.IP.To4() != nil { + ipInt := binary.BigEndian.Uint32(cidr.IP) | (^binary.BigEndian.Uint32(cidr.Mask)) + binary.BigEndian.PutUint32(ip, ipInt) + } else { + ipInt := binary.BigEndian.Uint64(cidr.IP) | (^binary.BigEndian.Uint64(cidr.Mask)) + binary.BigEndian.PutUint64(ip, ipInt) + } end, err = s.getIndexForCIDR(&net.IPNet{ - IP: net.IP(ip).To4().Mask(subNetMask), + IP: net.IP(ip).Mask(subNetMask), Mask: subNetMask, }) if err != nil { @@ -140,11 +189,21 @@ func (s *cidrSet) occupy(cidr *net.IPNet) (err error) { } func (s *cidrSet) getIndexForCIDR(cidr *net.IPNet) (int, error) { - cidrIndex := (binary.BigEndian.Uint32(s.clusterIP) ^ binary.BigEndian.Uint32(cidr.IP.To4())) >> uint32(32-s.subNetMaskSize) + var cidrIndex uint32 + if cidr.IP.To4() != nil { + cidrIndex = (binary.BigEndian.Uint32(s.clusterIP) ^ binary.BigEndian.Uint32(cidr.IP.To4())) >> uint32(32-s.subNetMaskSize) + if cidrIndex >= uint32(s.maxCIDRs) { + return 0, fmt.Errorf("CIDR: %v is out of the range of CIDR allocator", cidr) + } + } else if cidr.IP.To16() != nil { + cidrIndex64 := (binary.BigEndian.Uint64(s.clusterIP) ^ binary.BigEndian.Uint64(cidr.IP.To16())) >> uint64(64-s.subNetMaskSize) - if cidrIndex >= uint32(s.maxCIDRs) { - return 0, fmt.Errorf("CIDR: %v is out of the range of CIDR allocator", cidr) + if cidrIndex64 >= uint64(s.maxCIDRs) { + return 0, fmt.Errorf("CIDR: %v is out of the range of CIDR allocator", cidr) + } + cidrIndex = uint32(cidrIndex64) + } else { + return 0, fmt.Errorf("invalid CIDR block: %v", cidr) } - return int(cidrIndex), nil } diff --git a/pkg/controller/node/cidr_set_test.go b/pkg/controller/node/cidr_set_test.go index ff1db26a9a2..6c9f6c54d87 100644 --- a/pkg/controller/node/cidr_set_test.go +++ b/pkg/controller/node/cidr_set_test.go @@ -26,124 +26,244 @@ import ( ) func TestCIDRSetFullyAllocated(t *testing.T) { - _, clusterCIDR, _ := net.ParseCIDR("127.123.234.0/30") - a := newCIDRSet(clusterCIDR, 30) + cases := []struct { + clusterCIDRStr string + subNetMaskSize int + expectedCIDR string + description string + }{ + { + clusterCIDRStr: "127.123.234.0/30", + subNetMaskSize: 30, + expectedCIDR: "127.123.234.0/30", + description: "Fully allocated CIDR with IPv4", + }, + { + clusterCIDRStr: "beef:1234::/30", + subNetMaskSize: 30, + expectedCIDR: "beef:1234::/30", + description: "Fully allocated CIDR with IPv6", + }, + } + for _, tc := range cases { + _, clusterCIDR, _ := net.ParseCIDR(tc.clusterCIDRStr) + a := newCIDRSet(clusterCIDR, tc.subNetMaskSize) - p, err := a.allocateNext() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if p.String() != "127.123.234.0/30" { - t.Fatalf("unexpected allocated cidr: %s", p.String()) - } + p, err := a.allocateNext() + if err != nil { + t.Fatalf("unexpected error: %v for %v", err, tc.description) + } + if p.String() != tc.expectedCIDR { + t.Fatalf("unexpected allocated cidr: %v, expecting %v for %v", + p.String(), tc.expectedCIDR, tc.description) + } - _, err = a.allocateNext() - if err == nil { - t.Fatalf("expected error because of fully-allocated range") - } + _, err = a.allocateNext() + if err == nil { + t.Fatalf("expected error because of fully-allocated range for %v", tc.description) + } - a.release(p) - p, err = a.allocateNext() - if err != nil { - t.Fatalf("unexpected error: %v", err) + a.release(p) + + p, err = a.allocateNext() + if err != nil { + t.Fatalf("unexpected error: %v for %v", err, tc.description) + } + if p.String() != tc.expectedCIDR { + t.Fatalf("unexpected allocated cidr: %v, expecting %v for %v", + p.String(), tc.expectedCIDR, tc.description) + } + _, err = a.allocateNext() + if err == nil { + t.Fatalf("expected error because of fully-allocated range for %v", tc.description) + } } - if p.String() != "127.123.234.0/30" { - t.Fatalf("unexpected allocated cidr: %s", p.String()) +} + +func TestIndexToCIDRBlock(t *testing.T) { + cases := []struct { + clusterCIDRStr string + subnetMaskSize int + index int + CIDRBlock string + description string + }{ + { + clusterCIDRStr: "127.123.3.0/16", + subnetMaskSize: 24, + index: 0, + CIDRBlock: "127.123.0.0/24", + description: "Index with IPv4", + }, + { + clusterCIDRStr: "127.123.0.0/16", + subnetMaskSize: 24, + index: 15, + CIDRBlock: "127.123.15.0/24", + description: "Index with IPv4", + }, + { + clusterCIDRStr: "192.168.5.219/28", + subnetMaskSize: 32, + index: 5, + CIDRBlock: "192.168.5.213/32", + description: "Index with IPv4", + }, + { + clusterCIDRStr: "2001:0db8:1234:3::/48", + subnetMaskSize: 64, + index: 0, + CIDRBlock: "2001:db8:1234::/64", + description: "Index with IPv6", + }, + { + clusterCIDRStr: "2001:0db8:1234::/48", + subnetMaskSize: 64, + index: 15, + CIDRBlock: "2001:db8:1234:f::/64", + description: "Index with IPv6", + }, + { + clusterCIDRStr: "2001:0db8:85a3::8a2e:0370:7334/50", + subnetMaskSize: 63, + index: 6425, + CIDRBlock: "2001:db8:85a3:3232::/63", + description: "Index with IPv6", + }, } - _, err = a.allocateNext() - if err == nil { - t.Fatalf("expected error because of fully-allocated range") + for _, tc := range cases { + _, clusterCIDR, _ := net.ParseCIDR(tc.clusterCIDRStr) + a := newCIDRSet(clusterCIDR, tc.subnetMaskSize) + cidr := a.indexToCIDRBlock(tc.index) + if cidr.String() != tc.CIDRBlock { + t.Fatalf("error for %v index %d %s", tc.description, tc.index, cidr.String()) + } } } func TestCIDRSet_RandomishAllocation(t *testing.T) { - _, clusterCIDR, _ := net.ParseCIDR("127.123.234.0/16") - a := newCIDRSet(clusterCIDR, 24) - // allocate all the CIDRs - var err error - cidrs := make([]*net.IPNet, 256) + cases := []struct { + clusterCIDRStr string + description string + }{ + { + clusterCIDRStr: "127.123.234.0/16", + description: "RandomishAllocation with IPv4", + }, + { + clusterCIDRStr: "beef:1234::/16", + description: "RandomishAllocation with IPv6", + }, + } + for _, tc := range cases { + _, clusterCIDR, _ := net.ParseCIDR(tc.clusterCIDRStr) + a := newCIDRSet(clusterCIDR, 24) + // allocate all the CIDRs + var cidrs []*net.IPNet - for i := 0; i < 256; i++ { - cidrs[i], err = a.allocateNext() - if err != nil { - t.Fatalf("unexpected error: %v", err) + for i := 0; i < 256; i++ { + if c, err := a.allocateNext(); err == nil { + cidrs = append(cidrs, c) + } else { + t.Fatalf("unexpected error: %v for %v", err, tc.description) + } } - } - _, err = a.allocateNext() - if err == nil { - t.Fatalf("expected error because of fully-allocated range") - } - // release them all - for i := 0; i < 256; i++ { - a.release(cidrs[i]) - } - - // allocate the CIDRs again - rcidrs := make([]*net.IPNet, 256) - for i := 0; i < 256; i++ { - rcidrs[i], err = a.allocateNext() - if err != nil { - t.Fatalf("unexpected error: %d, %v", i, err) + var err error + _, err = a.allocateNext() + if err == nil { + t.Fatalf("expected error because of fully-allocated range for %v", tc.description) + } + // release them all + for i := 0; i < len(cidrs); i++ { + a.release(cidrs[i]) } - } - _, err = a.allocateNext() - if err == nil { - t.Fatalf("expected error because of fully-allocated range") - } - if !reflect.DeepEqual(cidrs, rcidrs) { - t.Fatalf("expected re-allocated cidrs are the same collection") + // allocate the CIDRs again + var rcidrs []*net.IPNet + for i := 0; i < 256; i++ { + if c, err := a.allocateNext(); err == nil { + rcidrs = append(rcidrs, c) + } else { + t.Fatalf("unexpected error: %d, %v for %v", i, err, tc.description) + } + } + _, err = a.allocateNext() + if err == nil { + t.Fatalf("expected error because of fully-allocated range for %v", tc.description) + } + + if !reflect.DeepEqual(cidrs, rcidrs) { + t.Fatalf("expected re-allocated cidrs are the same collection for %v", tc.description) + } } } func TestCIDRSet_AllocationOccupied(t *testing.T) { - _, clusterCIDR, _ := net.ParseCIDR("127.123.234.0/16") - a := newCIDRSet(clusterCIDR, 24) + cases := []struct { + clusterCIDRStr string + description string + }{ + { + clusterCIDRStr: "127.123.234.0/16", + description: "AllocationOccupied with IPv4", + }, + { + clusterCIDRStr: "beef:1234::/16", + description: "AllocationOccupied with IPv6", + }, + } + for _, tc := range cases { + _, clusterCIDR, _ := net.ParseCIDR(tc.clusterCIDRStr) + a := newCIDRSet(clusterCIDR, 24) - // allocate all the CIDRs - var err error - cidrs := make([]*net.IPNet, 256) + // allocate all the CIDRs + var cidrs []*net.IPNet + var num_cidrs = 256 - for i := 0; i < 256; i++ { - cidrs[i], err = a.allocateNext() - if err != nil { - t.Fatalf("unexpected error: %v", err) + for i := 0; i < num_cidrs; i++ { + if c, err := a.allocateNext(); err == nil { + cidrs = append(cidrs, c) + } else { + t.Fatalf("unexpected error: %v for %v", err, tc.description) + } } - } - _, err = a.allocateNext() - if err == nil { - t.Fatalf("expected error because of fully-allocated range") - } - // release them all - for i := 0; i < 256; i++ { - a.release(cidrs[i]) - } - // occupy the last 128 CIDRs - for i := 128; i < 256; i++ { - a.occupy(cidrs[i]) - } - - // allocate the first 128 CIDRs again - rcidrs := make([]*net.IPNet, 128) - for i := 0; i < 128; i++ { - rcidrs[i], err = a.allocateNext() - if err != nil { - t.Fatalf("unexpected error: %d, %v", i, err) + var err error + _, err = a.allocateNext() + if err == nil { + t.Fatalf("expected error because of fully-allocated range for %v", tc.description) + } + // release them all + for i := 0; i < len(cidrs); i++ { + a.release(cidrs[i]) + } + // occupy the last 128 CIDRs + for i := num_cidrs / 2; i < num_cidrs; i++ { + a.occupy(cidrs[i]) } - } - _, err = a.allocateNext() - if err == nil { - t.Fatalf("expected error because of fully-allocated range") - } - // check Occupy() work properly - for i := 128; i < 256; i++ { - rcidrs = append(rcidrs, cidrs[i]) - } - if !reflect.DeepEqual(cidrs, rcidrs) { - t.Fatalf("expected re-allocated cidrs are the same collection") + // allocate the first 128 CIDRs again + var rcidrs []*net.IPNet + for i := 0; i < num_cidrs/2; i++ { + if c, err := a.allocateNext(); err == nil { + rcidrs = append(rcidrs, c) + } else { + t.Fatalf("unexpected error: %d, %v for %v", i, err, tc.description) + } + } + _, err = a.allocateNext() + if err == nil { + t.Fatalf("expected error because of fully-allocated range for %v", tc.description) + } + + // check Occupy() work properly + for i := num_cidrs / 2; i < num_cidrs; i++ { + rcidrs = append(rcidrs, cidrs[i]) + } + if !reflect.DeepEqual(cidrs, rcidrs) { + t.Fatalf("expected re-allocated cidrs are the same collection for %v", tc.description) + } } } @@ -154,6 +274,7 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr string expectedBit int expectErr bool + description string }{ { clusterCIDRStr: "127.0.0.0/8", @@ -161,6 +282,15 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr: "127.0.0.0/16", expectedBit: 0, expectErr: false, + description: "Get 0 Bit with IPv4", + }, + { + clusterCIDRStr: "be00::/8", + subNetMaskSize: 16, + subNetCIDRStr: "be00::/16", + expectedBit: 0, + expectErr: false, + description: "Get 0 Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/8", @@ -168,6 +298,15 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr: "127.123.0.0/16", expectedBit: 123, expectErr: false, + description: "Get 123rd Bit with IPv4", + }, + { + clusterCIDRStr: "be00::/8", + subNetMaskSize: 16, + subNetCIDRStr: "beef::/16", + expectedBit: 0xef, + expectErr: false, + description: "Get xef Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/8", @@ -175,6 +314,15 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr: "127.168.0.0/16", expectedBit: 168, expectErr: false, + description: "Get 168th Bit with IPv4", + }, + { + clusterCIDRStr: "be00::/8", + subNetMaskSize: 16, + subNetCIDRStr: "be68::/16", + expectedBit: 0x68, + expectErr: false, + description: "Get x68th Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/8", @@ -182,6 +330,15 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr: "127.224.0.0/16", expectedBit: 224, expectErr: false, + description: "Get 224th Bit with IPv4", + }, + { + clusterCIDRStr: "be00::/8", + subNetMaskSize: 16, + subNetCIDRStr: "be24::/16", + expectedBit: 0x24, + expectErr: false, + description: "Get x24th Bit with IPv6", }, { clusterCIDRStr: "192.168.0.0/16", @@ -189,6 +346,15 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr: "192.168.12.0/24", expectedBit: 12, expectErr: false, + description: "Get 12th Bit with IPv4", + }, + { + clusterCIDRStr: "beef::/16", + subNetMaskSize: 24, + subNetCIDRStr: "beef:1200::/24", + expectedBit: 0x12, + expectErr: false, + description: "Get x12th Bit with IPv6", }, { clusterCIDRStr: "192.168.0.0/16", @@ -196,41 +362,58 @@ func TestGetBitforCIDR(t *testing.T) { subNetCIDRStr: "192.168.151.0/24", expectedBit: 151, expectErr: false, + description: "Get 151st Bit with IPv4", + }, + { + clusterCIDRStr: "beef::/16", + subNetMaskSize: 24, + subNetCIDRStr: "beef:9700::/24", + expectedBit: 0x97, + expectErr: false, + description: "Get x97st Bit with IPv6", }, { clusterCIDRStr: "192.168.0.0/16", subNetMaskSize: 24, subNetCIDRStr: "127.168.224.0/24", expectErr: true, + description: "Get error with IPv4", + }, + { + clusterCIDRStr: "beef::/16", + subNetMaskSize: 24, + subNetCIDRStr: "2001:db00::/24", + expectErr: true, + description: "Get error with IPv6", }, } for _, tc := range cases { _, clusterCIDR, err := net.ParseCIDR(tc.clusterCIDRStr) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v for %v", err, tc.description) } cs := newCIDRSet(clusterCIDR, tc.subNetMaskSize) _, subnetCIDR, err := net.ParseCIDR(tc.subNetCIDRStr) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v for %v", err, tc.description) } got, err := cs.getIndexForCIDR(subnetCIDR) if err == nil && tc.expectErr { - glog.Errorf("expected error but got null") + glog.Errorf("expected error but got null for %v", tc.description) continue } if err != nil && !tc.expectErr { - glog.Errorf("unexpected error: %v", err) + glog.Errorf("unexpected error: %v for %v", err, tc.description) continue } if got != tc.expectedBit { - glog.Errorf("expected %v, but got %v", tc.expectedBit, got) + glog.Errorf("expected %v, but got %v for %v", tc.expectedBit, got, tc.description) } } } @@ -243,6 +426,7 @@ func TestOccupy(t *testing.T) { expectedUsedBegin int expectedUsedEnd int expectErr bool + description string }{ { clusterCIDRStr: "127.0.0.0/8", @@ -251,6 +435,16 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 0, expectedUsedEnd: 255, expectErr: false, + description: "Occupy all Bits with IPv4", + }, + { + clusterCIDRStr: "2001:beef:1200::/40", + subNetMaskSize: 48, + subNetCIDRStr: "2001:beef:1200::/40", + expectedUsedBegin: 0, + expectedUsedEnd: 255, + expectErr: false, + description: "Occupy all Bits with IPv6", }, { clusterCIDRStr: "127.0.0.0/8", @@ -259,6 +453,16 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 0, expectedUsedEnd: 255, expectErr: false, + description: "Occupy every Bit with IPv4", + }, + { + clusterCIDRStr: "2001:beef:1200::/40", + subNetMaskSize: 48, + subNetCIDRStr: "2001:beef:1234::/34", + expectedUsedBegin: 0, + expectedUsedEnd: 255, + expectErr: false, + description: "Occupy every Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/8", @@ -267,6 +471,16 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 0, expectedUsedEnd: 0, expectErr: false, + description: "Occupy 1st Bit with IPv4", + }, + { + clusterCIDRStr: "2001:beef:1200::/40", + subNetMaskSize: 48, + subNetCIDRStr: "2001:beef:1200::/48", + expectedUsedBegin: 0, + expectedUsedEnd: 0, + expectErr: false, + description: "Occupy 1st Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/8", @@ -275,6 +489,16 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 0, expectedUsedEnd: 65535, expectErr: false, + description: "Occupy 65535 Bits with IPv4", + }, + { + clusterCIDRStr: "2001:beef:1200::/48", + subNetMaskSize: 64, + subNetCIDRStr: "2001:beef:1200::/48", + expectedUsedBegin: 0, + expectedUsedEnd: 65535, + expectErr: false, + description: "Occupy 65535 Bits with IPv6", }, { clusterCIDRStr: "127.0.0.0/7", @@ -283,6 +507,16 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 256, expectedUsedEnd: 257, expectErr: false, + description: "Occupy 257th Bit with IPv4", + }, + { + clusterCIDRStr: "2001:beef:7f00::/39", + subNetMaskSize: 48, + subNetCIDRStr: "2001:beef:7f00::/47", + expectedUsedBegin: 256, + expectedUsedEnd: 257, + expectErr: false, + description: "Occupy 257th Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/7", @@ -291,6 +525,16 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 128, expectedUsedEnd: 128, expectErr: false, + description: "Occupy 128th Bit with IPv4", + }, + { + clusterCIDRStr: "2001:beef:7f00::/39", + subNetMaskSize: 47, + subNetCIDRStr: "2001:beef:7f00::/47", + expectedUsedBegin: 128, + expectedUsedEnd: 128, + expectErr: false, + description: "Occupy 128th Bit with IPv6", }, { clusterCIDRStr: "127.0.0.0/7", @@ -299,29 +543,39 @@ func TestOccupy(t *testing.T) { expectedUsedBegin: 1024, expectedUsedEnd: 1031, expectErr: false, + description: "Occupy 1031st Bit with IPv4", + }, + { + clusterCIDRStr: "2001:beef:7f00::/39", + subNetMaskSize: 50, + subNetCIDRStr: "2001:beef:7f00::/47", + expectedUsedBegin: 1024, + expectedUsedEnd: 1031, + expectErr: false, + description: "Occupy 1031st Bit with IPv6", }, } for _, tc := range cases { _, clusterCIDR, err := net.ParseCIDR(tc.clusterCIDRStr) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v for %v", err, tc.description) } cs := newCIDRSet(clusterCIDR, tc.subNetMaskSize) _, subnetCIDR, err := net.ParseCIDR(tc.subNetCIDRStr) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v for %v", err, tc.description) } err = cs.occupy(subnetCIDR) if err == nil && tc.expectErr { - t.Errorf("expected error but got none") + t.Errorf("expected error but got none for %v", tc.description) continue } if err != nil && !tc.expectErr { - t.Errorf("unexpected error: %v", err) + t.Errorf("unexpected error: %v for %v", err, tc.description) continue } @@ -330,7 +584,72 @@ func TestOccupy(t *testing.T) { expectedUsed.SetBit(&expectedUsed, i, 1) } if expectedUsed.Cmp(&cs.used) != 0 { - t.Errorf("error") + t.Errorf("error for %v", tc.description) + } + } +} + +func TestCIDRSetv6(t *testing.T) { + cases := []struct { + clusterCIDRStr string + subNetMaskSize int + expectedCIDR string + expectedCIDR2 string + expectErr bool + description string + }{ + { + clusterCIDRStr: "127.0.0.0/8", + subNetMaskSize: 32, + expectErr: false, + expectedCIDR: "127.0.0.0/32", + expectedCIDR2: "127.0.0.1/32", + description: "Max cluster subnet size with IPv4", + }, + { + clusterCIDRStr: "beef:1234::/32", + subNetMaskSize: 49, + expectErr: true, + description: "Max cluster subnet size with IPv6", + }, + { + clusterCIDRStr: "beef:1234::/60", + subNetMaskSize: 65, + expectErr: true, + description: "Max prefix length with IPv6", + }, + { + clusterCIDRStr: "2001:beef:1234:369b::/60", + subNetMaskSize: 64, + expectedCIDR: "2001:beef:1234:3690::/64", + expectedCIDR2: "2001:beef:1234:3691::/64", + expectErr: false, + description: "Allocate a few IPv6", + }, + } + for _, tc := range cases { + _, clusterCIDR, _ := net.ParseCIDR(tc.clusterCIDRStr) + a := newCIDRSet(clusterCIDR, tc.subNetMaskSize) + + p, err := a.allocateNext() + if err == nil && tc.expectErr { + t.Errorf("expected error but got none for %v", tc.description) + continue + } + if err != nil && !tc.expectErr { + t.Errorf("unexpected error: %v for %v", err, tc.description) + continue + } + if !tc.expectErr { + if p.String() != tc.expectedCIDR { + t.Fatalf("unexpected allocated cidr: %s for %v", p.String(), tc.description) + } + } + p2, err := a.allocateNext() + if !tc.expectErr { + if p2.String() != tc.expectedCIDR2 { + t.Fatalf("unexpected allocated cidr: %s for %v", p2.String(), tc.description) + } } } }