Skip to content

Commit

Permalink
Merge 09a29fa into ad900b7
Browse files Browse the repository at this point in the history
  • Loading branch information
Levovar committed Feb 5, 2020
2 parents ad900b7 + 09a29fa commit f4136b0
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 93 deletions.
20 changes: 15 additions & 5 deletions crd/apis/danm/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ type DanmNetOption struct {
Routes map[string]string `json:"routes,omitempty"`
// bit array of tracking address allocation
Alloc string `json:"alloc,omitempty"`
// subset of the Cidr from where dynamic IP address allocation happens
Pool IP4Pool `json:"allocation_pool,omitEmpty"`
// subset of the IPv4 subnet from which IPs can be allocated
Pool IpPool `json:"allocation_pool,omitEmpty"`
// IPv6 specific parameters
// IPv6 unique global address prefix
Net6 string `json:"net6,omitempty"`
// IPv6 routes for this network
Routes6 map[string]string `json:"routes6,omitempty"`
// bit array tracking IPv6 allocations
Alloc6 string `json:"alloc6,omitempty"`
// subset of the IPv6 subnet from which IPs can be allocated
Pool6 IpPoolV6 `json:"allocation_pool_v6,omitEmpty"`
// Routing table number for policy routing
RTables int `json:"rt_tables,omitempty"`
// the VLAN id of the VLAN interface created on top of the host device
Expand All @@ -55,9 +59,15 @@ type DanmNetList struct {
Items []DanmNet `json:"items"`
}

type IP4Pool struct {
Start string `json:"start"`
End string `json:"end"`
type IpPool struct {
Start string `json:"start,omitEmpty"`
End string `json:"end,omitEmpty"`
}

type IpPoolV6 struct {
IpPool
Cidr string `json:"cidr"`
LastIp string `json:"lastIp,omitEmpty"`
}

// +genclient
Expand Down
34 changes: 26 additions & 8 deletions crd/apis/danm/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 3 additions & 22 deletions pkg/admit/netadmit.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package admit
import (
"bytes"
"errors"
"net"
"reflect"
"strings"
"time"
Expand All @@ -13,9 +12,7 @@ import (
"k8s.io/api/admission/v1beta1"
danmtypes "github.com/nokia/danm/crd/apis/danm/v1"
danmclientset "github.com/nokia/danm/crd/client/clientset/versioned"
"github.com/nokia/danm/pkg/bitarray"
"github.com/nokia/danm/pkg/confman"
"github.com/nokia/danm/pkg/ipam"
"github.com/nokia/danm/pkg/metacni"
)

Expand All @@ -24,7 +21,9 @@ var (
"NetworkType": "/spec/NetworkType",
"NetworkID": "/spec/NetworkID",
"Alloc": "/spec/Options/alloc",
"Alloc6": "/spec/Options/alloc6",
"Pool": "/spec/Options/allocation_pool",
"Pool6": "/spec/Options/allocation_pool_v6",
"Device": "/spec/Options/host_device",
"Vlan": "/spec/Options/vlan",
"Vxlan": "/spec/Options/vxlan",
Expand Down Expand Up @@ -119,9 +118,6 @@ func mutateNetManifest(danmClient danmclientset.Interface, dnet *danmtypes.DanmN
}
var err error
//L3, freshly added network
if dnet.Spec.Options.Cidr != "" && dnet.Spec.Options.Alloc == "" {
CreateAllocationArray(dnet)
}
if dnet.TypeMeta.Kind == "TenantNetwork" {
err = addTenantSpecificDetails(danmClient, dnet)
}
Expand All @@ -136,21 +132,6 @@ func postValidateManifest(dnet *danmtypes.DanmNet) error {
return validateNetworkId(nil, dnet, "", nil)
}

func CreateAllocationArray(dnet *danmtypes.DanmNet) {
_,ipnet,_ := net.ParseCIDR(dnet.Spec.Options.Cidr)
bitArray,_ := bitarray.CreateBitArrayFromIpnet(ipnet)
reserveGatewayIps(dnet.Spec.Options.Routes, bitArray, ipnet)
dnet.Spec.Options.Alloc = bitArray.Encode()
return
}

func reserveGatewayIps(routes map[string]string, bitArray *bitarray.BitArray, ipnet *net.IPNet) {
for _, gw := range routes {
gatewayPosition := ipam.Ip2int(net.ParseIP(gw)) - ipam.Ip2int(ipnet.IP)
bitArray.Set(gatewayPosition)
}
}

//TODO: we could easily add CIDR + allocation pool overwrites as well for TenantNetworks, if needed
//Open an issue with your use-case if you see the need!
func addTenantSpecificDetails(danmClient danmclientset.Interface, tnet *danmtypes.DanmNet) error {
Expand Down Expand Up @@ -185,7 +166,7 @@ func allocateDetailsForDynamicBackends(danmClient danmclientset.Interface, tnet
return attachNetworkToIfaceProfile(danmClient, tnet,tconf,iface)
}
//DevicePools generally look like this: "xyz.abc.io/resource_name".
//Here we separate "real" NICs from abstract K8s Devices
//Here we separate "real" NICs from abstract K8s Devices
if !strings.Contains(iface.Name,"/") {
pfProfiles = append(pfProfiles,iface)
}
Expand Down
122 changes: 97 additions & 25 deletions pkg/admit/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ import (
"errors"
"net"
"strconv"
"encoding/binary"
admissionv1 "k8s.io/api/admission/v1beta1"
danmtypes "github.com/nokia/danm/crd/apis/danm/v1"
danmclientset "github.com/nokia/danm/crd/client/clientset/versioned"
"github.com/apparentlymart/go-cidr/cidr"
"github.com/nokia/danm/pkg/datastructs"
"github.com/nokia/danm/pkg/danmep"
"github.com/nokia/danm/pkg/ipam"
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
)

const (
MaxNidLength = 11
MaxNetMaskLength = 8
)

var (
DanmNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId,validateAbsenceOfAllowedTenants,validateNeType,validateVniChange}
ClusterNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId,validateNeType,validateVniChange}
TenantNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateAbsenceOfAllowedTenants,validateTenantNetRules,validateNeType}
DanmNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPools,validateVids,validateNetworkId,validateAbsenceOfAllowedTenants,validateNeType,validateVniChange}
ClusterNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPools,validateVids,validateNetworkId,validateNeType,validateVniChange}
TenantNetMapping = []ValidatorFunc{validateIpv4Fields,validateIpv6Fields,validateAllocationPools,validateAbsenceOfAllowedTenants,validateTenantNetRules,validateNeType}
danmValidationConfig = map[string]ValidatorMapping {
"DanmNet": DanmNetMapping,
"ClusterNetwork": ClusterNetMapping,
Expand Down Expand Up @@ -51,12 +51,6 @@ func validateIpFields(cidr string, routes map[string]string) error {
if err != nil {
return errors.New("Invalid CIDR: " + cidr)
}
if ipnet.IP.To4() != nil {
ones, _ := ipnet.Mask.Size()
if ones < MaxNetMaskLength {
return errors.New("Netmask of the IPv4 CIDR is bigger than the maximum allowed /"+ strconv.Itoa(MaxNetMaskLength))
}
}
for _, gw := range routes {
if !ipnet.Contains(net.ParseIP(gw)) {
return errors.New("Specified GW address:" + gw + " is not part of CIDR:" + cidr)
Expand All @@ -65,42 +59,120 @@ func validateIpFields(cidr string, routes map[string]string) error {
return nil
}

func validateAllocationPool(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if opType == admissionv1.Create && newManifest.Spec.Options.Alloc != "" {
return errors.New("Allocation bitmask shall not be manually defined upon creation!")
func validateAllocationPools(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
if opType == admissionv1.Create &&
(newManifest.Spec.Options.Alloc != "" || newManifest.Spec.Options.Alloc6 != "") {
return errors.New("Allocation bitmasks shall not be manually defined upon creation!")
}
cidr := newManifest.Spec.Options.Cidr
if cidr == "" {
v4PoolMask, err := validateAllocV4(newManifest)
if err != nil {
return err
}
err = validateAllocV6(newManifest, v4PoolMask)
if err != nil {
return err
}
return nil
}

func validateAllocV4(newManifest *danmtypes.DanmNet) (int, error) {
cidrV4 := newManifest.Spec.Options.Cidr
if cidrV4 == "" {
if newManifest.Spec.Options.Pool.Start != "" || newManifest.Spec.Options.Pool.End != "" {
return errors.New("Allocation pool cannot be defined without CIDR!")
return datastructs.MinV4MaskLength, errors.New("V4 Allocation pool cannot be defined without CIDR!")
}
return nil
return datastructs.MinV4MaskLength, nil
}
_, ipnet, _ := net.ParseCIDR(cidrV4)
if ipnet.IP.To4() == nil {
return datastructs.MinV4MaskLength, errors.New("Options.CIDR is not a valid V4 subnet!")
}
_, ipnet, _ := net.ParseCIDR(cidr)
if newManifest.Spec.Options.Pool.Start == "" {
newManifest.Spec.Options.Pool.Start = (ipam.Int2ip(ipam.Ip2int(ipnet.IP) + 1)).String()
newManifest.Spec.Options.Pool.Start = cidr.Inc(ipnet.IP).String()
}
if newManifest.Spec.Options.Pool.End == "" {
newManifest.Spec.Options.Pool.End = (ipam.Int2ip(ipam.Ip2int(GetBroadcastAddress(ipnet)) - 1)).String()
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 errors.New("Allocation pool is outside of defined CIDR")
return datastructs.MinV4MaskLength, 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)
}
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))
}
if newManifest.Spec.Options.Alloc == "" {
newManifest.Spec.Options.Alloc = ipam.CreateAllocationArray(ipnet, newManifest.Spec.Options.Routes)
}
return netMaskSize, nil
}

func validateAllocV6(newManifest *danmtypes.DanmNet, v4PoolMask int) error {
net6 := newManifest.Spec.Options.Net6
if net6 == "" {
if newManifest.Spec.Options.Pool6.Start != "" ||
newManifest.Spec.Options.Pool6.End != "" ||
newManifest.Spec.Options.Pool6.Cidr != "" {
return errors.New("IPv6 allocation pool cannot be defined without Net6!")
}
return nil
}
_, netCidr, _ := net.ParseCIDR(net6)
// 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)
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()
}
_, allocCidr, err := net.ParseCIDR(newManifest.Spec.Options.Pool6.Cidr)
if err != nil {
return errors.New("spec.Options.Pool6.CIDR is invalid!")
}
if allocCidr.IP.To16() == nil || netCidr.IP.To16() == nil {
return errors.New("IPv6 CIDRs are not valid V6 subnets!")
}
netMaskSize, _ := allocCidr.Mask.Size()
// We don't have enough storage space left for storing IPv6 allocations
if netMaskSize < maxV6AllocPrefix {
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))) ||
(newManifest.Spec.Options.Pool6.End != "" && !allocCidr.Contains(net.ParseIP(newManifest.Spec.Options.Pool6.End))) ||
(!ipam.DoV6CidrsIntersect(netCidr, allocCidr)) {
return errors.New("IPv6 allocation pool is outside of the defined IPv6 subnet!")
}
if newManifest.Spec.Options.Pool6.Start == "" {
newManifest.Spec.Options.Pool6.Start = cidr.Inc(allocCidr.IP).String()
}
if newManifest.Spec.Options.Pool.End == "" {
newManifest.Spec.Options.Pool.End = cidr.Dec(GetBroadcastAddress(allocCidr)).String()
}
if ipam.Ip2int(net.ParseIP(newManifest.Spec.Options.Pool.End)) <= ipam.Ip2int(net.ParseIP(newManifest.Spec.Options.Pool.Start)) {
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)
}
if newManifest.Spec.Options.Alloc6 == "" {
newManifest.Spec.Options.Alloc6 = ipam.CreateAllocationArray(allocCidr, newManifest.Spec.Options.Routes6)
}
return nil
}

func GetBroadcastAddress(subnet *net.IPNet) (net.IP) {
ip := make(net.IP, len(subnet.IP.To4()))
/* 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()))
return ip
*/
_, lastIp := cidr.AddressRange(subnet)
return lastIp
}

func validateVids(oldManifest, newManifest *danmtypes.DanmNet, opType admissionv1.Operation, client danmclientset.Interface) error {
isVlanDefined := (newManifest.Spec.Options.Vlan!=0)
isVlanDefined := (newManifest.Spec.Options.Vlan !=0)
isVxlanDefined := (newManifest.Spec.Options.Vxlan!=0)
if isVlanDefined && isVxlanDefined {
return errors.New("VLAN ID and VxLAN ID parameters are mutually exclusive")
Expand Down

0 comments on commit f4136b0

Please sign in to comment.