Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cni/network/multitenancy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion cni/network/network_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 4 additions & 2 deletions cns/NetworkContainerContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -497,6 +498,7 @@ type GetNetworkContainerResponse struct {
Response Response
AllowHostToNCCommunication bool
AllowNCToHostCommunication bool
SkipDefaultRoutes bool
NetworkInterfaceInfo NetworkInterfaceInfo
}

Expand Down
37 changes: 37 additions & 0 deletions cns/NetworkContainerContract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
})
}
}
4 changes: 4 additions & 0 deletions cns/restserver/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ func (service *HTTPRestService) getAllNetworkContainerResponses(
LocalIPConfiguration: savedReq.LocalIPConfiguration,
AllowHostToNCCommunication: savedReq.AllowHostToNCCommunication,
AllowNCToHostCommunication: savedReq.AllowNCToHostCommunication,
SkipDefaultRoutes: savedReq.SkipDefaultRoutes,
NetworkInterfaceInfo: savedReq.NetworkInterfaceInfo,
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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++
Expand Down
72 changes: 69 additions & 3 deletions network/transparent_vlan_endpointclient_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,14 +400,38 @@
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)
Comment on lines +405 to +406
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using shell command execution with string formatting could be vulnerable to command injection if ifName contains malicious characters. Consider using a safer approach or validating the interface name against a whitelist of allowed characters.

Copilot uses AI. Check for mistakes.

if err != nil {
logger.Error("Failed to set ARP proxy", zap.String("interface", ifName), zap.Error(err))
} else {
logger.Info("ARP proxy enabled", zap.String("interface", ifName))
}
return err

Check failure on line 412 in network/transparent_vlan_endpointclient_linux.go

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest)

error returned from interface method should be wrapped: sig: func (github.com/Azure/azure-container-networking/platform.ExecClient).ExecuteRawCommand(command string) (string, error) (wrapcheck)
}

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))
if err := client.setArpProxy(client.vnetVethName); err != nil {
logger.Error("setArpProxy failed with", zap.Error(err))
return err
}

return nil
})

return err
}

Expand Down Expand Up @@ -519,9 +543,19 @@
}
}

if err := client.addDefaultRoutes(client.containerVethName, 0); err != nil {
return errors.Wrap(err, "failed container ns add default routes")
if epInfo.SkipDefaultRoutes {
logger.Info("Skipping adding default routes in container ns as requested")
if err := client.addCustomRoutes(client.containerVethName, epInfo.Subnets[0].Gateway, epInfo.Subnets[0].Prefix, 0); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we decided not to add any custom routes. let the orchestrator add it

return errors.Wrap(err, "failed container ns add custom routes")
}
return nil
} else {

Check failure on line 552 in network/transparent_vlan_endpointclient_linux.go

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest)

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove else

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")
}
Expand Down Expand Up @@ -614,6 +648,38 @@
return nil
}

// Helper that creates routing rules for the current NS which direct packets
// to the subnet gateway ip on linkToName device interface
// Route 1: <gatewayIP> dev <linkToName>
// Route 2: <subnetCIDR> via <gatewayIP> dev <linkToName>
func (client *TransparentVlanEndpointClient) addCustomRoutes(linkToName string, gatewayIP net.IP, subnetCIDR net.IPNet, table int) error {
// Add route for subnetgwIP (ip route add <gatewayIP> dev <linkToName>)
gWIP, gwNet, _ := net.ParseCIDR(gatewayIP.String() + "/32")
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error values from net.ParseCIDR are being ignored. These parsing operations could fail and should be handled to prevent potential runtime panics or incorrect network configuration.

Suggested change
gWIP, gwNet, _ := net.ParseCIDR(gatewayIP.String() + "/32")
gWIP, gwNet, err := net.ParseCIDR(gatewayIP.String() + "/32")
if err != nil {
return errors.Wrapf(err, "failed to parse CIDR for gatewayIP %s", gatewayIP.String())
}

Copilot uses AI. Check for mistakes.

routeInfo := RouteInfo{
Dst: *gwNet,
Scope: netlink.RT_SCOPE_LINK,
Table: table,
}
// Difference between interface name in addRoutes and DevName: in RouteInfo?
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment indicates uncertainty about the API design. Either clarify the difference or remove the comment if it's not actionable.

Copilot uses AI. Check for mistakes.

if err := addRoutes(client.netlink, client.netioshim, linkToName, []RouteInfo{routeInfo}); err != nil {
return err
}

// Add subnet route (ip route add <subnetCIDR> via <gatewayIP> dev <linkToName>)
subnetPrefix, subnetIPNet, _ := net.ParseCIDR(subnetCIDR.String())
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error values from net.ParseCIDR are being ignored. These parsing operations could fail and should be handled to prevent potential runtime panics or incorrect network configuration.

Suggested change
subnetPrefix, subnetIPNet, _ := net.ParseCIDR(subnetCIDR.String())
subnetPrefix, subnetIPNet, err := net.ParseCIDR(subnetCIDR.String())
if err != nil {
return fmt.Errorf("failed to parse subnetCIDR %q: %w", subnetCIDR.String(), err)
}

Copilot uses AI. Check for mistakes.

dstIP := net.IPNet{IP: subnetPrefix, Mask: subnetIPNet.Mask}
routeInfo = RouteInfo{
Dst: dstIP,
Gw: gWIP,
Table: table,
}

if err := addRoutes(client.netlink, client.netioshim, linkToName, []RouteInfo{routeInfo}); err != nil {
return err
}
return nil
}

// Helper that creates arp entry for the current NS which maps the virtual
// gateway (169.254.2.1) to destMac on a particular interfaceName
// Example: (169.254.2.1) at 12:34:56:78:9a:bc [ether] PERM on <interfaceName>
Expand Down
72 changes: 72 additions & 0 deletions network/transparent_vlan_endpointclient_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,74 @@
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 {
Expand Down Expand Up @@ -1008,3 +1076,7 @@
})
}
}

func TestAddCustomRoutes(t *testing.T) {

Check failure on line 1080 in network/transparent_vlan_endpointclient_linux_test.go

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest)

unused-parameter: parameter 't' seems to be unused, consider removing or renaming it as _ (revive)

Check failure on line 1081 in network/transparent_vlan_endpointclient_linux_test.go

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest)

File is not properly formatted (gofumpt)
}
Loading