From 5d37cd586f6f8cd6814ee266e96b82f241e08447 Mon Sep 17 00:00:00 2001 From: Levente Kale Date: Fri, 7 Feb 2020 12:33:35 +0100 Subject: [PATCH] Finished testing IPv6 allocation pool feature Fixed the bug of improperly calculated remaining address storage capacity Fixed the bug of not recognizing Pool6 CIDR falling outside of Net6 is they had some overlaps --- pkg/admit/validators.go | 41 ++++++++++++++------------- pkg/ipam/ipam.go | 33 +++++++++++++++++---- test/uts/admit_tests/netadmit_test.go | 20 +++++++++++++ 3 files changed, 68 insertions(+), 26 deletions(-) diff --git a/pkg/admit/validators.go b/pkg/admit/validators.go index dbef8e6c..7db7199a 100644 --- a/pkg/admit/validators.go +++ b/pkg/admit/validators.go @@ -12,6 +12,7 @@ import ( "github.com/nokia/danm/pkg/danmep" "github.com/nokia/danm/pkg/ipam" "k8s.io/kubernetes/pkg/kubelet/cm/cpuset" + "log" ) const ( @@ -64,28 +65,28 @@ func validateAllocationPools(oldManifest, newManifest *danmtypes.DanmNet, opType (newManifest.Spec.Options.Alloc != "" || newManifest.Spec.Options.Alloc6 != "") { return errors.New("Allocation bitmasks shall not be manually defined upon creation!") } - v4PoolMask, err := validateAllocV4(newManifest) + err := validateAllocV4(newManifest) if err != nil { return err } - err = validateAllocV6(newManifest, v4PoolMask) + err = validateAllocV6(newManifest) if err != nil { return err } return nil } -func validateAllocV4(newManifest *danmtypes.DanmNet) (int, error) { +func validateAllocV4(newManifest *danmtypes.DanmNet) error { cidrV4 := newManifest.Spec.Options.Cidr if cidrV4 == "" { if newManifest.Spec.Options.Pool.Start != "" || newManifest.Spec.Options.Pool.End != "" { - return datastructs.MinV4MaskLength, errors.New("V4 Allocation pool cannot be defined without CIDR!") + return errors.New("V4 Allocation pool cannot be defined without CIDR!") } - return datastructs.MinV4MaskLength, nil + return nil } _, ipnet, _ := net.ParseCIDR(cidrV4) if ipnet.IP.To4() == nil { - return datastructs.MinV4MaskLength, errors.New("Options.CIDR is not a valid V4 subnet!") + return errors.New("Options.CIDR is not a valid V4 subnet!") } if newManifest.Spec.Options.Pool.Start == "" { newManifest.Spec.Options.Pool.Start = cidr.Inc(ipnet.IP).String() @@ -94,22 +95,22 @@ func validateAllocV4(newManifest *danmtypes.DanmNet) (int, error) { newManifest.Spec.Options.Pool.End = cidr.Dec(GetBroadcastAddress(ipnet)).String() } if !ipnet.Contains(net.ParseIP(newManifest.Spec.Options.Pool.Start)) || !ipnet.Contains(net.ParseIP(newManifest.Spec.Options.Pool.End)) { - return datastructs.MinV4MaskLength, errors.New("Allocation pool is outside of defined CIDR!") + return errors.New("Allocation pool is outside of defined CIDR!") } if ipam.Ip2int(net.ParseIP(newManifest.Spec.Options.Pool.End)) <= ipam.Ip2int(net.ParseIP(newManifest.Spec.Options.Pool.Start)) { - return datastructs.MinV4MaskLength, errors.New("Allocation pool start:" + newManifest.Spec.Options.Pool.Start + " is bigger than or equal to allocation pool end:" + newManifest.Spec.Options.Pool.End) + return errors.New("Allocation pool start:" + newManifest.Spec.Options.Pool.Start + " is bigger than or equal to allocation pool end:" + newManifest.Spec.Options.Pool.End) } netMaskSize, _ := ipnet.Mask.Size() if netMaskSize < datastructs.MaxV4MaskLength { - return datastructs.MinV4MaskLength, errors.New("Netmask of the IPv4 CIDR is bigger than the maximum allowed /"+ strconv.Itoa(datastructs.MaxV4MaskLength)) + return errors.New("Netmask of the IPv4 CIDR is bigger than the maximum allowed /"+ strconv.Itoa(datastructs.MaxV4MaskLength)) } if newManifest.Spec.Options.Alloc == "" { newManifest.Spec.Options.Alloc = ipam.CreateAllocationArray(ipnet, newManifest.Spec.Options.Routes) } - return netMaskSize, nil + return nil } -func validateAllocV6(newManifest *danmtypes.DanmNet, v4PoolMask int) error { +func validateAllocV6(newManifest *danmtypes.DanmNet) error { net6 := newManifest.Spec.Options.Net6 if net6 == "" { if newManifest.Spec.Options.Pool6.Start != "" || @@ -120,26 +121,30 @@ func validateAllocV6(newManifest *danmtypes.DanmNet, v4PoolMask int) error { return nil } _, netCidr, _ := net.ParseCIDR(net6) + if netCidr.IP.To4() != nil { + return errors.New("spec.Options.Net6 is not a valid V6 subnet!") + } // The limit of the current storage algorithm is 16M addresses per network. // This means that the summarized size of the IPv4, and IPv6 allocation pools shall not go over this threshold. // Therefore we need to calculate the maximum usable prefix for our V6 pool, discounting the space we have already reserved for the V4 pool. - maxV6AllocPrefix := datastructs.MaxV6PrefixLength + (datastructs.MinV4MaskLength - v4PoolMask) + maxV6AllocPrefix := ipam.GetMaxUsableV6Prefix(newManifest) if newManifest.Spec.Options.Pool6.Cidr == "" { baseCidrStart := netCidr.IP maskedV6AllocCidrBase := net.CIDRMask(maxV6AllocPrefix, 128) maskedV6AllocCidr := net.IPNet{IP:baseCidrStart, Mask:maskedV6AllocCidrBase} newManifest.Spec.Options.Pool6.Cidr = maskedV6AllocCidr.String() } + log.Println("Alloc6 CIDR:" + newManifest.Spec.Options.Pool6.Cidr) _, allocCidr, err := net.ParseCIDR(newManifest.Spec.Options.Pool6.Cidr) if err != nil { return errors.New("spec.Options.Pool6.CIDR is invalid!") } - if allocCidr.IP.To4() != nil || netCidr.IP.To4() != nil { - return errors.New("IPv6 CIDRs are not valid V6 subnets!") + if allocCidr.IP.To4() != nil { + return errors.New("spec.Options.Allocation_Pool_V6.Cidr is not a valid V6 subnet!") } netMaskSize, _ := allocCidr.Mask.Size() // We don't have enough storage space left for storing IPv6 allocations - if netMaskSize < maxV6AllocPrefix { + if netMaskSize < maxV6AllocPrefix || netMaskSize == datastructs.MinV6PrefixLength { return errors.New("The defined IPv6 allocation pool exceeds the maximum - 16M-size(IPv4 allocation pool) - storage capacity!") } if (newManifest.Spec.Options.Pool6.Start != "" && !allocCidr.Contains(net.ParseIP(newManifest.Spec.Options.Pool6.Start))) || @@ -154,7 +159,7 @@ func validateAllocV6(newManifest *danmtypes.DanmNet, v4PoolMask int) error { newManifest.Spec.Options.Pool6.End = cidr.Dec(GetBroadcastAddress(allocCidr)).String() } if ipam.Ip62int(net.ParseIP(newManifest.Spec.Options.Pool6.End)).Cmp(ipam.Ip62int(net.ParseIP(newManifest.Spec.Options.Pool6.Start))) <=0 { - return errors.New("Allocation pool start:" + newManifest.Spec.Options.Pool.Start + " is bigger than or equal to allocation pool end:" + newManifest.Spec.Options.Pool.End) + return errors.New("Allocation pool start:" + newManifest.Spec.Options.Pool6.Start + " is bigger than or equal to allocation pool end:" + newManifest.Spec.Options.Pool6.End) } if newManifest.Spec.Options.Alloc6 == "" { newManifest.Spec.Options.Alloc6 = ipam.CreateAllocationArray(allocCidr, newManifest.Spec.Options.Routes6) @@ -163,10 +168,6 @@ func validateAllocV6(newManifest *danmtypes.DanmNet, v4PoolMask int) error { } func GetBroadcastAddress(subnet *net.IPNet) (net.IP) { -/* ip := make(net.IP, len(subnet.IP.To4())) - //Don't ask - binary.BigEndian.PutUint32(ip, binary.BigEndian.Uint32(subnet.IP.To4())|^binary.BigEndian.Uint32(net.IP(subnet.Mask).To4())) -*/ _, lastIp := cidr.AddressRange(subnet) return lastIp } diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 7fbd0c0f..5597744f 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -3,6 +3,7 @@ package ipam import ( "errors" "fmt" + "math" "net" "strconv" "strings" @@ -10,10 +11,12 @@ import ( "encoding/binary" "math/big" "math/rand" + "github.com/apparentlymart/go-cidr/cidr" danmtypes "github.com/nokia/danm/crd/apis/danm/v1" danmclientset "github.com/nokia/danm/crd/client/clientset/versioned" - "github.com/nokia/danm/pkg/netcontrol" "github.com/nokia/danm/pkg/bitarray" + "github.com/nokia/danm/pkg/datastructs" + "github.com/nokia/danm/pkg/netcontrol" ) const ( @@ -283,12 +286,30 @@ func reserveGatewayIps(routes map[string]string, bitArray *bitarray.BitArray, ip } } -// Here be Stackoverflow magic + func DoV6CidrsIntersect(masterCidr, subCidr *net.IPNet) bool { - for i := range masterCidr.IP { - if masterCidr.IP[i] & masterCidr.Mask[i] != subCidr.IP[i] & subCidr.Mask[i] & masterCidr.Mask[i] { - return false + firstAllocIp, lastAllocIp := cidr.AddressRange(subCidr) + //Brute force: if the Alloc6 CIDR's first, and last IP both belongs to Net6, we assume the whole CIDR also does + if masterCidr.Contains(firstAllocIp) && masterCidr.Contains(lastAllocIp) { + return true + } + return false +} + +func GetMaxUsableV6Prefix(dnet *danmtypes.DanmNet) int { + if dnet.Spec.Options.Cidr == "" { + return datastructs.MaxV6PrefixLength + } + _, v4Cidr, _ := net.ParseCIDR(dnet.Spec.Options.Cidr) + sizeOfV4AllocPool := cidr.AddressCount(v4Cidr) + maxRemainingCapacity := uint64(math.Pow(2,float64(bitarray.MaxSupportedAllocLength))) - sizeOfV4AllocPool + maxUsableV6Prefix := datastructs.MinV6PrefixLength + for pref := datastructs.MaxV6PrefixLength; pref <= datastructs.MinV6PrefixLength; pref++ { + _, testCidr, _ := net.ParseCIDR("2a00:8a00:a000:1193:f816:3eff:fe24:e348/" + strconv.Itoa(pref)) + if cidr.AddressCount(testCidr) < maxRemainingCapacity { + maxUsableV6Prefix = pref + break } } - return true + return maxUsableV6Prefix } \ No newline at end of file diff --git a/test/uts/admit_tests/netadmit_test.go b/test/uts/admit_tests/netadmit_test.go index b637ee91..fc8f41a4 100644 --- a/test/uts/admit_tests/netadmit_test.go +++ b/test/uts/admit_tests/netadmit_test.go @@ -143,6 +143,10 @@ var validateNetworkTcs = []struct { {"CreateV6NetworkWithoutPool6DNet", "", "net6-without-pool6", DnetType, v1beta1.Create, nil, nil, false, v6Allocs, 0}, {"CreateV6NetworkWithoutPool6TNet", "", "net6-without-pool6", TnetType, v1beta1.Create, randomDev, nil, false, v6AllocsForTnet, 1}, {"CreateV6NetworkWithoutPool6CNet", "", "net6-without-pool6", CnetType, v1beta1.Create, nil, nil, false, v6Allocs, 0}, + {"V4PlusV6IsOverCapacity", "", "no-space-for-v6-alloc", DnetType, "", nil, nil, true, nil, 0}, + {"Pool6CidrBiggerThanNet6", "", "pool6-cidr-outside-net6", DnetType, "", nil, nil, true, nil, 0}, + {"InvalidPool6StartAddress", "", "invalid-pool6-start", DnetType, "", nil, nil, true, nil, 0}, + {"Pool6StartAddressMatchesEnd", "", "pool6-end-equals-start", DnetType, "", nil, nil, true, nil, 0}, } var ( @@ -346,6 +350,22 @@ var ( ObjectMeta: meta_v1.ObjectMeta {Name: "net6-without-pool6"}, Spec: danmtypes.DanmNetSpec{NetworkType: "ipvlan", NetworkID: "nanomsg", Options: danmtypes.DanmNetOption{Net6: "2a00:8a00:a000:1193::/64"}}, }, + danmtypes.DanmNet { + ObjectMeta: meta_v1.ObjectMeta {Name: "no-space-for-v6-alloc"}, + Spec: danmtypes.DanmNetSpec{NetworkType: "ipvlan", NetworkID: "nanomsg", Options: danmtypes.DanmNetOption{Cidr: "37.0.0.0/8", Net6: "2001:db8:85a3::8a2e:370:7334/104"}}, + }, + danmtypes.DanmNet { + ObjectMeta: meta_v1.ObjectMeta {Name: "pool6-cidr-outside-net6"}, + Spec: danmtypes.DanmNetSpec{NetworkType: "ipvlan", NetworkID: "nanomsg", Options: danmtypes.DanmNetOption{Net6: "2001:db8:85a3::8a2e:370:7334/110", Pool6: danmtypes.IpPoolV6{Cidr: "2001:db8:85a3::8a2e:370:7334/109"}}}, + }, + danmtypes.DanmNet { + ObjectMeta: meta_v1.ObjectMeta {Name: "invalid-pool6-start"}, + Spec: danmtypes.DanmNetSpec{NetworkType: "ipvlan", NetworkID: "nanomsg", Options: danmtypes.DanmNetOption{Net6: "2001:db8:85a3::8a2e:370:7334/108", Pool6: danmtypes.IpPoolV6{Cidr: "2001:db8:85a3::8a2e:370:7334/109", IpPool: danmtypes.IpPool{Start: "2001:db8:85a3::8a2e:370:734g"}}}}, + }, + danmtypes.DanmNet { + ObjectMeta: meta_v1.ObjectMeta {Name: "pool6-end-equals-start"}, + Spec: danmtypes.DanmNetSpec{NetworkType: "ipvlan", NetworkID: "nanomsg", Options: danmtypes.DanmNetOption{Net6: "2001:db8:85a3::8a2e:370:7334/108", Pool6: danmtypes.IpPoolV6{Cidr: "2001:db8:85a3::8a2e:370:7334/109", IpPool: danmtypes.IpPool{Start: "2001:db8:85a3::8a2e:370:7340", End: "2001:db8:85a3::8a2e:370:7340"}}}}, + }, } )