Skip to content

Commit ebbd31c

Browse files
authored
feat: Enable same VM same VNET packet tunneling to host for Transparent Vlan (#1529)
* Disabled rp filter to enable packet tunneling Tests ok (all basic functionality, 2 VMs, NS, delete, add) * Added tests * Typo * Typo * No need to disable rp filter in VM NS
1 parent 8779700 commit ebbd31c

File tree

2 files changed

+85
-9
lines changed

2 files changed

+85
-9
lines changed

network/transparent_vlan_endpointclient_linux.go

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package network
33
import (
44
"fmt"
55
"net"
6+
"strings"
67

8+
"github.com/Azure/azure-container-networking/iptables"
79
"github.com/Azure/azure-container-networking/log"
810
"github.com/Azure/azure-container-networking/netio"
911
"github.com/Azure/azure-container-networking/netlink"
@@ -16,9 +18,12 @@ import (
1618
)
1719

1820
const (
19-
azureMac = "12:34:56:78:9a:bc" // Packets leaving the VM should have this MAC
20-
loopbackIf = "lo" // The name of the loopback interface
21-
numDefaultRoutes = 2 // VNET NS, when no containers use it, has this many routes
21+
azureMac = "12:34:56:78:9a:bc" // Packets leaving the VM should have this MAC
22+
loopbackIf = "lo" // The name of the loopback interface
23+
numDefaultRoutes = 2 // VNET NS, when no containers use it, has this many routes
24+
tunnelingTable = 2 // Packets not entering on the vlan interface go to this routing table
25+
tunnelingMark = 333 // The packets that are to tunnel will be marked with this number
26+
DisableRPFilterCmd = "sysctl -w net.ipv4.conf.all.rp_filter=0" // Command to disable the rp filter for tunneling
2227
)
2328

2429
type netnsClient interface {
@@ -229,13 +234,63 @@ func (client *TransparentVlanEndpointClient) PopulateVnet(epInfo *EndpointInfo)
229234
return errors.Wrap(err, "vnet veth doesn't exist")
230235
}
231236
client.vnetMac = vnetVethIf.HardwareAddr
237+
// Disable rp filter again to allow asymmetric routing for tunneling packets
238+
_, err = client.plClient.ExecuteCommand(DisableRPFilterCmd)
239+
if err != nil {
240+
return errors.Wrap(err, "transparent vlan failed to disable rp filter in vnet")
241+
}
242+
disableRPFilterVlanIfCmd := strings.Replace(DisableRPFilterCmd, "all", client.vlanIfName, 1)
243+
_, err = client.plClient.ExecuteCommand(disableRPFilterVlanIfCmd)
244+
if err != nil {
245+
return errors.Wrap(err, "transparent vlan failed to disable rp filter vlan interface in vnet")
246+
}
232247
return nil
233248
}
234249

235250
func (client *TransparentVlanEndpointClient) AddEndpointRules(epInfo *EndpointInfo) error {
236251
if err := client.AddSnatEndpointRules(); err != nil {
237252
return errors.Wrap(err, "failed to add snat endpoint rules")
238253
}
254+
log.Printf("[transparent vlan] Adding tunneling rules in vnet namespace")
255+
err := ExecuteInNS(client.vnetNSName, func() error {
256+
return client.AddVnetRules(epInfo)
257+
})
258+
return err
259+
}
260+
261+
// Add rules related to tunneling the packet outside of the VM, assumes all calls are idempotent. Namespace: vnet
262+
func (client *TransparentVlanEndpointClient) AddVnetRules(epInfo *EndpointInfo) error {
263+
// iptables -t mangle -I PREROUTING -j MARK --set-mark <TUNNELING MARK>
264+
markOption := fmt.Sprintf("MARK --set-mark %d", tunnelingMark)
265+
if err := iptables.InsertIptableRule(iptables.V4, "mangle", "PREROUTING", "", markOption); err != nil {
266+
return errors.Wrap(err, "unable to insert iptables rule mark all packets not entering on vlan interface")
267+
}
268+
// iptables -t mangle -I PREROUTING -j ACCEPT -i <VLAN IF>
269+
match := fmt.Sprintf("-i %s", client.vlanIfName)
270+
if err := iptables.InsertIptableRule(iptables.V4, "mangle", "PREROUTING", match, "ACCEPT"); err != nil {
271+
return errors.Wrap(err, "unable to insert iptables rule accept all incoming from vlan interface")
272+
}
273+
// Packets that are marked should go to the tunneling table
274+
newRule := vishnetlink.NewRule()
275+
newRule.Mark = tunnelingMark
276+
newRule.Table = tunnelingTable
277+
rules, err := vishnetlink.RuleList(vishnetlink.FAMILY_V4)
278+
if err != nil {
279+
return errors.Wrap(err, "unable to get existing ip rule list")
280+
}
281+
// Check if rule exists already
282+
ruleExists := false
283+
for index := range rules {
284+
if rules[index].Mark == newRule.Mark {
285+
ruleExists = true
286+
}
287+
}
288+
if !ruleExists {
289+
if err := vishnetlink.RuleAdd(newRule); err != nil {
290+
return errors.Wrap(err, "failed to add rule that forwards packet with mark to tunneling routing table")
291+
}
292+
}
293+
239294
return nil
240295
}
241296

@@ -306,7 +361,7 @@ func (client *TransparentVlanEndpointClient) ConfigureContainerInterfacesAndRout
306361
}
307362
}
308363

