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  )