Skip to content

Commit fdc9b72

Browse files
mugeshspMugesh SP
andauthored
enable dual NIC support in transparent VLAN (#4057)
* feat: add SkipDefaultRoutes field to network container request and responses * feat: implement ARP proxy setting and custom route addition for VLAN interfaces * feat: enable dual NIC feature support and improve ARP proxy handling in transparent VLAN client * feat: add tests for SkipDefaultRoutes handling in network container requests * feat: remove addition of custom routes * fix: improve ARP proxy error handling * fix: Lint Errors * refactor: streamline ARP proxy setup in AddEndpointRules * fix: update comments for dual NIC support and clarify ARP proxy function --------- Co-authored-by: Mugesh SP <mugeshsp@microsoft.com>
1 parent c51987e commit fdc9b72

File tree

7 files changed

+141
-6
lines changed

7 files changed

+141
-6
lines changed

cni/network/multitenancy.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ func (m *Multitenancy) GetAllNetworkContainers(
230230
ifInfo.IPConfigs = append(ifInfo.IPConfigs, ipconfig)
231231
ifInfo.Routes = routes
232232
ifInfo.NICType = cns.InfraNIC
233+
ifInfo.SkipDefaultRoutes = ncResponses[i].SkipDefaultRoutes
233234

234235
// assuming we only assign infra nics in this function
235236
ipamResult.interfaceInfo[m.getInterfaceInfoKey(ifInfo.NICType, i)] = ifInfo

cni/network/network_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ func getNATInfo(_ *cni.NetworkConfig, _ interface{}, _ bool) (natInfo []policy.N
125125

126126
func platformInit(cniConfig *cni.NetworkConfig) {}
127127

128-
// isDualNicFeatureSupported returns if the dual nic feature is supported. Currently it's only supported for windows hnsv2 path
128+
// isDualNicFeatureSupported returns true as the dual nic feature is supported on Linux.
129129
func (plugin *NetPlugin) isDualNicFeatureSupported(netNs string) bool {
130-
return false
130+
return true
131131
}
132132

133133
func getOverlayGateway(_ *net.IPNet) (net.IP, error) {

cns/NetworkContainerContract.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ type CreateNetworkContainerRequest struct {
126126
Routes []Route
127127
AllowHostToNCCommunication bool
128128
AllowNCToHostCommunication bool
129+
SkipDefaultRoutes bool
129130
EndpointPolicies []NetworkContainerRequestPolicies
130131
NCStatus v1alpha.NCStatus
131132
NetworkInterfaceInfo NetworkInterfaceInfo //nolint // introducing new field for backendnic, to be used later by cni code
@@ -161,10 +162,10 @@ func (req *CreateNetworkContainerRequest) String() string {
161162
return fmt.Sprintf("CreateNetworkContainerRequest"+
162163
"{Version: %s, NetworkContainerType: %s, NetworkContainerid: %s, PrimaryInterfaceIdentifier: %s, "+
163164
"LocalIPConfiguration: %+v, IPConfiguration: %+v, SecondaryIPConfigs: %+v, MultitenancyInfo: %+v, "+
164-
"AllowHostToNCCommunication: %t, AllowNCToHostCommunication: %t, NCStatus: %s, NetworkInterfaceInfo: %+v}",
165+
"AllowHostToNCCommunication: %t, AllowNCToHostCommunication: %t, SkipDefaultRoutes: %t, NCStatus: %s, NetworkInterfaceInfo: %+v}",
165166
req.Version, req.NetworkContainerType, req.NetworkContainerid, req.PrimaryInterfaceIdentifier, req.LocalIPConfiguration,
166167
req.IPConfiguration, req.SecondaryIPConfigs, req.MultiTenancyInfo, req.AllowHostToNCCommunication, req.AllowNCToHostCommunication,
167-
string(req.NCStatus), req.NetworkInterfaceInfo)
168+
req.SkipDefaultRoutes, string(req.NCStatus), req.NetworkInterfaceInfo)
168169
}
169170

170171
// NetworkContainerRequestPolicies - specifies policies associated with create network request
@@ -498,6 +499,7 @@ type GetNetworkContainerResponse struct {
498499
Response Response
499500
AllowHostToNCCommunication bool
500501
AllowNCToHostCommunication bool
502+
SkipDefaultRoutes bool
501503
NetworkInterfaceInfo NetworkInterfaceInfo
502504
}
503505

cns/NetworkContainerContract_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,40 @@ func TestPostNetworkContainersRequest_Validate(t *testing.T) {
240240
})
241241
}
242242
}
243+
244+
func TestCreateNetworkContainerRequest_SkipDefaultRoutes(t *testing.T) {
245+
tests := []struct {
246+
name string
247+
req CreateNetworkContainerRequest
248+
expected bool
249+
}{
250+
{
251+
name: "SkipDefaultRoutesTrue",
252+
req: CreateNetworkContainerRequest{
253+
NetworkContainerid: "f47ac10b-58cc-0372-8567-0e02b2c3d479",
254+
SkipDefaultRoutes: true,
255+
},
256+
expected: true,
257+
},
258+
{
259+
name: "SkipDefaultRoutesFalse",
260+
req: CreateNetworkContainerRequest{
261+
NetworkContainerid: "f47ac10b-58cc-0372-8567-0e02b2c3d479",
262+
SkipDefaultRoutes: false,
263+
},
264+
expected: false,
265+
},
266+
{
267+
name: "SkipDefaultRoutesIgnored",
268+
req: CreateNetworkContainerRequest{
269+
NetworkContainerid: "f47ac10b-58cc-0372-8567-0e02b2c3d479",
270+
},
271+
expected: false,
272+
},
273+
}
274+
for _, tt := range tests {
275+
t.Run(tt.name, func(t *testing.T) {
276+
assert.Equal(t, tt.expected, tt.req.SkipDefaultRoutes, "SkipDefaultRoutes value should match expected")
277+
})
278+
}
279+
}

cns/restserver/util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ func (service *HTTPRestService) getAllNetworkContainerResponses(
530530
LocalIPConfiguration: savedReq.LocalIPConfiguration,
531531
AllowHostToNCCommunication: savedReq.AllowHostToNCCommunication,
532532
AllowNCToHostCommunication: savedReq.AllowNCToHostCommunication,
533+
SkipDefaultRoutes: savedReq.SkipDefaultRoutes,
533534
NetworkInterfaceInfo: savedReq.NetworkInterfaceInfo,
534535
}
535536

@@ -831,6 +832,8 @@ func (service *HTTPRestService) populateIPConfigInfoUntransacted(ipConfigStatus
831832

832833
primaryIPCfg := ncStatus.CreateNetworkContainerRequest.IPConfiguration
833834

835+
podIPInfo.SkipDefaultRoutes = ncStatus.CreateNetworkContainerRequest.SkipDefaultRoutes
836+
834837
podIPInfo.PodIPConfig = cns.IPSubnet{
835838
IPAddress: ipConfigStatus.IPAddress,
836839
PrefixLength: primaryIPCfg.IPSubnet.PrefixLength,
@@ -934,6 +937,7 @@ func (service *HTTPRestService) handleGetNetworkContainers(w http.ResponseWriter
934937
LocalIPConfiguration: ncDetails.CreateNetworkContainerRequest.LocalIPConfiguration,
935938
AllowHostToNCCommunication: ncDetails.CreateNetworkContainerRequest.AllowHostToNCCommunication,
936939
AllowNCToHostCommunication: ncDetails.CreateNetworkContainerRequest.AllowNCToHostCommunication,
940+
SkipDefaultRoutes: ncDetails.CreateNetworkContainerRequest.SkipDefaultRoutes,
937941
}
938942
networkContainers[i] = getNcResp
939943
i++

network/transparent_vlan_endpointclient_linux.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,6 @@ func (client *TransparentVlanEndpointClient) PopulateVM(epInfo *EndpointInfo) er
292292
_, err = client.netioshim.GetNetworkInterfaceByName(client.vlanIfName)
293293
return errors.Wrap(err, "failed to get vlan interface")
294294
}, numRetries, sleepInMs)
295-
296295
if err != nil {
297296
deleteNSIfNotNilErr = errors.Wrapf(err, "failed to get vlan interface: %s", client.vlanIfName)
298297
return deleteNSIfNotNilErr
@@ -400,14 +399,32 @@ func (client *TransparentVlanEndpointClient) PopulateVnet(epInfo *EndpointInfo)
400399
return nil
401400
}
402401

402+
// Set ARP proxy on the specified interface to respond to ARP requests for the gateway IP
403+
func (client *TransparentVlanEndpointClient) setArpProxy(ifName string) error {
404+
cmd := fmt.Sprintf("echo 1 > /proc/sys/net/ipv4/conf/%v/proxy_arp", ifName)
405+
_, err := client.plClient.ExecuteRawCommand(cmd)
406+
if err != nil {
407+
logger.Error("Failed to set ARP proxy", zap.String("interface", ifName), zap.Error(err))
408+
return errors.Wrap(err, "failed to set arp proxy")
409+
}
410+
return nil
411+
}
412+
403413
func (client *TransparentVlanEndpointClient) AddEndpointRules(epInfo *EndpointInfo) error {
404414
if err := client.AddSnatEndpointRules(); err != nil {
405415
return errors.Wrap(err, "failed to add snat endpoint rules")
406416
}
407417
logger.Info("[transparent-vlan] Adding tunneling rules in vnet namespace")
408418
err := ExecuteInNS(client.nsClient, client.vnetNSName, func() error {
409-
return client.AddVnetRules(epInfo)
419+
if err := client.AddVnetRules(epInfo); err != nil {
420+
return err
421+
}
422+
423+
// Set ARP proxy on vnet veth (inside vnet namespace)
424+
logger.Info("calling setArpProxy for", zap.String("vnetVethName", client.vnetVethName))
425+
return client.setArpProxy(client.vnetVethName)
410426
})
427+
411428
return err
412429
}
413430

@@ -519,9 +536,15 @@ func (client *TransparentVlanEndpointClient) ConfigureContainerInterfacesAndRout
519536
}
520537
}
521538