309-
if err := client.AddDefaultRoutes(client.containerVethName); err != nil {
364+
if err := client.AddDefaultRoutes(client.containerVethName, 0); err != nil {
310365
return errors.Wrap(err, "failed container ns add default routes")
311366
}
312367
if err := client.AddDefaultArp(client.containerVethName, client.vnetMac.String()); err != nil {
@@ -324,8 +379,7 @@ func (client *TransparentVlanEndpointClient) ConfigureVnetInterfacesAndRoutesImp
324379

325380
// Add route specifying which device the pod ip(s) are on
326381
routeInfoList := client.GetVnetRoutes(epInfo.IPAddresses)
327-
328-
if err = client.AddDefaultRoutes(client.vlanIfName); err != nil {
382+
if err = client.AddDefaultRoutes(client.vlanIfName, 0); err != nil {
329383
return errors.Wrap(err, "failed vnet ns add default/gateway routes (idempotent)")
330384
}
331385
if err = client.AddDefaultArp(client.vlanIfName, azureMac); err != nil {
@@ -334,6 +388,9 @@ func (client *TransparentVlanEndpointClient) ConfigureVnetInterfacesAndRoutesImp
334388
if err = addRoutes(client.netlink, client.netioshim, client.vnetVethName, routeInfoList); err != nil {
335389
return errors.Wrap(err, "failed adding routes to vnet specific to this container")
336390
}
391+
if err = client.AddDefaultRoutes(client.vlanIfName, tunnelingTable); err != nil {
392+
return errors.Wrap(err, "failed vnet ns add outbound routing table routes for tunneling (idempotent)")
393+
}
337394
// Return to ConfigureContainerInterfacesAndRoutes
338395
return err
339396
}
@@ -366,12 +423,13 @@ func (client *TransparentVlanEndpointClient) GetVnetRoutes(ipAddresses []net.IPN
366423
// to the virtual gateway ip on linkToName device interface
367424
// Route 1: 169.254.1.1 dev <linkToName>
368425
// Route 2: default via 169.254.1.1 dev <linkToName>
369-
func (client *TransparentVlanEndpointClient) AddDefaultRoutes(linkToName string) error {
426+
func (client *TransparentVlanEndpointClient) AddDefaultRoutes(linkToName string, table int) error {
370427
// Add route for virtualgwip (ip route add 169.254.1.1/32 dev eth0)
371428
virtualGwIP, virtualGwNet, _ := net.ParseCIDR(virtualGwIPString)
372429
routeInfo := RouteInfo{
373430
Dst: *virtualGwNet,
374431
Scope: netlink.RT_SCOPE_LINK,
432+
Table: table,
375433
}
376434
// Difference between interface name in addRoutes and DevName: in RouteInfo?
377435
if err := addRoutes(client.netlink, client.netioshim, linkToName, []RouteInfo{routeInfo}); err != nil {
@@ -382,8 +440,9 @@ func (client *TransparentVlanEndpointClient) AddDefaultRoutes(linkToName string)
382440
_, defaultIPNet, _ := net.ParseCIDR(defaultGwCidr)
383441
dstIP := net.IPNet{IP: net.ParseIP(defaultGw), Mask: defaultIPNet.Mask}
384442
routeInfo = RouteInfo{
385-
Dst: dstIP,
386-
Gw: virtualGwIP,
443+
Dst: dstIP,
444+
Gw: virtualGwIP,
445+
Table: table,
387446
}
388447

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

network/transparent_vlan_endpointclient_linux_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,23 @@ func TestTransparentVlanAddEndpoints(t *testing.T) {
327327
wantErr: true,
328328
wantErrMsg: "vnet veth doesn't exist: " + netio.ErrMockNetIOFail.Error() + ":A1veth0",
329329
},
330+
{
331+
name: "Add endpoints fail populate vnet disable rp filter",
332+
client: &TransparentVlanEndpointClient{
333+
primaryHostIfName: "eth0",
334+
vlanIfName: "eth0.1",
335+
vnetVethName: "A1veth0",
336+
containerVethName: "B1veth0",
337+
vnetNSName: "az_ns_1",
338+
netlink: netlink.NewMockNetlink(false, ""),
339+
plClient: platform.NewMockExecClient(true),
340+
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
341+
netioshim: netio.NewMockNetIO(false, 0),
342+
},
343+
epInfo: &EndpointInfo{},
344+
wantErr: true,
345+
wantErrMsg: "transparent vlan failed to disable rp filter in vnet: " + platform.ErrMockExec.Error(),
346+
},
330347
}
331348

332349
for _, tt := range tests {

0 commit comments

Comments
 (0)