github.com/cilium/cilium@v1.16.2/pkg/datapath/linux/node_linux_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package linux
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"crypto/rand"
    10  	"fmt"
    11  	"log/slog"
    12  	"net"
    13  	"runtime"
    14  	"slices"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/cilium/ebpf/rlimit"
    20  	"github.com/cilium/hive/cell"
    21  	"github.com/cilium/hive/hivetest"
    22  	"github.com/cilium/statedb"
    23  	"github.com/spf13/afero"
    24  	"github.com/stretchr/testify/require"
    25  	"github.com/vishvananda/netlink"
    26  
    27  	"github.com/cilium/cilium/pkg/cidr"
    28  	cmtypes "github.com/cilium/cilium/pkg/clustermesh/types"
    29  	fakeTypes "github.com/cilium/cilium/pkg/datapath/fake/types"
    30  	"github.com/cilium/cilium/pkg/datapath/linux/ipsec"
    31  	"github.com/cilium/cilium/pkg/datapath/linux/route"
    32  	"github.com/cilium/cilium/pkg/datapath/linux/sysctl"
    33  	"github.com/cilium/cilium/pkg/datapath/tables"
    34  	datapath "github.com/cilium/cilium/pkg/datapath/types"
    35  	"github.com/cilium/cilium/pkg/hive"
    36  	nodemapfake "github.com/cilium/cilium/pkg/maps/nodemap/fake"
    37  	"github.com/cilium/cilium/pkg/maps/tunnel"
    38  	"github.com/cilium/cilium/pkg/mtu"
    39  	"github.com/cilium/cilium/pkg/node"
    40  	nodeaddressing "github.com/cilium/cilium/pkg/node/addressing"
    41  	nodeTypes "github.com/cilium/cilium/pkg/node/types"
    42  	"github.com/cilium/cilium/pkg/option"
    43  	"github.com/cilium/cilium/pkg/testutils"
    44  	"github.com/cilium/cilium/pkg/testutils/netns"
    45  )
    46  
    47  type linuxPrivilegedBaseTestSuite struct {
    48  	sysctl     sysctl.Sysctl
    49  	mtuConfig  mtu.Configuration
    50  	enableIPv4 bool
    51  	enableIPv6 bool
    52  
    53  	// nodeConfigTemplate is the partially filled template for local node configuration.
    54  	// copy it, don't mutate it.
    55  	nodeConfigTemplate datapath.LocalNodeConfiguration
    56  }
    57  
    58  type linuxPrivilegedIPv6OnlyTestSuite struct {
    59  	linuxPrivilegedBaseTestSuite
    60  }
    61  
    62  type linuxPrivilegedIPv4OnlyTestSuite struct {
    63  	linuxPrivilegedBaseTestSuite
    64  }
    65  
    66  type linuxPrivilegedIPv4AndIPv6TestSuite struct {
    67  	linuxPrivilegedBaseTestSuite
    68  }
    69  
    70  func setup(tb testing.TB, family string) *linuxPrivilegedBaseTestSuite {
    71  	switch family {
    72  	case "IPv4":
    73  		return &setupLinuxPrivilegedIPv4OnlyTestSuite(tb).linuxPrivilegedBaseTestSuite
    74  	case "IPv6":
    75  		return &setupLinuxPrivilegedIPv6OnlyTestSuite(tb).linuxPrivilegedBaseTestSuite
    76  	case "dual":
    77  		return &setupLinuxPrivilegedIPv4AndIPv6TestSuite(tb).linuxPrivilegedBaseTestSuite
    78  	default:
    79  		return nil
    80  	}
    81  }
    82  
    83  const (
    84  	dummyHostDeviceName     = "dummy_host"
    85  	dummyExternalDeviceName = "dummy_external"
    86  
    87  	baseTime = 2500
    88  	mcastNum = 6
    89  )
    90  
    91  var (
    92  	baseIPv4Time = []string{"net", "ipv4", "neigh", "default", "base_reachable_time_ms"}
    93  	baseIPv6Time = []string{"net", "ipv6", "neigh", "default", "base_reachable_time_ms"}
    94  
    95  	mcastNumIPv4 = []string{"net", "ipv4", "neigh", "default", "mcast_solicit"}
    96  	mcastNumIPv6 = []string{"net", "ipv6", "neigh", "default", "mcast_solicit"}
    97  )
    98  
    99  func setupLinuxPrivilegedBaseTestSuite(tb testing.TB, addressing datapath.NodeAddressing, enableIPv6, enableIPv4 bool) *linuxPrivilegedBaseTestSuite {
   100  	testutils.PrivilegedTest(tb)
   101  	s := &linuxPrivilegedBaseTestSuite{}
   102  
   103  	s.sysctl = sysctl.NewDirectSysctl(afero.NewOsFs(), "/proc")
   104  
   105  	rlimit.RemoveMemlock()
   106  	s.mtuConfig = mtu.NewConfiguration(0, false, false, false, false, 1500, nil, false)
   107  	s.enableIPv6 = enableIPv6
   108  	s.enableIPv4 = enableIPv4
   109  
   110  	node.SetTestLocalNodeStore()
   111  
   112  	removeDevice(dummyHostDeviceName)
   113  	removeDevice(dummyExternalDeviceName)
   114  
   115  	ips := make([]net.IP, 0)
   116  	if enableIPv6 {
   117  		ips = append(ips, addressing.IPv6().PrimaryExternal())
   118  	}
   119  	if enableIPv4 {
   120  		ips = append(ips, addressing.IPv4().PrimaryExternal())
   121  	}
   122  	devExt, err := setupDummyDevice(dummyExternalDeviceName, ips...)
   123  	require.NoError(tb, err)
   124  
   125  	ips = []net.IP{}
   126  	if enableIPv4 {
   127  		ips = append(ips, addressing.IPv4().Router())
   128  	}
   129  	if enableIPv6 {
   130  		ips = append(ips, addressing.IPv6().Router())
   131  	}
   132  	devHost, err := setupDummyDevice(dummyHostDeviceName, ips...)
   133  	require.NoError(tb, err)
   134  
   135  	s.nodeConfigTemplate = datapath.LocalNodeConfiguration{
   136  		Devices:             []*tables.Device{devExt, devHost},
   137  		NodeIPv4:            addressing.IPv4().PrimaryExternal(),
   138  		NodeIPv6:            addressing.IPv6().PrimaryExternal(),
   139  		CiliumInternalIPv4:  addressing.IPv4().Router(),
   140  		CiliumInternalIPv6:  addressing.IPv6().Router(),
   141  		AllocCIDRIPv4:       addressing.IPv4().AllocationCIDR(),
   142  		AllocCIDRIPv6:       addressing.IPv6().AllocationCIDR(),
   143  		EnableIPv4:          s.enableIPv4,
   144  		EnableIPv6:          s.enableIPv6,
   145  		DeviceMTU:           s.mtuConfig.GetDeviceMTU(),
   146  		RouteMTU:            s.mtuConfig.GetRouteMTU(),
   147  		RoutePostEncryptMTU: s.mtuConfig.GetRoutePostEncryptMTU(),
   148  	}
   149  
   150  	tunnel.SetTunnelMap(tunnel.NewTunnelMap("test_cilium_tunnel_map"))
   151  	err = tunnel.TunnelMap().OpenOrCreate()
   152  	require.NoError(tb, err)
   153  
   154  	return s
   155  }
   156  
   157  func setupLinuxPrivilegedIPv6OnlyTestSuite(tb testing.TB) *linuxPrivilegedIPv6OnlyTestSuite {
   158  	testutils.PrivilegedTest(tb)
   159  
   160  	addressing := fakeTypes.NewIPv6OnlyNodeAddressing()
   161  	s := &linuxPrivilegedIPv6OnlyTestSuite{
   162  		linuxPrivilegedBaseTestSuite: *setupLinuxPrivilegedBaseTestSuite(tb, addressing, true, false),
   163  	}
   164  
   165  	tb.Cleanup(func() {
   166  		tearDownTest(tb)
   167  	})
   168  
   169  	return s
   170  }
   171  
   172  func setupLinuxPrivilegedIPv4OnlyTestSuite(tb testing.TB) *linuxPrivilegedIPv4OnlyTestSuite {
   173  	testutils.PrivilegedTest(tb)
   174  
   175  	addressing := fakeTypes.NewIPv4OnlyNodeAddressing()
   176  	s := &linuxPrivilegedIPv4OnlyTestSuite{
   177  		linuxPrivilegedBaseTestSuite: *setupLinuxPrivilegedBaseTestSuite(tb, addressing, false, true),
   178  	}
   179  
   180  	tb.Cleanup(func() {
   181  		tearDownTest(tb)
   182  	})
   183  
   184  	return s
   185  }
   186  
   187  func setupLinuxPrivilegedIPv4AndIPv6TestSuite(tb testing.TB) *linuxPrivilegedIPv4AndIPv6TestSuite {
   188  	testutils.PrivilegedTest(tb)
   189  
   190  	addressing := fakeTypes.NewNodeAddressing()
   191  	s := &linuxPrivilegedIPv4AndIPv6TestSuite{
   192  		linuxPrivilegedBaseTestSuite: *setupLinuxPrivilegedBaseTestSuite(tb, addressing, true, true),
   193  	}
   194  
   195  	tb.Cleanup(func() {
   196  		tearDownTest(tb)
   197  	})
   198  	return s
   199  }
   200  
   201  func tearDownTest(tb testing.TB) {
   202  	ipsec.DeleteXFRM(hivetest.Logger(tb))
   203  	node.UnsetTestLocalNodeStore()
   204  	removeDevice(dummyHostDeviceName)
   205  	removeDevice(dummyExternalDeviceName)
   206  	err := tunnel.TunnelMap().Unpin()
   207  	require.NoError(tb, err)
   208  }
   209  
   210  func setupDummyDevice(name string, ips ...net.IP) (*tables.Device, error) {
   211  	dummy := &netlink.Dummy{
   212  		LinkAttrs: netlink.LinkAttrs{
   213  			Name: name,
   214  		},
   215  	}
   216  	if err := netlink.LinkAdd(dummy); err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	if err := netlink.LinkSetUp(dummy); err != nil {
   221  		removeDevice(name)
   222  		return nil, err
   223  	}
   224  
   225  	for _, ip := range ips {
   226  		var ipnet *net.IPNet
   227  		if ip.To4() != nil {
   228  			ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
   229  		} else {
   230  			ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}
   231  		}
   232  
   233  		addr := &netlink.Addr{IPNet: ipnet}
   234  		if err := netlink.AddrAdd(dummy, addr); err != nil {
   235  			removeDevice(name)
   236  			return nil, err
   237  		}
   238  	}
   239  
   240  	link, err := netlink.LinkByName(name)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	return &tables.Device{
   245  		Index:        link.Attrs().Index,
   246  		MTU:          link.Attrs().MTU,
   247  		Name:         name,
   248  		HardwareAddr: tables.HardwareAddr(link.Attrs().HardwareAddr),
   249  		Type:         "dummy",
   250  		Selected:     true,
   251  	}, nil
   252  }
   253  
   254  func removeDevice(name string) {
   255  	l, err := netlink.LinkByName(name)
   256  	if err == nil {
   257  		netlink.LinkDel(l)
   258  	}
   259  }
   260  
   261  func TestAll(t *testing.T) {
   262  
   263  	for _, tt := range []string{"IPv4", "IPv6", "dual"} {
   264  		t.Run(tt, func(t *testing.T) {
   265  			t.Run("TestUpdateNodeRoute", func(t *testing.T) {
   266  				s := setup(t, tt)
   267  				s.TestUpdateNodeRoute(t)
   268  			})
   269  			t.Run("TestAuxiliaryPrefixes", func(t *testing.T) {
   270  				s := setup(t, tt)
   271  				s.TestAuxiliaryPrefixes(t)
   272  			})
   273  			t.Run("TestNodeUpdateEncapsulation", func(t *testing.T) {
   274  				s := setup(t, tt)
   275  				s.TestNodeUpdateEncapsulation(t)
   276  			})
   277  			t.Run("TestNodeUpdateEncapsulationWithOverride", func(t *testing.T) {
   278  				s := setup(t, tt)
   279  				s.TestNodeUpdateEncapsulationWithOverride(t)
   280  			})
   281  			t.Run("TestNodeUpdateIDs", func(t *testing.T) {
   282  				s := setup(t, tt)
   283  				s.TestNodeUpdateIDs(t)
   284  			})
   285  			t.Run("TestNodeChurnXFRMLeaks", func(t *testing.T) {
   286  				s := setup(t, tt)
   287  				s.TestNodeChurnXFRMLeaks(t)
   288  			})
   289  			t.Run("TestNodeUpdateDirectRouting", func(t *testing.T) {
   290  				s := setup(t, tt)
   291  				s.TestNodeUpdateDirectRouting(t)
   292  			})
   293  			t.Run("TestAgentRestartOptionChanges", func(t *testing.T) {
   294  				s := setup(t, tt)
   295  				s.TestAgentRestartOptionChanges(t)
   296  			})
   297  			t.Run("TestNodeValidationDirectRouting", func(t *testing.T) {
   298  				s := setup(t, tt)
   299  				s.TestAgentRestartOptionChanges(t)
   300  			})
   301  		})
   302  	}
   303  }
   304  
   305  func (s *linuxPrivilegedBaseTestSuite) TestUpdateNodeRoute(t *testing.T) {
   306  	ip4CIDR := cidr.MustParseCIDR("254.254.254.0/24")
   307  	require.NotNil(t, ip4CIDR)
   308  
   309  	ip6CIDR := cidr.MustParseCIDR("cafe:cafe:cafe:cafe::/96")
   310  	require.NotNil(t, ip6CIDR)
   311  
   312  	var linuxNodeHandler *linuxNodeHandler
   313  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
   314  	log := hivetest.Logger(t)
   315  	linuxNodeHandler = newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
   316  
   317  	require.NotNil(t, linuxNodeHandler)
   318  	nodeConfig := s.nodeConfigTemplate
   319  
   320  	err := linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
   321  	require.NoError(t, err)
   322  
   323  	if s.enableIPv4 {
   324  		// add & remove IPv4 node route
   325  		err = linuxNodeHandler.updateNodeRoute(ip4CIDR, true, false)
   326  		require.NoError(t, err)
   327  
   328  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4CIDR, false)
   329  		require.NoError(t, err)
   330  		require.NotNil(t, foundRoute)
   331  
   332  		err = linuxNodeHandler.deleteNodeRoute(ip4CIDR, false)
   333  		require.NoError(t, err)
   334  
   335  		foundRoute, err = linuxNodeHandler.lookupNodeRoute(ip4CIDR, false)
   336  		require.NoError(t, err)
   337  		require.Nil(t, foundRoute)
   338  	}
   339  
   340  	if s.enableIPv6 {
   341  		// add & remove IPv6 node route
   342  		err = linuxNodeHandler.updateNodeRoute(ip6CIDR, true, false)
   343  		require.NoError(t, err)
   344  
   345  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6CIDR, false)
   346  		require.NoError(t, err)
   347  		require.NotNil(t, foundRoute)
   348  
   349  		err = linuxNodeHandler.deleteNodeRoute(ip6CIDR, false)
   350  		require.NoError(t, err)
   351  
   352  		foundRoute, err = linuxNodeHandler.lookupNodeRoute(ip6CIDR, false)
   353  		require.NoError(t, err)
   354  		require.Nil(t, foundRoute)
   355  	}
   356  }
   357  
   358  func (s *linuxPrivilegedBaseTestSuite) TestAuxiliaryPrefixes(t *testing.T) {
   359  	net1 := cidr.MustParseCIDR("30.30.0.0/24")
   360  	net2 := cidr.MustParseCIDR("cafe:f00d::/112")
   361  
   362  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
   363  	log := hivetest.Logger(t)
   364  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
   365  
   366  	require.NotNil(t, linuxNodeHandler)
   367  	nodeConfig := s.nodeConfigTemplate
   368  	nodeConfig.AuxiliaryPrefixes = []*cidr.CIDR{net1, net2}
   369  
   370  	err := linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
   371  	require.NoError(t, err)
   372  
   373  	if s.enableIPv4 {
   374  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(net1, false)
   375  		require.NoError(t, err)
   376  		require.NotNil(t, foundRoute)
   377  	}
   378  
   379  	if s.enableIPv6 {
   380  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(net2, false)
   381  		require.NoError(t, err)
   382  		require.NotNil(t, foundRoute)
   383  	}
   384  
   385  	// remove aux prefix net2
   386  	nodeConfig.AuxiliaryPrefixes = []*cidr.CIDR{net1}
   387  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
   388  	require.NoError(t, err)
   389  
   390  	if s.enableIPv4 {
   391  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(net1, false)
   392  		require.NoError(t, err)
   393  		require.NotNil(t, foundRoute)
   394  	}
   395  
   396  	if s.enableIPv6 {
   397  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(net2, false)
   398  		require.NoError(t, err)
   399  		require.Nil(t, foundRoute)
   400  	}
   401  
   402  	// remove aux prefix net1, re-add net2
   403  	nodeConfig.AuxiliaryPrefixes = []*cidr.CIDR{net2}
   404  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
   405  	require.NoError(t, err)
   406  
   407  	if s.enableIPv4 {
   408  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(net1, false)
   409  		require.NoError(t, err)
   410  		require.Nil(t, foundRoute)
   411  	}
   412  
   413  	if s.enableIPv6 {
   414  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(net2, false)
   415  		require.NoError(t, err)
   416  		require.NotNil(t, foundRoute)
   417  	}
   418  }
   419  
   420  func (s *linuxPrivilegedBaseTestSuite) TestNodeUpdateEncapsulation(t *testing.T) {
   421  	s.commonNodeUpdateEncapsulation(t, true, nil)
   422  }
   423  
   424  func (s *linuxPrivilegedBaseTestSuite) TestNodeUpdateEncapsulationWithOverride(t *testing.T) {
   425  	s.commonNodeUpdateEncapsulation(t, false, func(*nodeTypes.Node) bool { return true })
   426  }
   427  
   428  func (s *linuxPrivilegedBaseTestSuite) commonNodeUpdateEncapsulation(t *testing.T, encap bool, override func(*nodeTypes.Node) bool) {
   429  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
   430  	ip4Alloc2 := cidr.MustParseCIDR("6.6.6.0/24")
   431  	ip6Alloc1 := cidr.MustParseCIDR("2001:aaaa::/96")
   432  	ip6Alloc2 := cidr.MustParseCIDR("2001:bbbb::/96")
   433  
   434  	externalNodeIP1 := net.ParseIP("4.4.4.4")
   435  	externalNodeIP2 := net.ParseIP("8.8.8.8")
   436  
   437  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
   438  	log := hivetest.Logger(t)
   439  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
   440  
   441  	require.NotNil(t, linuxNodeHandler)
   442  	linuxNodeHandler.OverrideEnableEncapsulation(override)
   443  	nodeConfig := s.nodeConfigTemplate
   444  	nodeConfig.EnableEncapsulation = encap
   445  	err := linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
   446  	require.NoError(t, err)
   447  
   448  	// nodev1: ip4Alloc1, ip6alloc1 => externalNodeIP1
   449  	nodev1 := nodeTypes.Node{
   450  		Name:      "node1",
   451  		ClusterID: 11,
   452  		IPAddresses: []nodeTypes.Address{
   453  			{IP: externalNodeIP1, Type: nodeaddressing.NodeInternalIP},
   454  		},
   455  	}
   456  
   457  	if s.enableIPv4 {
   458  		nodev1.IPv4AllocCIDR = ip4Alloc1
   459  	}
   460  	if s.enableIPv6 {
   461  		nodev1.IPv6AllocCIDR = ip6Alloc1
   462  	}
   463  
   464  	err = linuxNodeHandler.NodeAdd(nodev1)
   465  	require.NoError(t, err)
   466  
   467  	if s.enableIPv4 {
   468  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
   469  		require.NoError(t, err)
   470  		require.Equal(t, true, underlayIP.Equal(externalNodeIP1))
   471  
   472  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4Alloc1, false)
   473  		require.NoError(t, err)
   474  		require.NotNil(t, foundRoute)
   475  	}
   476  
   477  	if s.enableIPv6 {
   478  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
   479  		require.NoError(t, err)
   480  		require.Equal(t, true, underlayIP.Equal(externalNodeIP1))
   481  
   482  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6Alloc1, false)
   483  		require.NoError(t, err)
   484  		require.NotNil(t, foundRoute)
   485  	}
   486  
   487  	// nodev2: ip4Alloc1, ip6alloc1 => externalNodeIP2
   488  	nodev2 := nodeTypes.Node{
   489  		Name:      "node1",
   490  		ClusterID: 11,
   491  		IPAddresses: []nodeTypes.Address{
   492  			{IP: externalNodeIP2, Type: nodeaddressing.NodeInternalIP},
   493  		},
   494  	}
   495  
   496  	if s.enableIPv4 {
   497  		nodev2.IPv4AllocCIDR = ip4Alloc1
   498  	}
   499  	if s.enableIPv6 {
   500  		nodev2.IPv6AllocCIDR = ip6Alloc1
   501  	}
   502  
   503  	err = linuxNodeHandler.NodeUpdate(nodev1, nodev2)
   504  	require.NoError(t, err)
   505  
   506  	// alloc range v1 should map to underlay2
   507  	if s.enableIPv4 {
   508  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
   509  		require.NoError(t, err)
   510  		require.Equal(t, true, underlayIP.Equal(externalNodeIP2))
   511  
   512  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4Alloc1, false)
   513  		require.NoError(t, err)
   514  		require.NotNil(t, foundRoute)
   515  	}
   516  
   517  	if s.enableIPv6 {
   518  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
   519  		require.NoError(t, err)
   520  		require.Equal(t, true, underlayIP.Equal(externalNodeIP2))
   521  
   522  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6Alloc1, false)
   523  		require.NoError(t, err)
   524  		require.NotNil(t, foundRoute)
   525  	}
   526  
   527  	// nodev3: ip4Alloc2, ip6alloc2 => externalNodeIP1
   528  	nodev3 := nodeTypes.Node{
   529  		Name:      "node1",
   530  		ClusterID: 11,
   531  		IPAddresses: []nodeTypes.Address{
   532  			{IP: externalNodeIP1, Type: nodeaddressing.NodeInternalIP},
   533  		},
   534  	}
   535  
   536  	if s.enableIPv4 {
   537  		nodev3.IPv4AllocCIDR = ip4Alloc2
   538  	}
   539  	if s.enableIPv6 {
   540  		nodev3.IPv6AllocCIDR = ip6Alloc2
   541  	}
   542  
   543  	err = linuxNodeHandler.NodeUpdate(nodev2, nodev3)
   544  	require.NoError(t, err)
   545  
   546  	// alloc range v1 should fail
   547  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
   548  	require.Error(t, err)
   549  
   550  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
   551  	require.Error(t, err)
   552  
   553  	if s.enableIPv4 {
   554  		// alloc range v2 should map to underlay1
   555  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc2.IP))
   556  		require.NoError(t, err)
   557  		require.Equal(t, true, underlayIP.Equal(externalNodeIP1))
   558  
   559  		// node routes for alloc1 ranges should be gone
   560  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4Alloc1, false)
   561  		require.NoError(t, err)
   562  		require.Nil(t, foundRoute)
   563  
   564  		// node routes for alloc2 ranges should have been installed
   565  		foundRoute, err = linuxNodeHandler.lookupNodeRoute(ip4Alloc2, false)
   566  		require.NoError(t, err)
   567  		require.NotNil(t, foundRoute)
   568  	}
   569  
   570  	if s.enableIPv6 {
   571  		// alloc range v2 should map to underlay1
   572  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc2.IP))
   573  		require.NoError(t, err)
   574  		require.Equal(t, true, underlayIP.Equal(externalNodeIP1))
   575  
   576  		// node routes for alloc1 ranges should be gone
   577  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6Alloc1, false)
   578  		require.NoError(t, err)
   579  		require.Nil(t, foundRoute)
   580  
   581  		// node routes for alloc2 ranges should have been installed
   582  		foundRoute, err = linuxNodeHandler.lookupNodeRoute(ip6Alloc2, false)
   583  		require.NoError(t, err)
   584  		require.NotNil(t, foundRoute)
   585  	}
   586  
   587  	// nodev4: stop announcing CIDRs
   588  	nodev4 := nodeTypes.Node{
   589  		Name:      "node1",
   590  		ClusterID: 11,
   591  		IPAddresses: []nodeTypes.Address{
   592  			{IP: externalNodeIP1, Type: nodeaddressing.NodeInternalIP},
   593  		},
   594  	}
   595  	err = linuxNodeHandler.NodeUpdate(nodev3, nodev4)
   596  	require.NoError(t, err)
   597  
   598  	// alloc range v2 should fail
   599  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc2.IP))
   600  	require.Error(t, err)
   601  
   602  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc2.IP))
   603  	require.Error(t, err)
   604  
   605  	if s.enableIPv4 {
   606  		// node routes for alloc2 ranges should be gone
   607  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4Alloc2, false)
   608  		require.NoError(t, err)
   609  		require.Nil(t, foundRoute)
   610  	}
   611  
   612  	if s.enableIPv6 {
   613  		// node routes for alloc2 ranges should be gone
   614  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6Alloc2, false)
   615  		require.NoError(t, err)
   616  		require.Nil(t, foundRoute)
   617  	}
   618  
   619  	// nodev5: re-announce CIDRs
   620  	nodev5 := nodeTypes.Node{
   621  		Name:      "node1",
   622  		ClusterID: 11,
   623  		IPAddresses: []nodeTypes.Address{
   624  			{IP: externalNodeIP1, Type: nodeaddressing.NodeInternalIP},
   625  		},
   626  	}
   627  
   628  	if s.enableIPv4 {
   629  		nodev5.IPv4AllocCIDR = ip4Alloc2
   630  	}
   631  	if s.enableIPv6 {
   632  		nodev5.IPv6AllocCIDR = ip6Alloc2
   633  	}
   634  
   635  	err = linuxNodeHandler.NodeUpdate(nodev4, nodev5)
   636  	require.NoError(t, err)
   637  
   638  	if s.enableIPv4 {
   639  		// alloc range v2 should map to underlay1
   640  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc2.IP))
   641  		require.NoError(t, err)
   642  		require.Equal(t, true, underlayIP.Equal(externalNodeIP1))
   643  
   644  		// node routes for alloc2 ranges should have been installed
   645  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4Alloc2, false)
   646  		require.NoError(t, err)
   647  		require.NotNil(t, foundRoute)
   648  	}
   649  
   650  	if s.enableIPv6 {
   651  		// alloc range v2 should map to underlay1
   652  		underlayIP, err := tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc2.IP))
   653  		require.NoError(t, err)
   654  		require.Equal(t, true, underlayIP.Equal(externalNodeIP1))
   655  
   656  		// node routes for alloc2 ranges should have been installed
   657  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6Alloc2, false)
   658  		require.NoError(t, err)
   659  		require.NotNil(t, foundRoute)
   660  	}
   661  
   662  	// delete nodev5
   663  	err = linuxNodeHandler.NodeDelete(nodev5)
   664  	require.NoError(t, err)
   665  
   666  	// alloc range v1 should fail
   667  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
   668  	require.Error(t, err)
   669  
   670  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
   671  	require.Error(t, err)
   672  
   673  	// alloc range v2 should fail
   674  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc2.IP))
   675  	require.Error(t, err)
   676  
   677  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc2.IP))
   678  	require.Error(t, err)
   679  
   680  	if s.enableIPv4 {
   681  		// node routes for alloc2 ranges should be gone
   682  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip4Alloc2, false)
   683  		require.NoError(t, err)
   684  		require.Nil(t, foundRoute)
   685  	}
   686  
   687  	if s.enableIPv6 {
   688  		// node routes for alloc2 ranges should be gone
   689  		foundRoute, err := linuxNodeHandler.lookupNodeRoute(ip6Alloc2, false)
   690  		require.NoError(t, err)
   691  		require.Nil(t, foundRoute)
   692  	}
   693  }
   694  
   695  // Tests that the node ID BPF map is correctly updated during the lifecycle of
   696  // nodes and that the mapping nodeID:node remains 1:1.
   697  func (s *linuxPrivilegedBaseTestSuite) TestNodeUpdateIDs(t *testing.T) {
   698  	nodeIP1 := net.ParseIP("4.4.4.4")
   699  	nodeIP2 := net.ParseIP("8.8.8.8")
   700  	nodeIP3 := net.ParseIP("1.1.1.1")
   701  
   702  	nodeMap := nodemapfake.NewFakeNodeMapV2()
   703  
   704  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
   705  	log := hivetest.Logger(t)
   706  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodeMap, new(mockEnqueuer))
   707  
   708  	nodeConfig := s.nodeConfigTemplate
   709  	err := linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
   710  	require.NoError(t, err)
   711  
   712  	// New node receives a node ID.
   713  	node1v1 := nodeTypes.Node{
   714  		Name: "node1",
   715  		IPAddresses: []nodeTypes.Address{
   716  			{IP: nodeIP1, Type: nodeaddressing.NodeInternalIP},
   717  		},
   718  	}
   719  	err = linuxNodeHandler.NodeAdd(node1v1)
   720  	require.NoError(t, err)
   721  
   722  	nodeID1, err := nodeMap.Lookup(nodeIP1)
   723  	require.NoError(t, err)
   724  	require.NotEqual(t, 0, nodeID1)
   725  
   726  	// When the node is updated, the new IPs are mapped to the existing node ID.
   727  	node1v2 := nodeTypes.Node{
   728  		Name: "node1",
   729  		IPAddresses: []nodeTypes.Address{
   730  			{IP: nodeIP1, Type: nodeaddressing.NodeInternalIP},
   731  			{IP: nodeIP2, Type: nodeaddressing.NodeExternalIP},
   732  		},
   733  	}
   734  	err = linuxNodeHandler.NodeUpdate(node1v1, node1v2)
   735  	require.NoError(t, err)
   736  
   737  	_, err = nodeMap.Lookup(nodeIP1)
   738  	require.NoError(t, err)
   739  	nodeID2, err := nodeMap.Lookup(nodeIP2)
   740  	require.NoError(t, err)
   741  	require.Equal(t, *nodeID1, *nodeID2)
   742  
   743  	// When the node is updated, the old IPs are unmapped from the node ID.
   744  	node1v3 := nodeTypes.Node{
   745  		Name: "node1",
   746  		IPAddresses: []nodeTypes.Address{
   747  			{IP: nodeIP2, Type: nodeaddressing.NodeExternalIP},
   748  		},
   749  	}
   750  	err = linuxNodeHandler.NodeUpdate(node1v2, node1v3)
   751  	require.NoError(t, err)
   752  
   753  	_, err = nodeMap.Lookup(nodeIP1)
   754  	require.ErrorContains(t, err, "IP not found in node ID map")
   755  	nodeID3, err := nodeMap.Lookup(nodeIP2)
   756  	require.NoError(t, err)
   757  	require.Equal(t, *nodeID2, *nodeID3)
   758  
   759  	// If a second node is created, it receives a different node ID.
   760  	node2 := nodeTypes.Node{
   761  		Name: "node2",
   762  		IPAddresses: []nodeTypes.Address{
   763  			{IP: nodeIP1, Type: nodeaddressing.NodeInternalIP},
   764  		},
   765  	}
   766  	err = linuxNodeHandler.NodeAdd(node2)
   767  	require.NoError(t, err)
   768  
   769  	nodeID4, err := nodeMap.Lookup(nodeIP1)
   770  	require.NoError(t, err)
   771  	require.NotEqual(t, nodeID3, nodeID4)
   772  
   773  	// When the node is deleted, all references to its ID are also removed.
   774  	err = linuxNodeHandler.NodeDelete(node1v3)
   775  	require.NoError(t, err)
   776  
   777  	_, err = nodeMap.Lookup(nodeIP2)
   778  	require.ErrorContains(t, err, "IP not found in node ID map")
   779  
   780  	// When a node is created with multiple IP addresses, they all have the same ID.
   781  	node3 := nodeTypes.Node{
   782  		Name: "node3",
   783  		IPAddresses: []nodeTypes.Address{
   784  			{IP: nodeIP2, Type: nodeaddressing.NodeInternalIP},
   785  			{IP: nodeIP3, Type: nodeaddressing.NodeCiliumInternalIP},
   786  		},
   787  	}
   788  	err = linuxNodeHandler.NodeAdd(node3)
   789  	require.NoError(t, err)
   790  
   791  	nodeID5, err := nodeMap.Lookup(nodeIP2)
   792  	require.NoError(t, err)
   793  	nodeID6, err := nodeMap.Lookup(nodeIP3)
   794  	require.NoError(t, err)
   795  	require.Equal(t, *nodeID6, *nodeID5)
   796  }
   797  
   798  // Tests that we don't leak XFRM policies and states as nodes come and go.
   799  func (s *linuxPrivilegedBaseTestSuite) TestNodeChurnXFRMLeaks(t *testing.T) {
   800  
   801  	// Cover the XFRM configuration for IPAM modes cluster-pool, kubernetes, etc.
   802  	config := s.nodeConfigTemplate
   803  	config.EnableIPSec = true
   804  	s.testNodeChurnXFRMLeaksWithConfig(t, config)
   805  }
   806  
   807  // Tests the same as linuxPrivilegedBaseTestSuite.TestNodeChurnXFRMLeaks just
   808  // for the subnet encryption. IPv4-only because of https://github.com/cilium/cilium/issues/27280.
   809  func TestNodeChurnXFRMLeaks(t *testing.T) {
   810  	s := setupLinuxPrivilegedIPv4OnlyTestSuite(t)
   811  
   812  	externalNodeDevice := "ipsec_interface"
   813  
   814  	// Cover the XFRM configuration for IPAM modes cluster-pool, kubernetes, etc.
   815  	config := s.nodeConfigTemplate
   816  	config.EnableIPSec = true
   817  	s.testNodeChurnXFRMLeaksWithConfig(t, config)
   818  
   819  	// In the case of subnet encryption (tested below), the IPsec logic
   820  	// retrieves the IP address of the encryption interface directly so we need
   821  	// a dummy interface.
   822  	removeDevice(externalNodeDevice)
   823  	_, err := setupDummyDevice(externalNodeDevice, net.ParseIP("1.1.1.1"), net.ParseIP("face::1"))
   824  	require.NoError(t, err)
   825  	defer removeDevice(externalNodeDevice)
   826  	option.Config.EncryptInterface = []string{externalNodeDevice}
   827  	option.Config.RoutingMode = option.RoutingModeNative
   828  
   829  	// Cover the XFRM configuration for subnet encryption: IPAM modes AKS and EKS.
   830  	_, ipv4PodSubnets, err := net.ParseCIDR("4.4.0.0/16")
   831  	require.NoError(t, err)
   832  	require.NotNil(t, ipv4PodSubnets)
   833  	config.IPv4PodSubnets = []*net.IPNet{ipv4PodSubnets}
   834  	_, ipv6PodSubnets, err := net.ParseCIDR("2001:aaaa::/64")
   835  	require.NoError(t, err)
   836  	require.NotNil(t, ipv6PodSubnets)
   837  	config.IPv6PodSubnets = []*net.IPNet{ipv6PodSubnets}
   838  	s.testNodeChurnXFRMLeaksWithConfig(t, config)
   839  }
   840  
   841  func (s *linuxPrivilegedIPv4OnlyTestSuite) TestEncryptedOverlayXFRMLeaks(t *testing.T) {
   842  	// Cover the XFRM configuration for IPAM modes cluster-pool, kubernetes, etc.
   843  	config := datapath.LocalNodeConfiguration{
   844  		EnableIPv4:  s.enableIPv4,
   845  		EnableIPv6:  s.enableIPv6,
   846  		EnableIPSec: true,
   847  	}
   848  	s.testEncryptedOverlayXFRMLeaks(t, config)
   849  }
   850  
   851  // TestEncryptedOverlayXFRMLeaks tests that the XFRM policies and states are accurate when the encrypted overlay
   852  // feature is enabled and disabled.
   853  func (s *linuxPrivilegedIPv4OnlyTestSuite) testEncryptedOverlayXFRMLeaks(t *testing.T, config datapath.LocalNodeConfiguration) {
   854  	tlog := hivetest.Logger(t)
   855  	keys := bytes.NewReader([]byte("6 rfc4106(gcm(aes)) 44434241343332312423222114131211f4f3f2f1 128\n"))
   856  	_, _, err := ipsec.LoadIPSecKeys(tlog, keys)
   857  	require.NoError(t, err)
   858  
   859  	var linuxNodeHandler *linuxNodeHandler
   860  	h := hive.New(
   861  		DevicesControllerCell,
   862  		cell.Invoke(func(db *statedb.DB, devices statedb.Table[*tables.Device]) {
   863  			dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
   864  			linuxNodeHandler = newNodeHandler(tlog, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
   865  		}),
   866  	)
   867  
   868  	require.Nil(t, h.Start(tlog, context.TODO()))
   869  	defer func() { require.Nil(t, h.Stop(tlog, context.TODO())) }()
   870  	require.NotNil(t, linuxNodeHandler)
   871  
   872  	err = linuxNodeHandler.NodeConfigurationChanged(config)
   873  	require.NoError(t, err)
   874  
   875  	// Adding a node adds some XFRM states and policies.
   876  	node := nodeTypes.Node{
   877  		Name: "node",
   878  		IPAddresses: []nodeTypes.Address{
   879  			{IP: net.ParseIP("3.3.3.3"), Type: nodeaddressing.NodeInternalIP},
   880  			{IP: net.ParseIP("4.4.4.4"), Type: nodeaddressing.NodeCiliumInternalIP},
   881  		},
   882  		IPv4AllocCIDR: cidr.MustParseCIDR("4.4.4.0/24"),
   883  		BootID:        "test-boot-id",
   884  	}
   885  	err = linuxNodeHandler.NodeAdd(node)
   886  	require.NoError(t, err)
   887  
   888  	states, err := netlink.XfrmStateList(netlink.FAMILY_ALL)
   889  	require.NoError(t, err)
   890  	require.Equal(t, 4, len(states))
   891  	policies, err := netlink.XfrmPolicyList(netlink.FAMILY_ALL)
   892  	require.NoError(t, err)
   893  	require.Equal(t, 2, countXFRMPolicies(policies))
   894  
   895  	// disable encrypted overlay feature
   896  	config.EnableIPSecEncryptedOverlay = false
   897  
   898  	err = linuxNodeHandler.NodeConfigurationChanged(config)
   899  	require.NoError(t, err)
   900  
   901  	states, err = netlink.XfrmStateList(netlink.FAMILY_ALL)
   902  	require.NoError(t, err)
   903  	require.Equal(t, 2, len(states))
   904  	policies, err = netlink.XfrmPolicyList(netlink.FAMILY_ALL)
   905  	require.NoError(t, err)
   906  	require.Equal(t, 1, countXFRMPolicies(policies))
   907  }
   908  
   909  func (s *linuxPrivilegedBaseTestSuite) testNodeChurnXFRMLeaksWithConfig(t *testing.T, config datapath.LocalNodeConfiguration) {
   910  	log := hivetest.Logger(t)
   911  	keys := bytes.NewReader([]byte("6 rfc4106(gcm(aes)) 44434241343332312423222114131211f4f3f2f1 128\n"))
   912  	_, _, err := ipsec.LoadIPSecKeys(log, keys)
   913  	require.NoError(t, err)
   914  
   915  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
   916  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
   917  
   918  	err = linuxNodeHandler.NodeConfigurationChanged(config)
   919  	require.NoError(t, err)
   920  
   921  	// Adding a node adds some XFRM states and policies.
   922  	node := nodeTypes.Node{
   923  		Name: "node",
   924  		IPAddresses: []nodeTypes.Address{
   925  			{IP: net.ParseIP("4.4.4.4"), Type: nodeaddressing.NodeCiliumInternalIP},
   926  			{IP: net.ParseIP("2001:aaaa::1"), Type: nodeaddressing.NodeCiliumInternalIP},
   927  		},
   928  		IPv4AllocCIDR: cidr.MustParseCIDR("4.4.4.0/24"),
   929  		IPv6AllocCIDR: cidr.MustParseCIDR("2001:aaaa::/96"),
   930  		BootID:        "test-boot-id",
   931  	}
   932  	err = linuxNodeHandler.NodeAdd(node)
   933  	require.NoError(t, err)
   934  
   935  	states, err := netlink.XfrmStateList(netlink.FAMILY_ALL)
   936  	require.NoError(t, err)
   937  	require.NotEqual(t, 0, len(states))
   938  	policies, err := netlink.XfrmPolicyList(netlink.FAMILY_ALL)
   939  	require.NoError(t, err)
   940  	require.NotEqual(t, 0, countXFRMPolicies(policies))
   941  
   942  	// Removing the node removes those XFRM states and policies.
   943  	err = linuxNodeHandler.NodeDelete(node)
   944  	require.NoError(t, err)
   945  
   946  	states, err = netlink.XfrmStateList(netlink.FAMILY_ALL)
   947  	require.NoError(t, err)
   948  	require.Equal(t, 0, len(states))
   949  	policies, err = netlink.XfrmPolicyList(netlink.FAMILY_ALL)
   950  	require.NoError(t, err)
   951  	require.Equal(t, 0, countXFRMPolicies(policies))
   952  }
   953  
   954  // Counts the number of XFRM OUT policies excluding the catch-all default-drop
   955  // one. The default-drop is always installed and shouldn't be removed. The IN
   956  // and FWD policies are installed once and for all in reaction to new nodes;
   957  // contrary to XFRM IN states, they don't need to be unique per remote node.
   958  func countXFRMPolicies(policies []netlink.XfrmPolicy) int {
   959  	nbPolicies := 0
   960  	for _, policy := range policies {
   961  		if policy.Action != netlink.XFRM_POLICY_BLOCK &&
   962  			policy.Dir == netlink.XFRM_DIR_OUT {
   963  			nbPolicies++
   964  		}
   965  	}
   966  	return nbPolicies
   967  }
   968  
   969  func lookupDirectRoute(log *slog.Logger, CIDR *cidr.CIDR, nodeIP net.IP) ([]netlink.Route, error) {
   970  	routeSpec, _, err := createDirectRouteSpec(log, CIDR, nodeIP, false)
   971  	if err != nil {
   972  		return nil, err
   973  	}
   974  
   975  	family := netlink.FAMILY_V4
   976  	if nodeIP.To4() == nil {
   977  		family = netlink.FAMILY_V6
   978  	}
   979  	return netlink.RouteListFiltered(family, routeSpec, netlink.RT_FILTER_DST|netlink.RT_FILTER_GW|netlink.RT_FILTER_OIF)
   980  }
   981  
   982  func (s *linuxPrivilegedBaseTestSuite) TestNodeUpdateDirectRouting(t *testing.T) {
   983  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
   984  	ip4Alloc2 := cidr.MustParseCIDR("5.5.5.0/26")
   985  
   986  	ipv4SecondaryAlloc1 := cidr.MustParseCIDR("5.5.6.0/24")
   987  	ipv4SecondaryAlloc2 := cidr.MustParseCIDR("5.5.7.0/24")
   988  	ipv4SecondaryAlloc3 := cidr.MustParseCIDR("5.5.8.0/24")
   989  
   990  	externalNode1IP4v1 := net.ParseIP("4.4.4.4")
   991  	externalNode1IP4v2 := net.ParseIP("4.4.4.5")
   992  
   993  	externalNode1Device := "dummy_node1"
   994  	removeDevice(externalNode1Device)
   995  	dev1, err := setupDummyDevice(externalNode1Device, externalNode1IP4v1, net.ParseIP("face::1"))
   996  	require.NoError(t, err)
   997  	defer removeDevice(externalNode1Device)
   998  
   999  	externalNode2Device := "dummy_node2"
  1000  	removeDevice(externalNode2Device)
  1001  	dev2, err := setupDummyDevice(externalNode2Device, externalNode1IP4v2, net.ParseIP("face::2"))
  1002  	require.NoError(t, err)
  1003  	defer removeDevice(externalNode2Device)
  1004  
  1005  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
  1006  	log := hivetest.Logger(t)
  1007  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
  1008  
  1009  	require.NotNil(t, linuxNodeHandler)
  1010  	nodeConfig := s.nodeConfigTemplate
  1011  	nodeConfig.Devices = append(slices.Clone(nodeConfig.Devices), dev1, dev2)
  1012  	nodeConfig.EnableAutoDirectRouting = true
  1013  
  1014  	expectedIPv4Routes := 0
  1015  	if s.enableIPv4 {
  1016  		expectedIPv4Routes = 1
  1017  	}
  1018  
  1019  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  1020  	require.NoError(t, err)
  1021  
  1022  	// nodev1: ip4Alloc1 => externalNodeIP1
  1023  	nodev1 := nodeTypes.Node{
  1024  		Name: "node1",
  1025  		IPAddresses: []nodeTypes.Address{
  1026  			{IP: externalNode1IP4v1, Type: nodeaddressing.NodeInternalIP},
  1027  		},
  1028  		IPv4AllocCIDR: ip4Alloc1,
  1029  	}
  1030  	err = linuxNodeHandler.NodeAdd(nodev1)
  1031  	require.NoError(t, err)
  1032  
  1033  	foundRoutes, err := lookupDirectRoute(log, ip4Alloc1, externalNode1IP4v1)
  1034  	require.NoError(t, err)
  1035  	require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1036  
  1037  	// nodev2: ip4Alloc1 => externalNodeIP2
  1038  	nodev2 := nodeTypes.Node{
  1039  		Name: "node1",
  1040  		IPAddresses: []nodeTypes.Address{
  1041  			{IP: externalNode1IP4v2, Type: nodeaddressing.NodeInternalIP},
  1042  		},
  1043  		IPv4AllocCIDR: ip4Alloc1,
  1044  	}
  1045  
  1046  	err = linuxNodeHandler.NodeUpdate(nodev1, nodev2)
  1047  	require.NoError(t, err)
  1048  
  1049  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc1, externalNode1IP4v2)
  1050  	require.NoError(t, err)
  1051  	require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1052  
  1053  	// nodev3: ip4Alloc2 => externalNodeIP2
  1054  	nodev3 := nodeTypes.Node{
  1055  		Name: "node1",
  1056  		IPAddresses: []nodeTypes.Address{
  1057  			{IP: externalNode1IP4v2, Type: nodeaddressing.NodeInternalIP},
  1058  		},
  1059  		IPv4AllocCIDR: ip4Alloc2,
  1060  	}
  1061  	err = linuxNodeHandler.NodeUpdate(nodev2, nodev3)
  1062  	require.NoError(t, err)
  1063  
  1064  	// node routes for alloc1 ranges should be gone
  1065  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc1, externalNode1IP4v2)
  1066  	require.NoError(t, err)
  1067  	require.Equal(t, 0, len(foundRoutes)) // route should not exist regardless whether ipv4 is enabled or not
  1068  
  1069  	// node routes for alloc2 ranges should have been installed
  1070  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc2, externalNode1IP4v2)
  1071  	require.NoError(t, err)
  1072  	require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1073  
  1074  	// nodev4: no longer announce CIDR
  1075  	nodev4 := nodeTypes.Node{
  1076  		Name: "node1",
  1077  		IPAddresses: []nodeTypes.Address{
  1078  			{IP: externalNode1IP4v2, Type: nodeaddressing.NodeInternalIP},
  1079  		},
  1080  	}
  1081  	err = linuxNodeHandler.NodeUpdate(nodev3, nodev4)
  1082  	require.NoError(t, err)
  1083  
  1084  	// node routes for alloc2 ranges should have been removed
  1085  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc2, externalNode1IP4v2)
  1086  	require.NoError(t, err)
  1087  	require.Equal(t, 0, len(foundRoutes))
  1088  
  1089  	// nodev5: Re-announce CIDR
  1090  	nodev5 := nodeTypes.Node{
  1091  		Name: "node1",
  1092  		IPAddresses: []nodeTypes.Address{
  1093  			{IP: externalNode1IP4v2, Type: nodeaddressing.NodeInternalIP},
  1094  		},
  1095  		IPv4AllocCIDR: ip4Alloc2,
  1096  	}
  1097  	err = linuxNodeHandler.NodeUpdate(nodev4, nodev5)
  1098  	require.NoError(t, err)
  1099  
  1100  	// node routes for alloc2 ranges should have been removed
  1101  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc2, externalNode1IP4v2)
  1102  	require.NoError(t, err)
  1103  	require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1104  
  1105  	// delete nodev5
  1106  	err = linuxNodeHandler.NodeDelete(nodev5)
  1107  	require.NoError(t, err)
  1108  
  1109  	// node routes for alloc2 ranges should be gone
  1110  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc2, externalNode1IP4v2)
  1111  	require.NoError(t, err)
  1112  	require.Equal(t, 0, len(foundRoutes)) // route should not exist regardless whether ipv4 is enabled or not
  1113  
  1114  	// nodev6: Re-introduce node with secondary CIDRs
  1115  	nodev6 := nodeTypes.Node{
  1116  		Name: "node2",
  1117  		IPAddresses: []nodeTypes.Address{
  1118  			{IP: externalNode1IP4v1, Type: nodeaddressing.NodeInternalIP},
  1119  		},
  1120  		IPv4AllocCIDR:           ip4Alloc1,
  1121  		IPv4SecondaryAllocCIDRs: []*cidr.CIDR{ipv4SecondaryAlloc1, ipv4SecondaryAlloc2},
  1122  	}
  1123  	err = linuxNodeHandler.NodeAdd(nodev6)
  1124  	require.NoError(t, err)
  1125  
  1126  	// expecting both primary and secondary routes to exist
  1127  	for _, ip4Alloc := range []*cidr.CIDR{ip4Alloc1, ipv4SecondaryAlloc1, ipv4SecondaryAlloc2} {
  1128  		foundRoutes, err = lookupDirectRoute(log, ip4Alloc, externalNode1IP4v1)
  1129  		require.NoError(t, err)
  1130  		require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1131  	}
  1132  
  1133  	// nodev7: Replace a secondary route
  1134  	nodev7 := nodeTypes.Node{
  1135  		Name: "node2",
  1136  		IPAddresses: []nodeTypes.Address{
  1137  			{IP: externalNode1IP4v1, Type: nodeaddressing.NodeInternalIP},
  1138  		},
  1139  		IPv4AllocCIDR:           ip4Alloc1,
  1140  		IPv4SecondaryAllocCIDRs: []*cidr.CIDR{ipv4SecondaryAlloc1, ipv4SecondaryAlloc3},
  1141  	}
  1142  	err = linuxNodeHandler.NodeUpdate(nodev6, nodev7)
  1143  	require.NoError(t, err)
  1144  
  1145  	// Checks all three required routes exist
  1146  	for _, ip4Alloc := range []*cidr.CIDR{ip4Alloc1, ipv4SecondaryAlloc1, ipv4SecondaryAlloc3} {
  1147  		foundRoutes, err = lookupDirectRoute(log, ip4Alloc, externalNode1IP4v1)
  1148  		require.NoError(t, err)
  1149  		require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1150  	}
  1151  	// Checks route for removed CIDR has been deleted
  1152  	foundRoutes, err = lookupDirectRoute(log, ipv4SecondaryAlloc2, externalNode1IP4v1)
  1153  	require.NoError(t, err)
  1154  	require.Equal(t, 0, len(foundRoutes))
  1155  
  1156  	// nodev8: Change node IP to externalNode1IP4v2
  1157  	nodev8 := nodeTypes.Node{
  1158  		Name: "node2",
  1159  		IPAddresses: []nodeTypes.Address{
  1160  			{IP: externalNode1IP4v2, Type: nodeaddressing.NodeInternalIP},
  1161  		},
  1162  		IPv4AllocCIDR:           ip4Alloc1,
  1163  		IPv4SecondaryAllocCIDRs: []*cidr.CIDR{ipv4SecondaryAlloc1, ipv4SecondaryAlloc3},
  1164  	}
  1165  	err = linuxNodeHandler.NodeUpdate(nodev7, nodev8)
  1166  	require.NoError(t, err)
  1167  
  1168  	// Checks all routes with the new node IP exist
  1169  	for _, ip4Alloc := range []*cidr.CIDR{ip4Alloc1, ipv4SecondaryAlloc1, ipv4SecondaryAlloc3} {
  1170  		foundRoutes, err = lookupDirectRoute(log, ip4Alloc, externalNode1IP4v2)
  1171  		require.NoError(t, err)
  1172  		require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1173  	}
  1174  	// Checks all routes with the old node IP have been deleted
  1175  	for _, ip4Alloc := range []*cidr.CIDR{ip4Alloc1, ipv4SecondaryAlloc1, ipv4SecondaryAlloc3} {
  1176  		foundRoutes, err = lookupDirectRoute(log, ip4Alloc, externalNode1IP4v1)
  1177  		require.NoError(t, err)
  1178  		require.Equal(t, 0, len(foundRoutes))
  1179  	}
  1180  
  1181  	// nodev9: replacement of primary route, removal of secondary CIDRs
  1182  	nodev9 := nodeTypes.Node{
  1183  		Name: "node2",
  1184  		IPAddresses: []nodeTypes.Address{
  1185  			{IP: externalNode1IP4v2, Type: nodeaddressing.NodeInternalIP},
  1186  		},
  1187  		IPv4AllocCIDR:           ip4Alloc2,
  1188  		IPv4SecondaryAllocCIDRs: []*cidr.CIDR{},
  1189  	}
  1190  	err = linuxNodeHandler.NodeUpdate(nodev8, nodev9)
  1191  	require.NoError(t, err)
  1192  
  1193  	// Checks primary route has been created
  1194  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc2, externalNode1IP4v2)
  1195  	require.NoError(t, err)
  1196  	require.Equal(t, expectedIPv4Routes, len(foundRoutes))
  1197  
  1198  	// Checks all old routes have been deleted
  1199  	for _, ip4Alloc := range []*cidr.CIDR{ip4Alloc1, ipv4SecondaryAlloc1, ipv4SecondaryAlloc3} {
  1200  		foundRoutes, err = lookupDirectRoute(log, ip4Alloc, externalNode1IP4v2)
  1201  		require.NoError(t, err)
  1202  		require.Equal(t, 0, len(foundRoutes))
  1203  	}
  1204  
  1205  	// delete nodev9
  1206  	err = linuxNodeHandler.NodeDelete(nodev9)
  1207  	require.NoError(t, err)
  1208  
  1209  	// remaining primary node route must have been deleted
  1210  	foundRoutes, err = lookupDirectRoute(log, ip4Alloc2, externalNode1IP4v2)
  1211  	require.NoError(t, err)
  1212  	require.Equal(t, 0, len(foundRoutes))
  1213  }
  1214  
  1215  func (s *linuxPrivilegedBaseTestSuite) TestAgentRestartOptionChanges(t *testing.T) {
  1216  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
  1217  	ip6Alloc1 := cidr.MustParseCIDR("2001:aaaa::/96")
  1218  	underlayIP := net.ParseIP("4.4.4.4")
  1219  
  1220  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
  1221  	log := hivetest.Logger(t)
  1222  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
  1223  
  1224  	require.NotNil(t, linuxNodeHandler)
  1225  	nodeConfig := s.nodeConfigTemplate
  1226  	nodeConfig.EnableEncapsulation = true
  1227  
  1228  	err := linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  1229  	require.NoError(t, err)
  1230  
  1231  	nodev1 := nodeTypes.Node{
  1232  		Name: "node1",
  1233  		IPAddresses: []nodeTypes.Address{
  1234  			{IP: underlayIP, Type: nodeaddressing.NodeInternalIP},
  1235  		},
  1236  	}
  1237  
  1238  	if s.enableIPv6 {
  1239  		nodev1.IPv6AllocCIDR = ip6Alloc1
  1240  	}
  1241  
  1242  	if s.enableIPv4 {
  1243  		nodev1.IPv4AllocCIDR = ip4Alloc1
  1244  	}
  1245  
  1246  	err = linuxNodeHandler.NodeAdd(nodev1)
  1247  	require.NoError(t, err)
  1248  
  1249  	// tunnel map entries must exist
  1250  	if s.enableIPv4 {
  1251  		_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
  1252  		require.NoError(t, err)
  1253  	}
  1254  	if s.enableIPv6 {
  1255  		_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
  1256  		require.NoError(t, err)
  1257  	}
  1258  
  1259  	// Simulate agent restart with address families disables
  1260  	nodeConfig.EnableIPv4 = false
  1261  	nodeConfig.EnableIPv6 = false
  1262  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  1263  	require.NoError(t, err)
  1264  
  1265  	// Simulate initial node addition
  1266  	err = linuxNodeHandler.NodeAdd(nodev1)
  1267  	require.NoError(t, err)
  1268  
  1269  	// tunnel map entries should have been removed
  1270  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
  1271  	require.Error(t, err)
  1272  	_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
  1273  	require.Error(t, err)
  1274  
  1275  	// Simulate agent restart with address families enabled again
  1276  	nodeConfig.EnableIPv4 = true
  1277  	nodeConfig.EnableIPv6 = true
  1278  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  1279  	require.NoError(t, err)
  1280  
  1281  	// Simulate initial node addition
  1282  	err = linuxNodeHandler.NodeAdd(nodev1)
  1283  	require.NoError(t, err)
  1284  
  1285  	// tunnel map entries must exist
  1286  	if s.enableIPv4 {
  1287  		_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip4Alloc1.IP))
  1288  		require.NoError(t, err)
  1289  	}
  1290  	if s.enableIPv6 {
  1291  		_, err = tunnel.TunnelMap().GetTunnelEndpoint(cmtypes.MustAddrClusterFromIP(ip6Alloc1.IP))
  1292  		require.NoError(t, err)
  1293  	}
  1294  
  1295  	err = linuxNodeHandler.NodeDelete(nodev1)
  1296  	require.NoError(t, err)
  1297  }
  1298  
  1299  func insertFakeRoute(t *testing.T, n *linuxNodeHandler, prefix *cidr.CIDR) {
  1300  	nodeRoute, err := n.createNodeRouteSpec(prefix, false)
  1301  	require.NoError(t, err)
  1302  
  1303  	nodeRoute.Device = dummyExternalDeviceName
  1304  
  1305  	err = route.Upsert(nodeRoute)
  1306  	require.NoError(t, err)
  1307  }
  1308  
  1309  func lookupFakeRoute(t *testing.T, n *linuxNodeHandler, prefix *cidr.CIDR) bool {
  1310  	routeSpec, err := n.createNodeRouteSpec(prefix, false)
  1311  	require.NoError(t, err)
  1312  
  1313  	routeSpec.Device = dummyExternalDeviceName
  1314  	rt, err := route.Lookup(routeSpec)
  1315  	require.NoError(t, err)
  1316  	return rt != nil
  1317  }
  1318  
  1319  func (s *linuxPrivilegedBaseTestSuite) TestNodeValidationDirectRouting(t *testing.T) {
  1320  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
  1321  	ip6Alloc1 := cidr.MustParseCIDR("2001:aaaa::/96")
  1322  
  1323  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
  1324  	log := hivetest.Logger(t)
  1325  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
  1326  
  1327  	if s.enableIPv4 {
  1328  		insertFakeRoute(t, linuxNodeHandler, ip4Alloc1)
  1329  	}
  1330  
  1331  	if s.enableIPv6 {
  1332  		insertFakeRoute(t, linuxNodeHandler, ip6Alloc1)
  1333  	}
  1334  
  1335  	nodeConfig := s.nodeConfigTemplate
  1336  	nodeConfig.EnableEncapsulation = false
  1337  	err := linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  1338  	require.NoError(t, err)
  1339  
  1340  	nodev1 := nodeTypes.Node{
  1341  		Name:        "node1",
  1342  		IPAddresses: []nodeTypes.Address{},
  1343  	}
  1344  
  1345  	if s.enableIPv4 {
  1346  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  1347  			IP:   nodeConfig.NodeIPv4,
  1348  			Type: nodeaddressing.NodeInternalIP,
  1349  		})
  1350  		nodev1.IPv4AllocCIDR = ip4Alloc1
  1351  	}
  1352  
  1353  	if s.enableIPv6 {
  1354  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  1355  			IP:   nodeConfig.NodeIPv6,
  1356  			Type: nodeaddressing.NodeInternalIP,
  1357  		})
  1358  		nodev1.IPv6AllocCIDR = ip6Alloc1
  1359  	}
  1360  
  1361  	err = linuxNodeHandler.NodeAdd(nodev1)
  1362  	require.NoError(t, err)
  1363  
  1364  	err = linuxNodeHandler.NodeValidateImplementation(nodev1)
  1365  	require.NoError(t, err)
  1366  
  1367  	if s.enableIPv4 {
  1368  		require.Equal(t, true, lookupFakeRoute(t, linuxNodeHandler, ip4Alloc1))
  1369  	}
  1370  
  1371  	if s.enableIPv6 {
  1372  		require.Equal(t, true, lookupFakeRoute(t, linuxNodeHandler, ip6Alloc1))
  1373  	}
  1374  }
  1375  
  1376  func neighStateOk(n netlink.Neigh) bool {
  1377  	switch {
  1378  	case (n.State & netlink.NUD_REACHABLE) > 0:
  1379  		fallthrough
  1380  	case (n.State & netlink.NUD_STALE) > 0:
  1381  		// Current final state
  1382  		return true
  1383  	}
  1384  	return false
  1385  }
  1386  
  1387  func TestArpPingHandlingIPv6(t *testing.T) {
  1388  	s := setupLinuxPrivilegedIPv6OnlyTestSuite(t)
  1389  	runtime.LockOSThread()
  1390  	defer runtime.UnlockOSThread()
  1391  
  1392  	prevEnableL2NeighDiscovery := option.Config.EnableL2NeighDiscovery
  1393  	defer func() { option.Config.EnableL2NeighDiscovery = prevEnableL2NeighDiscovery }()
  1394  
  1395  	option.Config.EnableL2NeighDiscovery = true
  1396  
  1397  	prevStateDir := option.Config.StateDir
  1398  	defer func() { option.Config.StateDir = prevStateDir }()
  1399  
  1400  	tmpDir := t.TempDir()
  1401  	option.Config.StateDir = tmpDir
  1402  
  1403  	baseTimeOld, err := s.sysctl.Read(baseIPv6Time)
  1404  	require.NoError(t, err)
  1405  	err = s.sysctl.Write(baseIPv6Time, fmt.Sprintf("%d", baseTime))
  1406  	require.NoError(t, err)
  1407  	defer func() { s.sysctl.Write(baseIPv6Time, baseTimeOld) }()
  1408  
  1409  	mcastNumOld, err := s.sysctl.Read(mcastNumIPv6)
  1410  	require.NoError(t, err)
  1411  	err = s.sysctl.Write(mcastNumIPv6, fmt.Sprintf("%d", mcastNum))
  1412  	require.NoError(t, err)
  1413  	defer func() { s.sysctl.Write(mcastNumIPv6, mcastNumOld) }()
  1414  
  1415  	// 1. Test whether another node in the same L2 subnet can be arpinged.
  1416  	//    The other node is in the different netns reachable via the veth pair.
  1417  	//
  1418  	//      +--------------+     +--------------+
  1419  	//      |  host netns  |     |    netns0    |
  1420  	//      |              |     |    nodev1    |
  1421  	//      |         veth0+-----+veth1         |
  1422  	//      | f00d::249/96 |     | f00d::250/96 |
  1423  	//      +--------------+     +--------------+
  1424  
  1425  	// Setup
  1426  	veth := &netlink.Veth{
  1427  		LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
  1428  		PeerName:  "veth1",
  1429  	}
  1430  	err = netlink.LinkAdd(veth)
  1431  	require.NoError(t, err)
  1432  	t.Cleanup(func() { netlink.LinkDel(veth) })
  1433  	veth0, err := netlink.LinkByName("veth0")
  1434  	require.NoError(t, err)
  1435  	veth1, err := netlink.LinkByName("veth1")
  1436  	require.NoError(t, err)
  1437  	_, ipnet, _ := net.ParseCIDR("f00d::/96")
  1438  	ip0 := net.ParseIP("f00d::249")
  1439  	ip1 := net.ParseIP("f00d::250")
  1440  	ipG := net.ParseIP("f00d::251")
  1441  	ipnet.IP = ip0
  1442  	addr := &netlink.Addr{IPNet: ipnet}
  1443  	err = netlink.AddrAdd(veth0, addr)
  1444  	require.NoError(t, err)
  1445  	err = netlink.LinkSetUp(veth0)
  1446  	require.NoError(t, err)
  1447  
  1448  	ns := netns.NewNetNS(t)
  1449  
  1450  	err = netlink.LinkSetNsFd(veth1, int(ns.FD()))
  1451  	require.NoError(t, err)
  1452  	ns.Do(func() error {
  1453  		veth1, err := netlink.LinkByName("veth1")
  1454  		require.NoError(t, err)
  1455  		ipnet.IP = ip1
  1456  		addr = &netlink.Addr{IPNet: ipnet}
  1457  		netlink.AddrAdd(veth1, addr)
  1458  		require.NoError(t, err)
  1459  		ipnet.IP = ipG
  1460  		addr = &netlink.Addr{IPNet: ipnet}
  1461  		netlink.AddrAdd(veth1, addr)
  1462  		require.NoError(t, err)
  1463  		err = netlink.LinkSetUp(veth1)
  1464  		require.NoError(t, err)
  1465  		return nil
  1466  	})
  1467  
  1468  	prevRoutingMode := option.Config.RoutingMode
  1469  	defer func() { option.Config.RoutingMode = prevRoutingMode }()
  1470  	option.Config.RoutingMode = option.RoutingModeNative
  1471  	prevDRDev := option.Config.DirectRoutingDevice
  1472  	defer func() { option.Config.DirectRoutingDevice = prevDRDev }()
  1473  	option.Config.DirectRoutingDevice = "veth0"
  1474  	prevNP := option.Config.EnableNodePort
  1475  	defer func() { option.Config.EnableNodePort = prevNP }()
  1476  	option.Config.EnableNodePort = true
  1477  	prevARPPeriod := option.Config.ARPPingRefreshPeriod
  1478  	defer func() { option.Config.ARPPingRefreshPeriod = prevARPPeriod }()
  1479  	option.Config.ARPPingRefreshPeriod = time.Duration(1 * time.Nanosecond)
  1480  
  1481  	mq := new(mockEnqueuer)
  1482  	dpConfig := DatapathConfiguration{HostDevice: "veth0"}
  1483  	log := hivetest.Logger(t)
  1484  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), mq)
  1485  	mq.nh = linuxNodeHandler
  1486  
  1487  	nodeConfig := s.nodeConfigTemplate
  1488  	nodeConfig.EnableEncapsulation = false
  1489  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  1490  	require.NoError(t, err)
  1491  
  1492  	// wait waits for neigh entry update or waits for removal if waitForDelete=true
  1493  	wait := func(nodeID nodeTypes.Identity, link string, before *time.Time, waitForDelete bool) {
  1494  		t.Helper()
  1495  		err := testutils.WaitUntil(func() bool {
  1496  			linuxNodeHandler.neighLock.Lock()
  1497  			defer linuxNodeHandler.neighLock.Unlock()
  1498  			nextHopByLink, found := linuxNodeHandler.neighNextHopByNode6[nodeID]
  1499  			if !found {
  1500  				return waitForDelete
  1501  			}
  1502  			nextHop, found := nextHopByLink[link]
  1503  			if !found {
  1504  				return waitForDelete
  1505  			}
  1506  			lastPing, found := linuxNodeHandler.neighLastPingByNextHop[nextHop]
  1507  			if !found {
  1508  				return false
  1509  			}
  1510  			if waitForDelete {
  1511  				return false
  1512  			}
  1513  			return before.Before(lastPing)
  1514  		}, 5*time.Second)
  1515  		require.NoError(t, err)
  1516  	}
  1517  
  1518  	assertNeigh := func(ip net.IP, checkNeigh func(neigh netlink.Neigh) bool) {
  1519  		t.Helper()
  1520  		err := testutils.WaitUntil(func() bool {
  1521  			neighs, err := netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V6)
  1522  			require.NoError(t, err)
  1523  			for _, n := range neighs {
  1524  				if n.IP.Equal(ip) && checkNeigh(n) {
  1525  					return true
  1526  				}
  1527  			}
  1528  			return false
  1529  		}, 5*time.Second)
  1530  		require.Nil(t, err, fmt.Sprintf("expected neighbor %s", ip))
  1531  	}
  1532  
  1533  	assertNoNeigh := func(msg string, ips ...net.IP) {
  1534  		t.Helper()
  1535  		err := testutils.WaitUntil(func() bool {
  1536  			neighs, err := netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V6)
  1537  			require.NoError(t, err)
  1538  			for _, n := range neighs {
  1539  				for _, ip := range ips {
  1540  					if n.IP.Equal(ip) {
  1541  						return false
  1542  					}
  1543  				}
  1544  			}
  1545  			return true
  1546  		}, 5*time.Second)
  1547  		require.Nil(t, err, msg)
  1548  	}
  1549  
  1550  	nodev1 := nodeTypes.Node{
  1551  		Name: "node1",
  1552  		IPAddresses: []nodeTypes.Address{{
  1553  			Type: nodeaddressing.NodeInternalIP,
  1554  			IP:   ip1,
  1555  		}},
  1556  	}
  1557  	now := time.Now()
  1558  	err = linuxNodeHandler.NodeAdd(nodev1)
  1559  	require.NoError(t, err)
  1560  	// insertNeighbor is invoked async
  1561  	// Insert the same node second time. This should not increment refcount for
  1562  	// the same nextHop. We test it by checking that NodeDelete has removed the
  1563  	// related neigh entry.
  1564  	err = linuxNodeHandler.NodeAdd(nodev1)
  1565  	require.NoError(t, err)
  1566  	// insertNeighbor is invoked async, so thus this wait based on last ping
  1567  	wait(nodev1.Identity(), "veth0", &now, false)
  1568  
  1569  	// Check whether an arp entry for nodev1 IP addr (=veth1) was added
  1570  	assertNeigh(ip1, neighStateOk)
  1571  
  1572  	// Swap MAC addresses of veth0 and veth1 to ensure the MAC address of veth1 changed.
  1573  	// Trigger neighbor refresh on veth0 and check whether the arp entry was updated.
  1574  	var veth0HwAddr, veth1HwAddr, updatedHwAddrFromArpEntry net.HardwareAddr
  1575  	veth0HwAddr = veth0.Attrs().HardwareAddr
  1576  	ns.Do(func() error {
  1577  		veth1, err := netlink.LinkByName("veth1")
  1578  		require.NoError(t, err)
  1579  		veth1HwAddr = veth1.Attrs().HardwareAddr
  1580  		err = netlink.LinkSetHardwareAddr(veth1, veth0HwAddr)
  1581  		require.NoError(t, err)
  1582  		return nil
  1583  	})
  1584  
  1585  	now = time.Now()
  1586  	err = netlink.LinkSetHardwareAddr(veth0, veth1HwAddr)
  1587  	require.NoError(t, err)
  1588  
  1589  	linuxNodeHandler.NodeNeighborRefresh(context.TODO(), nodev1, true)
  1590  	wait(nodev1.Identity(), "veth0", &now, false)
  1591  
  1592  	assertNeigh(ip1, func(neigh netlink.Neigh) bool {
  1593  		if neighStateOk(neigh) {
  1594  			updatedHwAddrFromArpEntry = neigh.HardwareAddr
  1595  			return true
  1596  		}
  1597  		return false
  1598  	})
  1599  	require.Equal(t, veth0HwAddr.String(), updatedHwAddrFromArpEntry.String())
  1600  
  1601  	// Remove nodev1, and check whether the arp entry was removed
  1602  	err = linuxNodeHandler.NodeDelete(nodev1)
  1603  	require.NoError(t, err)
  1604  	// deleteNeighbor is invoked async too
  1605  	wait(nodev1.Identity(), "veth0", nil, true)
  1606  
  1607  	assertNoNeigh("expected removed neigh "+ip1.String(), ip1)
  1608  
  1609  	// Create multiple goroutines which call insertNeighbor and check whether
  1610  	// MAC changes of veth1 are properly handled. This is a basic randomized
  1611  	// testing of insertNeighbor() fine-grained locking.
  1612  	now = time.Now()
  1613  	err = linuxNodeHandler.NodeAdd(nodev1)
  1614  	require.NoError(t, err)
  1615  	wait(nodev1.Identity(), "veth0", &now, false)
  1616  
  1617  	rndHWAddr := func() net.HardwareAddr {
  1618  		mac := make([]byte, 6)
  1619  		_, err := rand.Read(mac)
  1620  		require.NoError(t, err)
  1621  		mac[0] = (mac[0] | 2) & 0xfe
  1622  		return net.HardwareAddr(mac)
  1623  	}
  1624  	neighRefCount := func(nextHopStr string) int {
  1625  		linuxNodeHandler.neighLock.Lock()
  1626  		defer linuxNodeHandler.neighLock.Unlock()
  1627  		return linuxNodeHandler.neighNextHopRefCount[nextHopStr]
  1628  	}
  1629  
  1630  	done := make(chan struct{})
  1631  	count := 30
  1632  	var wg sync.WaitGroup
  1633  	wg.Add(count)
  1634  	for i := 0; i < count; i++ {
  1635  		go func() {
  1636  			defer wg.Done()
  1637  			ticker := time.NewTicker(100 * time.Millisecond)
  1638  			for {
  1639  				linuxNodeHandler.insertNeighbor(context.Background(), &nodev1, true)
  1640  				select {
  1641  				case <-ticker.C:
  1642  				case <-done:
  1643  					return
  1644  				}
  1645  			}
  1646  		}()
  1647  	}
  1648  	for i := 0; i < 3; i++ {
  1649  		mac := rndHWAddr()
  1650  		// Change MAC
  1651  		ns.Do(func() error {
  1652  			veth1, err := netlink.LinkByName("veth1")
  1653  			require.NoError(t, err)
  1654  			err = netlink.LinkSetHardwareAddr(veth1, mac)
  1655  			require.NoError(t, err)
  1656  			return nil
  1657  		})
  1658  
  1659  		// Check that MAC has been changed in the neigh table
  1660  		var found bool
  1661  		err := testutils.WaitUntilWithSleep(func() bool {
  1662  			neighs, err := netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V6)
  1663  			require.NoError(t, err)
  1664  			found = false
  1665  			for _, n := range neighs {
  1666  				if n.IP.Equal(ip1) && (n.State&netlink.NUD_REACHABLE) > 0 &&
  1667  					n.HardwareAddr.String() == mac.String() &&
  1668  					neighRefCount(ip1.String()) == 1 {
  1669  					found = true
  1670  					return true
  1671  				}
  1672  			}
  1673  			return false
  1674  		}, 60*time.Second, 200*time.Millisecond)
  1675  		require.NoError(t, err)
  1676  		require.Equal(t, true, found)
  1677  	}
  1678  
  1679  	// Cleanup
  1680  	close(done)
  1681  	wg.Wait()
  1682  	now = time.Now()
  1683  	err = linuxNodeHandler.NodeDelete(nodev1)
  1684  	require.NoError(t, err)
  1685  	wait(nodev1.Identity(), "veth0", nil, true)
  1686  
  1687  	// Setup routine for the 2. test
  1688  	setupRemoteNode := func(vethName, vethPeerName, netnsName, vethCIDR, vethIPAddr,
  1689  		vethPeerIPAddr string) (cleanup func(), errRet error) {
  1690  
  1691  		veth := &netlink.Veth{
  1692  			LinkAttrs: netlink.LinkAttrs{Name: vethName},
  1693  			PeerName:  vethPeerName,
  1694  		}
  1695  		errRet = netlink.LinkAdd(veth)
  1696  		if errRet != nil {
  1697  			return nil, err
  1698  		}
  1699  		cleanup1 := func() { netlink.LinkDel(veth) }
  1700  		cleanup = cleanup1
  1701  
  1702  		veth2, err := netlink.LinkByName(vethName)
  1703  		if err != nil {
  1704  			errRet = err
  1705  			return
  1706  		}
  1707  		veth3, err := netlink.LinkByName(vethPeerName)
  1708  		if err != nil {
  1709  			errRet = err
  1710  			return
  1711  		}
  1712  		ns2 := netns.NewNetNS(t)
  1713  		cleanup = func() {
  1714  			cleanup1()
  1715  			ns2.Close()
  1716  		}
  1717  		if errRet = netlink.LinkSetNsFd(veth2, int(ns.FD())); errRet != nil {
  1718  			return
  1719  		}
  1720  		if errRet = netlink.LinkSetNsFd(veth3, int(ns2.FD())); errRet != nil {
  1721  			return
  1722  		}
  1723  
  1724  		ip, ipnet, err := net.ParseCIDR(vethCIDR)
  1725  		if err != nil {
  1726  			errRet = err
  1727  			return
  1728  		}
  1729  		ip2 := net.ParseIP(vethIPAddr)
  1730  		ip3 := net.ParseIP(vethPeerIPAddr)
  1731  		ipnet.IP = ip2
  1732  
  1733  		if errRet = ns.Do(func() error {
  1734  			addr = &netlink.Addr{IPNet: ipnet}
  1735  			if err := netlink.AddrAdd(veth2, addr); err != nil {
  1736  				return err
  1737  			}
  1738  			if err := netlink.LinkSetUp(veth2); err != nil {
  1739  				return err
  1740  			}
  1741  			if err := netlink.LinkSetUp(veth2); err != nil {
  1742  				return err
  1743  			}
  1744  			return nil
  1745  		}); errRet != nil {
  1746  			return
  1747  		}
  1748  
  1749  		ipnet.IP = ip
  1750  		route := &netlink.Route{
  1751  			Dst: ipnet,
  1752  			Gw:  ip1,
  1753  		}
  1754  		if errRet = netlink.RouteAdd(route); errRet != nil {
  1755  			return
  1756  		}
  1757  
  1758  		if errRet = ns2.Do(func() error {
  1759  			veth3, err := netlink.LinkByName(vethPeerName)
  1760  			if err != nil {
  1761  				return err
  1762  			}
  1763  			ipnet.IP = ip3
  1764  			addr = &netlink.Addr{IPNet: ipnet}
  1765  			if err := netlink.AddrAdd(veth3, addr); err != nil {
  1766  				return err
  1767  			}
  1768  			if err := netlink.LinkSetUp(veth3); err != nil {
  1769  				return err
  1770  			}
  1771  
  1772  			_, ipnet, err := net.ParseCIDR("f00d::/96")
  1773  			if err != nil {
  1774  				return err
  1775  			}
  1776  			route := &netlink.Route{
  1777  				Dst: ipnet,
  1778  				Gw:  ip2,
  1779  			}
  1780  			if err := netlink.RouteAdd(route); err != nil {
  1781  				return err
  1782  			}
  1783  			return nil
  1784  		}); errRet != nil {
  1785  			return
  1786  		}
  1787  
  1788  		return
  1789  	}
  1790  
  1791  	// 2. Add two nodes which are reachable from the host only via nodev1 (gw).
  1792  	//    Arping should ping veth1 IP addr instead of veth3 or veth5.
  1793  	//
  1794  	//      +--------------+     +--------------+     +--------------+
  1795  	//      |  host netns  |     |    netns0    |     |    netns1    |
  1796  	//      |              |     |    nodev1    |     |    nodev2    |
  1797  	//      |              |     |  f00a::249/96|     |              |
  1798  	//      |              |     |           |  |     |              |
  1799  	//      |         veth0+-----+veth1    veth2+-----+veth3         |
  1800  	//      |          |   |     |   |          |     | |            |
  1801  	//      | f00d::249/96 |     |f00d::250/96  |     | f00a::250/96 |
  1802  	//      +--------------+     |         veth4+-+   +--------------+
  1803  	//                           |           |  | |   +--------------+
  1804  	//                           | f00b::249/96 | |   |    netns2    |
  1805  	//                           +--------------+ |   |    nodev3    |
  1806  	//                                            |   |              |
  1807  	//                                            +---+veth5         |
  1808  	//                                                | |            |
  1809  	//                                                | f00b::250/96 |
  1810  	//                                                +--------------+
  1811  
  1812  	cleanup1, err := setupRemoteNode("veth2", "veth3", "test-arping-netns1",
  1813  		"f00a::/96", "f00a::249", "f00a::250")
  1814  	require.NoError(t, err)
  1815  	defer cleanup1()
  1816  	cleanup2, err := setupRemoteNode("veth4", "veth5", "test-arping-netns2",
  1817  		"f00b::/96", "f00b::249", "f00b::250")
  1818  	require.NoError(t, err)
  1819  	defer cleanup2()
  1820  
  1821  	node2IP := net.ParseIP("f00a::250")
  1822  	nodev2 := nodeTypes.Node{
  1823  		Name: "node2",
  1824  		IPAddresses: []nodeTypes.Address{{
  1825  			Type: nodeaddressing.NodeInternalIP,
  1826  			IP:   node2IP}},
  1827  	}
  1828  	now = time.Now()
  1829  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev2))
  1830  	wait(nodev2.Identity(), "veth0", &now, false)
  1831  
  1832  	node3IP := net.ParseIP("f00b::250")
  1833  	nodev3 := nodeTypes.Node{
  1834  		Name: "node3",
  1835  		IPAddresses: []nodeTypes.Address{{
  1836  			Type: nodeaddressing.NodeInternalIP,
  1837  			IP:   node3IP,
  1838  		}},
  1839  	}
  1840  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev3))
  1841  	wait(nodev3.Identity(), "veth0", &now, false)
  1842  
  1843  	nextHop := net.ParseIP("f00d::250")
  1844  	// Check that both node{2,3} are via nextHop (gw)
  1845  	assertNeigh(nextHop, neighStateOk)
  1846  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  1847  
  1848  	// Check that removing node2 will not remove nextHop, as it is still used by node3
  1849  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev2))
  1850  	wait(nodev2.Identity(), "veth0", nil, true)
  1851  
  1852  	assertNeigh(nextHop, func(n netlink.Neigh) bool { return true })
  1853  
  1854  	// However, removing node3 should remove the neigh entry for nextHop
  1855  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev3))
  1856  	wait(nodev3.Identity(), "veth0", nil, true)
  1857  
  1858  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  1859  
  1860  	now = time.Now()
  1861  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev3))
  1862  	wait(nodev3.Identity(), "veth0", &now, false)
  1863  
  1864  	nextHop = net.ParseIP("f00d::250")
  1865  	assertNeigh(nextHop, neighStateOk)
  1866  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  1867  
  1868  	// We have stored the devices in NodeConfigurationChanged
  1869  	linuxNodeHandler.NodeCleanNeighbors(false)
  1870  
  1871  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  1872  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  1873  
  1874  	// Setup routine for the 3. test
  1875  	setupNewGateway := func(vethCIDR, gwIP string) (errRet error) {
  1876  		ipGw := net.ParseIP(gwIP)
  1877  		ip, ipnet, err := net.ParseCIDR(vethCIDR)
  1878  		if err != nil {
  1879  			errRet = err
  1880  			return
  1881  		}
  1882  		ipnet.IP = ip
  1883  		route := &netlink.Route{
  1884  			Dst: ipnet,
  1885  			Gw:  ipGw,
  1886  		}
  1887  		errRet = netlink.RouteReplace(route)
  1888  		return
  1889  	}
  1890  
  1891  	// In the next test, we add node 2,3 again, and then change the nextHop
  1892  	// address to check the refcount behavior, and that the old one was
  1893  	// deleted from the neighbor table as well as the new one added.
  1894  	now = time.Now()
  1895  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev2))
  1896  	wait(nodev2.Identity(), "veth0", &now, false)
  1897  
  1898  	now = time.Now()
  1899  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev3))
  1900  	wait(nodev3.Identity(), "veth0", &now, false)
  1901  
  1902  	nextHop = net.ParseIP("f00d::250")
  1903  
  1904  	assertNeigh(nextHop, neighStateOk)
  1905  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  1906  
  1907  	// Switch to new nextHop address for node2
  1908  	err = setupNewGateway("f00a::/96", "f00d::251")
  1909  	require.NoError(t, err)
  1910  
  1911  	// waitGw waits for the nextHop to appear in the agent's nextHop table
  1912  	waitGw := func(nextHopNew string, nodeID nodeTypes.Identity, link string, before *time.Time) {
  1913  		t.Helper()
  1914  		err := testutils.WaitUntil(func() bool {
  1915  			linuxNodeHandler.neighLock.Lock()
  1916  			defer linuxNodeHandler.neighLock.Unlock()
  1917  			nextHopByLink, found := linuxNodeHandler.neighNextHopByNode6[nodeID]
  1918  			if !found {
  1919  				return false
  1920  			}
  1921  			nextHop, found := nextHopByLink[link]
  1922  			if !found {
  1923  				return false
  1924  			}
  1925  			if nextHop != nextHopNew {
  1926  				return false
  1927  			}
  1928  			lastPing, found := linuxNodeHandler.neighLastPingByNextHop[nextHop]
  1929  			if !found {
  1930  				return false
  1931  			}
  1932  			return before.Before(lastPing)
  1933  		}, 5*time.Second)
  1934  		require.NoError(t, err)
  1935  	}
  1936  
  1937  	// insertNeighbor is invoked async, so thus this wait based on last ping
  1938  	now = time.Now()
  1939  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev2, true)
  1940  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev3, true)
  1941  	waitGw("f00d::251", nodev2.Identity(), "veth0", &now)
  1942  	waitGw("f00d::250", nodev3.Identity(), "veth0", &now)
  1943  
  1944  	// Both nextHops now need to be present
  1945  	nextHop = net.ParseIP("f00d::250")
  1946  	assertNeigh(nextHop, neighStateOk)
  1947  	nextHop = net.ParseIP("f00d::251")
  1948  	assertNeigh(nextHop, neighStateOk)
  1949  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  1950  
  1951  	// Now also switch over the other node.
  1952  	err = setupNewGateway("f00b::/96", "f00d::251")
  1953  	require.NoError(t, err)
  1954  
  1955  	// insertNeighbor is invoked async, so thus this wait based on last ping
  1956  	now = time.Now()
  1957  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev2, true)
  1958  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev3, true)
  1959  	waitGw("f00d::251", nodev2.Identity(), "veth0", &now)
  1960  	waitGw("f00d::251", nodev3.Identity(), "veth0", &now)
  1961  
  1962  	nextHop = net.ParseIP("f00d::250")
  1963  
  1964  	// Check that old nextHop address got removed
  1965  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  1966  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  1967  
  1968  	nextHop = net.ParseIP("f00d::251")
  1969  	assertNeigh(nextHop, neighStateOk)
  1970  
  1971  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev3))
  1972  	wait(nodev3.Identity(), "veth0", nil, true)
  1973  
  1974  	// In the next test, we have node2 left in the neighbor table, and
  1975  	// we add an unrelated externally learned neighbor entry. Check that
  1976  	// NodeCleanNeighbors() removes the unrelated one. This is to simulate
  1977  	// the agent after kubeapi-server resync that it cleans up stale node
  1978  	// entries from previous runs.
  1979  
  1980  	nextHop = net.ParseIP("f00d::1")
  1981  	neigh := netlink.Neigh{
  1982  		LinkIndex: veth0.Attrs().Index,
  1983  		IP:        nextHop,
  1984  		State:     netlink.NUD_NONE,
  1985  		Flags:     netlink.NTF_EXT_LEARNED,
  1986  	}
  1987  	err = netlink.NeighSet(&neigh)
  1988  	require.NoError(t, err)
  1989  
  1990  	// Check that new nextHop address got added, we don't care about its NUD_* state
  1991  	assertNeigh(nextHop, func(neigh netlink.Neigh) bool { return true })
  1992  
  1993  	// Clean unrelated externally learned entries
  1994  	linuxNodeHandler.NodeCleanNeighborsLink(veth0, true)
  1995  
  1996  	// Check that new nextHop address got removed
  1997  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  1998  
  1999  	// Check that node2 nextHop address is still there
  2000  	nextHop = net.ParseIP("f00d::251")
  2001  	assertNeigh(nextHop, neighStateOk)
  2002  	assertNoNeigh("node2 should not be in the same L2", node2IP)
  2003  
  2004  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev2))
  2005  	wait(nodev2.Identity(), "veth0", nil, true)
  2006  
  2007  	linuxNodeHandler.NodeCleanNeighborsLink(veth0, false)
  2008  }
  2009  
  2010  func getDevice(tb testing.TB, name string) *tables.Device {
  2011  	link, err := netlink.LinkByName(name)
  2012  	require.NoError(tb, err, "LinkByName")
  2013  	return &tables.Device{Index: link.Attrs().Index, Name: name, Selected: true}
  2014  }
  2015  
  2016  func TestArpPingHandlingForMultiDeviceIPv6(t *testing.T) {
  2017  	s := setupLinuxPrivilegedIPv6OnlyTestSuite(t)
  2018  	runtime.LockOSThread()
  2019  	defer runtime.UnlockOSThread()
  2020  
  2021  	prevEnableL2NeighDiscovery := option.Config.EnableL2NeighDiscovery
  2022  	defer func() { option.Config.EnableL2NeighDiscovery = prevEnableL2NeighDiscovery }()
  2023  
  2024  	option.Config.EnableL2NeighDiscovery = true
  2025  
  2026  	prevStateDir := option.Config.StateDir
  2027  	defer func() { option.Config.StateDir = prevStateDir }()
  2028  
  2029  	tmpDir := t.TempDir()
  2030  	option.Config.StateDir = tmpDir
  2031  
  2032  	baseTimeOld, err := s.sysctl.Read(baseIPv6Time)
  2033  	require.NoError(t, err)
  2034  	err = s.sysctl.Write(baseIPv6Time, fmt.Sprintf("%d", baseTime))
  2035  	require.NoError(t, err)
  2036  	defer func() { s.sysctl.Write(baseIPv6Time, baseTimeOld) }()
  2037  
  2038  	mcastNumOld, err := s.sysctl.Read(mcastNumIPv6)
  2039  	require.NoError(t, err)
  2040  	err = s.sysctl.Write(mcastNumIPv6, fmt.Sprintf("%d", mcastNum))
  2041  	require.NoError(t, err)
  2042  	defer func() { s.sysctl.Write(mcastNumIPv6, mcastNumOld) }()
  2043  
  2044  	// 1. Test whether another node with multiple paths can be arpinged.
  2045  	//    Each node has two devices and the other node in the different netns
  2046  	//    is reachable via either pair.
  2047  	//    Neighbor entries are not installed on devices where no route exists
  2048  	//
  2049  	//      +--------------+     +-------------------+
  2050  	//      |  host netns  |     |      netns1       |
  2051  	//      |              |     |      nodev1       |
  2052  	//      |              |     |  fc00:c111::1/128 |
  2053  	//      |         veth0+-----+veth1              |
  2054  	//      |          |   |     |   |               |
  2055  	//      | f00a::249/96 |     | f00a::250/96      |
  2056  	//      |              |     |                   |
  2057  	//      |         veth2+-----+veth3              |
  2058  	//      |          |   |     | |                 |
  2059  	//      | f00b::249/96 |     | f00b::250/96      |
  2060  	//      |              |     |                   |
  2061  	//      | f00c::249/96 |     |                   |
  2062  	//      |  |           |     |                   |
  2063  	//      | veth4        |     |                   |
  2064  	//      +-+------------+     +-------------------+
  2065  	//        |
  2066  	//      +-+--------------------------------------+
  2067  	//      | veth5        other netns               |
  2068  	//      |  |                                     |
  2069  	//      | f00c::250/96                           |
  2070  	//      +----------------------------------------+
  2071  
  2072  	// Setup
  2073  	vethPair01 := &netlink.Veth{
  2074  		LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
  2075  		PeerName:  "veth1",
  2076  	}
  2077  	err = netlink.LinkAdd(vethPair01)
  2078  	require.NoError(t, err)
  2079  	t.Cleanup(func() { netlink.LinkDel(vethPair01) })
  2080  	veth0, err := netlink.LinkByName("veth0")
  2081  	require.NoError(t, err)
  2082  	veth1, err := netlink.LinkByName("veth1")
  2083  	require.NoError(t, err)
  2084  	_, ipnet, _ := net.ParseCIDR("f00a::/96")
  2085  	v1IP0 := net.ParseIP("f00a::249")
  2086  	v1IP1 := net.ParseIP("f00a::250")
  2087  	v1IPG := net.ParseIP("f00a::251")
  2088  	ipnet.IP = v1IP0
  2089  	addr := &netlink.Addr{IPNet: ipnet}
  2090  	err = netlink.AddrAdd(veth0, addr)
  2091  	require.NoError(t, err)
  2092  	err = netlink.LinkSetUp(veth0)
  2093  	require.NoError(t, err)
  2094  
  2095  	ns := netns.NewNetNS(t)
  2096  	err = netlink.LinkSetNsFd(veth1, int(ns.FD()))
  2097  	require.NoError(t, err)
  2098  	node1Addr, err := netlink.ParseAddr("fc00:c111::1/128")
  2099  	require.NoError(t, err)
  2100  	ns.Do(func() error {
  2101  		lo, err := netlink.LinkByName("lo")
  2102  		require.NoError(t, err)
  2103  		err = netlink.LinkSetUp(lo)
  2104  		require.NoError(t, err)
  2105  		err = netlink.AddrAdd(lo, node1Addr)
  2106  		require.NoError(t, err)
  2107  
  2108  		veth1, err := netlink.LinkByName("veth1")
  2109  		require.NoError(t, err)
  2110  		ipnet.IP = v1IP1
  2111  		addr = &netlink.Addr{IPNet: ipnet}
  2112  		err = netlink.AddrAdd(veth1, addr)
  2113  		require.NoError(t, err)
  2114  		ipnet.IP = v1IPG
  2115  		addr = &netlink.Addr{IPNet: ipnet}
  2116  		err = netlink.AddrAdd(veth1, addr)
  2117  		require.NoError(t, err)
  2118  		err = netlink.LinkSetUp(veth1)
  2119  		require.NoError(t, err)
  2120  		return nil
  2121  	})
  2122  
  2123  	vethPair23 := &netlink.Veth{
  2124  		LinkAttrs: netlink.LinkAttrs{Name: "veth2"},
  2125  		PeerName:  "veth3",
  2126  	}
  2127  	err = netlink.LinkAdd(vethPair23)
  2128  	require.NoError(t, err)
  2129  	t.Cleanup(func() { netlink.LinkDel(vethPair23) })
  2130  	veth2, err := netlink.LinkByName("veth2")
  2131  	require.NoError(t, err)
  2132  	veth3, err := netlink.LinkByName("veth3")
  2133  	require.NoError(t, err)
  2134  	_, ipnet, _ = net.ParseCIDR("f00b::/96")
  2135  	v2IP0 := net.ParseIP("f00b::249")
  2136  	v2IP1 := net.ParseIP("f00b::250")
  2137  	v2IPG := net.ParseIP("f00b::251")
  2138  	ipnet.IP = v2IP0
  2139  	addr = &netlink.Addr{IPNet: ipnet}
  2140  	err = netlink.AddrAdd(veth2, addr)
  2141  	require.NoError(t, err)
  2142  	err = netlink.LinkSetUp(veth2)
  2143  	require.NoError(t, err)
  2144  
  2145  	err = netlink.LinkSetNsFd(veth3, int(ns.FD()))
  2146  	require.NoError(t, err)
  2147  	err = ns.Do(func() error {
  2148  		veth3, err := netlink.LinkByName("veth3")
  2149  		require.NoError(t, err)
  2150  		ipnet.IP = v2IP1
  2151  		addr = &netlink.Addr{IPNet: ipnet}
  2152  		err = netlink.AddrAdd(veth3, addr)
  2153  		require.NoError(t, err)
  2154  		ipnet.IP = v2IPG
  2155  		addr = &netlink.Addr{IPNet: ipnet}
  2156  		err = netlink.AddrAdd(veth3, addr)
  2157  		require.NoError(t, err)
  2158  		err = netlink.LinkSetUp(veth3)
  2159  		require.NoError(t, err)
  2160  		return nil
  2161  	})
  2162  	require.NoError(t, err)
  2163  
  2164  	r := &netlink.Route{
  2165  		Dst: netlink.NewIPNet(node1Addr.IP),
  2166  		MultiPath: []*netlink.NexthopInfo{
  2167  			{
  2168  				LinkIndex: veth0.Attrs().Index,
  2169  				Gw:        v1IP1,
  2170  			},
  2171  			{
  2172  				LinkIndex: veth2.Attrs().Index,
  2173  				Gw:        v2IP1,
  2174  			},
  2175  		}}
  2176  
  2177  	err = netlink.RouteAdd(r)
  2178  	require.NoError(t, err)
  2179  	defer netlink.RouteDel(r)
  2180  
  2181  	// Setup another veth pair that doesn't have a route to node
  2182  	vethPair45 := &netlink.Veth{
  2183  		LinkAttrs: netlink.LinkAttrs{Name: "veth4"},
  2184  		PeerName:  "veth5",
  2185  	}
  2186  	err = netlink.LinkAdd(vethPair45)
  2187  	require.NoError(t, err)
  2188  	t.Cleanup(func() { netlink.LinkDel(vethPair45) })
  2189  	veth4, err := netlink.LinkByName("veth4")
  2190  	require.NoError(t, err)
  2191  	veth5, err := netlink.LinkByName("veth5")
  2192  	require.NoError(t, err)
  2193  	_, ipnet, _ = net.ParseCIDR("f00c::/96")
  2194  	v3IP0 := net.ParseIP("f00c::249")
  2195  	v3IP1 := net.ParseIP("f00c::250")
  2196  	ipnet.IP = v3IP0
  2197  	addr = &netlink.Addr{IPNet: ipnet}
  2198  	err = netlink.AddrAdd(veth4, addr)
  2199  	require.NoError(t, err)
  2200  	err = netlink.LinkSetUp(veth4)
  2201  	require.NoError(t, err)
  2202  
  2203  	ns2 := netns.NewNetNS(t)
  2204  
  2205  	err = netlink.LinkSetNsFd(veth5, int(ns2.FD()))
  2206  	require.NoError(t, err)
  2207  	err = ns2.Do(func() error {
  2208  		veth5, err := netlink.LinkByName("veth5")
  2209  		require.NoError(t, err)
  2210  		ipnet.IP = v3IP1
  2211  		addr = &netlink.Addr{IPNet: ipnet}
  2212  		err = netlink.AddrAdd(veth5, addr)
  2213  		require.NoError(t, err)
  2214  		err = netlink.LinkSetUp(veth5)
  2215  		require.NoError(t, err)
  2216  		return nil
  2217  	})
  2218  	require.NoError(t, err)
  2219  
  2220  	prevRoutingMode := option.Config.RoutingMode
  2221  	defer func() { option.Config.RoutingMode = prevRoutingMode }()
  2222  	option.Config.RoutingMode = option.RoutingModeNative
  2223  	prevDRDev := option.Config.DirectRoutingDevice
  2224  	defer func() { option.Config.DirectRoutingDevice = prevDRDev }()
  2225  	option.Config.DirectRoutingDevice = "veth0"
  2226  	prevNP := option.Config.EnableNodePort
  2227  	defer func() { option.Config.EnableNodePort = prevNP }()
  2228  	option.Config.EnableNodePort = true
  2229  	prevARPPeriod := option.Config.ARPPingRefreshPeriod
  2230  	defer func() { option.Config.ARPPingRefreshPeriod = prevARPPeriod }()
  2231  	option.Config.ARPPingRefreshPeriod = 1 * time.Nanosecond
  2232  
  2233  	mq := new(mockEnqueuer)
  2234  	dpConfig := DatapathConfiguration{HostDevice: "veth0"}
  2235  	log := hivetest.Logger(t)
  2236  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), mq)
  2237  	mq.nh = linuxNodeHandler
  2238  
  2239  	nodeConfig := s.nodeConfigTemplate
  2240  	nodeConfig.EnableEncapsulation = false
  2241  	nodeConfig.Devices = append(slices.Clone(nodeConfig.Devices),
  2242  		getDevice(t, "veth0"),
  2243  		getDevice(t, "veth2"),
  2244  		getDevice(t, "veth4"))
  2245  
  2246  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  2247  	require.NoError(t, err)
  2248  
  2249  	// wait waits for neigh entry update or waits for removal if waitForDelete=true
  2250  	wait := func(nodeID nodeTypes.Identity, link string, before *time.Time, waitForDelete bool) {
  2251  		t.Helper()
  2252  		err := testutils.WaitUntil(func() bool {
  2253  			linuxNodeHandler.neighLock.Lock()
  2254  			defer linuxNodeHandler.neighLock.Unlock()
  2255  			nextHopByLink, found := linuxNodeHandler.neighNextHopByNode6[nodeID]
  2256  			if !found {
  2257  				return waitForDelete
  2258  			}
  2259  			nextHop, found := nextHopByLink[link]
  2260  			if !found {
  2261  				return waitForDelete
  2262  			}
  2263  			lastPing, found := linuxNodeHandler.neighLastPingByNextHop[nextHop]
  2264  			if !found {
  2265  				return false
  2266  			}
  2267  			if waitForDelete {
  2268  				return false
  2269  			}
  2270  			return before.Before(lastPing)
  2271  		}, 5*time.Second)
  2272  		require.NoError(t, err)
  2273  	}
  2274  
  2275  	assertNeigh := func(ip net.IP, link netlink.Link, checkNeigh func(neigh netlink.Neigh) bool) {
  2276  		t.Helper()
  2277  		err := testutils.WaitUntil(func() bool {
  2278  			neighs, err := netlink.NeighList(link.Attrs().Index, netlink.FAMILY_V6)
  2279  			require.NoError(t, err)
  2280  			for _, n := range neighs {
  2281  				if n.IP.Equal(ip) && checkNeigh(n) {
  2282  					return true
  2283  				}
  2284  			}
  2285  			return false
  2286  		}, 5*time.Second)
  2287  		require.NoError(t, err, "expected neighbor %s", ip)
  2288  	}
  2289  
  2290  	assertNoNeigh := func(link netlink.Link, ips ...net.IP) {
  2291  		t.Helper()
  2292  		err := testutils.WaitUntil(func() bool {
  2293  			neighs, err := netlink.NeighList(link.Attrs().Index, netlink.FAMILY_V6)
  2294  			require.NoError(t, err)
  2295  			for _, n := range neighs {
  2296  				for _, ip := range ips {
  2297  					if n.IP.Equal(ip) {
  2298  						return false
  2299  					}
  2300  				}
  2301  			}
  2302  			return true
  2303  		}, 5*time.Second)
  2304  		require.NoError(t, err, "expected no neighbors: %v", ips)
  2305  	}
  2306  
  2307  	nodev1 := nodeTypes.Node{
  2308  		Name: "node1",
  2309  		IPAddresses: []nodeTypes.Address{{
  2310  			Type: nodeaddressing.NodeInternalIP,
  2311  			IP:   node1Addr.IP,
  2312  		}},
  2313  	}
  2314  	now := time.Now()
  2315  	err = linuxNodeHandler.NodeAdd(nodev1)
  2316  	require.NoError(t, err)
  2317  	// insertNeighbor is invoked async
  2318  	// Insert the same node second time. This should not increment refcount for
  2319  	// the same nextHop. We test it by checking that NodeDelete has removed the
  2320  	// related neigh entry.
  2321  	err = linuxNodeHandler.NodeAdd(nodev1)
  2322  	require.NoError(t, err)
  2323  	// insertNeighbor is invoked async, so thus this wait based on last ping
  2324  	wait(nodev1.Identity(), "veth0", &now, false)
  2325  	wait(nodev1.Identity(), "veth2", &now, false)
  2326  
  2327  	assertNeigh(v1IP1, veth0, neighStateOk)
  2328  	assertNeigh(v2IP1, veth2, neighStateOk)
  2329  
  2330  	// Check whether we don't install the neighbor entries to nodes on the device where the actual route isn't.
  2331  	// "Consistently(<check>, 5sec, 1sec)"
  2332  	start := time.Now()
  2333  	for {
  2334  		if time.Since(start) > 5*time.Second {
  2335  			break
  2336  		}
  2337  
  2338  		neighs, err := netlink.NeighList(veth4.Attrs().Index, netlink.FAMILY_V6)
  2339  		require.NoError(t, err)
  2340  		found := false
  2341  		for _, n := range neighs {
  2342  			if n.IP.Equal(v3IP1) || n.IP.Equal(node1Addr.IP) {
  2343  				found = true
  2344  			}
  2345  		}
  2346  		require.Equal(t, false, found)
  2347  
  2348  		time.Sleep(1 * time.Second)
  2349  	}
  2350  
  2351  	// Swap MAC addresses of veth0 and veth1, veth2 and veth3 to ensure the MAC address of veth1 changed.
  2352  	// Trigger neighbor refresh on veth0 and check whether the arp entry was updated.
  2353  	var veth0HwAddr, veth1HwAddr, veth2HwAddr, veth3HwAddr, updatedHwAddrFromArpEntry net.HardwareAddr
  2354  	veth0HwAddr = veth0.Attrs().HardwareAddr
  2355  	veth2HwAddr = veth2.Attrs().HardwareAddr
  2356  	err = ns.Do(func() error {
  2357  		veth1, err := netlink.LinkByName("veth1")
  2358  		require.NoError(t, err)
  2359  		veth1HwAddr = veth1.Attrs().HardwareAddr
  2360  		err = netlink.LinkSetHardwareAddr(veth1, veth0HwAddr)
  2361  		require.NoError(t, err)
  2362  
  2363  		veth3, err := netlink.LinkByName("veth3")
  2364  		require.NoError(t, err)
  2365  		veth3HwAddr = veth3.Attrs().HardwareAddr
  2366  		err = netlink.LinkSetHardwareAddr(veth3, veth2HwAddr)
  2367  		require.NoError(t, err)
  2368  		return nil
  2369  	})
  2370  	require.NoError(t, err)
  2371  
  2372  	now = time.Now()
  2373  	err = netlink.LinkSetHardwareAddr(veth0, veth1HwAddr)
  2374  	require.NoError(t, err)
  2375  	err = netlink.LinkSetHardwareAddr(veth2, veth3HwAddr)
  2376  	require.NoError(t, err)
  2377  
  2378  	linuxNodeHandler.NodeNeighborRefresh(context.TODO(), nodev1, true)
  2379  	wait(nodev1.Identity(), "veth0", &now, false)
  2380  	wait(nodev1.Identity(), "veth2", &now, false)
  2381  
  2382  	assertNeigh(v1IP1, veth0,
  2383  		func(neigh netlink.Neigh) bool {
  2384  			if neighStateOk(neigh) {
  2385  				updatedHwAddrFromArpEntry = neigh.HardwareAddr
  2386  				return true
  2387  			}
  2388  			return false
  2389  		})
  2390  
  2391  	require.Equal(t, veth0HwAddr.String(), updatedHwAddrFromArpEntry.String())
  2392  
  2393  	assertNeigh(v2IP1, veth2,
  2394  		func(neigh netlink.Neigh) bool {
  2395  			if neighStateOk(neigh) {
  2396  				updatedHwAddrFromArpEntry = neigh.HardwareAddr
  2397  				return true
  2398  			}
  2399  			return false
  2400  		})
  2401  
  2402  	require.Equal(t, veth2HwAddr.String(), updatedHwAddrFromArpEntry.String())
  2403  
  2404  	// Remove nodev1, and check whether the arp entry was removed
  2405  	err = linuxNodeHandler.NodeDelete(nodev1)
  2406  	require.NoError(t, err)
  2407  	// deleteNeighbor is invoked async too
  2408  	wait(nodev1.Identity(), "veth0", nil, true)
  2409  	wait(nodev1.Identity(), "veth2", nil, true)
  2410  
  2411  	assertNoNeigh(veth0, v1IP1)
  2412  	assertNoNeigh(veth2, v2IP1)
  2413  }
  2414  
  2415  func TestArpPingHandlingIPv4(t *testing.T) {
  2416  	s := setupLinuxPrivilegedIPv4OnlyTestSuite(t)
  2417  	runtime.LockOSThread()
  2418  	defer runtime.UnlockOSThread()
  2419  
  2420  	prevEnableL2NeighDiscovery := option.Config.EnableL2NeighDiscovery
  2421  	defer func() { option.Config.EnableL2NeighDiscovery = prevEnableL2NeighDiscovery }()
  2422  
  2423  	option.Config.EnableL2NeighDiscovery = true
  2424  
  2425  	prevStateDir := option.Config.StateDir
  2426  	defer func() { option.Config.StateDir = prevStateDir }()
  2427  
  2428  	tmpDir := t.TempDir()
  2429  	option.Config.StateDir = tmpDir
  2430  
  2431  	baseTimeOld, err := s.sysctl.Read(baseIPv4Time)
  2432  	require.NoError(t, err)
  2433  	err = s.sysctl.Write(baseIPv4Time, fmt.Sprintf("%d", baseTime))
  2434  	require.NoError(t, err)
  2435  	defer func() { s.sysctl.Write(baseIPv4Time, baseTimeOld) }()
  2436  
  2437  	mcastNumOld, err := s.sysctl.Read(mcastNumIPv4)
  2438  	require.NoError(t, err)
  2439  	err = s.sysctl.Write(mcastNumIPv4, fmt.Sprintf("%d", mcastNum))
  2440  	require.NoError(t, err)
  2441  	defer func() { s.sysctl.Write(mcastNumIPv4, mcastNumOld) }()
  2442  
  2443  	// 1. Test whether another node in the same L2 subnet can be arpinged.
  2444  	//    The other node is in the different netns reachable via the veth pair.
  2445  	//
  2446  	//      +--------------+     +--------------+
  2447  	//      |  host netns  |     |    netns0    |
  2448  	//      |              |     |    nodev1    |
  2449  	//      |         veth0+-----+veth1         |
  2450  	//      | 9.9.9.249/29 |     | 9.9.9.250/29 |
  2451  	//      +--------------+     +--------------+
  2452  
  2453  	// Setup
  2454  	veth := &netlink.Veth{
  2455  		LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
  2456  		PeerName:  "veth1",
  2457  	}
  2458  	err = netlink.LinkAdd(veth)
  2459  	require.NoError(t, err)
  2460  	t.Cleanup(func() { netlink.LinkDel(veth) })
  2461  	veth0, err := netlink.LinkByName("veth0")
  2462  	require.NoError(t, err)
  2463  	veth1, err := netlink.LinkByName("veth1")
  2464  	require.NoError(t, err)
  2465  	_, ipnet, _ := net.ParseCIDR("9.9.9.252/29")
  2466  	ip0 := net.ParseIP("9.9.9.249")
  2467  	ip1 := net.ParseIP("9.9.9.250")
  2468  	ipG := net.ParseIP("9.9.9.251")
  2469  	ipnet.IP = ip0
  2470  	addr := &netlink.Addr{IPNet: ipnet}
  2471  	err = netlink.AddrAdd(veth0, addr)
  2472  	require.NoError(t, err)
  2473  	err = netlink.LinkSetUp(veth0)
  2474  	require.NoError(t, err)
  2475  
  2476  	ns := netns.NewNetNS(t)
  2477  
  2478  	err = netlink.LinkSetNsFd(veth1, int(ns.FD()))
  2479  	require.NoError(t, err)
  2480  	ns.Do(func() error {
  2481  		veth1, err := netlink.LinkByName("veth1")
  2482  		require.NoError(t, err)
  2483  		ipnet.IP = ip1
  2484  		addr = &netlink.Addr{IPNet: ipnet}
  2485  		netlink.AddrAdd(veth1, addr)
  2486  		require.NoError(t, err)
  2487  		ipnet.IP = ipG
  2488  		addr = &netlink.Addr{IPNet: ipnet}
  2489  		netlink.AddrAdd(veth1, addr)
  2490  		require.NoError(t, err)
  2491  		err = netlink.LinkSetUp(veth1)
  2492  		require.NoError(t, err)
  2493  		return nil
  2494  	})
  2495  
  2496  	prevRoutingMode := option.Config.RoutingMode
  2497  	defer func() { option.Config.RoutingMode = prevRoutingMode }()
  2498  	option.Config.RoutingMode = option.RoutingModeNative
  2499  	prevDRDev := option.Config.DirectRoutingDevice
  2500  	defer func() { option.Config.DirectRoutingDevice = prevDRDev }()
  2501  	option.Config.DirectRoutingDevice = "veth0"
  2502  	prevNP := option.Config.EnableNodePort
  2503  	defer func() { option.Config.EnableNodePort = prevNP }()
  2504  	option.Config.EnableNodePort = true
  2505  	prevARPPeriod := option.Config.ARPPingRefreshPeriod
  2506  	defer func() { option.Config.ARPPingRefreshPeriod = prevARPPeriod }()
  2507  	option.Config.ARPPingRefreshPeriod = time.Duration(1 * time.Nanosecond)
  2508  
  2509  	mq := new(mockEnqueuer)
  2510  	dpConfig := DatapathConfiguration{HostDevice: "veth0"}
  2511  	log := hivetest.Logger(t)
  2512  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), mq)
  2513  	mq.nh = linuxNodeHandler
  2514  
  2515  	nodeConfig := s.nodeConfigTemplate
  2516  	nodeConfig.Devices = []*tables.Device{
  2517  		{Index: veth0.Attrs().Index, Name: "veth0", Selected: true},
  2518  	}
  2519  	nodeConfig.EnableEncapsulation = false
  2520  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  2521  	require.NoError(t, err)
  2522  
  2523  	// wait waits for neigh entry update or waits for removal if waitForDelete=true
  2524  	wait := func(nodeID nodeTypes.Identity, link string, before *time.Time, waitForDelete bool) {
  2525  		t.Helper()
  2526  		err := testutils.WaitUntil(func() bool {
  2527  			linuxNodeHandler.neighLock.Lock()
  2528  			defer linuxNodeHandler.neighLock.Unlock()
  2529  			nextHopByLink, found := linuxNodeHandler.neighNextHopByNode4[nodeID]
  2530  			if !found {
  2531  				return waitForDelete
  2532  			}
  2533  			nextHop, found := nextHopByLink[link]
  2534  			if !found {
  2535  				return waitForDelete
  2536  			}
  2537  			lastPing, found := linuxNodeHandler.neighLastPingByNextHop[nextHop]
  2538  			if !found {
  2539  				return false
  2540  			}
  2541  			if waitForDelete {
  2542  				return false
  2543  			}
  2544  			return before.Before(lastPing)
  2545  		}, 5*time.Second)
  2546  		require.NoError(t, err)
  2547  	}
  2548  
  2549  	assertNeigh := func(ip net.IP, checkNeigh func(neigh netlink.Neigh) bool) {
  2550  		t.Helper()
  2551  		err := testutils.WaitUntil(func() bool {
  2552  			neighs, err := netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V4)
  2553  			require.NoError(t, err)
  2554  			for _, n := range neighs {
  2555  				if n.IP.Equal(ip) && checkNeigh(n) {
  2556  					return true
  2557  				}
  2558  			}
  2559  			return false
  2560  		}, 5*time.Second)
  2561  		require.NoError(t, err, "expected neighbor %s", ip)
  2562  	}
  2563  
  2564  	assertNoNeigh := func(msg string, ips ...net.IP) {
  2565  		t.Helper()
  2566  		err := testutils.WaitUntil(func() bool {
  2567  			neighs, err := netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V4)
  2568  			require.NoError(t, err)
  2569  			for _, n := range neighs {
  2570  				for _, ip := range ips {
  2571  					if n.IP.Equal(ip) {
  2572  						return false
  2573  					}
  2574  				}
  2575  			}
  2576  			return true
  2577  		}, 5*time.Second)
  2578  		require.NoError(t, err, msg)
  2579  	}
  2580  
  2581  	nodev1 := nodeTypes.Node{
  2582  		Name: "node1",
  2583  		IPAddresses: []nodeTypes.Address{{
  2584  			Type: nodeaddressing.NodeInternalIP,
  2585  			IP:   ip1,
  2586  		}},
  2587  	}
  2588  	now := time.Now()
  2589  	err = linuxNodeHandler.NodeAdd(nodev1)
  2590  	require.NoError(t, err)
  2591  	// insertNeighbor is invoked async
  2592  	// Insert the same node second time. This should not increment refcount for
  2593  	// the same nextHop. We test it by checking that NodeDelete has removed the
  2594  	// related neigh entry.
  2595  	err = linuxNodeHandler.NodeAdd(nodev1)
  2596  	require.NoError(t, err)
  2597  	// insertNeighbor is invoked async, so thus this wait based on last ping
  2598  	wait(nodev1.Identity(), "veth0", &now, false)
  2599  
  2600  	assertNeigh(ip1, neighStateOk)
  2601  
  2602  	// Swap MAC addresses of veth0 and veth1 to ensure the MAC address of veth1 changed.
  2603  	// Trigger neighbor refresh on veth0 and check whether the arp entry was updated.
  2604  	var veth0HwAddr, veth1HwAddr, updatedHwAddrFromArpEntry net.HardwareAddr
  2605  	veth0HwAddr = veth0.Attrs().HardwareAddr
  2606  	ns.Do(func() error {
  2607  		veth1, err := netlink.LinkByName("veth1")
  2608  		require.NoError(t, err)
  2609  		veth1HwAddr = veth1.Attrs().HardwareAddr
  2610  		err = netlink.LinkSetHardwareAddr(veth1, veth0HwAddr)
  2611  		require.NoError(t, err)
  2612  		return nil
  2613  	})
  2614  
  2615  	now = time.Now()
  2616  	err = netlink.LinkSetHardwareAddr(veth0, veth1HwAddr)
  2617  	require.NoError(t, err)
  2618  
  2619  	linuxNodeHandler.NodeNeighborRefresh(context.TODO(), nodev1, true)
  2620  	wait(nodev1.Identity(), "veth0", &now, false)
  2621  
  2622  	assertNeigh(ip1,
  2623  		func(neigh netlink.Neigh) bool {
  2624  			if neighStateOk(neigh) {
  2625  				updatedHwAddrFromArpEntry = neigh.HardwareAddr
  2626  				return true
  2627  			}
  2628  			return false
  2629  		})
  2630  
  2631  	require.Equal(t, veth0HwAddr.String(), updatedHwAddrFromArpEntry.String())
  2632  
  2633  	// Remove nodev1, and check whether the arp entry was removed
  2634  	err = linuxNodeHandler.NodeDelete(nodev1)
  2635  	require.NoError(t, err)
  2636  	// deleteNeighbor is invoked async too
  2637  	wait(nodev1.Identity(), "veth0", nil, true)
  2638  
  2639  	assertNoNeigh("expected removed neigh "+ip1.String(), ip1)
  2640  
  2641  	// Create multiple goroutines which call insertNeighbor and check whether
  2642  	// MAC changes of veth1 are properly handled. This is a basic randomized
  2643  	// testing of insertNeighbor() fine-grained locking.
  2644  	now = time.Now()
  2645  	err = linuxNodeHandler.NodeAdd(nodev1)
  2646  	require.NoError(t, err)
  2647  	wait(nodev1.Identity(), "veth0", &now, false)
  2648  
  2649  	rndHWAddr := func() net.HardwareAddr {
  2650  		mac := make([]byte, 6)
  2651  		_, err := rand.Read(mac)
  2652  		require.NoError(t, err)
  2653  		mac[0] = (mac[0] | 2) & 0xfe
  2654  		return net.HardwareAddr(mac)
  2655  	}
  2656  	neighRefCount := func(nextHopStr string) int {
  2657  		linuxNodeHandler.neighLock.Lock()
  2658  		defer linuxNodeHandler.neighLock.Unlock()
  2659  		return linuxNodeHandler.neighNextHopRefCount[nextHopStr]
  2660  	}
  2661  
  2662  	done := make(chan struct{})
  2663  	count := 30
  2664  	var wg sync.WaitGroup
  2665  	wg.Add(count)
  2666  	for i := 0; i < count; i++ {
  2667  		go func() {
  2668  			defer wg.Done()
  2669  			ticker := time.NewTicker(100 * time.Millisecond)
  2670  			for {
  2671  				linuxNodeHandler.insertNeighbor(context.Background(), &nodev1, true)
  2672  				select {
  2673  				case <-ticker.C:
  2674  				case <-done:
  2675  					return
  2676  				}
  2677  			}
  2678  		}()
  2679  	}
  2680  	for i := 0; i < 3; i++ {
  2681  		mac := rndHWAddr()
  2682  		// Change MAC
  2683  		ns.Do(func() error {
  2684  			veth1, err := netlink.LinkByName("veth1")
  2685  			require.NoError(t, err)
  2686  			err = netlink.LinkSetHardwareAddr(veth1, mac)
  2687  			require.NoError(t, err)
  2688  			return nil
  2689  		})
  2690  
  2691  		// Check that MAC has been changed in the neigh table
  2692  		var found bool
  2693  		err := testutils.WaitUntilWithSleep(func() bool {
  2694  			neighs, err := netlink.NeighList(veth0.Attrs().Index, netlink.FAMILY_V4)
  2695  			require.NoError(t, err)
  2696  			found = false
  2697  			for _, n := range neighs {
  2698  				if n.IP.Equal(ip1) && (n.State&netlink.NUD_REACHABLE) > 0 &&
  2699  					n.HardwareAddr.String() == mac.String() &&
  2700  					neighRefCount(ip1.String()) == 1 {
  2701  					found = true
  2702  					return true
  2703  				}
  2704  			}
  2705  			return false
  2706  		}, 60*time.Second, 200*time.Millisecond)
  2707  		require.NoError(t, err)
  2708  		require.Equal(t, true, found)
  2709  	}
  2710  
  2711  	// Cleanup
  2712  	close(done)
  2713  	wg.Wait()
  2714  	now = time.Now()
  2715  	err = linuxNodeHandler.NodeDelete(nodev1)
  2716  	require.NoError(t, err)
  2717  	wait(nodev1.Identity(), "veth0", nil, true)
  2718  
  2719  	// Setup routine for the 2. test
  2720  	setupRemoteNode := func(vethName, vethPeerName, netnsName, vethCIDR, vethIPAddr,
  2721  		vethPeerIPAddr string) (cleanup func(), errRet error) {
  2722  
  2723  		veth := &netlink.Veth{
  2724  			LinkAttrs: netlink.LinkAttrs{Name: vethName},
  2725  			PeerName:  vethPeerName,
  2726  		}
  2727  		errRet = netlink.LinkAdd(veth)
  2728  		if errRet != nil {
  2729  			return nil, err
  2730  		}
  2731  		cleanup1 := func() { netlink.LinkDel(veth) }
  2732  		cleanup = cleanup1
  2733  
  2734  		veth2, err := netlink.LinkByName(vethName)
  2735  		if err != nil {
  2736  			errRet = err
  2737  			return
  2738  		}
  2739  		veth3, err := netlink.LinkByName(vethPeerName)
  2740  		if err != nil {
  2741  			errRet = err
  2742  			return
  2743  		}
  2744  		ns2 := netns.NewNetNS(t)
  2745  		cleanup = func() {
  2746  			cleanup1()
  2747  			ns2.Close()
  2748  		}
  2749  		if errRet = netlink.LinkSetNsFd(veth2, int(ns.FD())); errRet != nil {
  2750  			return
  2751  		}
  2752  		if errRet = netlink.LinkSetNsFd(veth3, int(ns2.FD())); errRet != nil {
  2753  			return
  2754  		}
  2755  
  2756  		ip, ipnet, err := net.ParseCIDR(vethCIDR)
  2757  		if err != nil {
  2758  			errRet = err
  2759  			return
  2760  		}
  2761  		ip2 := net.ParseIP(vethIPAddr)
  2762  		ip3 := net.ParseIP(vethPeerIPAddr)
  2763  		ipnet.IP = ip2
  2764  
  2765  		if errRet = ns.Do(func() error {
  2766  			addr = &netlink.Addr{IPNet: ipnet}
  2767  			if err := netlink.AddrAdd(veth2, addr); err != nil {
  2768  				return err
  2769  			}
  2770  			if err := netlink.LinkSetUp(veth2); err != nil {
  2771  				return err
  2772  			}
  2773  			if err := netlink.LinkSetUp(veth2); err != nil {
  2774  				return err
  2775  			}
  2776  			return nil
  2777  		}); errRet != nil {
  2778  			return
  2779  		}
  2780  
  2781  		ipnet.IP = ip
  2782  		route := &netlink.Route{
  2783  			Dst: ipnet,
  2784  			Gw:  ip1,
  2785  		}
  2786  		if errRet = netlink.RouteAdd(route); errRet != nil {
  2787  			return
  2788  		}
  2789  
  2790  		if errRet = ns2.Do(func() error {
  2791  			veth3, err := netlink.LinkByName(vethPeerName)
  2792  			if err != nil {
  2793  				return err
  2794  			}
  2795  			ipnet.IP = ip3
  2796  			addr = &netlink.Addr{IPNet: ipnet}
  2797  			if err := netlink.AddrAdd(veth3, addr); err != nil {
  2798  				return err
  2799  			}
  2800  			if err := netlink.LinkSetUp(veth3); err != nil {
  2801  				return err
  2802  			}
  2803  
  2804  			_, ipnet, err := net.ParseCIDR("9.9.9.248/29")
  2805  			if err != nil {
  2806  				return err
  2807  			}
  2808  			route := &netlink.Route{
  2809  				Dst: ipnet,
  2810  				Gw:  ip2,
  2811  			}
  2812  			if err := netlink.RouteAdd(route); err != nil {
  2813  				return err
  2814  			}
  2815  			return nil
  2816  		}); errRet != nil {
  2817  			return
  2818  		}
  2819  
  2820  		return
  2821  	}
  2822  
  2823  	// 2. Add two nodes which are reachable from the host only via nodev1 (gw).
  2824  	//    Arping should ping veth1 IP addr instead of veth3 or veth5.
  2825  	//
  2826  	//      +--------------+     +--------------+     +--------------+
  2827  	//      |  host netns  |     |    netns0    |     |    netns1    |
  2828  	//      |              |     |    nodev1    |     |    nodev2    |
  2829  	//      |              |     |  8.8.8.249/29|     |              |
  2830  	//      |              |     |           |  |     |              |
  2831  	//      |         veth0+-----+veth1    veth2+-----+veth3         |
  2832  	//      |          |   |     |   |          |     | |            |
  2833  	//      | 9.9.9.249/29 |     |9.9.9.250/29  |     | 8.8.8.250/29 |
  2834  	//      +--------------+     |         veth4+-+   +--------------+
  2835  	//                           |           |  | |   +--------------+
  2836  	//                           | 7.7.7.249/29 | |   |    netns2    |
  2837  	//                           +--------------+ |   |    nodev3    |
  2838  	//                                            |   |              |
  2839  	//                                            +---+veth5         |
  2840  	//                                                | |            |
  2841  	//                                                | 7.7.7.250/29 |
  2842  	//                                                +--------------+
  2843  
  2844  	cleanup1, err := setupRemoteNode("veth2", "veth3", "test-arping-netns1",
  2845  		"8.8.8.248/29", "8.8.8.249", "8.8.8.250")
  2846  	require.NoError(t, err)
  2847  	defer cleanup1()
  2848  	cleanup2, err := setupRemoteNode("veth4", "veth5", "test-arping-netns2",
  2849  		"7.7.7.248/29", "7.7.7.249", "7.7.7.250")
  2850  	require.NoError(t, err)
  2851  	defer cleanup2()
  2852  
  2853  	node2IP := net.ParseIP("8.8.8.250")
  2854  	nodev2 := nodeTypes.Node{
  2855  		Name: "node2",
  2856  		IPAddresses: []nodeTypes.Address{{
  2857  			Type: nodeaddressing.NodeInternalIP,
  2858  			IP:   node2IP,
  2859  		}},
  2860  	}
  2861  	now = time.Now()
  2862  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev2))
  2863  	wait(nodev2.Identity(), "veth0", &now, false)
  2864  
  2865  	node3IP := net.ParseIP("7.7.7.250")
  2866  	nodev3 := nodeTypes.Node{
  2867  		Name: "node3",
  2868  		IPAddresses: []nodeTypes.Address{{
  2869  			Type: nodeaddressing.NodeInternalIP,
  2870  			IP:   node3IP,
  2871  		}},
  2872  	}
  2873  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev3))
  2874  	wait(nodev3.Identity(), "veth0", &now, false)
  2875  
  2876  	nextHop := net.ParseIP("9.9.9.250")
  2877  	assertNeigh(nextHop, neighStateOk)
  2878  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  2879  
  2880  	// Check that removing node2 will not remove nextHop, as it is still used by node3
  2881  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev2))
  2882  	wait(nodev2.Identity(), "veth0", nil, true)
  2883  
  2884  	assertNeigh(nextHop, func(n netlink.Neigh) bool { return true })
  2885  
  2886  	// However, removing node3 should remove the neigh entry for nextHop
  2887  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev3))
  2888  	wait(nodev3.Identity(), "veth0", nil, true)
  2889  
  2890  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  2891  
  2892  	now = time.Now()
  2893  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev3))
  2894  	wait(nodev3.Identity(), "veth0", &now, false)
  2895  
  2896  	nextHop = net.ParseIP("9.9.9.250")
  2897  	assertNeigh(nextHop, neighStateOk)
  2898  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  2899  
  2900  	// We have stored the devices in NodeConfigurationChanged
  2901  	linuxNodeHandler.NodeCleanNeighbors(false)
  2902  
  2903  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  2904  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  2905  
  2906  	// Setup routine for the 3. test
  2907  	setupNewGateway := func(vethCIDR, gwIP string) (errRet error) {
  2908  		ipGw := net.ParseIP(gwIP)
  2909  		ip, ipnet, err := net.ParseCIDR(vethCIDR)
  2910  		if err != nil {
  2911  			errRet = err
  2912  			return
  2913  		}
  2914  		ipnet.IP = ip
  2915  		route := &netlink.Route{
  2916  			Dst: ipnet,
  2917  			Gw:  ipGw,
  2918  		}
  2919  		errRet = netlink.RouteReplace(route)
  2920  		return
  2921  	}
  2922  
  2923  	// In the next test, we add node 2,3 again, and then change the nextHop
  2924  	// address to check the refcount behavior, and that the old one was
  2925  	// deleted from the neighbor table as well as the new one added.
  2926  	now = time.Now()
  2927  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev2))
  2928  	wait(nodev2.Identity(), "veth0", &now, false)
  2929  
  2930  	now = time.Now()
  2931  	require.Nil(t, linuxNodeHandler.NodeAdd(nodev3))
  2932  	wait(nodev3.Identity(), "veth0", &now, false)
  2933  
  2934  	nextHop = net.ParseIP("9.9.9.250")
  2935  
  2936  	assertNeigh(nextHop, neighStateOk)
  2937  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  2938  
  2939  	// Switch to new nextHop address for node2
  2940  	err = setupNewGateway("8.8.8.248/29", "9.9.9.251")
  2941  	require.NoError(t, err)
  2942  
  2943  	// waitGw waits for the nextHop to appear in the agent's nextHop table
  2944  	waitGw := func(nextHopNew string, nodeID nodeTypes.Identity, link string, before *time.Time) {
  2945  		t.Helper()
  2946  		err := testutils.WaitUntil(func() bool {
  2947  			linuxNodeHandler.neighLock.Lock()
  2948  			defer linuxNodeHandler.neighLock.Unlock()
  2949  			nextHopByLink, found := linuxNodeHandler.neighNextHopByNode4[nodeID]
  2950  			if !found {
  2951  				return false
  2952  			}
  2953  			nextHop, found := nextHopByLink[link]
  2954  			if !found {
  2955  				return false
  2956  			}
  2957  			if nextHop != nextHopNew {
  2958  				return false
  2959  			}
  2960  			lastPing, found := linuxNodeHandler.neighLastPingByNextHop[nextHop]
  2961  			if !found {
  2962  				return false
  2963  			}
  2964  			return before.Before(lastPing)
  2965  		}, 5*time.Second)
  2966  		require.NoError(t, err)
  2967  	}
  2968  
  2969  	// insertNeighbor is invoked async, so thus this wait based on last ping
  2970  	now = time.Now()
  2971  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev2, true)
  2972  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev3, true)
  2973  	waitGw("9.9.9.251", nodev2.Identity(), "veth0", &now)
  2974  	waitGw("9.9.9.250", nodev3.Identity(), "veth0", &now)
  2975  
  2976  	// Both nextHops now need to be present
  2977  	nextHop = net.ParseIP("9.9.9.250")
  2978  	assertNeigh(nextHop, neighStateOk)
  2979  	nextHop = net.ParseIP("9.9.9.251")
  2980  	assertNeigh(nextHop, neighStateOk)
  2981  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  2982  
  2983  	// Now also switch over the other node.
  2984  	err = setupNewGateway("7.7.7.248/29", "9.9.9.251")
  2985  	require.NoError(t, err)
  2986  
  2987  	// insertNeighbor is invoked async, so thus this wait based on last ping
  2988  	now = time.Now()
  2989  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev2, true)
  2990  	linuxNodeHandler.NodeNeighborRefresh(context.Background(), nodev3, true)
  2991  	waitGw("9.9.9.251", nodev2.Identity(), "veth0", &now)
  2992  	waitGw("9.9.9.251", nodev3.Identity(), "veth0", &now)
  2993  
  2994  	nextHop = net.ParseIP("9.9.9.250")
  2995  
  2996  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  2997  	assertNoNeigh("node{2,3} should not be in the same L2", node2IP, node3IP)
  2998  
  2999  	nextHop = net.ParseIP("9.9.9.251")
  3000  	assertNeigh(nextHop, neighStateOk)
  3001  
  3002  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev3))
  3003  	wait(nodev3.Identity(), "veth0", nil, true)
  3004  
  3005  	// In the next test, we have node2 left in the neighbor table, and
  3006  	// we add an unrelated externally learned neighbor entry. Check that
  3007  	// NodeCleanNeighbors() removes the unrelated one. This is to simulate
  3008  	// the agent after kubeapi-server resync that it cleans up stale node
  3009  	// entries from previous runs.
  3010  
  3011  	nextHop = net.ParseIP("9.9.9.1")
  3012  	neigh := netlink.Neigh{
  3013  		LinkIndex: veth0.Attrs().Index,
  3014  		IP:        nextHop,
  3015  		State:     netlink.NUD_NONE,
  3016  		Flags:     netlink.NTF_EXT_LEARNED,
  3017  	}
  3018  	err = netlink.NeighSet(&neigh)
  3019  	require.NoError(t, err)
  3020  
  3021  	// Check that new nextHop address got added, we don't care about its NUD_* state
  3022  	assertNeigh(nextHop, func(n netlink.Neigh) bool { return true })
  3023  
  3024  	// Clean unrelated externally learned entries
  3025  	linuxNodeHandler.NodeCleanNeighborsLink(veth0, true)
  3026  
  3027  	// Check that new nextHop address got removed
  3028  	assertNoNeigh("expected removed neigh "+nextHop.String(), nextHop)
  3029  
  3030  	// Check that node2 nextHop address is still there
  3031  	nextHop = net.ParseIP("9.9.9.251")
  3032  	assertNeigh(nextHop, neighStateOk)
  3033  	assertNoNeigh("node2 should not be in the same L2", node2IP)
  3034  
  3035  	require.Nil(t, linuxNodeHandler.NodeDelete(nodev2))
  3036  	wait(nodev2.Identity(), "veth0", nil, true)
  3037  
  3038  	linuxNodeHandler.NodeCleanNeighborsLink(veth0, false)
  3039  }
  3040  
  3041  func TestArpPingHandlingForMultiDeviceIPv4(t *testing.T) {
  3042  	s := setupLinuxPrivilegedIPv4OnlyTestSuite(t)
  3043  	runtime.LockOSThread()
  3044  	defer runtime.UnlockOSThread()
  3045  
  3046  	prevEnableL2NeighDiscovery := option.Config.EnableL2NeighDiscovery
  3047  	defer func() { option.Config.EnableL2NeighDiscovery = prevEnableL2NeighDiscovery }()
  3048  
  3049  	option.Config.EnableL2NeighDiscovery = true
  3050  
  3051  	prevStateDir := option.Config.StateDir
  3052  	defer func() { option.Config.StateDir = prevStateDir }()
  3053  
  3054  	tmpDir := t.TempDir()
  3055  	option.Config.StateDir = tmpDir
  3056  
  3057  	baseTimeOld, err := s.sysctl.Read(baseIPv4Time)
  3058  	require.NoError(t, err)
  3059  	err = s.sysctl.Write(baseIPv4Time, fmt.Sprintf("%d", baseTime))
  3060  	require.NoError(t, err)
  3061  	defer func() { s.sysctl.Write(baseIPv4Time, baseTimeOld) }()
  3062  
  3063  	mcastNumOld, err := s.sysctl.Read(mcastNumIPv4)
  3064  	require.NoError(t, err)
  3065  	err = s.sysctl.Write(mcastNumIPv4, fmt.Sprintf("%d", mcastNum))
  3066  	require.NoError(t, err)
  3067  	defer func() { s.sysctl.Write(mcastNumIPv4, mcastNumOld) }()
  3068  
  3069  	// 1. Test whether another node with multiple paths can be arpinged.
  3070  	//    Each node has two devices and the other node in the different netns
  3071  	//    is reachable via either pair.
  3072  	//    Neighbor entries are not installed on devices where no route exists.
  3073  	//
  3074  	//      +--------------+     +--------------+
  3075  	//      |  host netns  |     |    netns1    |
  3076  	//      |              |     |    nodev1    |
  3077  	//      |              |     |  10.0.0.1/32 |
  3078  	//      |         veth0+-----+veth1         |
  3079  	//      |          |   |     |   |          |
  3080  	//      | 9.9.9.249/29 |     |9.9.9.250/29  |
  3081  	//      |              |     |              |
  3082  	//      |         veth2+-----+veth3         |
  3083  	//      |          |   |     | |            |
  3084  	//      | 8.8.8.249/29 |     | 8.8.8.250/29 |
  3085  	//      |              |     |              |
  3086  	//      | 7.7.7.249/29 |     |              |
  3087  	//      |  |           |     |              |
  3088  	//      | veth4        |     |              |
  3089  	//      +-+------------+     +--------------+
  3090  	//        |
  3091  	//      +-+---------------------------------+
  3092  	//      | veth5      other netns            |
  3093  	//      |  |                                |
  3094  	//      | 7.7.7.250/29                      |
  3095  	//      +-----------------------------------+
  3096  
  3097  	// Setup
  3098  	vethPair01 := &netlink.Veth{
  3099  		LinkAttrs: netlink.LinkAttrs{Name: "veth0"},
  3100  		PeerName:  "veth1",
  3101  	}
  3102  	err = netlink.LinkAdd(vethPair01)
  3103  	require.NoError(t, err)
  3104  	t.Cleanup(func() { netlink.LinkDel(vethPair01) })
  3105  	veth0, err := netlink.LinkByName("veth0")
  3106  	require.NoError(t, err)
  3107  	veth1, err := netlink.LinkByName("veth1")
  3108  	require.NoError(t, err)
  3109  	_, ipnet, _ := net.ParseCIDR("9.9.9.252/29")
  3110  	v1IP0 := net.ParseIP("9.9.9.249")
  3111  	v1IP1 := net.ParseIP("9.9.9.250")
  3112  	v1IPG := net.ParseIP("9.9.9.251")
  3113  	ipnet.IP = v1IP0
  3114  	addr := &netlink.Addr{IPNet: ipnet}
  3115  	err = netlink.AddrAdd(veth0, addr)
  3116  	require.NoError(t, err)
  3117  	err = netlink.LinkSetUp(veth0)
  3118  	require.NoError(t, err)
  3119  
  3120  	ns := netns.NewNetNS(t)
  3121  
  3122  	err = netlink.LinkSetNsFd(veth1, int(ns.FD()))
  3123  	require.NoError(t, err)
  3124  	node1Addr, err := netlink.ParseAddr("10.0.0.1/32")
  3125  	require.NoError(t, err)
  3126  	err = ns.Do(func() error {
  3127  		lo, err := netlink.LinkByName("lo")
  3128  		require.NoError(t, err)
  3129  		err = netlink.LinkSetUp(lo)
  3130  		require.NoError(t, err)
  3131  		err = netlink.AddrAdd(lo, node1Addr)
  3132  		require.NoError(t, err)
  3133  
  3134  		veth1, err := netlink.LinkByName("veth1")
  3135  		require.NoError(t, err)
  3136  		ipnet.IP = v1IP1
  3137  		addr = &netlink.Addr{IPNet: ipnet}
  3138  		err = netlink.AddrAdd(veth1, addr)
  3139  		require.NoError(t, err)
  3140  		ipnet.IP = v1IPG
  3141  		addr = &netlink.Addr{IPNet: ipnet}
  3142  		err = netlink.AddrAdd(veth1, addr)
  3143  		require.NoError(t, err)
  3144  		err = netlink.LinkSetUp(veth1)
  3145  		require.NoError(t, err)
  3146  		return nil
  3147  	})
  3148  	require.NoError(t, err)
  3149  
  3150  	vethPair23 := &netlink.Veth{
  3151  		LinkAttrs: netlink.LinkAttrs{Name: "veth2"},
  3152  		PeerName:  "veth3",
  3153  	}
  3154  	err = netlink.LinkAdd(vethPair23)
  3155  	require.NoError(t, err)
  3156  	t.Cleanup(func() { netlink.LinkDel(vethPair23) })
  3157  	veth2, err := netlink.LinkByName("veth2")
  3158  	require.NoError(t, err)
  3159  	veth3, err := netlink.LinkByName("veth3")
  3160  	require.NoError(t, err)
  3161  	_, ipnet, _ = net.ParseCIDR("8.8.8.252/29")
  3162  	v2IP0 := net.ParseIP("8.8.8.249")
  3163  	v2IP1 := net.ParseIP("8.8.8.250")
  3164  	v2IPG := net.ParseIP("8.8.8.251")
  3165  	ipnet.IP = v2IP0
  3166  	addr = &netlink.Addr{IPNet: ipnet}
  3167  	err = netlink.AddrAdd(veth2, addr)
  3168  	require.NoError(t, err)
  3169  	err = netlink.LinkSetUp(veth2)
  3170  	require.NoError(t, err)
  3171  
  3172  	err = netlink.LinkSetNsFd(veth3, int(ns.FD()))
  3173  	require.NoError(t, err)
  3174  	err = ns.Do(func() error {
  3175  		veth3, err := netlink.LinkByName("veth3")
  3176  		require.NoError(t, err)
  3177  		ipnet.IP = v2IP1
  3178  		addr = &netlink.Addr{IPNet: ipnet}
  3179  		err = netlink.AddrAdd(veth3, addr)
  3180  		require.NoError(t, err)
  3181  		ipnet.IP = v2IPG
  3182  		addr = &netlink.Addr{IPNet: ipnet}
  3183  		err = netlink.AddrAdd(veth3, addr)
  3184  		require.NoError(t, err)
  3185  		err = netlink.LinkSetUp(veth3)
  3186  		require.NoError(t, err)
  3187  		return nil
  3188  	})
  3189  	require.NoError(t, err)
  3190  
  3191  	r := &netlink.Route{
  3192  		Dst: netlink.NewIPNet(node1Addr.IP),
  3193  		MultiPath: []*netlink.NexthopInfo{
  3194  			{
  3195  				LinkIndex: veth0.Attrs().Index,
  3196  				Gw:        v1IP1,
  3197  			},
  3198  			{
  3199  				LinkIndex: veth2.Attrs().Index,
  3200  				Gw:        v2IP1,
  3201  			},
  3202  		}}
  3203  	err = netlink.RouteAdd(r)
  3204  	require.NoError(t, err)
  3205  	defer netlink.RouteDel(r)
  3206  
  3207  	// Setup another veth pair that doesn't have a route to node
  3208  	vethPair45 := &netlink.Veth{
  3209  		LinkAttrs: netlink.LinkAttrs{Name: "veth4"},
  3210  		PeerName:  "veth5",
  3211  	}
  3212  	err = netlink.LinkAdd(vethPair45)
  3213  	require.NoError(t, err)
  3214  	t.Cleanup(func() { netlink.LinkDel(vethPair45) })
  3215  	veth4, err := netlink.LinkByName("veth4")
  3216  	require.NoError(t, err)
  3217  	veth5, err := netlink.LinkByName("veth5")
  3218  	require.NoError(t, err)
  3219  	_, ipnet, _ = net.ParseCIDR("7.7.7.252/29")
  3220  	v3IP0 := net.ParseIP("7.7.7.249")
  3221  	v3IP1 := net.ParseIP("7.7.7.250")
  3222  	ipnet.IP = v3IP0
  3223  	addr = &netlink.Addr{IPNet: ipnet}
  3224  	err = netlink.AddrAdd(veth4, addr)
  3225  	require.NoError(t, err)
  3226  	err = netlink.LinkSetUp(veth4)
  3227  	require.NoError(t, err)
  3228  
  3229  	ns2 := netns.NewNetNS(t)
  3230  
  3231  	err = netlink.LinkSetNsFd(veth5, int(ns2.FD()))
  3232  	require.NoError(t, err)
  3233  	err = ns2.Do(func() error {
  3234  		veth5, err := netlink.LinkByName("veth5")
  3235  		require.NoError(t, err)
  3236  		ipnet.IP = v3IP1
  3237  		addr = &netlink.Addr{IPNet: ipnet}
  3238  		err = netlink.AddrAdd(veth5, addr)
  3239  		require.NoError(t, err)
  3240  		err = netlink.LinkSetUp(veth5)
  3241  		require.NoError(t, err)
  3242  		return nil
  3243  	})
  3244  	require.NoError(t, err)
  3245  
  3246  	prevRoutingMode := option.Config.RoutingMode
  3247  	defer func() { option.Config.RoutingMode = prevRoutingMode }()
  3248  	option.Config.RoutingMode = option.RoutingModeNative
  3249  	prevDRDev := option.Config.DirectRoutingDevice
  3250  	defer func() { option.Config.DirectRoutingDevice = prevDRDev }()
  3251  	option.Config.DirectRoutingDevice = "veth0"
  3252  	prevNP := option.Config.EnableNodePort
  3253  	defer func() { option.Config.EnableNodePort = prevNP }()
  3254  	option.Config.EnableNodePort = true
  3255  	prevARPPeriod := option.Config.ARPPingRefreshPeriod
  3256  	defer func() { option.Config.ARPPingRefreshPeriod = prevARPPeriod }()
  3257  	option.Config.ARPPingRefreshPeriod = 1 * time.Nanosecond
  3258  
  3259  	mq := new(mockEnqueuer)
  3260  	dpConfig := DatapathConfiguration{HostDevice: "veth0"}
  3261  	log := hivetest.Logger(t)
  3262  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), mq)
  3263  	mq.nh = linuxNodeHandler
  3264  
  3265  	nodeConfig := s.nodeConfigTemplate
  3266  	nodeConfig.EnableEncapsulation = false
  3267  	nodeConfig.Devices = append(slices.Clone(nodeConfig.Devices),
  3268  		getDevice(t, "veth0"),
  3269  		getDevice(t, "veth2"),
  3270  		getDevice(t, "veth4"))
  3271  	err = linuxNodeHandler.NodeConfigurationChanged(nodeConfig)
  3272  	require.NoError(t, err)
  3273  
  3274  	// wait waits for neigh entry update or waits for removal if waitForDelete=true
  3275  	wait := func(nodeID nodeTypes.Identity, link string, before *time.Time, waitForDelete bool) {
  3276  		t.Helper()
  3277  		err := testutils.WaitUntil(func() bool {
  3278  			linuxNodeHandler.neighLock.Lock()
  3279  			defer linuxNodeHandler.neighLock.Unlock()
  3280  			nextHopByLink, found := linuxNodeHandler.neighNextHopByNode4[nodeID]
  3281  			if !found {
  3282  				return waitForDelete
  3283  			}
  3284  			nextHop, found := nextHopByLink[link]
  3285  			if !found {
  3286  				return waitForDelete
  3287  			}
  3288  			lastPing, found := linuxNodeHandler.neighLastPingByNextHop[nextHop]
  3289  			if !found {
  3290  				return false
  3291  			}
  3292  			if waitForDelete {
  3293  				return false
  3294  			}
  3295  			return before.Before(lastPing)
  3296  		}, 5*time.Second)
  3297  		require.NoError(t, err)
  3298  	}
  3299  
  3300  	assertNeigh := func(ip net.IP, link netlink.Link, checkNeigh func(neigh netlink.Neigh) bool) {
  3301  		t.Helper()
  3302  		err := testutils.WaitUntil(func() bool {
  3303  			neighs, err := netlink.NeighList(link.Attrs().Index, netlink.FAMILY_V4)
  3304  			require.NoError(t, err)
  3305  			for _, n := range neighs {
  3306  				if n.IP.Equal(ip) && checkNeigh(n) {
  3307  					return true
  3308  				}
  3309  			}
  3310  			return false
  3311  		}, 5*time.Second)
  3312  		require.NoError(t, err, "expected neighbor %s", ip)
  3313  	}
  3314  
  3315  	assertNoNeigh := func(link netlink.Link, ips ...net.IP) {
  3316  		t.Helper()
  3317  		err := testutils.WaitUntil(func() bool {
  3318  			neighs, err := netlink.NeighList(link.Attrs().Index, netlink.FAMILY_V4)
  3319  			require.NoError(t, err)
  3320  			for _, n := range neighs {
  3321  				for _, ip := range ips {
  3322  					if n.IP.Equal(ip) {
  3323  						return false
  3324  					}
  3325  				}
  3326  			}
  3327  			return true
  3328  		}, 5*time.Second)
  3329  		require.NoError(t, err, "expected no neighbors: %v", ips)
  3330  	}
  3331  
  3332  	nodev1 := nodeTypes.Node{
  3333  		Name: "node1",
  3334  		IPAddresses: []nodeTypes.Address{{
  3335  			Type: nodeaddressing.NodeInternalIP,
  3336  			IP:   node1Addr.IP,
  3337  		}},
  3338  	}
  3339  	now := time.Now()
  3340  	err = linuxNodeHandler.NodeAdd(nodev1)
  3341  	require.NoError(t, err)
  3342  	// insertNeighbor is invoked async
  3343  	// Insert the same node second time. This should not increment refcount for
  3344  	// the same nextHop. We test it by checking that NodeDelete has removed the
  3345  	// related neigh entry.
  3346  	err = linuxNodeHandler.NodeAdd(nodev1)
  3347  	require.NoError(t, err)
  3348  	// insertNeighbor is invoked async, so thus this wait based on last ping
  3349  	wait(nodev1.Identity(), "veth0", &now, false)
  3350  	wait(nodev1.Identity(), "veth2", &now, false)
  3351  
  3352  	// Check whether an arp entry for nodev1 IP addr (=veth1) was added
  3353  	assertNeigh(v1IP1, veth0, neighStateOk)
  3354  
  3355  	// Check whether an arp entry for nodev2 IP addr (=veth3) was added
  3356  	assertNeigh(v2IP1, veth2, neighStateOk)
  3357  
  3358  	// Check whether we don't install the neighbor entries to nodes on the device where the actual route isn't.
  3359  	// "Consistently(<check>, 5sec, 1sec)"
  3360  	start := time.Now()
  3361  	for {
  3362  		if time.Since(start) > 5*time.Second {
  3363  			break
  3364  		}
  3365  
  3366  		neighs, err := netlink.NeighList(veth4.Attrs().Index, netlink.FAMILY_V4)
  3367  		require.NoError(t, err)
  3368  		found := false
  3369  		for _, n := range neighs {
  3370  			if n.IP.Equal(v3IP1) || n.IP.Equal(node1Addr.IP) {
  3371  				found = true
  3372  			}
  3373  		}
  3374  		require.Equal(t, false, found)
  3375  
  3376  		time.Sleep(1 * time.Second)
  3377  	}
  3378  
  3379  	// Swap MAC addresses of veth0 and veth1, veth2 and veth3 to ensure the MAC address of veth1 changed.
  3380  	// Trigger neighbor refresh on veth0 and check whether the arp entry was updated.
  3381  	var veth0HwAddr, veth1HwAddr, veth2HwAddr, veth3HwAddr, updatedHwAddrFromArpEntry net.HardwareAddr
  3382  	veth0HwAddr = veth0.Attrs().HardwareAddr
  3383  	veth2HwAddr = veth2.Attrs().HardwareAddr
  3384  	err = ns.Do(func() error {
  3385  		veth1, err := netlink.LinkByName("veth1")
  3386  		require.NoError(t, err)
  3387  		veth1HwAddr = veth1.Attrs().HardwareAddr
  3388  		err = netlink.LinkSetHardwareAddr(veth1, veth0HwAddr)
  3389  		require.NoError(t, err)
  3390  
  3391  		veth3, err := netlink.LinkByName("veth3")
  3392  		require.NoError(t, err)
  3393  		veth3HwAddr = veth3.Attrs().HardwareAddr
  3394  		err = netlink.LinkSetHardwareAddr(veth3, veth2HwAddr)
  3395  		require.NoError(t, err)
  3396  		return nil
  3397  	})
  3398  	require.NoError(t, err)
  3399  
  3400  	now = time.Now()
  3401  	err = netlink.LinkSetHardwareAddr(veth0, veth1HwAddr)
  3402  	require.NoError(t, err)
  3403  	err = netlink.LinkSetHardwareAddr(veth2, veth3HwAddr)
  3404  	require.NoError(t, err)
  3405  
  3406  	linuxNodeHandler.NodeNeighborRefresh(context.TODO(), nodev1, true)
  3407  	wait(nodev1.Identity(), "veth0", &now, false)
  3408  	wait(nodev1.Identity(), "veth2", &now, false)
  3409  
  3410  	assertNeigh(v1IP1, veth0,
  3411  		func(neigh netlink.Neigh) bool {
  3412  			if neighStateOk(neigh) {
  3413  				updatedHwAddrFromArpEntry = neigh.HardwareAddr
  3414  				return true
  3415  			}
  3416  			return false
  3417  		})
  3418  
  3419  	require.Equal(t, veth0HwAddr.String(), updatedHwAddrFromArpEntry.String())
  3420  
  3421  	assertNeigh(v2IP1, veth2,
  3422  		func(neigh netlink.Neigh) bool {
  3423  			if neighStateOk(neigh) {
  3424  				updatedHwAddrFromArpEntry = neigh.HardwareAddr
  3425  				return true
  3426  			}
  3427  			return false
  3428  		})
  3429  
  3430  	require.Equal(t, veth2HwAddr.String(), updatedHwAddrFromArpEntry.String())
  3431  
  3432  	// Remove nodev1, and check whether the arp entry was removed
  3433  	err = linuxNodeHandler.NodeDelete(nodev1)
  3434  	require.NoError(t, err)
  3435  	// deleteNeighbor is invoked async too
  3436  	wait(nodev1.Identity(), "veth0", nil, true)
  3437  	wait(nodev1.Identity(), "veth2", nil, true)
  3438  
  3439  	assertNoNeigh(veth0, v1IP1)
  3440  	assertNoNeigh(veth2, v2IP1)
  3441  }
  3442  
  3443  func BenchmarkAll(b *testing.B) {
  3444  	for _, tt := range []string{"IPv4", "IPv6", "dual"} {
  3445  		b.Run(tt, func(b *testing.B) {
  3446  			b.Run("BenchmarkNodeUpdate", func(b *testing.B) {
  3447  				s := setup(b, tt)
  3448  				s.BenchmarkNodeUpdate(b)
  3449  			})
  3450  			b.Run("BenchmarkNodeUpdateEncap", func(b *testing.B) {
  3451  				s := setup(b, tt)
  3452  				s.BenchmarkNodeUpdateEncap(b)
  3453  			})
  3454  			b.Run("BenchmarkNodeUpdateDirectRoute", func(b *testing.B) {
  3455  				s := setup(b, tt)
  3456  				s.BenchmarkNodeUpdateDirectRoute(b)
  3457  			})
  3458  			b.Run("BenchmarkNoChangeNodeUpdate", func(b *testing.B) {
  3459  				s := setup(b, tt)
  3460  				s.BenchmarkNoChangeNodeUpdate(b)
  3461  			})
  3462  			b.Run("BenchmarkNoChangeNodeUpdateEncapAll", func(b *testing.B) {
  3463  				s := setup(b, tt)
  3464  				s.BenchmarkNoChangeNodeUpdateEncapAll(b)
  3465  			})
  3466  			b.Run("BenchmarkNoChangeNodeUpdateDirectRouteAll", func(b *testing.B) {
  3467  				s := setup(b, tt)
  3468  				s.BenchmarkNoChangeNodeUpdateDirectRouteAll(b)
  3469  			})
  3470  			b.Run("BenchmarkNodeValidateImplementation", func(b *testing.B) {
  3471  				s := setup(b, tt)
  3472  				s.BenchmarkNodeValidateImplementation(b)
  3473  			})
  3474  			b.Run("BenchmarkNodeValidateImplementationEncap", func(b *testing.B) {
  3475  				s := setup(b, tt)
  3476  				s.BenchmarkNodeValidateImplementationEncap(b)
  3477  			})
  3478  			b.Run("BenchmarkNodeValidateImplementationDirectRoute", func(b *testing.B) {
  3479  				s := setup(b, tt)
  3480  				s.BenchmarkNodeValidateImplementationDirectRoute(b)
  3481  			})
  3482  		})
  3483  	}
  3484  }
  3485  
  3486  func (s *linuxPrivilegedBaseTestSuite) benchmarkNodeUpdate(b *testing.B, config datapath.LocalNodeConfiguration) {
  3487  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
  3488  	ip4Alloc2 := cidr.MustParseCIDR("6.6.6.0/24")
  3489  	ip6Alloc1 := cidr.MustParseCIDR("2001:aaaa::/96")
  3490  	ip6Alloc2 := cidr.MustParseCIDR("2001:bbbb::/96")
  3491  
  3492  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
  3493  	log := hivetest.Logger(b)
  3494  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
  3495  
  3496  	err := linuxNodeHandler.NodeConfigurationChanged(config)
  3497  	require.NoError(b, err)
  3498  
  3499  	nodev1 := nodeTypes.Node{
  3500  		Name:        "node1",
  3501  		IPAddresses: []nodeTypes.Address{},
  3502  	}
  3503  
  3504  	if s.enableIPv4 {
  3505  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  3506  			IP:   config.NodeIPv4,
  3507  			Type: nodeaddressing.NodeInternalIP,
  3508  		})
  3509  		nodev1.IPv4AllocCIDR = ip4Alloc1
  3510  	}
  3511  
  3512  	if s.enableIPv6 {
  3513  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  3514  			IP:   config.NodeIPv6,
  3515  			Type: nodeaddressing.NodeInternalIP,
  3516  		})
  3517  		nodev1.IPv6AllocCIDR = ip6Alloc1
  3518  	}
  3519  
  3520  	nodev2 := nodeTypes.Node{
  3521  		Name:        "node1",
  3522  		IPAddresses: []nodeTypes.Address{},
  3523  	}
  3524  
  3525  	if s.enableIPv4 {
  3526  		nodev2.IPAddresses = append(nodev2.IPAddresses, nodeTypes.Address{
  3527  			IP:   config.NodeIPv4,
  3528  			Type: nodeaddressing.NodeInternalIP,
  3529  		})
  3530  		nodev2.IPv4AllocCIDR = ip4Alloc2
  3531  	}
  3532  
  3533  	if s.enableIPv6 {
  3534  		nodev2.IPAddresses = append(nodev2.IPAddresses, nodeTypes.Address{
  3535  			IP:   config.NodeIPv6,
  3536  			Type: nodeaddressing.NodeInternalIP,
  3537  		})
  3538  		nodev2.IPv6AllocCIDR = ip6Alloc2
  3539  	}
  3540  
  3541  	err = linuxNodeHandler.NodeAdd(nodev1)
  3542  	require.NoError(b, err)
  3543  
  3544  	oldNode := nodev1
  3545  	newNode := nodev2
  3546  
  3547  	b.ResetTimer()
  3548  	for i := 0; i < b.N; i++ {
  3549  		err = linuxNodeHandler.NodeUpdate(oldNode, newNode)
  3550  		require.NoError(b, err)
  3551  
  3552  		tmp := oldNode
  3553  		oldNode = newNode
  3554  		newNode = tmp
  3555  	}
  3556  	b.StopTimer()
  3557  
  3558  	err = linuxNodeHandler.NodeDelete(oldNode)
  3559  	require.NoError(b, err)
  3560  }
  3561  
  3562  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNodeUpdate(b *testing.B) {
  3563  	s.benchmarkNodeUpdate(b, datapath.LocalNodeConfiguration{
  3564  		EnableIPv4: s.enableIPv4,
  3565  		EnableIPv6: s.enableIPv6,
  3566  	})
  3567  }
  3568  
  3569  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNodeUpdateEncap(b *testing.B) {
  3570  	s.benchmarkNodeUpdate(b, datapath.LocalNodeConfiguration{
  3571  		EnableIPv4:          s.enableIPv4,
  3572  		EnableIPv6:          s.enableIPv6,
  3573  		EnableEncapsulation: true,
  3574  	})
  3575  }
  3576  
  3577  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNodeUpdateDirectRoute(b *testing.B) {
  3578  	s.benchmarkNodeUpdate(b, datapath.LocalNodeConfiguration{
  3579  		EnableIPv4:              s.enableIPv4,
  3580  		EnableIPv6:              s.enableIPv6,
  3581  		EnableAutoDirectRouting: true,
  3582  	})
  3583  }
  3584  
  3585  func (s *linuxPrivilegedBaseTestSuite) benchmarkNodeUpdateNOP(b *testing.B, config datapath.LocalNodeConfiguration) {
  3586  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
  3587  	ip6Alloc1 := cidr.MustParseCIDR("2001:aaaa::/96")
  3588  
  3589  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
  3590  	log := hivetest.Logger(b)
  3591  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
  3592  
  3593  	err := linuxNodeHandler.NodeConfigurationChanged(config)
  3594  	require.NoError(b, err)
  3595  
  3596  	nodev1 := nodeTypes.Node{
  3597  		Name:        "node1",
  3598  		IPAddresses: []nodeTypes.Address{},
  3599  	}
  3600  
  3601  	if s.enableIPv4 {
  3602  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  3603  			IP:   config.NodeIPv4,
  3604  			Type: nodeaddressing.NodeInternalIP,
  3605  		})
  3606  		nodev1.IPv4AllocCIDR = ip4Alloc1
  3607  	}
  3608  
  3609  	if s.enableIPv6 {
  3610  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  3611  			IP:   config.NodeIPv6,
  3612  			Type: nodeaddressing.NodeInternalIP,
  3613  		})
  3614  		nodev1.IPv6AllocCIDR = ip6Alloc1
  3615  	}
  3616  
  3617  	err = linuxNodeHandler.NodeAdd(nodev1)
  3618  	require.NoError(b, err)
  3619  
  3620  	b.ResetTimer()
  3621  	for i := 0; i < b.N; i++ {
  3622  		err = linuxNodeHandler.NodeUpdate(nodev1, nodev1)
  3623  		require.NoError(b, err)
  3624  	}
  3625  	b.StopTimer()
  3626  
  3627  	err = linuxNodeHandler.NodeDelete(nodev1)
  3628  	require.NoError(b, err)
  3629  }
  3630  
  3631  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNoChangeNodeUpdate(b *testing.B) {
  3632  	s.benchmarkNodeUpdateNOP(b, datapath.LocalNodeConfiguration{
  3633  		EnableIPv4: s.enableIPv4,
  3634  		EnableIPv6: s.enableIPv6,
  3635  	})
  3636  }
  3637  
  3638  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNoChangeNodeUpdateEncapAll(b *testing.B) {
  3639  	s.benchmarkNodeUpdateNOP(b, datapath.LocalNodeConfiguration{
  3640  		EnableIPv4:          s.enableIPv4,
  3641  		EnableIPv6:          s.enableIPv6,
  3642  		EnableEncapsulation: true,
  3643  	})
  3644  }
  3645  
  3646  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNoChangeNodeUpdateDirectRouteAll(b *testing.B) {
  3647  	s.benchmarkNodeUpdateNOP(b, datapath.LocalNodeConfiguration{
  3648  		EnableIPv4:              s.enableIPv4,
  3649  		EnableIPv6:              s.enableIPv6,
  3650  		EnableAutoDirectRouting: true,
  3651  	})
  3652  }
  3653  
  3654  func (s *linuxPrivilegedBaseTestSuite) benchmarkNodeValidateImplementation(b *testing.B, config datapath.LocalNodeConfiguration) {
  3655  	ip4Alloc1 := cidr.MustParseCIDR("5.5.5.0/24")
  3656  	ip6Alloc1 := cidr.MustParseCIDR("2001:aaaa::/96")
  3657  
  3658  	dpConfig := DatapathConfiguration{HostDevice: dummyHostDeviceName}
  3659  	log := hivetest.Logger(b)
  3660  	linuxNodeHandler := newNodeHandler(log, dpConfig, nodemapfake.NewFakeNodeMapV2(), new(mockEnqueuer))
  3661  
  3662  	err := linuxNodeHandler.NodeConfigurationChanged(config)
  3663  	require.NoError(b, err)
  3664  
  3665  	nodev1 := nodeTypes.Node{
  3666  		Name:        "node1",
  3667  		IPAddresses: []nodeTypes.Address{},
  3668  	}
  3669  
  3670  	if s.enableIPv4 {
  3671  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  3672  			IP:   config.NodeIPv4,
  3673  			Type: nodeaddressing.NodeInternalIP,
  3674  		})
  3675  		nodev1.IPv4AllocCIDR = ip4Alloc1
  3676  	}
  3677  
  3678  	if s.enableIPv6 {
  3679  		nodev1.IPAddresses = append(nodev1.IPAddresses, nodeTypes.Address{
  3680  			IP:   config.NodeIPv6,
  3681  			Type: nodeaddressing.NodeInternalIP,
  3682  		})
  3683  		nodev1.IPv6AllocCIDR = ip6Alloc1
  3684  	}
  3685  
  3686  	err = linuxNodeHandler.NodeAdd(nodev1)
  3687  	require.NoError(b, err)
  3688  
  3689  	b.ResetTimer()
  3690  	for i := 0; i < b.N; i++ {
  3691  		err = linuxNodeHandler.NodeValidateImplementation(nodev1)
  3692  		require.NoError(b, err)
  3693  	}
  3694  	b.StopTimer()
  3695  
  3696  	err = linuxNodeHandler.NodeDelete(nodev1)
  3697  	require.NoError(b, err)
  3698  }
  3699  
  3700  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNodeValidateImplementation(b *testing.B) {
  3701  	s.benchmarkNodeValidateImplementation(b, s.nodeConfigTemplate)
  3702  }
  3703  
  3704  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNodeValidateImplementationEncap(b *testing.B) {
  3705  	config := s.nodeConfigTemplate
  3706  	config.EnableEncapsulation = true
  3707  	s.benchmarkNodeValidateImplementation(b, config)
  3708  }
  3709  
  3710  func (s *linuxPrivilegedBaseTestSuite) BenchmarkNodeValidateImplementationDirectRoute(b *testing.B) {
  3711  	config := s.nodeConfigTemplate
  3712  	config.EnableAutoDirectRouting = true
  3713  	s.benchmarkNodeValidateImplementation(b, config)
  3714  }