diff --git a/cni/network/multitenancy.go b/cni/network/multitenancy.go index 67013863bd..e617afb7d6 100644 --- a/cni/network/multitenancy.go +++ b/cni/network/multitenancy.go @@ -230,6 +230,7 @@ func (m *Multitenancy) GetAllNetworkContainers( ifInfo.IPConfigs = append(ifInfo.IPConfigs, ipconfig) ifInfo.Routes = routes ifInfo.NICType = cns.InfraNIC + ifInfo.SkipDefaultRoutes = ncResponses[i].SkipDefaultRoutes // assuming we only assign infra nics in this function ipamResult.interfaceInfo[m.getInterfaceInfoKey(ifInfo.NICType, i)] = ifInfo diff --git a/cni/network/network_linux.go b/cni/network/network_linux.go index 2b090523c0..67ba6b2d49 100644 --- a/cni/network/network_linux.go +++ b/cni/network/network_linux.go @@ -127,7 +127,7 @@ func platformInit(cniConfig *cni.NetworkConfig) {} // isDualNicFeatureSupported returns if the dual nic feature is supported. Currently it's only supported for windows hnsv2 path func (plugin *NetPlugin) isDualNicFeatureSupported(netNs string) bool { - return false + return true } func getOverlayGateway(_ *net.IPNet) (net.IP, error) { diff --git a/cns/NetworkContainerContract.go b/cns/NetworkContainerContract.go index 406c45b554..8acf320094 100644 --- a/cns/NetworkContainerContract.go +++ b/cns/NetworkContainerContract.go @@ -126,6 +126,7 @@ type CreateNetworkContainerRequest struct { Routes []Route AllowHostToNCCommunication bool AllowNCToHostCommunication bool + SkipDefaultRoutes bool EndpointPolicies []NetworkContainerRequestPolicies NCStatus v1alpha.NCStatus NetworkInterfaceInfo NetworkInterfaceInfo //nolint // introducing new field for backendnic, to be used later by cni code @@ -161,10 +162,10 @@ func (req *CreateNetworkContainerRequest) String() string { return fmt.Sprintf("CreateNetworkContainerRequest"+ "{Version: %s, NetworkContainerType: %s, NetworkContainerid: %s, PrimaryInterfaceIdentifier: %s, "+ "LocalIPConfiguration: %+v, IPConfiguration: %+v, SecondaryIPConfigs: %+v, MultitenancyInfo: %+v, "+ - "AllowHostToNCCommunication: %t, AllowNCToHostCommunication: %t, NCStatus: %s, NetworkInterfaceInfo: %+v}", + "AllowHostToNCCommunication: %t, AllowNCToHostCommunication: %t, SkipDefaultRoutes: %t, NCStatus: %s, NetworkInterfaceInfo: %+v}", req.Version, req.NetworkContainerType, req.NetworkContainerid, req.PrimaryInterfaceIdentifier, req.LocalIPConfiguration, req.IPConfiguration, req.SecondaryIPConfigs, req.MultiTenancyInfo, req.AllowHostToNCCommunication, req.AllowNCToHostCommunication, - string(req.NCStatus), req.NetworkInterfaceInfo) + req.SkipDefaultRoutes, string(req.NCStatus), req.NetworkInterfaceInfo) } // NetworkContainerRequestPolicies - specifies policies associated with create network request @@ -497,6 +498,7 @@ type GetNetworkContainerResponse struct { Response Response AllowHostToNCCommunication bool AllowNCToHostCommunication bool + SkipDefaultRoutes bool NetworkInterfaceInfo NetworkInterfaceInfo } diff --git a/cns/NetworkContainerContract_test.go b/cns/NetworkContainerContract_test.go index fc17a58a4d..28cfa8fe9e 100644 --- a/cns/NetworkContainerContract_test.go +++ b/cns/NetworkContainerContract_test.go @@ -240,3 +240,40 @@ func TestPostNetworkContainersRequest_Validate(t *testing.T) { }) } } + +func TestCreateNetworkContainerRequest_SkipDefaultRoutes(t *testing.T) { + tests := []struct { + name string + req CreateNetworkContainerRequest + expected bool + }{ + { + name: "SkipDefaultRoutesTrue", + req: CreateNetworkContainerRequest{ + NetworkContainerid: "f47ac10b-58cc-0372-8567-0e02b2c3d479", + SkipDefaultRoutes: true, + }, + expected: true, + }, + { + name: "SkipDefaultRoutesFalse", + req: CreateNetworkContainerRequest{ + NetworkContainerid: "f47ac10b-58cc-0372-8567-0e02b2c3d479", + SkipDefaultRoutes: false, + }, + expected: false, + }, + { + name: "SkipDefaultRoutesIgnored", + req: CreateNetworkContainerRequest{ + NetworkContainerid: "f47ac10b-58cc-0372-8567-0e02b2c3d479", + }, + expected: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.req.SkipDefaultRoutes, "SkipDefaultRoutes value should match expected") + }) + } +} diff --git a/cns/restserver/util.go b/cns/restserver/util.go index 43d1e1aef9..29daa59acb 100644 --- a/cns/restserver/util.go +++ b/cns/restserver/util.go @@ -530,6 +530,7 @@ func (service *HTTPRestService) getAllNetworkContainerResponses( LocalIPConfiguration: savedReq.LocalIPConfiguration, AllowHostToNCCommunication: savedReq.AllowHostToNCCommunication, AllowNCToHostCommunication: savedReq.AllowNCToHostCommunication, + SkipDefaultRoutes: savedReq.SkipDefaultRoutes, NetworkInterfaceInfo: savedReq.NetworkInterfaceInfo, } @@ -831,6 +832,8 @@ func (service *HTTPRestService) populateIPConfigInfoUntransacted(ipConfigStatus primaryIPCfg := ncStatus.CreateNetworkContainerRequest.IPConfiguration + podIPInfo.SkipDefaultRoutes = ncStatus.CreateNetworkContainerRequest.SkipDefaultRoutes + podIPInfo.PodIPConfig = cns.IPSubnet{ IPAddress: ipConfigStatus.IPAddress, PrefixLength: primaryIPCfg.IPSubnet.PrefixLength, @@ -933,6 +936,7 @@ func (service *HTTPRestService) handleGetNetworkContainers(w http.ResponseWriter LocalIPConfiguration: ncDetails.CreateNetworkContainerRequest.LocalIPConfiguration, AllowHostToNCCommunication: ncDetails.CreateNetworkContainerRequest.AllowHostToNCCommunication, AllowNCToHostCommunication: ncDetails.CreateNetworkContainerRequest.AllowNCToHostCommunication, + SkipDefaultRoutes: ncDetails.CreateNetworkContainerRequest.SkipDefaultRoutes, } networkContainers[i] = getNcResp i++ diff --git a/network/transparent_vlan_endpointclient_linux.go b/network/transparent_vlan_endpointclient_linux.go index 326fc0c87e..3c96244539 100644 --- a/network/transparent_vlan_endpointclient_linux.go +++ b/network/transparent_vlan_endpointclient_linux.go @@ -292,7 +292,6 @@ func (client *TransparentVlanEndpointClient) PopulateVM(epInfo *EndpointInfo) er _, err = client.netioshim.GetNetworkInterfaceByName(client.vlanIfName) return errors.Wrap(err, "failed to get vlan interface") }, numRetries, sleepInMs) - if err != nil { deleteNSIfNotNilErr = errors.Wrapf(err, "failed to get vlan interface: %s", client.vlanIfName) return deleteNSIfNotNilErr @@ -400,14 +399,32 @@ func (client *TransparentVlanEndpointClient) PopulateVnet(epInfo *EndpointInfo) return nil } +// Set ARP proxy on the vlan interface to respond to ARP requests for the gateway IP +func (client *TransparentVlanEndpointClient) setArpProxy(ifName string) error { + cmd := fmt.Sprintf("echo 1 > /proc/sys/net/ipv4/conf/%v/proxy_arp", ifName) + _, err := client.plClient.ExecuteRawCommand(cmd) + if err != nil { + logger.Error("Failed to set ARP proxy", zap.String("interface", ifName), zap.Error(err)) + return errors.Wrap(err, "failed to set arp proxy") + } + return nil +} + func (client *TransparentVlanEndpointClient) AddEndpointRules(epInfo *EndpointInfo) error { if err := client.AddSnatEndpointRules(); err != nil { return errors.Wrap(err, "failed to add snat endpoint rules") } logger.Info("[transparent-vlan] Adding tunneling rules in vnet namespace") err := ExecuteInNS(client.nsClient, client.vnetNSName, func() error { - return client.AddVnetRules(epInfo) + if err := client.AddVnetRules(epInfo); err != nil { + return err + } + + // Set ARP proxy on vnet veth (inside vnet namespace) + logger.Info("calling setArpProxy for", zap.String("vnetVethName", client.vnetVethName)) + return client.setArpProxy(client.vnetVethName) }) + return err } @@ -519,9 +536,15 @@ func (client *TransparentVlanEndpointClient) ConfigureContainerInterfacesAndRout } } + if epInfo.SkipDefaultRoutes { + logger.Info("Skipping adding routes in container ns as requested") + return nil + } + logger.Info("Adding default routes in container ns") if err := client.addDefaultRoutes(client.containerVethName, 0); err != nil { return errors.Wrap(err, "failed container ns add default routes") } + if err := client.AddDefaultArp(client.containerVethName, client.vnetMac.String()); err != nil { return errors.Wrap(err, "failed container ns add default arp") } diff --git a/network/transparent_vlan_endpointclient_linux_test.go b/network/transparent_vlan_endpointclient_linux_test.go index 79e70adb98..3639608dbd 100644 --- a/network/transparent_vlan_endpointclient_linux_test.go +++ b/network/transparent_vlan_endpointclient_linux_test.go @@ -867,6 +867,74 @@ func TestTransparentVlanConfigureContainerInterfacesAndRoutes(t *testing.T) { wantErr: true, wantErrMsg: "failed container ns add default routes: addRoutes failed: " + netio.ErrMockNetIOFail.Error() + ":B1veth0", }, + { + name: "Configure interface and routes good path with SkipDefaultRoutes set to true for container", + client: &TransparentVlanEndpointClient{ + primaryHostIfName: "eth0", + vlanIfName: "eth0.1", + vnetVethName: "A1veth0", + containerVethName: "B1veth0", + vnetNSName: "az_ns_1", + vnetMac: vnetMac, + netlink: netlink.NewMockNetlink(false, ""), + plClient: platform.NewMockExecClient(false), + netUtilsClient: networkutils.NewNetworkUtils(nl, plc), + netioshim: netio.NewMockNetIO(false, 0), + }, + epInfo: &EndpointInfo{ + SkipDefaultRoutes: true, + IPAddresses: []net.IPNet{ + { + IP: net.ParseIP("192.168.0.4"), + Mask: net.CIDRMask(subnetv4Mask, ipv4Bits), + }, + }, + Subnets: []SubnetInfo{ + { + Gateway: net.ParseIP("192.168.0.1"), + Prefix: net.IPNet{ + IP: net.ParseIP("192.168.0.0"), + Mask: net.CIDRMask(subnetv4Mask, ipv4Bits), + }, + }, + }, + }, + wantErr: false, + }, + { + name: "Configure interface and routes good path with SkipDefaultRoutes set to false for container", + client: &TransparentVlanEndpointClient{ + primaryHostIfName: "eth0", + vlanIfName: "eth0.1", + vnetVethName: "A1veth0", + containerVethName: "B1veth0", + vnetNSName: "az_ns_1", + vnetMac: vnetMac, + netlink: netlink.NewMockNetlink(false, ""), + plClient: platform.NewMockExecClient(false), + netUtilsClient: networkutils.NewNetworkUtils(nl, plc), + netioshim: netio.NewMockNetIO(false, 0), + }, + epInfo: &EndpointInfo{ + SkipDefaultRoutes: true, + IPAddresses: []net.IPNet{ + { + IP: net.ParseIP("192.168.0.4"), + Mask: net.CIDRMask(subnetv4Mask, ipv4Bits), + }, + }, + Subnets: []SubnetInfo{ + { + Gateway: net.ParseIP("192.168.0.1"), + Prefix: net.IPNet{ + IP: net.ParseIP("192.168.0.0"), + Mask: net.CIDRMask(subnetv4Mask, ipv4Bits), + }, + }, + }, + }, + wantErr: false, + }, } for _, tt := range tests {