Skip to content

Commit

Permalink
Added support for InfraVnet connectivity in multitenancy CNI (#234)
Browse files Browse the repository at this point in the history
* Added infravnet connectivity support for multitenancy CNI
  • Loading branch information
tamilmani1989 authored and sharmasushant committed Aug 18, 2018
1 parent f84799b commit 9a9c2cd
Show file tree
Hide file tree
Showing 23 changed files with 1,195 additions and 559 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ COREFILES = \
$(wildcard log/*.go) \
$(wildcard netlink/*.go) \
$(wildcard network/*.go) \
$(wildcard network/epcommon/*.go) \
$(wildcard network/ovsnfravnet/*.go) \
$(wildcard network/ovssnat/*.go) \
$(wildcard network/policy/*.go) \
$(wildcard platform/*.go) \
$(wildcard store/*.go)

Expand Down
25 changes: 14 additions & 11 deletions cni/netconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ type KVPair struct {

// NetworkConfig represents Azure CNI plugin network configuration.
type NetworkConfig struct {
CNIVersion string `json:"cniVersion"`
Name string `json:"name"`
Type string `json:"type"`
Mode string `json:"mode"`
Master string `json:"master"`
Bridge string `json:"bridge,omitempty"`
LogLevel string `json:"logLevel,omitempty"`
LogTarget string `json:"logTarget,omitempty"`
MultiTenancy bool `json:"multiTenancy,omitempty"`
EnableSnatOnHost bool `json:"enableSnatOnHost,omitempty"`
Ipam struct {
CNIVersion string `json:"cniVersion"`
Name string `json:"name"`
Type string `json:"type"`
Mode string `json:"mode"`
Master string `json:"master"`
Bridge string `json:"bridge,omitempty"`
LogLevel string `json:"logLevel,omitempty"`
LogTarget string `json:"logTarget,omitempty"`
InfraVnetAddressSpace string `json:"infraVnetAddressSpace,omitempty"`
PodNamespaceForDualNetwork []string `json:"podNamespaceForDualNetwork,omitempty"`
MultiTenancy bool `json:"multiTenancy,omitempty"`
EnableSnatOnHost bool `json:"enableSnatOnHost,omitempty"`
EnableExactMatchForPodName bool `json:"enableExactMatchForPodName,omitempty"`
Ipam struct {
Type string `json:"type"`
Environment string `json:"environment,omitempty"`
AddrSpace string `json:"addressSpace,omitempty"`
Expand Down
157 changes: 147 additions & 10 deletions cni/network/mutlitenancy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package network

import (
"encoding/json"
"errors"
"fmt"
"net"
"strings"
Expand All @@ -16,7 +17,12 @@ import (
cniTypesCurr "github.com/containernetworking/cni/pkg/types/current"
)

func SetupRoutingForMultitenancy(nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns.GetNetworkContainerResponse, epInfo *network.EndpointInfo, result *cniTypesCurr.Result) {
func SetupRoutingForMultitenancy(
nwCfg *cni.NetworkConfig,
cnsNetworkConfig *cns.GetNetworkContainerResponse,
azIpamResult *cniTypesCurr.Result,
epInfo *network.EndpointInfo,
result *cniTypesCurr.Result) {
// Adding default gateway
if nwCfg.MultiTenancy {
// if snat enabled, add 169.254.0.1 as default gateway
Expand All @@ -30,20 +36,34 @@ func SetupRoutingForMultitenancy(nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns
epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: dstIP, Gw: gwIP})
result.Routes = append(result.Routes, &cniTypes.Route{Dst: dstIP, GW: gwIP})
}

setupInfraVnetRoutingForMultitenancy(nwCfg, azIpamResult, epInfo, result)
}
}

func GetContainerNetworkConfiguration(multiTenancy bool, address string, podName string, podNamespace string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
if multiTenancy {
podNameWithoutSuffix := getPodNameWithoutSuffix(podName)
log.Printf("Podname without suffix %v", podNameWithoutSuffix)
return getContainerNetworkConfiguration(address, podNamespace, podNameWithoutSuffix)
func getContainerNetworkConfiguration(
nwCfg *cni.NetworkConfig,
address string,
podName string,
podNamespace string,
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
var podNameWithoutSuffix string

if !nwCfg.EnableExactMatchForPodName {
podNameWithoutSuffix = getPodNameWithoutSuffix(podName)
} else {
podNameWithoutSuffix = podName
}

return nil, nil, net.IPNet{}, nil
log.Printf("Podname without suffix %v", podNameWithoutSuffix)
return getContainerNetworkConfigurationInternal(address, podNamespace, podNameWithoutSuffix, ifName)
}

func getContainerNetworkConfiguration(address string, namespace string, podName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
func getContainerNetworkConfigurationInternal(
address string,
namespace string,
podName string,
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
cnsClient, err := cnsclient.NewCnsClient(address)
if err != nil {
log.Printf("Initializing CNS client error %v", err)
Expand Down Expand Up @@ -72,10 +92,10 @@ func getContainerNetworkConfiguration(address string, namespace string, podName
return nil, nil, net.IPNet{}, fmt.Errorf(errBuf)
}

return convertToCniResult(networkConfig), networkConfig, *subnetPrefix, nil
return convertToCniResult(networkConfig, ifName), networkConfig, *subnetPrefix, nil
}

func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse) *cniTypesCurr.Result {
func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse, ifName string) *cniTypesCurr.Result {
result := &cniTypesCurr.Result{}
resultIpconfig := &cniTypesCurr.IPConfig{}

Expand Down Expand Up @@ -109,6 +129,9 @@ func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse) *cniType
result.Routes = append(result.Routes, &cniTypes.Route{Dst: routeIPnet, GW: gwIP})
}

iface := &cniTypesCurr.Interface{Name: ifName}
result.Interfaces = append(result.Interfaces, iface)

return result
}

Expand All @@ -124,3 +147,117 @@ func getPodNameWithoutSuffix(podName string) string {
log.Printf("Pod name after splitting based on - : %v", nameSplit)
return strings.Join(nameSplit, "-")
}

func getInfraVnetIP(
enableInfraVnet bool,
infraSubnet string,
nwCfg *cni.NetworkConfig,
plugin *netPlugin,
) (*cniTypesCurr.Result, error) {

if enableInfraVnet {
_, ipNet, _ := net.ParseCIDR(infraSubnet)
nwCfg.Ipam.Subnet = ipNet.String()

log.Printf("call ipam to allocate ip from subnet %v", nwCfg.Ipam.Subnet)
azIpamResult, err := plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
if err != nil {
err = plugin.Errorf("Failed to allocate address: %v", err)
return nil, err
}

return azIpamResult, nil
}

return nil, nil
}

func cleanupInfraVnetIP(
enableInfraVnet bool,
infraIPNet *net.IPNet,
nwCfg *cni.NetworkConfig,
plugin *netPlugin) {

log.Printf("Cleanup infravnet ip")

if enableInfraVnet {
_, ipNet, _ := net.ParseCIDR(infraIPNet.String())
nwCfg.Ipam.Subnet = ipNet.String()
nwCfg.Ipam.Address = infraIPNet.IP.String()
plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
}
}

func checkIfSubnetOverlaps(enableInfraVnet bool, nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns.GetNetworkContainerResponse) bool {
if enableInfraVnet {
if cnsNetworkConfig != nil {
_, infraNet, _ := net.ParseCIDR(nwCfg.InfraVnetAddressSpace)
for _, cnetSpace := range cnsNetworkConfig.CnetAddressSpace {
cnetSpaceIPNet := &net.IPNet{
IP: net.ParseIP(cnetSpace.IPAddress),
Mask: net.CIDRMask(int(cnetSpace.PrefixLength), 32),
}

return infraNet.Contains(cnetSpaceIPNet.IP) || cnetSpaceIPNet.Contains(infraNet.IP)
}
}
}

return false
}

func GetMultiTenancyCNIResult(
enableInfraVnet bool,
nwCfg *cni.NetworkConfig,
plugin *netPlugin,
k8sPodName string,
k8sNamespace string,
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, *cniTypesCurr.Result, error) {

if nwCfg.MultiTenancy {
result, cnsNetworkConfig, subnetPrefix, err := getContainerNetworkConfiguration(nwCfg, "", k8sPodName, k8sNamespace, ifName)
if err != nil {
log.Printf("GetContainerNetworkConfiguration failed for podname %v namespace %v with error %v", k8sPodName, k8sNamespace, err)
return nil, nil, net.IPNet{}, nil, err
}

log.Printf("PrimaryInterfaceIdentifier :%v", subnetPrefix.IP.String())

if checkIfSubnetOverlaps(enableInfraVnet, nwCfg, cnsNetworkConfig) {
buf := fmt.Sprintf("InfraVnet %v overlaps with customerVnet %+v", nwCfg.InfraVnetAddressSpace, cnsNetworkConfig.CnetAddressSpace)
log.Printf(buf)
err = errors.New(buf)
return nil, nil, net.IPNet{}, nil, err
}

if nwCfg.EnableSnatOnHost {
if cnsNetworkConfig.LocalIPConfiguration.IPSubnet.IPAddress == "" {
log.Printf("Snat IP is not populated. Got empty string")
return nil, nil, net.IPNet{}, nil, fmt.Errorf("Snat IP is not populated. Got empty string")
}
}

if enableInfraVnet {
if nwCfg.InfraVnetAddressSpace == "" {
log.Printf("InfraVnetAddressSpace is not populated. Got empty string")
return nil, nil, net.IPNet{}, nil, fmt.Errorf("InfraVnetAddressSpace is not populated. Got empty string")
}
}

azIpamResult, err := getInfraVnetIP(enableInfraVnet, subnetPrefix.String(), nwCfg, plugin)
if err != nil {
log.Printf("GetInfraVnetIP failed with error %v", err)
return nil, nil, net.IPNet{}, nil, err
}

return result, cnsNetworkConfig, subnetPrefix, azIpamResult, nil
}

return nil, nil, net.IPNet{}, nil, nil
}

func CleanupMultitenancyResources(enableInfraVnet bool, nwCfg *cni.NetworkConfig, azIpamResult *cniTypesCurr.Result, plugin *netPlugin) {
if nwCfg.MultiTenancy && azIpamResult != nil && azIpamResult.IPs != nil {
cleanupInfraVnetIP(enableInfraVnet, &azIpamResult.IPs[0].Address, nwCfg, plugin)
}
}
Loading

0 comments on commit 9a9c2cd

Please sign in to comment.