539+
if epInfo.SkipDefaultRoutes {
540+
logger.Info("Skipping adding routes in container ns as requested")
541+
return nil
542+
}
543+
logger.Info("Adding default routes in container ns")
522544
if err := client.addDefaultRoutes(client.containerVethName, 0); err != nil {
523545
return errors.Wrap(err, "failed container ns add default routes")
524546
}
547+
525548
if err := client.AddDefaultArp(client.containerVethName, client.vnetMac.String()); err != nil {
526549
return errors.Wrap(err, "failed container ns add default arp")
527550
}

network/transparent_vlan_endpointclient_linux_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,74 @@ func TestTransparentVlanConfigureContainerInterfacesAndRoutes(t *testing.T) {
867867
wantErr: true,
868868
wantErrMsg: "failed container ns add default routes: addRoutes failed: " + netio.ErrMockNetIOFail.Error() + ":B1veth0",
869869
},
870+
{
871+
name: "Configure interface and routes good path with SkipDefaultRoutes set to true for container",
872+
client: &TransparentVlanEndpointClient{
873+
primaryHostIfName: "eth0",
874+
vlanIfName: "eth0.1",
875+
vnetVethName: "A1veth0",
876+
containerVethName: "B1veth0",
877+
vnetNSName: "az_ns_1",
878+
vnetMac: vnetMac,
879+
netlink: netlink.NewMockNetlink(false, ""),
880+
plClient: platform.NewMockExecClient(false),
881+
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
882+
netioshim: netio.NewMockNetIO(false, 0),
883+
},
884+
epInfo: &EndpointInfo{
885+
SkipDefaultRoutes: true,
886+
IPAddresses: []net.IPNet{
887+
{
888+
IP: net.ParseIP("192.168.0.4"),
889+
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
890+
},
891+
},
892+
Subnets: []SubnetInfo{
893+
{
894+
Gateway: net.ParseIP("192.168.0.1"),
895+
Prefix: net.IPNet{
896+
IP: net.ParseIP("192.168.0.0"),
897+
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
898+
},
899+
},
900+
},
901+
},
902+
wantErr: false,
903+
},
904+
{
905+
name: "Configure interface and routes good path with SkipDefaultRoutes set to false for container",
906+
client: &TransparentVlanEndpointClient{
907+
primaryHostIfName: "eth0",
908+
vlanIfName: "eth0.1",
909+
vnetVethName: "A1veth0",
910+
containerVethName: "B1veth0",
911+
vnetNSName: "az_ns_1",
912+
vnetMac: vnetMac,
913+
netlink: netlink.NewMockNetlink(false, ""),
914+
plClient: platform.NewMockExecClient(false),
915+
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
916+
netioshim: netio.NewMockNetIO(false, 0),
917+
},
918+
epInfo: &EndpointInfo{
919+
SkipDefaultRoutes: true,
920+
IPAddresses: []net.IPNet{
921+
{
922+
IP: net.ParseIP("192.168.0.4"),
923+
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
924+
},
925+
},
926+
Subnets: []SubnetInfo{
927+
{
928+
Gateway: net.ParseIP("192.168.0.1"),
929+
Prefix: net.IPNet{
930+
IP: net.ParseIP("192.168.0.0"),
931+
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
932+
},
933+
},
934+
},
935+
},
936+
wantErr: false,
937+
},
870938
}
871939

872940
for _, tt := range tests {

0 commit comments

Comments
 (0)