Skip to content

Commit 9a9c2cd

Browse files
tamilmani1989sharmasushant
authored andcommitted
Added support for InfraVnet connectivity in multitenancy CNI (#234)
* Added infravnet connectivity support for multitenancy CNI
1 parent f84799b commit 9a9c2cd

23 files changed

+1195
-559
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ COREFILES = \
66
$(wildcard log/*.go) \
77
$(wildcard netlink/*.go) \
88
$(wildcard network/*.go) \
9+
$(wildcard network/epcommon/*.go) \
10+
$(wildcard network/ovsnfravnet/*.go) \
11+
$(wildcard network/ovssnat/*.go) \
12+
$(wildcard network/policy/*.go) \
913
$(wildcard platform/*.go) \
1014
$(wildcard store/*.go)
1115

cni/netconfig.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,20 @@ 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-
MultiTenancy bool `json:"multiTenancy,omitempty"`
36-
EnableSnatOnHost bool `json:"enableSnatOnHost,omitempty"`
37-
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+
InfraVnetAddressSpace string `json:"infraVnetAddressSpace,omitempty"`
36+
PodNamespaceForDualNetwork []string `json:"podNamespaceForDualNetwork,omitempty"`
37+
MultiTenancy bool `json:"multiTenancy,omitempty"`
38+
EnableSnatOnHost bool `json:"enableSnatOnHost,omitempty"`
39+
EnableExactMatchForPodName bool `json:"enableExactMatchForPodName,omitempty"`
40+
Ipam struct {
3841
Type string `json:"type"`
3942
Environment string `json:"environment,omitempty"`
4043
AddrSpace string `json:"addressSpace,omitempty"`

cni/network/mutlitenancy.go

Lines changed: 147 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package network
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"net"
78
"strings"
@@ -16,7 +17,12 @@ import (
1617
cniTypesCurr "github.com/containernetworking/cni/pkg/types/current"
1718
)
1819

19-
func SetupRoutingForMultitenancy(nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns.GetNetworkContainerResponse, epInfo *network.EndpointInfo, result *cniTypesCurr.Result) {
20+
func SetupRoutingForMultitenancy(
21+
nwCfg *cni.NetworkConfig,
22+
cnsNetworkConfig *cns.GetNetworkContainerResponse,
23+
azIpamResult *cniTypesCurr.Result,
24+
epInfo *network.EndpointInfo,
25+
result *cniTypesCurr.Result) {
2026
// Adding default gateway
2127
if nwCfg.MultiTenancy {
2228
// if snat enabled, add 169.254.0.1 as default gateway
@@ -30,20 +36,34 @@ func SetupRoutingForMultitenancy(nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns
3036
epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: dstIP, Gw: gwIP})
3137
result.Routes = append(result.Routes, &cniTypes.Route{Dst: dstIP, GW: gwIP})
3238
}
39+
40+
setupInfraVnetRoutingForMultitenancy(nwCfg, azIpamResult, epInfo, result)
3341
}
3442
}
3543

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)
44+
func getContainerNetworkConfiguration(
45+
nwCfg *cni.NetworkConfig,
46+
address string,
47+
podName string,
48+
podNamespace string,
49+
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
50+
var podNameWithoutSuffix string
51+
52+
if !nwCfg.EnableExactMatchForPodName {
53+
podNameWithoutSuffix = getPodNameWithoutSuffix(podName)
54+
} else {
55+
podNameWithoutSuffix = podName
4156
}
4257

43-
return nil, nil, net.IPNet{}, nil
58+
log.Printf("Podname without suffix %v", podNameWithoutSuffix)
59+
return getContainerNetworkConfigurationInternal(address, podNamespace, podNameWithoutSuffix, ifName)
4460
}
4561

46-
func getContainerNetworkConfiguration(address string, namespace string, podName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
62+
func getContainerNetworkConfigurationInternal(
63+
address string,
64+
namespace string,
65+
podName string,
66+
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
4767
cnsClient, err := cnsclient.NewCnsClient(address)
4868
if err != nil {
4969
log.Printf("Initializing CNS client error %v", err)
@@ -72,10 +92,10 @@ func getContainerNetworkConfiguration(address string, namespace string, podName
7292
return nil, nil, net.IPNet{}, fmt.Errorf(errBuf)
7393
}
7494

75-
return convertToCniResult(networkConfig), networkConfig, *subnetPrefix, nil
95+
return convertToCniResult(networkConfig, ifName), networkConfig, *subnetPrefix, nil
7696
}
7797

78-
func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse) *cniTypesCurr.Result {
98+
func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse, ifName string) *cniTypesCurr.Result {
7999
result := &cniTypesCurr.Result{}
80100
resultIpconfig := &cniTypesCurr.IPConfig{}
81101

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

132+
iface := &cniTypesCurr.Interface{Name: ifName}
133+
result.Interfaces = append(result.Interfaces, iface)
134+
112135
return result
113136
}
114137

@@ -124,3 +147,117 @@ func getPodNameWithoutSuffix(podName string) string {
124147
log.Printf("Pod name after splitting based on - : %v", nameSplit)
125148
return strings.Join(nameSplit, "-")
126149
}
150+
151+
func getInfraVnetIP(
152+
enableInfraVnet bool,
153+
infraSubnet string,
154+
nwCfg *cni.NetworkConfig,
155+
plugin *netPlugin,
156+
) (*cniTypesCurr.Result, error) {
157+
158+
if enableInfraVnet {
159+
_, ipNet, _ := net.ParseCIDR(infraSubnet)
160+
nwCfg.Ipam.Subnet = ipNet.String()
161+
162+
log.Printf("call ipam to allocate ip from subnet %v", nwCfg.Ipam.Subnet)
163+
azIpamResult, err := plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
164+
if err != nil {
165+
err = plugin.Errorf("Failed to allocate address: %v", err)
166+
return nil, err
167+
}
168+
169+
return azIpamResult, nil
170+
}
171+
172+
return nil, nil
173+
}
174+
175+
func cleanupInfraVnetIP(
176+
enableInfraVnet bool,
177+
infraIPNet *net.IPNet,
178+
nwCfg *cni.NetworkConfig,
179+
plugin *netPlugin) {
180+
181+
log.Printf("Cleanup infravnet ip")
182+
183+
if enableInfraVnet {
184+
_, ipNet, _ := net.ParseCIDR(infraIPNet.String())
185+
nwCfg.Ipam.Subnet = ipNet.String()
186+
nwCfg.Ipam.Address = infraIPNet.IP.String()
187+
plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
188+
}
189+
}
190+
191+
func checkIfSubnetOverlaps(enableInfraVnet bool, nwCfg *cni.NetworkConfig, cnsNetworkConfig *cns.GetNetworkContainerResponse) bool {
192+
if enableInfraVnet {
193+
if cnsNetworkConfig != nil {
194+
_, infraNet, _ := net.ParseCIDR(nwCfg.InfraVnetAddressSpace)
195+
for _, cnetSpace := range cnsNetworkConfig.CnetAddressSpace {
196+
cnetSpaceIPNet := &net.IPNet{
197+
IP: net.ParseIP(cnetSpace.IPAddress),
198+
Mask: net.CIDRMask(int(cnetSpace.PrefixLength), 32),
199+
}
200+
201+
return infraNet.Contains(cnetSpaceIPNet.IP) || cnetSpaceIPNet.Contains(infraNet.IP)
202+
}
203+
}
204+
}
205+
206+
return false
207+
}
208+
209+
func GetMultiTenancyCNIResult(
210+
enableInfraVnet bool,
211+
nwCfg *cni.NetworkConfig,
212+
plugin *netPlugin,
213+
k8sPodName string,
214+
k8sNamespace string,
215+
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, *cniTypesCurr.Result, error) {
216+
217+
if nwCfg.MultiTenancy {
218+
result, cnsNetworkConfig, subnetPrefix, err := getContainerNetworkConfiguration(nwCfg, "", k8sPodName, k8sNamespace, ifName)
219+
if err != nil {
220+
log.Printf("GetContainerNetworkConfiguration failed for podname %v namespace %v with error %v", k8sPodName, k8sNamespace, err)
221+
return nil, nil, net.IPNet{}, nil, err
222+
}
223+
224+
log.Printf("PrimaryInterfaceIdentifier :%v", subnetPrefix.IP.String())
225+
226+
if checkIfSubnetOverlaps(enableInfraVnet, nwCfg, cnsNetworkConfig) {
227+
buf := fmt.Sprintf("InfraVnet %v overlaps with customerVnet %+v", nwCfg.InfraVnetAddressSpace, cnsNetworkConfig.CnetAddressSpace)
228+
log.Printf(buf)
229+
err = errors.New(buf)
230+
return nil, nil, net.IPNet{}, nil, err
231+
}
232+
233+
if nwCfg.EnableSnatOnHost {
234+
if cnsNetworkConfig.LocalIPConfiguration.IPSubnet.IPAddress == "" {
235+
log.Printf("Snat IP is not populated. Got empty string")
236+
return nil, nil, net.IPNet{}, nil, fmt.Errorf("Snat IP is not populated. Got empty string")
237+
}
238+
}
239+
240+
if enableInfraVnet {
241+
if nwCfg.InfraVnetAddressSpace == "" {
242+
log.Printf("InfraVnetAddressSpace is not populated. Got empty string")
243+
return nil, nil, net.IPNet{}, nil, fmt.Errorf("InfraVnetAddressSpace is not populated. Got empty string")
244+
}
245+
}
246+
247+
azIpamResult, err := getInfraVnetIP(enableInfraVnet, subnetPrefix.String(), nwCfg, plugin)
248+
if err != nil {
249+
log.Printf("GetInfraVnetIP failed with error %v", err)
250+
return nil, nil, net.IPNet{}, nil, err
251+
}
252+
253+
return result, cnsNetworkConfig, subnetPrefix, azIpamResult, nil
254+
}
255+
256+
return nil, nil, net.IPNet{}, nil, nil
257+
}
258+
259+
func CleanupMultitenancyResources(enableInfraVnet bool, nwCfg *cni.NetworkConfig, azIpamResult *cniTypesCurr.Result, plugin *netPlugin) {
260+
if nwCfg.MultiTenancy && azIpamResult != nil && azIpamResult.IPs != nil {
261+
cleanupInfraVnetIP(enableInfraVnet, &azIpamResult.IPs[0].Address, nwCfg, plugin)
262+
}
263+
}

0 commit comments

Comments
 (0)