github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/net/interface.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package net
    18  
    19  import (
    20  	"bufio"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"os"
    26  
    27  	"strings"
    28  
    29  	"k8s.io/klog/v2"
    30  	netutils "k8s.io/utils/net"
    31  )
    32  
    33  type AddressFamily uint
    34  
    35  const (
    36  	familyIPv4 AddressFamily = 4
    37  	familyIPv6 AddressFamily = 6
    38  )
    39  
    40  type AddressFamilyPreference []AddressFamily
    41  
    42  var (
    43  	preferIPv4 = AddressFamilyPreference{familyIPv4, familyIPv6}
    44  	preferIPv6 = AddressFamilyPreference{familyIPv6, familyIPv4}
    45  )
    46  
    47  const (
    48  	// LoopbackInterfaceName is the default name of the loopback interface
    49  	LoopbackInterfaceName = "lo"
    50  )
    51  
    52  const (
    53  	ipv4RouteFile = "/proc/net/route"
    54  	ipv6RouteFile = "/proc/net/ipv6_route"
    55  )
    56  
    57  type Route struct {
    58  	Interface   string
    59  	Destination net.IP
    60  	Gateway     net.IP
    61  	Family      AddressFamily
    62  }
    63  
    64  type RouteFile struct {
    65  	name  string
    66  	parse func(input io.Reader) ([]Route, error)
    67  }
    68  
    69  // noRoutesError can be returned in case of no routes
    70  type noRoutesError struct {
    71  	message string
    72  }
    73  
    74  func (e noRoutesError) Error() string {
    75  	return e.message
    76  }
    77  
    78  // IsNoRoutesError checks if an error is of type noRoutesError
    79  func IsNoRoutesError(err error) bool {
    80  	if err == nil {
    81  		return false
    82  	}
    83  	switch err.(type) {
    84  	case noRoutesError:
    85  		return true
    86  	default:
    87  		return false
    88  	}
    89  }
    90  
    91  var (
    92  	v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
    93  	v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
    94  )
    95  
    96  func (rf RouteFile) extract() ([]Route, error) {
    97  	file, err := os.Open(rf.name)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	defer file.Close()
   102  	return rf.parse(file)
   103  }
   104  
   105  // getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
   106  func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
   107  	routes := []Route{}
   108  	scanner := bufio.NewReader(input)
   109  	for {
   110  		line, err := scanner.ReadString('\n')
   111  		if err == io.EOF {
   112  			break
   113  		}
   114  		//ignore the headers in the route info
   115  		if strings.HasPrefix(line, "Iface") {
   116  			continue
   117  		}
   118  		fields := strings.Fields(line)
   119  		// Interested in fields:
   120  		//  0 - interface name
   121  		//  1 - destination address
   122  		//  2 - gateway
   123  		dest, err := parseIP(fields[1], familyIPv4)
   124  		if err != nil {
   125  			return nil, err
   126  		}
   127  		gw, err := parseIP(fields[2], familyIPv4)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  		if !dest.Equal(net.IPv4zero) {
   132  			continue
   133  		}
   134  		routes = append(routes, Route{
   135  			Interface:   fields[0],
   136  			Destination: dest,
   137  			Gateway:     gw,
   138  			Family:      familyIPv4,
   139  		})
   140  	}
   141  	return routes, nil
   142  }
   143  
   144  func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
   145  	routes := []Route{}
   146  	scanner := bufio.NewReader(input)
   147  	for {
   148  		line, err := scanner.ReadString('\n')
   149  		if err == io.EOF {
   150  			break
   151  		}
   152  		fields := strings.Fields(line)
   153  		// Interested in fields:
   154  		//  0 - destination address
   155  		//  4 - gateway
   156  		//  9 - interface name
   157  		dest, err := parseIP(fields[0], familyIPv6)
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  		gw, err := parseIP(fields[4], familyIPv6)
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  		if !dest.Equal(net.IPv6zero) {
   166  			continue
   167  		}
   168  		if gw.Equal(net.IPv6zero) {
   169  			continue // loopback
   170  		}
   171  		routes = append(routes, Route{
   172  			Interface:   fields[9],
   173  			Destination: dest,
   174  			Gateway:     gw,
   175  			Family:      familyIPv6,
   176  		})
   177  	}
   178  	return routes, nil
   179  }
   180  
   181  // parseIP takes the hex IP address string from route file and converts it
   182  // to a net.IP address. For IPv4, the value must be converted to big endian.
   183  func parseIP(str string, family AddressFamily) (net.IP, error) {
   184  	if str == "" {
   185  		return nil, fmt.Errorf("input is nil")
   186  	}
   187  	bytes, err := hex.DecodeString(str)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	if family == familyIPv4 {
   192  		if len(bytes) != net.IPv4len {
   193  			return nil, fmt.Errorf("invalid IPv4 address in route")
   194  		}
   195  		return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
   196  	}
   197  	// Must be IPv6
   198  	if len(bytes) != net.IPv6len {
   199  		return nil, fmt.Errorf("invalid IPv6 address in route")
   200  	}
   201  	return net.IP(bytes), nil
   202  }
   203  
   204  func isInterfaceUp(intf *net.Interface) bool {
   205  	if intf == nil {
   206  		return false
   207  	}
   208  	if intf.Flags&net.FlagUp != 0 {
   209  		klog.V(4).Infof("Interface %v is up", intf.Name)
   210  		return true
   211  	}
   212  	return false
   213  }
   214  
   215  func isLoopbackOrPointToPoint(intf *net.Interface) bool {
   216  	return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
   217  }
   218  
   219  // getMatchingGlobalIP returns the first valid global unicast address of the given
   220  // 'family' from the list of 'addrs'.
   221  func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
   222  	if len(addrs) > 0 {
   223  		for i := range addrs {
   224  			klog.V(4).Infof("Checking addr  %s.", addrs[i].String())
   225  			ip, _, err := netutils.ParseCIDRSloppy(addrs[i].String())
   226  			if err != nil {
   227  				return nil, err
   228  			}
   229  			if memberOf(ip, family) {
   230  				if ip.IsGlobalUnicast() {
   231  					klog.V(4).Infof("IP found %v", ip)
   232  					return ip, nil
   233  				} else {
   234  					klog.V(4).Infof("Non-global unicast address found %v", ip)
   235  				}
   236  			} else {
   237  				klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
   238  			}
   239  
   240  		}
   241  	}
   242  	return nil, nil
   243  }
   244  
   245  // getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
   246  // interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
   247  func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
   248  	intf, err := nw.InterfaceByName(intfName)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	if isInterfaceUp(intf) {
   253  		addrs, err := nw.Addrs(intf)
   254  		if err != nil {
   255  			return nil, err
   256  		}
   257  		klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
   258  		matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
   259  		if err != nil {
   260  			return nil, err
   261  		}
   262  		if matchingIP != nil {
   263  			klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
   264  			return matchingIP, nil
   265  		}
   266  	}
   267  	return nil, nil
   268  }
   269  
   270  // getIPFromLoopbackInterface gets the IPs on a loopback interface and returns a global unicast address, if any.
   271  // The loopback interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
   272  func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
   273  	intfs, err := nw.Interfaces()
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	for _, intf := range intfs {
   278  		if !isInterfaceUp(&intf) {
   279  			continue
   280  		}
   281  		if intf.Flags&(net.FlagLoopback) != 0 {
   282  			addrs, err := nw.Addrs(&intf)
   283  			if err != nil {
   284  				return nil, err
   285  			}
   286  			klog.V(4).Infof("Interface %q has %d addresses :%v.", intf.Name, len(addrs), addrs)
   287  			matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
   288  			if err != nil {
   289  				return nil, err
   290  			}
   291  			if matchingIP != nil {
   292  				klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intf.Name)
   293  				return matchingIP, nil
   294  			}
   295  		}
   296  	}
   297  	return nil, nil
   298  }
   299  
   300  // memberOf tells if the IP is of the desired family. Used for checking interface addresses.
   301  func memberOf(ip net.IP, family AddressFamily) bool {
   302  	if ip.To4() != nil {
   303  		return family == familyIPv4
   304  	} else {
   305  		return family == familyIPv6
   306  	}
   307  }
   308  
   309  // chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
   310  // has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
   311  // addressFamilies determines whether it prefers IPv4 or IPv6
   312  func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
   313  	intfs, err := nw.Interfaces()
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  	if len(intfs) == 0 {
   318  		return nil, fmt.Errorf("no interfaces found on host.")
   319  	}
   320  	for _, family := range addressFamilies {
   321  		klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
   322  		for _, intf := range intfs {
   323  			if !isInterfaceUp(&intf) {
   324  				klog.V(4).Infof("Skipping: down interface %q", intf.Name)
   325  				continue
   326  			}
   327  			if isLoopbackOrPointToPoint(&intf) {
   328  				klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
   329  				continue
   330  			}
   331  			addrs, err := nw.Addrs(&intf)
   332  			if err != nil {
   333  				return nil, err
   334  			}
   335  			if len(addrs) == 0 {
   336  				klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
   337  				continue
   338  			}
   339  			for _, addr := range addrs {
   340  				ip, _, err := netutils.ParseCIDRSloppy(addr.String())
   341  				if err != nil {
   342  					return nil, fmt.Errorf("unable to parse CIDR for interface %q: %s", intf.Name, err)
   343  				}
   344  				if !memberOf(ip, family) {
   345  					klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
   346  					continue
   347  				}
   348  				// TODO: Decide if should open up to allow IPv6 LLAs in future.
   349  				if !ip.IsGlobalUnicast() {
   350  					klog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
   351  					continue
   352  				}
   353  				klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
   354  				return ip, nil
   355  			}
   356  		}
   357  	}
   358  	return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
   359  }
   360  
   361  // ChooseHostInterface is a method used fetch an IP for a daemon.
   362  // If there is no routing info file, it will choose a global IP from the system
   363  // interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
   364  // IP of the interface with a gateway on it (with priority given to IPv4). For a node
   365  // with no internet connection, it returns error.
   366  func ChooseHostInterface() (net.IP, error) {
   367  	return chooseHostInterface(preferIPv4)
   368  }
   369  
   370  func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
   371  	var nw networkInterfacer = networkInterface{}
   372  	if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
   373  		return chooseIPFromHostInterfaces(nw, addressFamilies)
   374  	}
   375  	routes, err := getAllDefaultRoutes()
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  	return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
   380  }
   381  
   382  // networkInterfacer defines an interface for several net library functions. Production
   383  // code will forward to net library functions, and unit tests will override the methods
   384  // for testing purposes.
   385  type networkInterfacer interface {
   386  	InterfaceByName(intfName string) (*net.Interface, error)
   387  	Addrs(intf *net.Interface) ([]net.Addr, error)
   388  	Interfaces() ([]net.Interface, error)
   389  }
   390  
   391  // networkInterface implements the networkInterfacer interface for production code, just
   392  // wrapping the underlying net library function calls.
   393  type networkInterface struct{}
   394  
   395  func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   396  	return net.InterfaceByName(intfName)
   397  }
   398  
   399  func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   400  	return intf.Addrs()
   401  }
   402  
   403  func (_ networkInterface) Interfaces() ([]net.Interface, error) {
   404  	return net.Interfaces()
   405  }
   406  
   407  // getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
   408  // to read the IPv4 routing info file, we return an error. If unable to read the IPv6
   409  // routing info file (which is optional), we'll just use the IPv4 route information.
   410  // Using all the routing info, if no default routes are found, an error is returned.
   411  func getAllDefaultRoutes() ([]Route, error) {
   412  	routes, err := v4File.extract()
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  	v6Routes, _ := v6File.extract()
   417  	routes = append(routes, v6Routes...)
   418  	if len(routes) == 0 {
   419  		return nil, noRoutesError{
   420  			message: fmt.Sprintf("no default routes found in %q or %q", v4File.name, v6File.name),
   421  		}
   422  	}
   423  	return routes, nil
   424  }
   425  
   426  // chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
   427  // global IP address from the interface for the route. If there are routes but no global
   428  // address is obtained from the interfaces, it checks if the loopback interface has a global address.
   429  // addressFamilies determines whether it prefers IPv4 or IPv6
   430  func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
   431  	for _, family := range addressFamilies {
   432  		klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
   433  		for _, route := range routes {
   434  			if route.Family != family {
   435  				continue
   436  			}
   437  			klog.V(4).Infof("Default route transits interface %q", route.Interface)
   438  			finalIP, err := getIPFromInterface(route.Interface, family, nw)
   439  			if err != nil {
   440  				return nil, err
   441  			}
   442  			if finalIP != nil {
   443  				klog.V(4).Infof("Found active IP %v ", finalIP)
   444  				return finalIP, nil
   445  			}
   446  			// In case of network setups where default routes are present, but network
   447  			// interfaces use only link-local addresses (e.g. as described in RFC5549).
   448  			// the global IP is assigned to the loopback interface, and we should use it
   449  			loopbackIP, err := getIPFromLoopbackInterface(family, nw)
   450  			if err != nil {
   451  				return nil, err
   452  			}
   453  			if loopbackIP != nil {
   454  				klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
   455  				return loopbackIP, nil
   456  			}
   457  		}
   458  	}
   459  	klog.V(4).Infof("No active IP found by looking at default routes")
   460  	return nil, fmt.Errorf("unable to select an IP from default routes.")
   461  }
   462  
   463  // ResolveBindAddress returns the IP address of a daemon, based on the given bindAddress:
   464  // If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
   465  // If bindAddress is unspecified or loopback, it returns the default IP of the same
   466  // address family as bindAddress.
   467  // Otherwise, it just returns bindAddress.
   468  func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
   469  	addressFamilies := preferIPv4
   470  	if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
   471  		addressFamilies = preferIPv6
   472  	}
   473  
   474  	if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
   475  		hostIP, err := chooseHostInterface(addressFamilies)
   476  		if err != nil {
   477  			return nil, err
   478  		}
   479  		bindAddress = hostIP
   480  	}
   481  	return bindAddress, nil
   482  }
   483  
   484  // ChooseBindAddressForInterface choose a global IP for a specific interface, with priority given to IPv4.
   485  // This is required in case of network setups where default routes are present, but network
   486  // interfaces use only link-local addresses (e.g. as described in RFC5549).
   487  // e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
   488  func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
   489  	var nw networkInterfacer = networkInterface{}
   490  	for _, family := range preferIPv4 {
   491  		ip, err := getIPFromInterface(intfName, family, nw)
   492  		if err != nil {
   493  			return nil, err
   494  		}
   495  		if ip != nil {
   496  			return ip, nil
   497  		}
   498  	}
   499  	return nil, fmt.Errorf("unable to select an IP from %s network interface", intfName)
   500  }