github.com/cilium/cilium@v1.16.2/pkg/datapath/tables/node_address.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package tables 5 6 import ( 7 "context" 8 "fmt" 9 "net" 10 "net/netip" 11 "slices" 12 "sort" 13 "strings" 14 15 "github.com/cilium/hive/cell" 16 "github.com/cilium/hive/job" 17 "github.com/cilium/statedb" 18 "github.com/cilium/statedb/index" 19 "github.com/cilium/stream" 20 "github.com/sirupsen/logrus" 21 "github.com/spf13/pflag" 22 "k8s.io/apimachinery/pkg/util/sets" 23 24 "github.com/cilium/cilium/pkg/cidr" 25 "github.com/cilium/cilium/pkg/defaults" 26 "github.com/cilium/cilium/pkg/ip" 27 "github.com/cilium/cilium/pkg/logging/logfields" 28 "github.com/cilium/cilium/pkg/node" 29 "github.com/cilium/cilium/pkg/option" 30 "github.com/cilium/cilium/pkg/rate" 31 "github.com/cilium/cilium/pkg/time" 32 ) 33 34 // WildcardDeviceName for looking up a fallback global address. This is used for 35 // picking a BPF masquerade or direct routing address in cases where the target 36 // device doesn't have an IP address (ECMP and similar setups). 37 const WildcardDeviceName = "*" 38 39 // NodeAddress is an IP address assigned to a network interface on a Cilium node 40 // that is considered a "host" IP address. 41 type NodeAddress struct { 42 Addr netip.Addr 43 44 // NodePort is true if this address is to be used for NodePort. 45 // If --nodeport-addresses is set, then all addresses on native 46 // devices that are contained within the specified CIDRs are chosen. 47 // If it is not set, then only the primary IPv4 and/or IPv6 address 48 // of each native device is used. 49 NodePort bool 50 51 // Primary is true if this is the primary IPv4 or IPv6 address of this device. 52 // This is mainly used to pick the address for BPF masquerading. 53 Primary bool 54 55 // DeviceName is the name of the network device from which this address 56 // is derived from. 57 DeviceName string 58 } 59 60 func (n *NodeAddress) IP() net.IP { 61 return n.Addr.AsSlice() 62 } 63 64 func (n *NodeAddress) String() string { 65 return fmt.Sprintf("%s (%s)", n.Addr, n.DeviceName) 66 } 67 68 // GetAddr returns the address. Useful when mapping over NodeAddress's with 69 // e.g. statedb.Map. 70 func (n NodeAddress) GetAddr() netip.Addr { 71 return n.Addr 72 } 73 74 func (n NodeAddress) TableHeader() []string { 75 return []string{ 76 "Address", 77 "NodePort", 78 "Primary", 79 "DeviceName", 80 } 81 } 82 83 func (n NodeAddress) TableRow() []string { 84 return []string{ 85 n.Addr.String(), 86 fmt.Sprintf("%v", n.NodePort), 87 fmt.Sprintf("%v", n.Primary), 88 n.DeviceName, 89 } 90 } 91 92 type NodeAddressConfig struct { 93 NodePortAddresses []*cidr.CIDR `mapstructure:"nodeport-addresses"` 94 } 95 96 type NodeAddressKey struct { 97 Addr netip.Addr 98 DeviceName string 99 } 100 101 func (k NodeAddressKey) Key() index.Key { 102 return append(index.NetIPAddr(k.Addr), []byte(k.DeviceName)...) 103 } 104 105 var ( 106 // NodeAddressIndex is the primary index for node addresses: 107 // 108 // var nodeAddresses Table[NodeAddress] 109 // nodeAddresses.First(txn, NodeAddressIndex.Query(netip.MustParseAddr("1.2.3.4"))) 110 NodeAddressIndex = statedb.Index[NodeAddress, NodeAddressKey]{ 111 Name: "id", 112 FromObject: func(a NodeAddress) index.KeySet { 113 return index.NewKeySet(NodeAddressKey{a.Addr, a.DeviceName}.Key()) 114 }, 115 FromKey: NodeAddressKey.Key, 116 Unique: true, 117 } 118 119 NodeAddressDeviceNameIndex = statedb.Index[NodeAddress, string]{ 120 Name: "name", 121 FromObject: func(a NodeAddress) index.KeySet { 122 return index.NewKeySet(index.String(a.DeviceName)) 123 }, 124 FromKey: index.String, 125 Unique: false, 126 } 127 128 NodeAddressNodePortIndex = statedb.Index[NodeAddress, bool]{ 129 Name: "node-port", 130 FromObject: func(a NodeAddress) index.KeySet { 131 return index.NewKeySet(index.Bool(a.NodePort)) 132 }, 133 FromKey: index.Bool, 134 Unique: false, 135 } 136 137 NodeAddressTableName statedb.TableName = "node-addresses" 138 139 // NodeAddressCell provides Table[NodeAddress] and a background controller 140 // that derives the node addresses from the low-level Table[*Device]. 141 // 142 // The Table[NodeAddress] contains the actual assigned addresses on the node, 143 // but not for example external Kubernetes node addresses that may be merely 144 // NATd to a private address. Those can be queried through [node.LocalNodeStore]. 145 NodeAddressCell = cell.Module( 146 "node-address", 147 "Table of node addresses derived from system network devices", 148 149 cell.ProvidePrivate(NewNodeAddressTable), 150 cell.Provide( 151 newNodeAddressController, 152 newAddressScopeMax, 153 ), 154 cell.Config(NodeAddressConfig{}), 155 ) 156 ) 157 158 func NewNodeAddressTable() (statedb.RWTable[NodeAddress], error) { 159 return statedb.NewTable( 160 NodeAddressTableName, 161 NodeAddressIndex, 162 NodeAddressDeviceNameIndex, 163 NodeAddressNodePortIndex, 164 ) 165 } 166 167 const ( 168 nodeAddressControllerMinInterval = 100 * time.Millisecond 169 ) 170 171 // AddressScopeMax sets the maximum scope an IP address can have. A scope 172 // is defined in rtnetlink(7) as the distance to the destination where a 173 // lower number signifies a wider scope with RT_SCOPE_UNIVERSE (0) being 174 // the widest. 175 // 176 // This defaults to RT_SCOPE_LINK-1 (defaults.AddressScopeMax) and can be 177 // set by the user with --local-max-addr-scope. 178 type AddressScopeMax uint8 179 180 func newAddressScopeMax(cfg NodeAddressConfig, daemonCfg *option.DaemonConfig) (AddressScopeMax, error) { 181 return AddressScopeMax(daemonCfg.AddressScopeMax), nil 182 } 183 184 func (cfg NodeAddressConfig) getNets() []*net.IPNet { 185 nets := make([]*net.IPNet, len(cfg.NodePortAddresses)) 186 for i, cidr := range cfg.NodePortAddresses { 187 nets[i] = cidr.IPNet 188 } 189 return nets 190 } 191 192 func (NodeAddressConfig) Flags(flags *pflag.FlagSet) { 193 flags.StringSlice( 194 "nodeport-addresses", 195 nil, 196 "A whitelist of CIDRs to limit which IPs are used for NodePort. If not set, primary IPv4 and/or IPv6 address of each native device is used.") 197 } 198 199 type nodeAddressControllerParams struct { 200 cell.In 201 202 Health cell.Health 203 Log logrus.FieldLogger 204 Config NodeAddressConfig 205 Lifecycle cell.Lifecycle 206 Jobs job.Registry 207 DB *statedb.DB 208 Devices statedb.Table[*Device] 209 NodeAddresses statedb.RWTable[NodeAddress] 210 AddressScopeMax AddressScopeMax 211 LocalNode *node.LocalNodeStore 212 } 213 214 type nodeAddressController struct { 215 nodeAddressControllerParams 216 217 deviceChanges statedb.ChangeIterator[*Device] 218 k8sIPv4, k8sIPv6 netip.Addr 219 fallbackAddresses fallbackAddresses 220 } 221 222 // newNodeAddressController constructs the node address controller & registers its 223 // lifecycle hooks and then provides Table[NodeAddress] to the application. 224 // This enforces proper ordering, e.g. controller is started before anything 225 // that depends on Table[NodeAddress] and allows it to populate it before 226 // it is accessed. 227 func newNodeAddressController(p nodeAddressControllerParams) (tbl statedb.Table[NodeAddress], err error) { 228 if err := p.DB.RegisterTable(p.NodeAddresses); err != nil { 229 return nil, err 230 } 231 232 n := nodeAddressController{nodeAddressControllerParams: p} 233 n.register() 234 return n.NodeAddresses, nil 235 } 236 237 func (n *nodeAddressController) register() { 238 g := n.Jobs.NewGroup(n.Health) 239 g.Add(job.OneShot("node-address-update", n.run)) 240 241 n.Lifecycle.Append( 242 cell.Hook{ 243 OnStart: func(ctx cell.HookContext) error { 244 txn := n.DB.WriteTxn(n.NodeAddresses, n.Devices /* for delete tracker */) 245 defer txn.Abort() 246 247 // Start tracking deletions of devices. 248 var err error 249 n.deviceChanges, err = n.Devices.Changes(txn) 250 if err != nil { 251 return fmt.Errorf("DeleteTracker: %w", err) 252 } 253 254 if node, err := n.LocalNode.Get(ctx); err == nil { 255 n.updateK8sNodeIPs(node) 256 } 257 258 // Do an immediate update to populate the table before it is read from. 259 devices := n.Devices.All(txn) 260 for dev, _, ok := devices.Next(); ok; dev, _, ok = devices.Next() { 261 n.update(txn, n.getAddressesFromDevice(dev), nil, dev.Name) 262 n.updateWildcardDevice(txn, dev, false) 263 } 264 txn.Commit() 265 266 // Start the job in the background to incremental refresh 267 // the node addresses. 268 return g.Start(ctx) 269 }, 270 OnStop: g.Stop, 271 }) 272 273 } 274 275 func (n *nodeAddressController) updateK8sNodeIPs(node node.LocalNode) (updated bool) { 276 if ip := node.GetNodeIP(true); ip != nil { 277 if newIP, ok := netip.AddrFromSlice(ip); ok { 278 if newIP != n.k8sIPv6 { 279 n.k8sIPv6 = newIP 280 updated = true 281 } 282 } 283 } 284 if ip := node.GetNodeIP(false); ip != nil { 285 if newIP, ok := netip.AddrFromSlice(ip); ok { 286 if newIP != n.k8sIPv4 { 287 n.k8sIPv4 = newIP 288 updated = true 289 } 290 } 291 } 292 return 293 } 294 295 func (n *nodeAddressController) run(ctx context.Context, reporter cell.Health) error { 296 defer n.deviceChanges.Close() 297 298 localNodeChanges := stream.ToChannel(ctx, n.LocalNode) 299 n.updateK8sNodeIPs(<-localNodeChanges) 300 301 limiter := rate.NewLimiter(nodeAddressControllerMinInterval, 1) 302 for { 303 txn := n.DB.WriteTxn(n.NodeAddresses) 304 for change, _, ok := n.deviceChanges.Next(); ok; change, _, ok = n.deviceChanges.Next() { 305 dev := change.Object 306 307 var new []NodeAddress 308 if !change.Deleted { 309 new = n.getAddressesFromDevice(dev) 310 } 311 n.update(txn, new, reporter, dev.Name) 312 n.updateWildcardDevice(txn, dev, change.Deleted) 313 } 314 txn.Commit() 315 316 select { 317 case <-ctx.Done(): 318 return nil 319 case <-n.deviceChanges.Watch(n.DB.ReadTxn()): 320 case localNode, ok := <-localNodeChanges: 321 if !ok { 322 localNodeChanges = nil 323 break 324 } 325 if n.updateK8sNodeIPs(localNode) { 326 // Recompute the node addresses as the k8s node IP has changed, which 327 // affects the prioritization. 328 txn := n.DB.WriteTxn(n.NodeAddresses) 329 devices := n.Devices.All(txn) 330 for dev, _, ok := devices.Next(); ok; dev, _, ok = devices.Next() { 331 n.update(txn, n.getAddressesFromDevice(dev), nil, dev.Name) 332 n.updateWildcardDevice(txn, dev, false) 333 } 334 txn.Commit() 335 } 336 } 337 if err := limiter.Wait(ctx); err != nil { 338 return err 339 } 340 } 341 } 342 343 // updateWildcardDevice updates the wildcard device ("*") with the fallback addresses. The fallback 344 // addresses are the most suitable IPv4 and IPv6 address on any network device, whether it's 345 // selected for datapath use or not. 346 func (n *nodeAddressController) updateWildcardDevice(txn statedb.WriteTxn, dev *Device, deleted bool) { 347 if strings.HasPrefix(dev.Name, "lxc") { 348 // Always ignore lxc devices. 349 return 350 } 351 352 if !n.updateFallbacks(txn, dev, deleted) { 353 // No changes 354 return 355 } 356 357 // Clear existing fallback addresses. 358 iter := n.NodeAddresses.List(txn, NodeAddressDeviceNameIndex.Query(WildcardDeviceName)) 359 for addr, _, ok := iter.Next(); ok; addr, _, ok = iter.Next() { 360 n.NodeAddresses.Delete(txn, addr) 361 } 362 363 newAddrs := []NodeAddress{} 364 for _, fallback := range n.fallbackAddresses.addrs() { 365 if !fallback.IsValid() { 366 continue 367 } 368 nodeAddr := NodeAddress{ 369 Addr: fallback, 370 NodePort: false, 371 Primary: true, 372 DeviceName: WildcardDeviceName, 373 } 374 newAddrs = append(newAddrs, nodeAddr) 375 n.NodeAddresses.Insert(txn, nodeAddr) 376 } 377 378 n.Log.WithFields(logrus.Fields{"node-addresses": showAddresses(newAddrs), logfields.Device: WildcardDeviceName}).Info("Fallback node addresses updated") 379 } 380 381 func (n *nodeAddressController) updateFallbacks(txn statedb.ReadTxn, dev *Device, deleted bool) (updated bool) { 382 if dev.Name == defaults.HostDevice { 383 return false 384 } 385 386 fallbacks := &n.fallbackAddresses 387 if deleted && fallbacks.fromDevice(dev) { 388 fallbacks.clear() 389 devices := n.Devices.All(txn) 390 for dev, _, ok := devices.Next(); ok; dev, _, ok = devices.Next() { 391 if strings.HasPrefix(dev.Name, "lxc") { 392 // Never pick the fallback from lxc* devices. 393 continue 394 } 395 fallbacks.update(dev) 396 } 397 return true 398 } else { 399 return n.fallbackAddresses.update(dev) 400 } 401 } 402 403 // updates the node addresses of a single device. 404 func (n *nodeAddressController) update(txn statedb.WriteTxn, new []NodeAddress, reporter cell.Health, device string) { 405 updated := false 406 407 // Gather the set of currently existing addresses for this device. 408 current := sets.New(statedb.Collect( 409 statedb.Map( 410 n.NodeAddresses.List(txn, NodeAddressDeviceNameIndex.Query(device)), 411 func(addr NodeAddress) netip.Addr { 412 return addr.Addr 413 }))...) 414 415 // Update the new set of addresses for this device. We try to avoid insertions when nothing has changed 416 // to avoid unnecessary wakeups to watchers of the table. 417 for _, addr := range new { 418 old, _, hadOld := n.NodeAddresses.Get(txn, NodeAddressIndex.Query(NodeAddressKey{Addr: addr.Addr, DeviceName: device})) 419 if !hadOld || old != addr { 420 updated = true 421 n.NodeAddresses.Insert(txn, addr) 422 } 423 current.Delete(addr.Addr) 424 } 425 426 // Delete the addresses no longer associated with the device. 427 for addr := range current { 428 updated = true 429 n.NodeAddresses.Delete(txn, NodeAddress{DeviceName: device, Addr: addr}) 430 } 431 432 if updated { 433 addrs := showAddresses(new) 434 n.Log.WithFields(logrus.Fields{"node-addresses": addrs, logfields.Device: device}).Info("Node addresses updated") 435 if reporter != nil { 436 reporter.OK(addrs) 437 } 438 } 439 } 440 441 // whiteListDevices are the devices from which node IPs are taken from regardless 442 // of whether they are selected or not. 443 var whitelistDevices = []string{ 444 defaults.HostDevice, 445 "lo", 446 } 447 448 func (n *nodeAddressController) getAddressesFromDevice(dev *Device) []NodeAddress { 449 if dev.Flags&net.FlagUp == 0 { 450 return nil 451 } 452 453 // Ignore non-whitelisted & non-selected devices. 454 if !slices.Contains(whitelistDevices, dev.Name) && !dev.Selected { 455 return nil 456 } 457 458 addrs := make([]NodeAddress, 0, len(dev.Addrs)) 459 460 // The indexes for the first public and private addresses for picking NodePort 461 // addresses. 462 ipv4PublicIndex, ipv4PrivateIndex := -1, -1 463 ipv6PublicIndex, ipv6PrivateIndex := -1, -1 464 465 // Do a first pass to pick the addresses. 466 for _, addr := range SortedAddresses(dev.Addrs) { 467 // We keep the scope-based address filtering as was introduced 468 // in 080857bdedca67d58ec39f8f96c5f38b22f6dc0b. 469 skip := addr.Scope > RouteScope(n.AddressScopeMax) || addr.Addr.IsLoopback() 470 471 // Always include LINK scope'd addresses for cilium_host device, regardless 472 // of what the maximum scope is. 473 skip = skip && !(dev.Name == defaults.HostDevice && addr.Scope == RT_SCOPE_LINK) 474 475 if skip { 476 continue 477 } 478 479 // index to which this address is appended. 480 index := len(addrs) 481 482 isPublic := ip.IsPublicAddr(addr.Addr.AsSlice()) 483 if addr.Addr.Is4() { 484 if addr.Addr.Unmap() == n.k8sIPv4.Unmap() { 485 // Address matches the K8s Node IP. Force this to be picked. 486 ipv4PublicIndex = index 487 ipv4PrivateIndex = index 488 } 489 490 if ipv4PublicIndex < 0 && isPublic { 491 ipv4PublicIndex = index 492 } 493 if ipv4PrivateIndex < 0 && !isPublic { 494 ipv4PrivateIndex = index 495 } 496 } 497 498 if addr.Addr.Is6() { 499 if addr.Addr == n.k8sIPv6 { 500 // Address matches the K8s Node IP. Force this to be picked. 501 ipv6PublicIndex = index 502 ipv6PrivateIndex = index 503 } 504 505 if ipv6PublicIndex < 0 && isPublic { 506 ipv6PublicIndex = index 507 } 508 if ipv6PrivateIndex < 0 && !isPublic { 509 ipv6PrivateIndex = index 510 } 511 } 512 513 // If the user has specified --nodeport-addresses use the addresses within the range for 514 // NodePort. If not, the first private (or public if private not found) will be picked 515 // by the logic following this loop. 516 nodePort := false 517 if len(n.Config.NodePortAddresses) > 0 { 518 nodePort = dev.Name != defaults.HostDevice && ip.NetsContainsAny(n.Config.getNets(), []*net.IPNet{ip.IPToPrefix(addr.AsIP())}) 519 } 520 addrs = append(addrs, 521 NodeAddress{ 522 Addr: addr.Addr, 523 NodePort: nodePort, 524 DeviceName: dev.Name, 525 }) 526 } 527 528 if len(n.Config.NodePortAddresses) == 0 && dev.Name != defaults.HostDevice { 529 // Pick the NodePort addresses. Prefer private addresses if possible. 530 if ipv4PrivateIndex >= 0 { 531 addrs[ipv4PrivateIndex].NodePort = true 532 } else if ipv4PublicIndex >= 0 { 533 addrs[ipv4PublicIndex].NodePort = true 534 } 535 if ipv6PrivateIndex >= 0 { 536 addrs[ipv6PrivateIndex].NodePort = true 537 } else if ipv6PublicIndex >= 0 { 538 addrs[ipv6PublicIndex].NodePort = true 539 } 540 } 541 542 // Pick the primary address. Prefer public over private. 543 if ipv4PublicIndex >= 0 { 544 addrs[ipv4PublicIndex].Primary = true 545 } else if ipv4PrivateIndex >= 0 { 546 addrs[ipv4PrivateIndex].Primary = true 547 } 548 if ipv6PublicIndex >= 0 { 549 addrs[ipv6PublicIndex].Primary = true 550 } else if ipv6PrivateIndex >= 0 { 551 addrs[ipv6PrivateIndex].Primary = true 552 } 553 554 return addrs 555 } 556 557 // showAddresses formats a Set[NodeAddress] as "1.2.3.4 (primary, nodeport), fe80::1" 558 func showAddresses(addrs []NodeAddress) string { 559 ss := make([]string, 0, len(addrs)) 560 for _, addr := range addrs { 561 var extras []string 562 if addr.Primary { 563 extras = append(extras, "primary") 564 } 565 if addr.NodePort { 566 extras = append(extras, "nodeport") 567 } 568 if extras != nil { 569 ss = append(ss, fmt.Sprintf("%s (%s)", addr.Addr, strings.Join(extras, ", "))) 570 } else { 571 ss = append(ss, addr.Addr.String()) 572 } 573 } 574 sort.Strings(ss) 575 return strings.Join(ss, ", ") 576 } 577 578 // sortedAddresses returns a copy of the addresses sorted by following predicates 579 // (first predicate matching in this order wins): 580 // - Primary (e.g. !IFA_F_SECONDARY) 581 // - Scope, with lower scope going first (e.g. UNIVERSE before LINK) 582 // - Public addresses before private (e.g. 1.2.3.4 before 192.168.1.1) 583 // - By address itself (192.168.1.1 before 192.168.1.2) 584 // 585 // The sorting order affects which address is marked 'Primary' and which is picked as 586 // the 'NodePort' address (when --nodeport-addresses is not specified). 587 func SortedAddresses(addrs []DeviceAddress) []DeviceAddress { 588 addrs = slices.Clone(addrs) 589 590 sort.SliceStable(addrs, func(i, j int) bool { 591 switch { 592 case !addrs[i].Secondary && addrs[j].Secondary: 593 return true 594 case addrs[i].Secondary && !addrs[j].Secondary: 595 return false 596 case addrs[i].Scope < addrs[j].Scope: 597 return true 598 case addrs[i].Scope > addrs[j].Scope: 599 return false 600 case ip.IsPublicAddr(addrs[i].Addr.AsSlice()) && !ip.IsPublicAddr(addrs[j].Addr.AsSlice()): 601 return true 602 case !ip.IsPublicAddr(addrs[i].Addr.AsSlice()) && ip.IsPublicAddr(addrs[j].Addr.AsSlice()): 603 return false 604 default: 605 return addrs[i].Addr.Less(addrs[j].Addr) 606 } 607 }) 608 return addrs 609 } 610 611 type fallbackAddress struct { 612 dev *Device 613 addr DeviceAddress 614 } 615 616 type fallbackAddresses struct { 617 ipv4 fallbackAddress 618 ipv6 fallbackAddress 619 } 620 621 func (f *fallbackAddresses) clear() { 622 f.ipv4 = fallbackAddress{} 623 f.ipv6 = fallbackAddress{} 624 } 625 626 func (f *fallbackAddresses) addrs() []netip.Addr { 627 return []netip.Addr{f.ipv4.addr.Addr, f.ipv6.addr.Addr} 628 } 629 630 func (f *fallbackAddresses) fromDevice(dev *Device) bool { 631 return (f.ipv4.dev != nil && f.ipv4.dev.Name == dev.Name) || 632 (f.ipv6.dev != nil && f.ipv6.dev.Name == dev.Name) 633 } 634 635 func (f *fallbackAddresses) clearDevice(dev *Device) { 636 // Clear the fallbacks if they were from a prior version of this device 637 // as the addresses may have been removed. 638 if f.ipv4.dev != nil && f.ipv4.dev.Name == dev.Name { 639 f.ipv4 = fallbackAddress{} 640 } 641 if f.ipv6.dev != nil && f.ipv6.dev.Name == dev.Name { 642 f.ipv6 = fallbackAddress{} 643 } 644 } 645 646 func (f *fallbackAddresses) update(dev *Device) (updated bool) { 647 prevIPv4, prevIPv6 := f.ipv4.addr, f.ipv6.addr 648 649 f.clearDevice(dev) 650 651 // Iterate over all addresses to see if any of them make for a better 652 // fallback address. 653 for _, addr := range dev.Addrs { 654 if addr.Secondary { 655 continue 656 } 657 fa := &f.ipv4 658 if addr.Addr.Is6() { 659 fa = &f.ipv6 660 } 661 better := false 662 switch { 663 case fa.dev == nil: 664 better = true 665 case dev.Selected && !fa.dev.Selected: 666 better = true 667 case !dev.Selected && fa.dev.Selected: 668 better = false 669 case ip.IsPublicAddr(addr.Addr.AsSlice()) && !ip.IsPublicAddr(fa.addr.Addr.AsSlice()): 670 better = true 671 case !ip.IsPublicAddr(addr.Addr.AsSlice()) && ip.IsPublicAddr(fa.addr.Addr.AsSlice()): 672 better = false 673 case addr.Scope < fa.addr.Scope: 674 better = true 675 case addr.Scope > fa.addr.Scope: 676 better = false 677 case dev.Index < fa.dev.Index: 678 better = true 679 case dev.Index > fa.dev.Index: 680 better = false 681 default: 682 better = addr.Addr.Less(fa.addr.Addr) 683 } 684 if better { 685 fa.dev = dev 686 fa.addr = addr 687 } 688 } 689 return prevIPv4 != f.ipv4.addr || prevIPv6 != f.ipv6.addr 690 } 691 692 // Shared test address definitions 693 var ( 694 TestIPv4InternalAddress = netip.MustParseAddr("10.0.0.2") 695 TestIPv4NodePortAddress = netip.MustParseAddr("10.0.0.3") 696 TestIPv6InternalAddress = netip.MustParseAddr("f00d::1") 697 TestIPv6NodePortAddress = netip.MustParseAddr("f00d::2") 698 699 TestAddresses = []NodeAddress{ 700 { 701 Addr: TestIPv4InternalAddress, 702 NodePort: true, 703 Primary: true, 704 DeviceName: "test", 705 }, 706 { 707 Addr: TestIPv4NodePortAddress, 708 NodePort: true, 709 Primary: false, 710 DeviceName: "test", 711 }, 712 { 713 Addr: TestIPv6InternalAddress, 714 NodePort: true, 715 Primary: true, 716 DeviceName: "test", 717 }, 718 { 719 Addr: TestIPv6NodePortAddress, 720 NodePort: true, 721 Primary: false, 722 DeviceName: "test", 723 }, 724 } 725 )