// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package networkutils import ( "fmt" "net" "os" "syscall" "testing" "time" "github.com/aws/amazon-vpc-cni-k8s/pkg/sgpp" "github.com/pkg/errors" "github.com/coreos/go-iptables/iptables" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/vishvananda/netlink" "golang.org/x/sys/unix" "github.com/aws/amazon-vpc-cni-k8s/pkg/iptableswrapper" mock_iptables "github.com/aws/amazon-vpc-cni-k8s/pkg/iptableswrapper/mocks" mocks_ip "github.com/aws/amazon-vpc-cni-k8s/pkg/ipwrapper/mocks" "github.com/aws/amazon-vpc-cni-k8s/pkg/netlinkwrapper/mock_netlink" mock_netlinkwrapper "github.com/aws/amazon-vpc-cni-k8s/pkg/netlinkwrapper/mocks" mock_nswrapper "github.com/aws/amazon-vpc-cni-k8s/pkg/nswrapper/mocks" ) const ( loopback = "" testMAC1 = "01:23:45:67:89:a0" testMAC2 = "01:23:45:67:89:a1" testTable = 10 testeniIP = "10.10.10.20" testeniSubnet = "10.10.0.0/16" // Default MTU of ENI and veth // defined in plugins/routed-eni/driver/driver.go, pkg/networkutils/network.go testMTU = 9001 eniPrefix = "eni" ) var ( _, testENINetIPNet, _ = net.ParseCIDR(testeniSubnet) testENINetIP = net.ParseIP(testeniIP) ) func setup(t *testing.T) (*gomock.Controller, *mock_netlinkwrapper.MockNetLink, *mocks_ip.MockIP, *mock_nswrapper.MockNS, iptableswrapper.IPTablesIface) { ctrl := gomock.NewController(t) return ctrl, mock_netlinkwrapper.NewMockNetLink(ctrl), mocks_ip.NewMockIP(ctrl), mock_nswrapper.NewMockNS(ctrl), mock_iptables.NewMockIptables() } func TestSetupENINetwork(t *testing.T) { ctrl, mockNetLink, _, _, _ := setup(t) defer ctrl.Finish() hwAddr, err := net.ParseMAC(testMAC1) assert.NoError(t, err) mockLinkAttrs1 := &netlink.LinkAttrs{ HardwareAddr: hwAddr, } hwAddr, err = net.ParseMAC(testMAC2) assert.NoError(t, err) mockLinkAttrs2 := &netlink.LinkAttrs{ HardwareAddr: hwAddr, } lo := mock_netlink.NewMockLink(ctrl) eth1 := mock_netlink.NewMockLink(ctrl) // Emulate a delay attaching the ENI so a retry is necessary // First attempt gets one links firstlistSet := mockNetLink.EXPECT().LinkList().Return([]netlink.Link{lo}, nil) lo.EXPECT().Attrs().Return(mockLinkAttrs1) // Second attempt gets both links secondlistSet := mockNetLink.EXPECT().LinkList().Return([]netlink.Link{lo, eth1}, nil) lo.EXPECT().Attrs().Return(mockLinkAttrs1) eth1.EXPECT().Attrs().Return(mockLinkAttrs2) gomock.InOrder(firstlistSet, secondlistSet) mockNetLink.EXPECT().LinkSetMTU(gomock.Any(), testMTU).Return(nil) mockNetLink.EXPECT().LinkSetUp(gomock.Any()).Return(nil) // eth1's device eth1.EXPECT().Attrs().Return(mockLinkAttrs2) eth1.EXPECT().Attrs().Return(mockLinkAttrs2) // eth1's IP address testeniAddr := &net.IPNet{ IP: net.ParseIP(testeniIP), Mask: testENINetIPNet.Mask, } mockNetLink.EXPECT().AddrList(gomock.Any(), unix.AF_INET).Return([]netlink.Addr{}, nil) mockNetLink.EXPECT().AddrAdd(gomock.Any(), &netlink.Addr{IPNet: testeniAddr}).Return(nil) mockNetLink.EXPECT().RouteDel(gomock.Any()) mockNetLink.EXPECT().RouteReplace(gomock.Any()).Return(nil) mockNetLink.EXPECT().RouteDel(gomock.Any()) mockNetLink.EXPECT().RouteReplace(gomock.Any()).Return(nil) mockNetLink.EXPECT().RouteDel(gomock.Any()).Return(nil) err = setupENINetwork(testeniIP, testMAC2, testTable, testeniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU) assert.NoError(t, err) } func TestSetupENINetworkMACFail(t *testing.T) { ctrl, mockNetLink, _, _, _ := setup(t) defer ctrl.Finish() // Emulate a delay attaching the ENI so a retry is necessary // First attempt gets one links for i := 0; i < maxAttemptsLinkByMac; i++ { mockNetLink.EXPECT().LinkList().Return(nil, fmt.Errorf("simulated failure")) } err := setupENINetwork(testeniIP, testMAC2, testTable, testeniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU) assert.Errorf(t, err, "simulated failure") } func TestSetupENINetworkErrorOnPrimaryENI(t *testing.T) { ctrl, mockNetLink, _, _, _ := setup(t) defer ctrl.Finish() deviceNumber := 0 err := setupENINetwork(testeniIP, testMAC2, deviceNumber, testeniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU) assert.Error(t, err) } func TestSetupHostNetworkNodePortDisabledAndSNATDisabled(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: true, nodePortSupportEnabled: false, mainENIMark: defaultConnmark, mtu: testMTU, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } mockPrimaryInterfaceLookup(ctrl, mockNetLink) mockNetLink.EXPECT().LinkSetMTU(gomock.Any(), testMTU).Return(nil) var mainENIRule netlink.Rule mockNetLink.EXPECT().NewRule().Return(&mainENIRule) mockNetLink.EXPECT().RuleDel(&mainENIRule) var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) } func mockPrimaryInterfaceLookup(ctrl *gomock.Controller, mockNetLink *mock_netlinkwrapper.MockNetLink) { lo := mock_netlink.NewMockLink(ctrl) mockLinkAttrs1 := &netlink.LinkAttrs{ HardwareAddr: net.HardwareAddr{}, } mockNetLink.EXPECT().LinkList().Return([]netlink.Link{lo}, nil) lo.EXPECT().Attrs().AnyTimes().Return(mockLinkAttrs1) } func TestUpdateRuleListBySrc(t *testing.T) { ctrl, mockNetLink, _, _, _ := setup(t) defer ctrl.Finish() ln := &linuxNetwork{netLink: mockNetLink} origRule := netlink.Rule{ Src: testENINetIPNet, Table: testTable, } testCases := []struct { name string oldRule netlink.Rule requiresSNAT bool ruleList []netlink.Rule newRule netlink.Rule }{ { "multiple destinations", origRule, true, []netlink.Rule{origRule}, netlink.Rule{}, }, { "single destination", origRule, false, []netlink.Rule{origRule}, netlink.Rule{}, }, } for _, tc := range testCases { mockNetLink.EXPECT().RuleDel(&tc.oldRule) mockNetLink.EXPECT().NewRule().Return(&tc.newRule) mockNetLink.EXPECT().RuleAdd(&tc.newRule) err := ln.UpdateRuleListBySrc(tc.ruleList, *testENINetIPNet) assert.NoError(t, err) } } func TestSetupHostNetworkNodePortEnabledAndSNATDisabled(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: true, ipv6EgressEnabled: true, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } log.Debugf("mockIPtables.Dp state: ", mockIptables.(*mock_iptables.MockIptables).DataplaneState) setupNetLinkMocks(ctrl, mockNetLink) log.Debugf("After: mockIPtables.Dp state: ", mockIptables.(*mock_iptables.MockIptables).DataplaneState) var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "filter": { "FORWARD": [][]string{ { "-d", "fd00::ac:00/118", "-m", "conntrack", "--ctstate", "NEW", "-m", "comment", "--comment", "Block Node Local Pod access via IPv6", "-j", "REJECT", }, }, }, "mangle": { "PREROUTING": [][]string{ { "-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80", }, { "-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80", }, { "-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80", }, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestSetupHostNetworkNodePortDisabledAndSNATEnabled(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, ipv6EgressEnabled: false, nodePortSupportEnabled: false, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } log.Debugf("mockIPtables.Dp state: ", mockIptables.(*mock_iptables.MockIptables).DataplaneState) setupNetLinkMocks(ctrl, mockNetLink) log.Debugf("After: mockIPtables.Dp state: ", mockIptables.(*mock_iptables.MockIptables).DataplaneState) var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20"}}, "POSTROUTING": [][]string{{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"}}, "AWS-CONNMARK-CHAIN-0": [][]string{{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"}}, "PREROUTING": [][]string{ {"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, "mangle": { "PREROUTING": [][]string{ { "-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80", }, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestLoadMTUFromEnvTooLow(t *testing.T) { _ = os.Setenv(envMTU, "1") assert.Equal(t, GetEthernetMTU(""), minimumMTU) } func TestLoadMTUFromEnv1500(t *testing.T) { _ = os.Setenv(envMTU, "1500") assert.Equal(t, GetEthernetMTU(""), 1500) } func TestLoadMTUFromEnvTooHigh(t *testing.T) { _ = os.Setenv(envMTU, "65536") assert.Equal(t, GetEthernetMTU(""), maximumMTU) } func TestLoadExcludeSNATCIDRsFromEnv(t *testing.T) { _ = os.Setenv(envExternalSNAT, "false") _ = os.Setenv(envExcludeSNATCIDRs, "10.12.0.0/16,10.13.0.0/16") expected := []string{"10.12.0.0/16", "10.13.0.0/16"} assert.Equal(t, parseCIDRString(envExcludeSNATCIDRs), expected) } func TestSetupHostNetworkWithExcludeSNATCIDRs(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, ipv6EgressEnabled: false, excludeSNATCIDRs: []string{"10.12.0.0/16", "10.13.0.0/16"}, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1"}}, "AWS-SNAT-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2"}}, "AWS-SNAT-CHAIN-2": [][]string{{"!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3"}}, "AWS-SNAT-CHAIN-3": [][]string{{"!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4"}}, "AWS-SNAT-CHAIN-4": [][]string{{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20"}}, "POSTROUTING": [][]string{{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"}}, "AWS-CONNMARK-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1"}}, "AWS-CONNMARK-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2"}}, "AWS-CONNMARK-CHAIN-2": [][]string{{"!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3"}}, "AWS-CONNMARK-CHAIN-3": [][]string{{"!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4"}}, "AWS-CONNMARK-CHAIN-4": [][]string{{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"}}, "PREROUTING": [][]string{ {"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, "mangle": { "PREROUTING": [][]string{ {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestSetupHostNetworkCleansUpStaleSNATRules(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, ipv6EgressEnabled: false, excludeSNATCIDRs: nil, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAN", "-j", "AWS-SNAT-CHAIN-1") //AWS SNAT CHAN proves backwards compatibility _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-4", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20") _ = mockIptables.NewChain("nat", "AWS-SNAT-CHAIN-5") _ = mockIptables.Append("nat", "POSTROUTING", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-4", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80") _ = mockIptables.Append("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0") _ = mockIptables.Append("nat", "PREROUTING", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1"}}, "AWS-SNAT-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2"}}, "AWS-SNAT-CHAIN-2": [][]string{{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20"}}, "AWS-SNAT-CHAIN-3": [][]string{}, "AWS-SNAT-CHAIN-4": [][]string{}, "POSTROUTING": [][]string{{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"}}, "AWS-CONNMARK-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1"}}, "AWS-CONNMARK-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2"}}, "AWS-CONNMARK-CHAIN-2": [][]string{{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"}}, "AWS-CONNMARK-CHAIN-3": [][]string{}, "AWS-CONNMARK-CHAIN-4": [][]string{}, "PREROUTING": [][]string{ {"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, "mangle": { "PREROUTING": [][]string{ {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestSetupHostNetworkWithDifferentVethPrefix(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, ipv6EgressEnabled: false, excludeSNATCIDRs: []string{"10.12.0.0/16", "10.13.0.0/16"}, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: "veth", netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAN", "-j", "AWS-SNAT-CHAIN-1") //AWS SNAT CHAN proves backwards compatibility _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-4", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20") _ = mockIptables.NewChain("nat", "AWS-SNAT-CHAIN-5") _ = mockIptables.Append("nat", "POSTROUTING", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-4", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80") _ = mockIptables.Append("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0") _ = mockIptables.Append("nat", "PREROUTING", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1"}}, "AWS-SNAT-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2"}}, "AWS-SNAT-CHAIN-2": [][]string{{"!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3"}}, "AWS-SNAT-CHAIN-3": [][]string{{"!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4"}}, "AWS-SNAT-CHAIN-4": [][]string{{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20"}}, "POSTROUTING": [][]string{{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"}}, "AWS-CONNMARK-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1"}}, "AWS-CONNMARK-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2"}}, "AWS-CONNMARK-CHAIN-2": [][]string{{"!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3"}}, "AWS-CONNMARK-CHAIN-3": [][]string{{"!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4"}}, "AWS-CONNMARK-CHAIN-4": [][]string{{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"}}, "PREROUTING": [][]string{ {"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-i", "veth+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, "mangle": { "PREROUTING": [][]string{ {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "veth+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestSetupHostNetworkExternalNATCleanupConnmark(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: true, ipv6EgressEnabled: false, excludeSNATCIDRs: []string{"10.12.0.0/16", "10.13.0.0/16"}, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-4", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20") _ = mockIptables.Append("nat", "POSTROUTING", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-4", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80") _ = mockIptables.Append("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0") _ = mockIptables.Append("nat", "PREROUTING", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") // remove exclusions vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{}, "AWS-SNAT-CHAIN-1": [][]string{}, "AWS-SNAT-CHAIN-2": [][]string{}, "AWS-SNAT-CHAIN-3": [][]string{}, "AWS-SNAT-CHAIN-4": [][]string{}, "POSTROUTING": [][]string{}, "AWS-CONNMARK-CHAIN-0": [][]string{}, "AWS-CONNMARK-CHAIN-1": [][]string{}, "AWS-CONNMARK-CHAIN-2": [][]string{}, "AWS-CONNMARK-CHAIN-3": [][]string{}, "AWS-CONNMARK-CHAIN-4": [][]string{}, "PREROUTING": [][]string{}, }, "mangle": { "PREROUTING": [][]string{ {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestSetupHostNetworkExcludedSNATCIDRsIdempotent(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, ipv6EgressEnabled: false, excludeSNATCIDRs: []string{"10.12.0.0/16", "10.13.0.0/16"}, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4") _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-4", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20") _ = mockIptables.Append("nat", "POSTROUTING", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-1", "!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-2", "!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-3", "!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-4", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80") _ = mockIptables.Append("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0") _ = mockIptables.Append("nat", "PREROUTING", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") // remove exclusions vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1"}}, "AWS-SNAT-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2"}}, "AWS-SNAT-CHAIN-2": [][]string{{"!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-3"}}, "AWS-SNAT-CHAIN-3": [][]string{{"!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN EXCLUSION", "-j", "AWS-SNAT-CHAIN-4"}}, "AWS-SNAT-CHAIN-4": [][]string{{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20"}}, "POSTROUTING": [][]string{{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"}}, "AWS-CONNMARK-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1"}}, "AWS-CONNMARK-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2"}}, "AWS-CONNMARK-CHAIN-2": [][]string{{"!", "-d", "10.12.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-3"}}, "AWS-CONNMARK-CHAIN-3": [][]string{{"!", "-d", "10.13.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, EXCLUDED CIDR", "-j", "AWS-CONNMARK-CHAIN-4"}}, "AWS-CONNMARK-CHAIN-4": [][]string{{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"}}, "PREROUTING": [][]string{ {"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, "mangle": { "PREROUTING": [][]string{ {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestUpdateHostIptablesRules(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, ipv6EgressEnabled: true, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: "veth", netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAN", "-j", "AWS-SNAT-CHAIN-1") //AWS SNAT CHAN proves backwards compatibility _ = mockIptables.Append("nat", "AWS-SNAT-CHAIN-1", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20") _ = mockIptables.Append("nat", "POSTROUTING", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-0", "!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1") _ = mockIptables.Append("nat", "AWS-CONNMARK-CHAIN-1", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80") _ = mockIptables.Append("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0") _ = mockIptables.Append("nat", "PREROUTING", "-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") _ = mockIptables.Append("mangle", "PREROUTING", "-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80") _ = mockIptables.Append("mangle", "PREROUTING", "-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") _ = mockIptables.Append("mangle", "PREROUTING", "-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80") vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "nat": { "AWS-SNAT-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-1"}}, "AWS-SNAT-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-2"}}, "AWS-SNAT-CHAIN-2": [][]string{{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "10.10.10.20"}}, "POSTROUTING": [][]string{{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"}}, "AWS-CONNMARK-CHAIN-0": [][]string{{"!", "-d", "10.10.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-1"}}, "AWS-CONNMARK-CHAIN-1": [][]string{{"!", "-d", "10.11.0.0/16", "-m", "comment", "--comment", "AWS CONNMARK CHAIN, VPC CIDR", "-j", "AWS-CONNMARK-CHAIN-2"}}, "AWS-CONNMARK-CHAIN-2": [][]string{{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"}}, "PREROUTING": [][]string{ {"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-i", "veth+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"}, {"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, "filter": { "FORWARD": [][]string{ { "-d", "fd00::ac:00/118", "-m", "conntrack", "--ctstate", "NEW", "-m", "comment", "--comment", "Block Node Local Pod access via IPv6", "-j", "REJECT", }, }, }, "mangle": { "PREROUTING": [][]string{ {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, {"-m", "comment", "--comment", "AWS, primary ENI", "-i", "veth+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"}, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestSetupHostNetworkMultipleCIDRs(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: true, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) vpcCIDRs := []string{"10.10.0.0/16", "10.11.0.0/16"} err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) } func TestSetupHostNetworkWithIPv6Enabled(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, excludeSNATCIDRs: nil, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, false, true) assert.NoError(t, err) assert.Equal(t, map[string]map[string][][]string{ "filter": { "FORWARD": [][]string{ { "-d", "169.254.172.0/22", "-m", "conntrack", "--ctstate", "NEW", "-m", "comment", "--comment", "Block Node Local Pod access via IPv4", "-j", "REJECT", }, }, }, }, mockIptables.(*mock_iptables.MockIptables).DataplaneState) } func TestIncrementIPv4Addr(t *testing.T) { testCases := []struct { name string ip net.IP expected net.IP err bool }{ {"increment", net.IPv4(10, 0, 0, 1), net.IPv4(10, 0, 0, 2).To4(), false}, {"carry up 1", net.IPv4(10, 0, 0, 255), net.IPv4(10, 0, 1, 0).To4(), false}, {"carry up 2", net.IPv4(10, 0, 255, 255), net.IPv4(10, 1, 0, 0).To4(), false}, {"overflow", net.IPv4(255, 255, 255, 255), nil, true}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := IncrementIPv4Addr(tc.ip) if tc.err { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, tc.expected, result, tc.name) }) } } func TestSetupHostNetworkIgnoringRpFilterUpdate(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: true, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) } func TestSetupHostNetworkUpdateLocalRule(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 }, } setupNetLinkMocks(ctrl, mockNetLink) setupVethNetLinkMocks(mockNetLink) mockNetLink.EXPECT() var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, true, true, false) assert.NoError(t, err) } func TestSetupHostNetworkDeleteOldConnmarkRuleForNonVpcOutboundTraffic(t *testing.T) { ctrl, mockNetLink, _, mockNS, mockIptables := setup(t) defer ctrl.Finish() ln := &linuxNetwork{ useExternalSNAT: false, excludeSNATCIDRs: []string{"10.12.0.0/16", "10.13.0.0/16"}, nodePortSupportEnabled: true, mainENIMark: defaultConnmark, mtu: testMTU, vethPrefix: eniPrefix, netLink: mockNetLink, ns: mockNS, newIptables: func(iptables.Protocol) (iptableswrapper.IPTablesIface, error) { return mockIptables, nil }, } setupNetLinkMocks(ctrl, mockNetLink) // add the "old" rule used in an ealier version of the CNI _ = mockIptables.Append("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-m", "state", "--state", "NEW", "-j", "AWS-CONNMARK-CHAIN-0") var vpcCIDRs []string err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testENINetIP, false, true, false) assert.NoError(t, err) var exists bool // after setting up the host network, the "old" rule should no longer be present exists, _ = mockIptables.Exists("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-m", "state", "--state", "NEW", "-j", "AWS-CONNMARK-CHAIN-0") assert.False(t, exists, "old rule is still present") // it should have been replaced by the "new" rule exists, _ = mockIptables.Exists("nat", "PREROUTING", "-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0") assert.True(t, exists, "new rule is not present") } // Validate parsing in parseCIDRString func TestGetExternalServiceCIDRs(t *testing.T) { ln := &linuxNetwork{} testCases := []struct { name string cidrString string expectedCidrs []string }{ { "empty CIDR string returns nil", "", nil, }, { "single IPV4 CIDR is accepted", "10.0.0.0/32", []string{"10.0.0.0/32"}, }, { "valid IPV6 CIDR is rejected", "2000::1/128", nil, }, { "IPV4 CIDR missing mask is rejected", "10.0.0.0", nil, }, { "multiple CIDRs, only IPV4 CIDRs are accepted", "10.0.0.0/32, 10.0.0.1/32, 2000::1/128", []string{"10.0.0.0/32", "10.0.0.1/32"}, }, { "multiple CIDRs, some malformed", "10.0.0.0/32,abcd", []string{"10.0.0.0/32"}, }, } for _, tc := range testCases { _ = os.Setenv(envExternalServiceCIDRs, tc.cidrString) cidrs := ln.GetExternalServiceCIDRs() assert.Equal(t, cidrs, tc.expectedCidrs) } } // Validate cleanup and programming in UpdateExternalIpRules func TestUpdateExternalIpRules(t *testing.T) { ctrl, mockNetLink, _, _, _ := setup(t) defer ctrl.Finish() ln := &linuxNetwork{netLink: mockNetLink} _, cidrOne, _ := net.ParseCIDR("10.0.1.1/32") ruleOne := netlink.Rule{ Dst: cidrOne, Priority: externalServiceIpRulePriority, Table: mainRoutingTable, } _, cidrTwo, _ := net.ParseCIDR("10.0.2.1/32") ruleTwo := netlink.Rule{ Dst: cidrTwo, Priority: 0, Table: mainRoutingTable, } _, cidrThree, _ := net.ParseCIDR("10.0.3.1/32") ruleThree := netlink.Rule{ Dst: cidrThree, Priority: externalServiceIpRulePriority, Table: mainRoutingTable, } testCases := []struct { name string existingRules []netlink.Rule newCidrs []string }{ { "no existing, no new rules", nil, nil, }, { "add new rules", nil, []string{"10.0.0.0/32", "10.0.0.1/32"}, }, { "delete existing rules", []netlink.Rule{ruleOne, ruleThree}, nil, }, { "skip existing rules", []netlink.Rule{ruleTwo}, nil, }, { "add and delete existing rule", []netlink.Rule{ruleOne}, []string{cidrOne.String()}, }, { "add and delete rules", []netlink.Rule{ruleOne, ruleTwo, ruleThree}, []string{"10.0.0.0/32", "10.0.0.1/32"}, }, } for _, tc := range testCases { // Check for delete calls on expected rules for idx, rule := range tc.existingRules { if rule.Priority == externalServiceIpRulePriority { // Need to use index as array iteration creates a copy mockNetLink.EXPECT().RuleDel(&tc.existingRules[idx]) } } // Check for add calls for _, cidr := range tc.newCidrs { _, netCidr, _ := net.ParseCIDR(cidr) newRule := netlink.Rule{ Dst: netCidr, Priority: externalServiceIpRulePriority, Table: mainRoutingTable, } mockNetLink.EXPECT().NewRule().Return(&newRule) mockNetLink.EXPECT().RuleAdd(&newRule) } err := ln.UpdateExternalServiceIpRules(tc.existingRules, tc.newCidrs) assert.NoError(t, err) } } func setupNetLinkMocks(ctrl *gomock.Controller, mockNetLink *mock_netlinkwrapper.MockNetLink) { mockPrimaryInterfaceLookup(ctrl, mockNetLink) mockNetLink.EXPECT().LinkSetMTU(gomock.Any(), testMTU).Return(nil) var mainENIRule netlink.Rule mockNetLink.EXPECT().NewRule().Return(&mainENIRule) mockNetLink.EXPECT().RuleDel(&mainENIRule) mockNetLink.EXPECT().RuleAdd(&mainENIRule) } func setupVethNetLinkMocks(mockNetLink *mock_netlinkwrapper.MockNetLink) { var localRule netlink.Rule mockNetLink.EXPECT().NewRule().Return(&localRule) mockNetLink.EXPECT().RuleAdd(&localRule) mockNetLink.EXPECT().RuleDel(&localRule) } func Test_netLinkRuleDelAll(t *testing.T) { testRule := netlink.NewRule() testRule.IifName = "eni00bcc08c834" testRule.Priority = VlanRulePriority type ruleDelCall struct { rule *netlink.Rule err error } type fields struct { ruleDelCalls []ruleDelCall } type args struct { rule *netlink.Rule } tests := []struct { name string fields fields args args wantErr error }{ { name: "single rule, succeed to delete", fields: fields{ ruleDelCalls: []ruleDelCall{ { rule: testRule, }, { rule: testRule, err: syscall.ENOENT, }, }, }, args: args{ rule: testRule, }, }, { name: "single rule, failed to delete", fields: fields{ ruleDelCalls: []ruleDelCall{ { rule: testRule, err: errors.New("some error"), }, }, }, args: args{ rule: testRule, }, wantErr: errors.New("some error"), }, { name: "multiple rules, succeed to delete", fields: fields{ ruleDelCalls: []ruleDelCall{ { rule: testRule, }, { rule: testRule, }, { rule: testRule, err: syscall.ENOENT, }, }, }, args: args{ rule: testRule, }, }, { name: "multiple rules, failed to delete", fields: fields{ ruleDelCalls: []ruleDelCall{ { rule: testRule, }, { rule: testRule, err: errors.New("some error"), }, }, }, args: args{ rule: testRule, }, wantErr: errors.New("some error"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() netLink := mock_netlinkwrapper.NewMockNetLink(ctrl) for _, call := range tt.fields.ruleDelCalls { netLink.EXPECT().RuleDel(call.rule).Return(call.err) } err := NetLinkRuleDelAll(netLink, tt.args.rule) if tt.wantErr != nil { assert.EqualError(t, err, tt.wantErr.Error()) } else { assert.NoError(t, err) } }) } } func Test_containsNoSuchRule(t *testing.T) { type args struct { err error } tests := []struct { name string args args want bool }{ { name: "syscall.EEXIST is rule not exists error", args: args{ err: syscall.ENOENT, }, want: true, }, { name: "syscall.ENOENT isn't rule not exists error", args: args{ err: syscall.EEXIST, }, want: false, }, { name: "non syscall error isn't rule not exists error", args: args{ err: errors.New("some error"), }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := containsNoSuchRule(tt.args.err) assert.Equal(t, tt.want, got) }) } } func Test_isRuleExistsError(t *testing.T) { type args struct { err error } tests := []struct { name string args args want bool }{ { name: "syscall.EEXIST is rule exists error", args: args{ err: syscall.EEXIST, }, want: true, }, { name: "syscall.ENOENT isn't rule exists error", args: args{ err: syscall.ENOENT, }, want: false, }, { name: "non syscall error isn't rule exists error", args: args{ err: errors.New("some error"), }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := isRuleExistsError(tt.args.err) assert.Equal(t, tt.want, got) }) } }