github.com/cilium/cilium@v1.16.2/pkg/node/address.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package node
     5  
     6  import (
     7  	"bufio"
     8  	"context"
     9  	"fmt"
    10  	"net"
    11  	"os"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/sirupsen/logrus"
    16  
    17  	"github.com/cilium/cilium/api/v1/models"
    18  	"github.com/cilium/cilium/pkg/byteorder"
    19  	"github.com/cilium/cilium/pkg/cidr"
    20  	"github.com/cilium/cilium/pkg/common"
    21  	"github.com/cilium/cilium/pkg/defaults"
    22  	"github.com/cilium/cilium/pkg/lock"
    23  	"github.com/cilium/cilium/pkg/logging/logfields"
    24  	"github.com/cilium/cilium/pkg/option"
    25  	wgTypes "github.com/cilium/cilium/pkg/wireguard/types"
    26  )
    27  
    28  const preferPublicIP bool = true
    29  
    30  var (
    31  	// addrsMu protects addrs. Outside the addresses struct
    32  	// so that we can Uninitialize() without linter complaining
    33  	// about lock copying.
    34  	addrsMu lock.RWMutex
    35  	addrs   addresses
    36  
    37  	// localNode holds the current state of the local "types.Node".
    38  	// This is defined here until all uses of the getters and
    39  	// setters in this file have been migrated to use LocalNodeStore
    40  	// directly.
    41  	// Initialized to proper instance via an invoke function in LocalNodeStoreCell,
    42  	// or temporarily in tests with 'WithTestLocalNodeStore'.
    43  	localNode *LocalNodeStore
    44  )
    45  
    46  func getLocalNode() LocalNode {
    47  	n, err := localNode.Get(context.TODO())
    48  	if err != nil {
    49  		// Only expecting errors if we're called after LocalNodeStore has stopped, e.g.
    50  		// we have a component that uses the legacy getters and setters here and does
    51  		// not depend on LocalNodeStore.
    52  		log.WithError(err).Fatal("getLocalNode: unexpected error")
    53  	}
    54  	return n
    55  }
    56  
    57  type addresses struct {
    58  	ipv4Loopback net.IP
    59  	routerInfo   RouterInfo
    60  }
    61  
    62  type RouterInfo interface {
    63  	GetIPv4CIDRs() []net.IPNet
    64  }
    65  
    66  func makeIPv6HostIP() net.IP {
    67  	ipstr := "fc00::10CA:1"
    68  	ip := net.ParseIP(ipstr)
    69  	if ip == nil {
    70  		log.WithField(logfields.IPAddr, ipstr).Fatal("Unable to parse IP")
    71  	}
    72  
    73  	return ip
    74  }
    75  
    76  // InitDefaultPrefix initializes the node address and allocation prefixes with
    77  // default values derived from the system. device can be set to the primary
    78  // network device of the system in which case the first address with global
    79  // scope will be regarded as the system's node address.
    80  func InitDefaultPrefix(device string) {
    81  	localNode.Update(func(n *LocalNode) {
    82  		SetDefaultPrefix(option.Config, device, n)
    83  	})
    84  }
    85  
    86  func SetDefaultPrefix(cfg *option.DaemonConfig, device string, node *LocalNode) {
    87  	if cfg.EnableIPv4 {
    88  		isIPv6 := false
    89  
    90  		ip, err := firstGlobalV4Addr(device, node.GetCiliumInternalIP(isIPv6), preferPublicIP)
    91  		if err != nil {
    92  			return
    93  		}
    94  
    95  		if node.GetNodeIP(isIPv6) == nil {
    96  			node.SetNodeInternalIP(ip)
    97  		}
    98  
    99  		ipv4range := node.IPv4AllocCIDR
   100  		ipv6range := node.IPv6AllocCIDR
   101  
   102  		if ipv4range == nil {
   103  			// If the IPv6AllocRange is not nil then the IPv4 allocation should be
   104  			// derived from the IPv6AllocRange.
   105  			//                     vvvv vvvv
   106  			// FD00:0000:0000:0000:0000:0000:0000:0000
   107  			if ipv6range != nil {
   108  				ip = net.IPv4(
   109  					ipv6range.IP[8],
   110  					ipv6range.IP[9],
   111  					ipv6range.IP[10],
   112  					ipv6range.IP[11])
   113  			}
   114  			v4range := fmt.Sprintf(defaults.DefaultIPv4Prefix+"/%d",
   115  				ip.To4()[3], defaults.DefaultIPv4PrefixLen)
   116  			_, ip4net, err := net.ParseCIDR(v4range)
   117  			if err != nil {
   118  				log.WithError(err).WithField(logfields.V4Prefix, v4range).Panic("BUG: Invalid default IPv4 prefix")
   119  			}
   120  
   121  			node.IPv4AllocCIDR = cidr.NewCIDR(ip4net)
   122  			log.WithField(logfields.V4Prefix, node.IPv4AllocCIDR).Info("Using autogenerated IPv4 allocation range")
   123  		}
   124  	}
   125  
   126  	if cfg.EnableIPv6 {
   127  		isIPv6 := true
   128  		ipv4range := node.IPv4AllocCIDR
   129  		ipv6range := node.IPv6AllocCIDR
   130  
   131  		if node.GetNodeIP(isIPv6) == nil {
   132  			// Find a IPv6 node address first
   133  			addr, _ := firstGlobalV6Addr(device, node.GetCiliumInternalIP(isIPv6), preferPublicIP)
   134  			if addr == nil {
   135  				addr = makeIPv6HostIP()
   136  			}
   137  			node.SetNodeInternalIP(addr)
   138  		}
   139  
   140  		if ipv6range == nil && ipv4range != nil {
   141  			// The IPv6 allocation should be derived from the IPv4 allocation.
   142  			ip := ipv4range.IP
   143  			v6range := fmt.Sprintf("%s%02x%02x:%02x%02x:0:0/%d",
   144  				cfg.IPv6ClusterAllocCIDRBase, ip[0], ip[1], ip[2], ip[3], 96)
   145  
   146  			_, ip6net, err := net.ParseCIDR(v6range)
   147  			if err != nil {
   148  				log.WithError(err).WithField(logfields.V6Prefix, v6range).Panic("BUG: Invalid default IPv6 prefix")
   149  			}
   150  
   151  			node.IPv6AllocCIDR = cidr.NewCIDR(ip6net)
   152  			log.WithField(logfields.V6Prefix, node.IPv6AllocCIDR).Info("Using autogenerated IPv6 allocation range")
   153  		}
   154  	}
   155  }
   156  
   157  func clone(ip net.IP) net.IP {
   158  	if ip == nil {
   159  		return nil
   160  	}
   161  	dup := make(net.IP, len(ip))
   162  	copy(dup, ip)
   163  	return dup
   164  }
   165  
   166  // GetIPv4Loopback returns the loopback IPv4 address of this node.
   167  func GetIPv4Loopback() net.IP {
   168  	addrsMu.RLock()
   169  	defer addrsMu.RUnlock()
   170  	return clone(addrs.ipv4Loopback)
   171  }
   172  
   173  // SetIPv4Loopback sets the loopback IPv4 address of this node.
   174  func SetIPv4Loopback(ip net.IP) {
   175  	addrsMu.Lock()
   176  	addrs.ipv4Loopback = clone(ip)
   177  	addrsMu.Unlock()
   178  }
   179  
   180  // GetIPv4AllocRange returns the IPv4 allocation prefix of this node
   181  func GetIPv4AllocRange() *cidr.CIDR {
   182  	return getLocalNode().IPv4AllocCIDR.DeepCopy()
   183  }
   184  
   185  // GetIPv6AllocRange returns the IPv6 allocation prefix of this node
   186  func GetIPv6AllocRange() *cidr.CIDR {
   187  	return getLocalNode().IPv6AllocCIDR.DeepCopy()
   188  }
   189  
   190  // GetIPv4 returns one of the IPv4 node address available with the following
   191  // priority:
   192  // - NodeInternalIP
   193  // - NodeExternalIP
   194  // - other IP address type.
   195  // It must be reachable on the network.
   196  func GetIPv4() net.IP {
   197  	n := getLocalNode()
   198  	return clone(n.GetNodeIP(false))
   199  }
   200  
   201  // GetInternalIPv4 returns node internal ipv4 address else return nil.
   202  func GetInternalIPv4() net.IP {
   203  	n := getLocalNode()
   204  	return clone(n.GetNodeInternalIPv4())
   205  }
   206  
   207  // GetInternalIPv6 returns node internal ipv6 address else return nil.
   208  func GetInternalIPv6() net.IP {
   209  	n := getLocalNode()
   210  	return clone(n.GetNodeInternalIPv6())
   211  }
   212  
   213  // GetCiliumEndpointNodeIP is the node IP that will be referenced by CiliumEndpoints with endpoints
   214  // running on this node.
   215  func GetCiliumEndpointNodeIP() string {
   216  	if option.Config.EnableIPv4 {
   217  		return GetIPv4().String()
   218  	}
   219  	return GetIPv6().String()
   220  }
   221  
   222  // SetInternalIPv4Router sets the cilium internal IPv4 node address, it is allocated from the node prefix.
   223  // This must not be conflated with k8s internal IP as this IP address is only relevant within the
   224  // Cilium-managed network (this means within the node for direct routing mode and on the overlay
   225  // for tunnel mode).
   226  func SetInternalIPv4Router(ip net.IP) {
   227  	localNode.Update(func(n *LocalNode) {
   228  		n.SetCiliumInternalIP(ip)
   229  	})
   230  }
   231  
   232  // GetInternalIPv4Router returns the cilium internal IPv4 node address. This must not be conflated with
   233  // k8s internal IP as this IP address is only relevant within the Cilium-managed network (this means
   234  // within the node for direct routing mode and on the overlay for tunnel mode).
   235  func GetInternalIPv4Router() net.IP {
   236  	n := getLocalNode()
   237  	return n.GetCiliumInternalIP(false)
   238  }
   239  
   240  // GetK8sExternalIPv4 returns the external IPv4 node address. It must be a public IP that is routable
   241  // on the network as well as the internet. It can return nil if no External IPv4 address is assigned.
   242  func GetK8sExternalIPv4() net.IP {
   243  	n := getLocalNode()
   244  	return n.GetExternalIP(false)
   245  }
   246  
   247  // GetRouterInfo returns additional information for the router, the cilium_host interface.
   248  func GetRouterInfo() RouterInfo {
   249  	addrsMu.RLock()
   250  	defer addrsMu.RUnlock()
   251  	return addrs.routerInfo
   252  }
   253  
   254  // SetRouterInfo sets additional information for the router, the cilium_host interface.
   255  func SetRouterInfo(info RouterInfo) {
   256  	addrsMu.Lock()
   257  	addrs.routerInfo = info
   258  	addrsMu.Unlock()
   259  }
   260  
   261  // GetHostMasqueradeIPv4 returns the IPv4 address to be used for masquerading
   262  // any traffic that is being forwarded from the host into the Cilium cluster.
   263  func GetHostMasqueradeIPv4() net.IP {
   264  	return GetInternalIPv4Router()
   265  }
   266  
   267  // SetIPv4AllocRange sets the IPv4 address pool to use when allocating
   268  // addresses for local endpoints
   269  func SetIPv4AllocRange(net *cidr.CIDR) {
   270  	localNode.Update(func(n *LocalNode) {
   271  		n.IPv4AllocCIDR = net
   272  	})
   273  }
   274  
   275  // SetIPv6NodeRange sets the IPv6 address pool to be used on this node
   276  func SetIPv6NodeRange(net *cidr.CIDR) {
   277  	localNode.Update(func(n *LocalNode) {
   278  		n.IPv6AllocCIDR = net
   279  	})
   280  }
   281  
   282  // AutoComplete completes the parts of addressing that can be auto derived
   283  func AutoComplete() error {
   284  	InitDefaultPrefix(option.Config.DirectRoutingDevice)
   285  
   286  	if option.Config.EnableIPv6 && GetIPv6AllocRange() == nil {
   287  		return fmt.Errorf("IPv6 allocation CIDR is not configured. Please specify --%s", option.IPv6Range)
   288  	}
   289  
   290  	if option.Config.EnableIPv4 && GetIPv4AllocRange() == nil {
   291  		return fmt.Errorf("IPv4 allocation CIDR is not configured. Please specify --%s", option.IPv4Range)
   292  	}
   293  
   294  	return nil
   295  }
   296  
   297  // ValidatePostInit validates the entire addressing setup and completes it as
   298  // required
   299  func ValidatePostInit() error {
   300  	if option.Config.EnableIPv4 || option.Config.TunnelingEnabled() {
   301  		if GetIPv4() == nil {
   302  			return fmt.Errorf("external IPv4 node address could not be derived, please configure via --ipv4-node")
   303  		}
   304  	}
   305  
   306  	if option.Config.EnableIPv4 && GetInternalIPv4Router() == nil {
   307  		return fmt.Errorf("BUG: Internal IPv4 node address was not configured")
   308  	}
   309  
   310  	return nil
   311  }
   312  
   313  // GetIPv6 returns the IPv6 address of the node
   314  func GetIPv6() net.IP {
   315  	n := getLocalNode()
   316  	return clone(n.GetNodeIP(true))
   317  }
   318  
   319  // GetHostMasqueradeIPv6 returns the IPv6 address to be used for masquerading
   320  // any traffic that is being forwarded from the host into the Cilium cluster.
   321  func GetHostMasqueradeIPv6() net.IP {
   322  	return GetIPv6Router()
   323  }
   324  
   325  // GetIPv6Router returns the IPv6 address of the router, e.g. address
   326  // of cilium_host device.
   327  func GetIPv6Router() net.IP {
   328  	n := getLocalNode()
   329  	return clone(n.GetCiliumInternalIP(true))
   330  }
   331  
   332  // SetIPv6Router sets the IPv6 address of the router address, e.g. address
   333  // of cilium_host device.
   334  func SetIPv6Router(ip net.IP) {
   335  	localNode.Update(func(n *LocalNode) {
   336  		n.SetCiliumInternalIP(ip)
   337  	})
   338  }
   339  
   340  // GetK8sExternalIPv6 returns the external IPv6 node address.
   341  func GetK8sExternalIPv6() net.IP {
   342  	n := getLocalNode()
   343  	return clone(n.GetExternalIP(false))
   344  }
   345  
   346  // GetNodeAddressing returns the NodeAddressing model for the local IPs.
   347  func GetNodeAddressing() *models.NodeAddressing {
   348  	a := &models.NodeAddressing{}
   349  
   350  	if option.Config.EnableIPv6 {
   351  		a.IPV6 = &models.NodeAddressingElement{
   352  			Enabled:    option.Config.EnableIPv6,
   353  			IP:         GetIPv6Router().String(),
   354  			AllocRange: GetIPv6AllocRange().String(),
   355  		}
   356  	}
   357  
   358  	if option.Config.EnableIPv4 {
   359  		a.IPV4 = &models.NodeAddressingElement{
   360  			Enabled:    option.Config.EnableIPv4,
   361  			IP:         GetInternalIPv4Router().String(),
   362  			AllocRange: GetIPv4AllocRange().String(),
   363  		}
   364  	}
   365  
   366  	return a
   367  }
   368  
   369  func getCiliumHostIPsFromFile(nodeConfig string) (ipv4GW, ipv6Router net.IP) {
   370  	// ipLen is the length of the IP address stored in the node_config.h
   371  	// it has the same length for both IPv4 and IPv6.
   372  	const ipLen = net.IPv6len
   373  
   374  	var hasIPv4, hasIPv6 bool
   375  	f, err := os.Open(nodeConfig)
   376  	switch {
   377  	case err != nil:
   378  	default:
   379  		defer f.Close()
   380  		scanner := bufio.NewScanner(f)
   381  		for scanner.Scan() {
   382  			txt := scanner.Text()
   383  			switch {
   384  			case !hasIPv6 && strings.Contains(txt, defaults.RestoreV6Addr):
   385  				defineLine := strings.Split(txt, defaults.RestoreV6Addr)
   386  				if len(defineLine) != 2 {
   387  					continue
   388  				}
   389  				ipv6 := common.C2GoArray(defineLine[1])
   390  				if len(ipv6) != ipLen {
   391  					continue
   392  				}
   393  				ipv6Router = net.IP(ipv6)
   394  				hasIPv6 = true
   395  			case !hasIPv4 && strings.Contains(txt, defaults.RestoreV4Addr):
   396  				defineLine := strings.Split(txt, defaults.RestoreV4Addr)
   397  				if len(defineLine) != 2 {
   398  					continue
   399  				}
   400  				ipv4 := common.C2GoArray(defineLine[1])
   401  				if len(ipv4) != ipLen {
   402  					continue
   403  				}
   404  				ipv4GW = net.IP(ipv4)
   405  				hasIPv4 = true
   406  
   407  			// Legacy cases based on the header defines:
   408  			case !hasIPv4 && strings.Contains(txt, "IPV4_GATEWAY"):
   409  				// #define IPV4_GATEWAY 0xee1c000a
   410  				defineLine := strings.Split(txt, " ")
   411  				if len(defineLine) != 3 {
   412  					continue
   413  				}
   414  				ipv4GWHex := strings.TrimPrefix(defineLine[2], "0x")
   415  				ipv4GWUint64, err := strconv.ParseUint(ipv4GWHex, 16, 32)
   416  				if err != nil {
   417  					continue
   418  				}
   419  				if ipv4GWUint64 != 0 {
   420  					bs := make([]byte, net.IPv4len)
   421  					byteorder.Native.PutUint32(bs, uint32(ipv4GWUint64))
   422  					ipv4GW = net.IPv4(bs[0], bs[1], bs[2], bs[3])
   423  					hasIPv4 = true
   424  				}
   425  			case !hasIPv6 && strings.Contains(txt, " ROUTER_IP "):
   426  				// #define ROUTER_IP 0xf0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8a, 0xd6
   427  				defineLine := strings.Split(txt, " ROUTER_IP ")
   428  				if len(defineLine) != 2 {
   429  					continue
   430  				}
   431  				ipv6 := common.C2GoArray(defineLine[1])
   432  				if len(ipv6) != net.IPv6len {
   433  					continue
   434  				}
   435  				ipv6Router = net.IP(ipv6)
   436  				hasIPv6 = true
   437  			}
   438  		}
   439  	}
   440  	return ipv4GW, ipv6Router
   441  }
   442  
   443  // ExtractCiliumHostIPFromFS returns the Cilium IPv4 gateway and router IPv6 address from
   444  // the node_config.h file if is present; or by deriving it from
   445  // defaults.HostDevice interface, on which only the IPv4 is possible to derive.
   446  func ExtractCiliumHostIPFromFS() (ipv4GW, ipv6Router net.IP) {
   447  	nodeConfig := option.Config.GetNodeConfigPath()
   448  	ipv4GW, ipv6Router = getCiliumHostIPsFromFile(nodeConfig)
   449  	if ipv4GW != nil || ipv6Router != nil {
   450  		log.WithFields(logrus.Fields{
   451  			"ipv4": ipv4GW,
   452  			"ipv6": ipv6Router,
   453  			"file": nodeConfig,
   454  		}).Info("Restored router address from node_config")
   455  		return ipv4GW, ipv6Router
   456  	}
   457  	return getCiliumHostIPsFromNetDev(defaults.HostDevice)
   458  }
   459  
   460  // SetIPsecKeyIdentity sets the IPsec key identity an opaque value used to
   461  // identity encryption keys used on the node.
   462  func SetIPsecKeyIdentity(id uint8) {
   463  	localNode.Update(func(n *LocalNode) {
   464  		n.EncryptionKey = id
   465  	})
   466  }
   467  
   468  // GetK8sNodeIPs returns k8s Node IP addr.
   469  func GetK8sNodeIP() net.IP {
   470  	n := getLocalNode()
   471  	return n.GetK8sNodeIP()
   472  }
   473  
   474  func GetWireguardPubKey() string {
   475  	return getLocalNode().WireguardPubKey
   476  }
   477  
   478  func GetOptOutNodeEncryption() bool {
   479  	return getLocalNode().OptOutNodeEncryption
   480  }
   481  
   482  // SetEndpointHealthIPv4 sets the IPv4 cilium-health endpoint address.
   483  func SetEndpointHealthIPv4(ip net.IP) {
   484  	localNode.Update(func(n *LocalNode) {
   485  		n.IPv4HealthIP = ip
   486  	})
   487  }
   488  
   489  // GetEndpointHealthIPv4 returns the IPv4 cilium-health endpoint address.
   490  func GetEndpointHealthIPv4() net.IP {
   491  	return getLocalNode().IPv4HealthIP
   492  }
   493  
   494  // SetEndpointHealthIPv6 sets the IPv6 cilium-health endpoint address.
   495  func SetEndpointHealthIPv6(ip net.IP) {
   496  	localNode.Update(func(n *LocalNode) {
   497  		n.IPv6HealthIP = ip
   498  	})
   499  }
   500  
   501  // GetEndpointHealthIPv6 returns the IPv6 cilium-health endpoint address.
   502  func GetEndpointHealthIPv6() net.IP {
   503  	return getLocalNode().IPv6HealthIP
   504  }
   505  
   506  // SetIngressIPv4 sets the local IPv4 source address for Cilium Ingress.
   507  func SetIngressIPv4(ip net.IP) {
   508  	localNode.Update(func(n *LocalNode) {
   509  		n.IPv4IngressIP = ip
   510  	})
   511  }
   512  
   513  // GetIngressIPv4 returns the local IPv4 source address for Cilium Ingress.
   514  func GetIngressIPv4() net.IP {
   515  	return getLocalNode().IPv4IngressIP
   516  }
   517  
   518  // SetIngressIPv6 sets the local IPv6 source address for Cilium Ingress.
   519  func SetIngressIPv6(ip net.IP) {
   520  	localNode.Update(func(n *LocalNode) {
   521  		n.IPv6IngressIP = ip
   522  	})
   523  }
   524  
   525  // GetIngressIPv6 returns the local IPv6 source address for Cilium Ingress.
   526  func GetIngressIPv6() net.IP {
   527  	return getLocalNode().IPv6IngressIP
   528  }
   529  
   530  // GetEndpointEncryptKeyIndex returns the encryption key value for an endpoint
   531  // owned by the local node.
   532  // With IPSec encryption, this is the ID of the currently loaded key.
   533  // With WireGuard, this returns a non-zero static value.
   534  // Note that the key index returned by this function is only valid for _endpoints_
   535  // of the local node. If you want to obtain the key index of the local node itself,
   536  // access the `EncryptionKey` field via the LocalNodeStore.
   537  func GetEndpointEncryptKeyIndex() uint8 {
   538  	switch {
   539  	case option.Config.EnableIPSec:
   540  		return getLocalNode().EncryptionKey
   541  	case option.Config.EnableWireguard:
   542  		return wgTypes.StaticEncryptKey
   543  
   544  	}
   545  	return 0
   546  }
   547  
   548  // WithTestLocalNodeStore sets the 'localNode' to a temporary instance and
   549  // runs the given test. Afterwards the 'localNode' is restored to nil.
   550  // This is a temporary workaround for tests until the LocalNodeStoreCell can be
   551  // used.
   552  func WithTestLocalNodeStore(runTest func()) {
   553  	SetTestLocalNodeStore()
   554  	defer UnsetTestLocalNodeStore()
   555  	runTest()
   556  }
   557  
   558  func SetTestLocalNodeStore() {
   559  	if localNode != nil {
   560  		panic("localNode already set")
   561  	}
   562  
   563  	// Set the localNode global variable temporarily so that the legacy getters
   564  	// and setters can access it.
   565  	localNode = NewTestLocalNodeStore(LocalNode{})
   566  }
   567  
   568  func UnsetTestLocalNodeStore() {
   569  	localNode = nil
   570  }
   571  
   572  // UpdateLocalNodeInTest provides access to modifying the local node
   573  // information from tests that are not yet using hive and the LocalNodeStoreCell.
   574  func UpdateLocalNodeInTest(mod func(n *LocalNode)) {
   575  	if localNode == nil {
   576  		panic("localNode not set, use node.LocalNodeStoreCell or WithTestLocalNodeStore()?")
   577  	}
   578  	localNode.Update(mod)
   579  }