Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a high priority rule to handle link-local traffic via local route table in SGPP Mode for both IPV4 and IPV6. #3148

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
60 changes: 59 additions & 1 deletion pkg/networkutils/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ func (n *linuxNetwork) SetupHostNetwork(vpcv4CIDRs []string, primaryMAC string,

// In strict mode, packets egressing pod veth interfaces must route via the trunk ENI in order for security group
// rules to be applied. Therefore, the rule to lookup the local routing table is moved to a lower priority than VLAN rules.
// A high priority rule is added to handle to/from link-local traffic by the local routing table
// A high priority rule is added for ICMPv6 packets from the gateway to lookup in the local routing table
if enablePodENI && n.podSGEnforcingMode == sgpp.EnforcingModeStrict {
localRule := n.netLink.NewRule()
localRule.Table = localRouteTable
Expand All @@ -358,8 +360,18 @@ func (n *linuxNetwork) SetupHostNetwork(vpcv4CIDRs []string, primaryMAC string,
return errors.Wrap(err, "ChangeLocalRulePriority: failed to delete priority 0 local rule")
}

// In IPv6 strict mode, ICMPv6 packets from the gateway must lookup in the local routing table so that branch interfaces can resolve their gateway.
// Add a high priority rule, to handle all link-local traffic by local table.
if err := n.createLinkLocalIPv4Rule(); err != nil {
return errors.Wrap(err, "failed to install rule to ignore link-local packets in V4 for SGPP Strict Mode.")
}

if v6Enabled {
// Add a high priority rule, to handle all link-local traffic by local table for V6 packets.
if err := n.createLinkLocalIPv6Rule(); err != nil {
return errors.Wrap(err, "failed to install rule to ignore link-local packets in V6 for SGPP Strict Mode.")
}

// In IPv6 strict mode, ICMPv6 packets from the gateway must lookup in the local routing table so that branch interfaces can resolve their gateway.
if err := n.createIPv6GatewayRule(); err != nil {
return errors.Wrapf(err, "failed to install IPv6 gateway rule")
}
Expand Down Expand Up @@ -1183,6 +1195,52 @@ func (n *linuxNetwork) createIPv6GatewayRule() error {
return nil
}

// With SGPP Strict Mode, all IPv4 link-local traffic (169.254.0.0/16) is handled by the local routing table with the highest priority
func (n *linuxNetwork) createLinkLocalIPv4Rule() error {
linkLocalV4Rule := n.netLink.NewRule()
linkLocalV4Rule.Src = &net.IPNet{IP: net.IPv4(169, 254, 0, 0), Mask: net.CIDRMask(16, 32)}
linkLocalV4Rule.Dst = &net.IPNet{IP: net.IPv4(169, 254, 0, 0), Mask: net.CIDRMask(16, 32)}
linkLocalV4Rule.Table = localRouteTable
linkLocalV4Rule.Priority = 0
linkLocalV4Rule.Family = unix.AF_INET
if n.podSGEnforcingMode == sgpp.EnforcingModeStrict {
err := n.netLink.RuleAdd(linkLocalV4Rule)
if err != nil && !isRuleExistsError(err) {
return errors.Wrap(err, "createLinkLocalIPv4Rule: unable to create rule to ignore link-local packets")
}
} else {
// Rule must be deleted when not in strict mode to support transitions.
err := n.netLink.RuleDel(linkLocalV4Rule)
if !netlinkwrapper.IsNotExistsError(err) {
return errors.Wrap(err, "createLinkLocalIPv4Rule: unable to delete rule to ignore link-local packets")
}
}
return nil
}

// With SGPP Strict Mode, all IPv6 link-local traffic (fd00::10/8) is handled by the local routing table with the highest priority
func (n *linuxNetwork) createLinkLocalIPv6Rule() error {
linkLocalV6Rule := n.netLink.NewRule()
linkLocalV6Rule.Src = &net.IPNet{IP: net.ParseIP("fd00::10"), Mask: net.CIDRMask(8, 128)}
linkLocalV6Rule.Dst = &net.IPNet{IP: net.ParseIP("fd00::10"), Mask: net.CIDRMask(8, 128)}
linkLocalV6Rule.Table = localRouteTable
linkLocalV6Rule.Priority = 0
linkLocalV6Rule.Family = unix.AF_INET6
if n.podSGEnforcingMode == sgpp.EnforcingModeStrict {
err := n.netLink.RuleAdd(linkLocalV6Rule)
if err != nil && !isRuleExistsError(err) {
return errors.Wrap(err, "createLinkLocalIPv6Rule: unable to create rule to ignore link-local packets")
}
} else {
// Rule must be deleted when not in strict mode to support transitions.
err := n.netLink.RuleDel(linkLocalV6Rule)
if !netlinkwrapper.IsNotExistsError(err) {
return errors.Wrap(err, "createLinkLocalIPv6Rule: unable to delete rule to ignore link-local packets")
}
}
return nil
}

// Increment the given net.IP by one. Incrementing the last IP in an IP space (IPv4, IPV6) is undefined.
func incrementIPAddr(ip net.IP) {
for i := len(ip) - 1; i >= 0; i-- {
Expand Down
241 changes: 241 additions & 0 deletions pkg/networkutils/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,72 @@ func TestUpdateIPv6GatewayRule(t *testing.T) {
assert.NoError(t, err)
}

func TestCreateLinkLocalIPv4Rule(t *testing.T) {
ctrl, mockNetLink, _, _, _ := setup(t)
defer ctrl.Finish()

ln := &linuxNetwork{
netLink: mockNetLink,
podSGEnforcingMode: sgpp.EnforcingModeStrict,
}

linkLocalV4Rule := netlink.Rule{
Src: &net.IPNet{IP: net.IPv4(169, 254, 0, 0), Mask: net.CIDRMask(16, 32)},
Dst: &net.IPNet{IP: net.IPv4(169, 254, 0, 0), Mask: net.CIDRMask(16, 32)},
Table: localRouteTable,
Priority: 0,
Family: unix.AF_INET,
}

// Validate rule add in strict mode
mockNetLink.EXPECT().NewRule().Return(&linkLocalV4Rule)
mockNetLink.EXPECT().RuleAdd(&linkLocalV4Rule)

err := ln.createLinkLocalIPv4Rule()
assert.NoError(t, err)

// Validate rule del in non-strict mode
ln.podSGEnforcingMode = sgpp.EnforcingModeStandard
mockNetLink.EXPECT().NewRule().Return(&linkLocalV4Rule)
mockNetLink.EXPECT().RuleDel(&linkLocalV4Rule)

err = ln.createLinkLocalIPv6Rule()
assert.NoError(t, err)
}

func TestCreateLinkLocalIPv6Rule(t *testing.T) {
ctrl, mockNetLink, _, _, _ := setup(t)
defer ctrl.Finish()

ln := &linuxNetwork{
netLink: mockNetLink,
podSGEnforcingMode: sgpp.EnforcingModeStrict,
}

linkLocalV6Rule := netlink.Rule{
Src: &net.IPNet{IP: net.ParseIP("fd00::10"), Mask: net.CIDRMask(8, 128)},
Dst: &net.IPNet{IP: net.ParseIP("fd00::10"), Mask: net.CIDRMask(8, 128)},
Table: localRouteTable,
Priority: 0,
Family: unix.AF_INET6,
}

// Validate rule add in strict mode
mockNetLink.EXPECT().NewRule().Return(&linkLocalV6Rule)
mockNetLink.EXPECT().RuleAdd(&linkLocalV6Rule)

err := ln.createLinkLocalIPv6Rule()
assert.NoError(t, err)

// Validate rule del in non-strict mode
ln.podSGEnforcingMode = sgpp.EnforcingModeStandard
mockNetLink.EXPECT().NewRule().Return(&linkLocalV6Rule)
mockNetLink.EXPECT().RuleDel(&linkLocalV6Rule)

err = ln.createLinkLocalIPv6Rule()
assert.NoError(t, err)
}

func TestSetupHostNetworkNodePortDisabledAndSNATDisabled(t *testing.T) {
ctrl, mockNetLink, _, mockNS, mockIptables := setup(t)
defer ctrl.Finish()
Expand Down Expand Up @@ -1074,6 +1140,7 @@ func TestSetupHostNetworkUpdateLocalRule(t *testing.T) {
}
setupNetLinkMocks(ctrl, mockNetLink)
setupVethNetLinkMocks(mockNetLink)
setupLinkLocalMocks(ctrl, mockNetLink)

mockNetLink.EXPECT()

Expand All @@ -1082,6 +1149,174 @@ func TestSetupHostNetworkUpdateLocalRule(t *testing.T) {
assert.NoError(t, err)
}

func TestSetupHostNetworkUpdateLinkLocalRulesIPv4(t *testing.T) {
ctrl, mockNetLink, _, mockNS, mockIptables := setup(t)
defer ctrl.Finish()

ln := &linuxNetwork{
useExternalSNAT: true,
nodePortSupportEnabled: true,
mainENIMark: defaultConnmark,
mtu: testMTU,
podSGEnforcingMode: sgpp.EnforcingModeStrict,
vethPrefix: eniPrefix,

netLink: mockNetLink,
ns: mockNS,
newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) {
return mockIptables, nil
},
}

// Expected IPV4 Link-Local Rule
expectedRule := &netlink.Rule{
Src: &net.IPNet{
IP: net.IPv4(169, 254, 0, 0),
Mask: net.CIDRMask(16, 32),
},
Dst: &net.IPNet{
IP: net.IPv4(169, 254, 0, 0),
Mask: net.CIDRMask(16, 32),
},
Table: localRouteTable,
Priority: 0,
Family: unix.AF_INET,
}

// Matcher to capture the rule being added
var capturedRule *netlink.Rule

setupNetLinkMocks(ctrl, mockNetLink)
setupVethNetLinkMocks(mockNetLink)

mockNetLink.EXPECT().NewRule().Return(expectedRule)
mockNetLink.EXPECT().RuleAdd(expectedRule).DoAndReturn(func(rule *netlink.Rule) error {
capturedRule = rule
return nil
})

var vpcCIDRs []string
err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testEniIPNet, true, true, false)
assert.NoError(t, err)
assert.NotNil(t, capturedRule)
assert.Equal(t, net.IPv4(169, 254, 0, 0).String(), capturedRule.Src.IP.String())
assert.Equal(t, net.IPv4(169, 254, 0, 0).String(), capturedRule.Dst.IP.String())
assert.Equal(t, localRouteTable, capturedRule.Table)
assert.Equal(t, 0, capturedRule.Priority)
assert.Equal(t, unix.AF_INET, capturedRule.Family)
}

func TestSetupHostNetworkUpdateLinkLocalRulesIPv6(t *testing.T) {
ctrl, mockNetLink, _, mockNS, mockIptables := setup(t)
defer ctrl.Finish()

ln := &linuxNetwork{
useExternalSNAT: true,
nodePortSupportEnabled: true,
mainENIMark: defaultConnmark,
mtu: testMTU,
podSGEnforcingMode: sgpp.EnforcingModeStrict,
vethPrefix: eniPrefix,

netLink: mockNetLink,
ns: mockNS,
newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) {
return mockIptables, nil
},
}

// Expected IPV4 rule
expectedRule := &netlink.Rule{
Src: &net.IPNet{
IP: net.IPv4(169, 254, 0, 0),
Mask: net.CIDRMask(16, 32),
},
Dst: &net.IPNet{
IP: net.IPv4(169, 254, 0, 0),
Mask: net.CIDRMask(16, 32),
},
Table: localRouteTable,
Priority: 0,
Family: unix.AF_INET,
}

var capturedRule *netlink.Rule

setupNetLinkMocks(ctrl, mockNetLink)
setupVethNetLinkMocks(mockNetLink)

mockNetLink.EXPECT().NewRule().Return(expectedRule)
mockNetLink.EXPECT().RuleAdd(expectedRule).DoAndReturn(func(rule *netlink.Rule) error {
capturedRule = rule
return nil
})

var capturedRuleV6 *netlink.Rule
// Expected IPV6 rule
expectedRuleV6 := &netlink.Rule{
Src: &net.IPNet{IP: net.ParseIP("fd00::10"), Mask: net.CIDRMask(8, 128)},
Dst: &net.IPNet{IP: net.ParseIP("fd00::10"), Mask: net.CIDRMask(8, 128)},
Table: localRouteTable,
Priority: 0,
Family: unix.AF_INET6,
}

mockNetLink.EXPECT().NewRule().Return(&netlink.Rule{})
mockNetLink.EXPECT().RuleAdd(expectedRuleV6).DoAndReturn(func(rule *netlink.Rule) error {
capturedRuleV6 = rule
return nil
})

// Expected IPV6 Gateway Rule
expectedGatewayRule := &netlink.Rule{
Src: &net.IPNet{
IP: GetIPv6Gateway(),
Mask: net.CIDRMask(128, 128),
},
IPProto: unix.IPPROTO_ICMPV6,
Table: localRouteTable,
Priority: 0,
Family: unix.AF_INET6,
}

var captureRuleIPV6GateWay *netlink.Rule

mockNetLink.EXPECT().NewRule().Return(&netlink.Rule{})
mockNetLink.EXPECT().RuleAdd(expectedGatewayRule).DoAndReturn(func(rule *netlink.Rule) error {
captureRuleIPV6GateWay = rule
return nil
})

var vpcCIDRs []string
err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testEniIPNet, true, false, true)
assert.NoError(t, err)
assert.NotNil(t, capturedRule)
assert.Equal(t, net.IPv4(169, 254, 0, 0).String(), capturedRule.Src.IP.String())
assert.Equal(t, net.IPv4(169, 254, 0, 0).String(), capturedRule.Dst.IP.String())
assert.Equal(t, localRouteTable, capturedRule.Table)
assert.Equal(t, 0, capturedRule.Priority)
assert.Equal(t, unix.AF_INET, capturedRule.Family)

// Expected Rules for IPV6 Link Local
expectedIP := net.ParseIP("fd00::10")
assert.Equal(t, expectedIP.String(), capturedRuleV6.Src.IP.String())
assert.Equal(t, expectedIP.String(), capturedRuleV6.Dst.IP.String())
assert.Equal(t, net.CIDRMask(8, 128).String(), capturedRuleV6.Src.Mask.String())
assert.Equal(t, net.CIDRMask(8, 128).String(), capturedRuleV6.Dst.Mask.String())
assert.Equal(t, localRouteTable, capturedRuleV6.Table)
assert.Equal(t, 0, capturedRuleV6.Priority)
assert.Equal(t, unix.AF_INET6, capturedRuleV6.Family)

// Expected Rules for IPV6 Gateway
expectedIP = GetIPv6Gateway()
assert.Equal(t, expectedIP.String(), captureRuleIPV6GateWay.Src.IP.String())
assert.Equal(t, net.CIDRMask(128, 128).String(), captureRuleIPV6GateWay.Src.Mask.String())
assert.Equal(t, unix.IPPROTO_ICMPV6, captureRuleIPV6GateWay.IPProto)
assert.Equal(t, localRouteTable, captureRuleIPV6GateWay.Table)
assert.Equal(t, 0, captureRuleIPV6GateWay.Priority)
assert.Equal(t, unix.AF_INET6, captureRuleIPV6GateWay.Family)
}

func TestSetupHostNetworkDeleteOldConnmarkRuleForNonVpcOutboundTraffic(t *testing.T) {
ctrl, mockNetLink, _, mockNS, mockIptables := setup(t)
defer ctrl.Finish()
Expand Down Expand Up @@ -1263,6 +1498,12 @@ func setupNetLinkMocks(ctrl *gomock.Controller, mockNetLink *mock_netlinkwrapper
mockNetLink.EXPECT().RuleAdd(&mainENIRule)
}

func setupLinkLocalMocks(ctrl *gomock.Controller, mockNetLink *mock_netlinkwrapper.MockNetLink) {
var mainENIRule netlink.Rule
mockNetLink.EXPECT().NewRule().Return(&mainENIRule)
mockNetLink.EXPECT().RuleAdd(&mainENIRule)
}

func setupVethNetLinkMocks(mockNetLink *mock_netlinkwrapper.MockNetLink) {
var localRule netlink.Rule
mockNetLink.EXPECT().NewRule().Return(&localRule)
Expand Down
Loading