github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/node/node_address_linux.go (about)

     1  // Copyright 2016-2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // +build !darwin
    16  
    17  package node
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"net"
    24  	"sort"
    25  
    26  	"golang.org/x/sys/unix"
    27  
    28  	"github.com/cilium/cilium/pkg/ip"
    29  
    30  	"github.com/vishvananda/netlink"
    31  )
    32  
    33  func firstGlobalAddr(intf string, preferredIP net.IP, family int) (net.IP, error) {
    34  	var link netlink.Link
    35  	var ipLen int
    36  	var err error
    37  
    38  	ipsToExclude := ip.GetExcludedIPs()
    39  	linkScopeMax := unix.RT_SCOPE_UNIVERSE
    40  	if family == netlink.FAMILY_V4 {
    41  		ipLen = 4
    42  	} else {
    43  		ipLen = 16
    44  	}
    45  
    46  	if intf != "" && intf != "undefined" {
    47  		link, err = netlink.LinkByName(intf)
    48  		if err != nil {
    49  			link = nil
    50  		} else {
    51  			ipsToExclude = []net.IP{}
    52  		}
    53  	}
    54  
    55  retryInterface:
    56  	addr, err := netlink.AddrList(link, family)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  retryScope:
    62  	ipsPublic := []net.IP{}
    63  	ipsPrivate := []net.IP{}
    64  	hasPreferred := false
    65  
    66  	for _, a := range addr {
    67  		if a.Scope <= linkScopeMax {
    68  			if ip.IsExcluded(ipsToExclude, a.IP) {
    69  				continue
    70  			}
    71  			if len(a.IP) >= ipLen {
    72  				if ip.IsPublicAddr(a.IP) {
    73  					ipsPublic = append(ipsPublic, a.IP)
    74  				} else {
    75  					ipsPrivate = append(ipsPrivate, a.IP)
    76  				}
    77  				// If the IP is the same as the preferredIP, that
    78  				// means that maybe it is restored from node_config.h,
    79  				// so if it is present we prefer this one.
    80  				if a.IP.Equal(preferredIP) {
    81  					hasPreferred = true
    82  				}
    83  			}
    84  		}
    85  	}
    86  
    87  	if len(ipsPublic) != 0 {
    88  		if hasPreferred && ip.IsPublicAddr(preferredIP) {
    89  			return preferredIP, nil
    90  		}
    91  
    92  		// Just make sure that we always return the same one and not a
    93  		// random one. More info in the issue GH-7637.
    94  		sort.Slice(ipsPublic, func(i, j int) bool {
    95  			return bytes.Compare(ipsPublic[i], ipsPublic[j]) < 0
    96  		})
    97  
    98  		return ipsPublic[0], nil
    99  	}
   100  
   101  	if len(ipsPrivate) != 0 {
   102  		if hasPreferred && !ip.IsPublicAddr(preferredIP) {
   103  			return preferredIP, nil
   104  		}
   105  
   106  		// Same stable order, see above ipsPublic.
   107  		sort.Slice(ipsPrivate, func(i, j int) bool {
   108  			return bytes.Compare(ipsPrivate[i], ipsPrivate[j]) < 0
   109  		})
   110  
   111  		return ipsPrivate[0], nil
   112  	}
   113  
   114  	// First, if a device is specified, fall back to anything wider
   115  	// than link (site, custom, ...) before trying all devices.
   116  	if linkScopeMax != unix.RT_SCOPE_SITE {
   117  		linkScopeMax = unix.RT_SCOPE_SITE
   118  		goto retryScope
   119  	}
   120  
   121  	// Fall back with retry for all interfaces with full scope again
   122  	// (which then goes back to lower scope again for all interfaces
   123  	// before we give up completely).
   124  	if link != nil {
   125  		linkScopeMax = unix.RT_SCOPE_UNIVERSE
   126  		link = nil
   127  		goto retryInterface
   128  	}
   129  
   130  	return nil, fmt.Errorf("No address found")
   131  }
   132  
   133  // firstGlobalV4Addr returns the first IPv4 global IP of an interface,
   134  // where the IPs are sorted in ascending order.
   135  //
   136  // Public IPs are preferred over private ones. When intf is defined only
   137  // IPs belonging to that interface are considered.
   138  //
   139  // If preferredIP is present in the IP list it is returned irrespective of
   140  // the sort order. However, if preferredIP is a private IP and there are
   141  // public IPs, then public one is selected.
   142  //
   143  // Passing intf and preferredIP will only return preferredIP if it is in
   144  // the IPs that belong to intf.
   145  //
   146  // In all cases, if intf is not found all interfaces are considered.
   147  //
   148  // If a intf-specific global address couldn't be found, we retry to find
   149  // an address with reduced scope (site, custom) on that particular device.
   150  //
   151  // If the latter fails as well, we retry on all interfaces beginning with
   152  // universe scope again (and then falling back to reduced scope).
   153  //
   154  // In case none of the above helped, we bail out with error.
   155  func firstGlobalV4Addr(intf string, preferredIP net.IP) (net.IP, error) {
   156  	return firstGlobalAddr(intf, preferredIP, netlink.FAMILY_V4)
   157  }
   158  
   159  // firstGlobalV6Addr returns first IPv6 global IP of an interface, see
   160  // firstGlobalV4Addr for more details.
   161  func firstGlobalV6Addr(intf string, preferredIP net.IP) (net.IP, error) {
   162  	return firstGlobalAddr(intf, preferredIP, netlink.FAMILY_V6)
   163  }
   164  
   165  // getCiliumHostIPsFromNetDev returns the first IPv4 link local and returns
   166  // it
   167  func getCiliumHostIPsFromNetDev(devName string) (ipv4GW, ipv6Router net.IP) {
   168  	hostDev, err := netlink.LinkByName(devName)
   169  	if err != nil {
   170  		return nil, nil
   171  	}
   172  	addrs, err := netlink.AddrList(hostDev, netlink.FAMILY_ALL)
   173  	if err != nil {
   174  		return nil, nil
   175  	}
   176  	for _, addr := range addrs {
   177  		if addr.IP.To4() != nil {
   178  			if addr.Scope == int(netlink.SCOPE_LINK) {
   179  				ipv4GW = addr.IP
   180  			}
   181  		} else {
   182  			if addr.Scope != int(netlink.SCOPE_LINK) {
   183  				ipv6Router = addr.IP
   184  			}
   185  		}
   186  	}
   187  	return ipv4GW, ipv6Router
   188  }
   189  
   190  // SetInternalIPv4From sets the internal IPv4 with the first global address
   191  // found in that interface.
   192  func SetInternalIPv4From(ifaceName string) error {
   193  	l, err := netlink.LinkByName(ifaceName)
   194  	if err != nil {
   195  		return errors.New("unable to retrieve interface attributes")
   196  	}
   197  	v4Addrs, err := netlink.AddrList(l, netlink.FAMILY_V4)
   198  	if err != nil {
   199  		return errors.New("unable to retrieve interface IPv4 address")
   200  	}
   201  	for _, ip := range v4Addrs {
   202  		if netlink.Scope(ip.Scope) == netlink.SCOPE_UNIVERSE {
   203  			SetInternalIPv4(ip.IP)
   204  			return nil
   205  		}
   206  	}
   207  	return errors.New("unable to find IP addresses with scope global")
   208  }