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 }