Skip to content

Commit 147c438

Browse files
tamilmani1989sharmasushant
authored andcommitted
Multitenancy Support for Linux (#156)
This PR adds the following capabilities 1. Support to create and connect containers to different Azure Vnets. Every VlanId corresponds to a different Azure Vnet. 2. SNAT support for containers to reach Internet using the container's host IP as the SNAT address. Both the above options are controlled by the config parameters in Azure CNI.
1 parent ec415fd commit 147c438

36 files changed

+2475
-620
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"cniVersion":"0.3.0",
3+
"name":"azure",
4+
"plugins":[
5+
{
6+
"type":"azure-vnet",
7+
"mode":"bridge",
8+
"bridge":"azure0",
9+
"multiTenancy":true,
10+
"enableSnatOnHost":true,
11+
"ipam":{
12+
"type":"azure-vnet-ipam"
13+
}
14+
},
15+
{
16+
"type":"portmap",
17+
"capabilities":{
18+
"portMappings":true
19+
},
20+
"snat":true
21+
}
22+
]
23+
}

cni/netconfig.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ type KVPair struct {
2424

2525
// NetworkConfig represents Azure CNI plugin network configuration.
2626
type NetworkConfig struct {
27-
CNIVersion string `json:"cniVersion"`
28-
Name string `json:"name"`
29-
Type string `json:"type"`
30-
Mode string `json:"mode"`
31-
Master string `json:"master"`
32-
Bridge string `json:"bridge,omitempty"`
33-
LogLevel string `json:"logLevel,omitempty"`
34-
LogTarget string `json:"logTarget,omitempty"`
35-
Ipam struct {
27+
CNIVersion string `json:"cniVersion"`
28+
Name string `json:"name"`
29+
Type string `json:"type"`
30+
Mode string `json:"mode"`
31+
Master string `json:"master"`
32+
Bridge string `json:"bridge,omitempty"`
33+
LogLevel string `json:"logLevel,omitempty"`
34+
LogTarget string `json:"logTarget,omitempty"`
35+
MultiTenancy bool `json:"multiTenancy,omitempty"`
36+
EnableSnatOnHost bool `json:"enableSnatOnHost,omitempty"`
37+
Ipam struct {
3638
Type string `json:"type"`
3739
Environment string `json:"environment,omitempty"`
3840
AddrSpace string `json:"addressSpace,omitempty"`

cni/network/mutlitenancy.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package network
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net"
7+
"strings"
8+
9+
"github.com/Azure/azure-container-networking/cni"
10+
"github.com/Azure/azure-container-networking/cns"
11+
"github.com/Azure/azure-container-networking/cns/cnsclient"
12+
"github.com/Azure/azure-container-networking/common"
13+
"github.com/Azure/azure-container-networking/log"
14+
"github.com/Azure/azure-container-networking/network"
15+
cniTypes "github.com/containernetworking/cni/pkg/types"
16+
cniTypesCurr "github.com/containernetworking/cni/pkg/types/current"
17+
)
18+
19+
func SetupRoutingForMultitenancy(nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns.GetNetworkContainerResponse, epInfo *network.EndpointInfo, result *cniTypesCurr.Result) {
20+
// Adding default gateway
21+
if nwCfg.MultiTenancy {
22+
// if snat enabled, add 169.254.0.1 as default gateway
23+
if nwCfg.EnableSnatOnHost {
24+
log.Printf("add default route for multitenancy.snat on host enabled")
25+
addDefaultRoute(cnsNetworkConfig.LocalIPConfiguration.GatewayIPAddress, epInfo, result)
26+
} else {
27+
_, defaultIPNet, _ := net.ParseCIDR("0.0.0.0/0")
28+
dstIP := net.IPNet{IP: net.ParseIP("0.0.0.0"), Mask: defaultIPNet.Mask}
29+
gwIP := net.ParseIP(cnsNetworkConfig.IPConfiguration.GatewayIPAddress)
30+
epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: dstIP, Gw: gwIP})
31+
result.Routes = append(result.Routes, &cniTypes.Route{Dst: dstIP, GW: gwIP})
32+
}
33+
}
34+
}
35+
36+
func GetContainerNetworkConfiguration(multiTenancy bool, address string, podName string, podNamespace string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
37+
if multiTenancy {
38+
podNameWithoutSuffix := getPodNameWithoutSuffix(podName)
39+
log.Printf("Podname without suffix %v", podNameWithoutSuffix)
40+
return getContainerNetworkConfiguration(address, podNamespace, podNameWithoutSuffix)
41+
}
42+
43+
return nil, nil, net.IPNet{}, nil
44+
}
45+
46+
func getContainerNetworkConfiguration(address string, namespace string, podName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
47+
cnsClient, err := cnsclient.NewCnsClient(address)
48+
if err != nil {
49+
log.Printf("Initializing CNS client error %v", err)
50+
return nil, nil, net.IPNet{}, err
51+
}
52+
53+
podInfo := cns.KubernetesPodInfo{PodName: podName, PodNamespace: namespace}
54+
orchestratorContext, err := json.Marshal(podInfo)
55+
if err != nil {
56+
log.Printf("Marshalling KubernetesPodInfo failed with %v", err)
57+
return nil, nil, net.IPNet{}, err
58+
}
59+
60+
networkConfig, err := cnsClient.GetNetworkConfiguration(orchestratorContext)
61+
if err != nil {
62+
log.Printf("GetNetworkConfiguration failed with %v", err)
63+
return nil, nil, net.IPNet{}, err
64+
}
65+
66+
log.Printf("Network config received from cns %+v", networkConfig)
67+
68+
subnetPrefix := common.GetInterfaceSubnetWithSpecificIp(networkConfig.PrimaryInterfaceIdentifier)
69+
if subnetPrefix == nil {
70+
errBuf := fmt.Sprintf("Interface not found for this ip %v", networkConfig.PrimaryInterfaceIdentifier)
71+
log.Printf(errBuf)
72+
return nil, nil, net.IPNet{}, fmt.Errorf(errBuf)
73+
}
74+
75+
return convertToCniResult(networkConfig), networkConfig, *subnetPrefix, nil
76+
}
77+
78+
func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse) *cniTypesCurr.Result {
79+
result := &cniTypesCurr.Result{}
80+
resultIpconfig := &cniTypesCurr.IPConfig{}
81+
82+
ipconfig := networkConfig.IPConfiguration
83+
ipAddr := net.ParseIP(ipconfig.IPSubnet.IPAddress)
84+
85+
if ipAddr.To4() != nil {
86+
resultIpconfig.Version = "4"
87+
resultIpconfig.Address = net.IPNet{IP: ipAddr, Mask: net.CIDRMask(int(ipconfig.IPSubnet.PrefixLength), 32)}
88+
} else {
89+
resultIpconfig.Version = "6"
90+
resultIpconfig.Address = net.IPNet{IP: ipAddr, Mask: net.CIDRMask(int(ipconfig.IPSubnet.PrefixLength), 128)}
91+
}
92+
93+
resultIpconfig.Gateway = net.ParseIP(ipconfig.GatewayIPAddress)
94+
result.IPs = append(result.IPs, resultIpconfig)
95+
result.DNS.Nameservers = ipconfig.DNSServers
96+
97+
if networkConfig.Routes != nil && len(networkConfig.Routes) > 0 {
98+
for _, route := range networkConfig.Routes {
99+
_, routeIPnet, _ := net.ParseCIDR(route.IPAddress)
100+
gwIP := net.ParseIP(route.GatewayIPAddress)
101+
result.Routes = append(result.Routes, &cniTypes.Route{Dst: *routeIPnet, GW: gwIP})
102+
}
103+
}
104+
105+
for _, ipRouteSubnet := range networkConfig.CnetAddressSpace {
106+
log.Printf("Adding cnetAddressspace routes %v %v", ipRouteSubnet.IPAddress, ipRouteSubnet.PrefixLength)
107+
routeIPnet := net.IPNet{IP: net.ParseIP(ipRouteSubnet.IPAddress), Mask: net.CIDRMask(int(ipRouteSubnet.PrefixLength), 32)}
108+
gwIP := net.ParseIP(ipconfig.GatewayIPAddress)
109+
result.Routes = append(result.Routes, &cniTypes.Route{Dst: routeIPnet, GW: gwIP})
110+
}
111+
112+
return result
113+
}
114+
115+
func getPodNameWithoutSuffix(podName string) string {
116+
nameSplit := strings.Split(podName, "-")
117+
log.Printf("namesplit %v", nameSplit)
118+
if len(nameSplit) > 2 {
119+
nameSplit = nameSplit[:len(nameSplit)-2]
120+
} else {
121+
return podName
122+
}
123+
124+
log.Printf("Pod name after splitting based on - : %v", nameSplit)
125+
return strings.Join(nameSplit, "-")
126+
}

0 commit comments

Comments
 (0)