github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/pkg/util/util.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 util
    18  
    19  import (
    20  	"bufio"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"os"
    26  	"os/exec"
    27  	"path/filepath"
    28  	"strings"
    29  	"time"
    30  
    31  	"k8s.io/klog"
    32  
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  )
    35  
    36  //AddressFamily is uint type var to describe family ips
    37  type AddressFamily uint
    38  
    39  const (
    40  	familyIPv4 AddressFamily = 4
    41  	familyIPv6 AddressFamily = 6
    42  )
    43  
    44  const (
    45  	ipv4RouteFile = "/proc/net/route"
    46  	ipv6RouteFile = "/proc/net/ipv6_route"
    47  )
    48  
    49  //Route is to define object of connection route
    50  type Route struct {
    51  	Interface   string
    52  	Destination net.IP
    53  	Gateway     net.IP
    54  	Family      AddressFamily
    55  }
    56  
    57  //RouteFile is object of file of routs
    58  type RouteFile struct {
    59  	name  string
    60  	parse func(input io.Reader) ([]Route, error)
    61  }
    62  
    63  var (
    64  	v4File = RouteFile{name: ipv4RouteFile, parse: GetIPv4DefaultRoutes}
    65  	v6File = RouteFile{name: ipv6RouteFile, parse: GetIPv6DefaultRoutes}
    66  )
    67  
    68  func GetLocalIP(hostName string) (string, error) {
    69  	var ipAddr net.IP
    70  	var err error
    71  	addrs, _ := net.LookupIP(hostName)
    72  	for _, addr := range addrs {
    73  		if err := ValidateNodeIP(addr); err == nil {
    74  			if addr.To4() != nil {
    75  				ipAddr = addr
    76  				break
    77  			}
    78  			if addr.To16() != nil && ipAddr == nil {
    79  				ipAddr = addr
    80  			}
    81  		}
    82  	}
    83  	if ipAddr == nil {
    84  		ipAddr, err = ChooseHostInterface()
    85  	}
    86  
    87  	if err != nil {
    88  		return "", err
    89  	}
    90  
    91  	return ipAddr.String(), nil
    92  }
    93  
    94  func (rf RouteFile) extract() ([]Route, error) {
    95  	file, err := os.Open(rf.name)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	defer file.Close()
   100  	return rf.parse(file)
   101  }
   102  
   103  // GetIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
   104  func GetIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
   105  	routes := []Route{}
   106  	scanner := bufio.NewReader(input)
   107  	for {
   108  		line, err := scanner.ReadString('\n')
   109  		if err == io.EOF {
   110  			break
   111  		}
   112  		//ignore the headers in the route info
   113  		if strings.HasPrefix(line, "Iface") {
   114  			continue
   115  		}
   116  		fields := strings.Fields(line)
   117  		// Interested in fields:
   118  		//  0 - interface name
   119  		//  1 - destination address
   120  		//  2 - gateway
   121  		dest, err := ParseIP(fields[1], familyIPv4)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  		gw, err := ParseIP(fields[2], familyIPv4)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  		if !dest.Equal(net.IPv4zero) {
   130  			continue
   131  		}
   132  		routes = append(routes, Route{
   133  			Interface:   fields[0],
   134  			Destination: dest,
   135  			Gateway:     gw,
   136  			Family:      familyIPv4,
   137  		})
   138  	}
   139  	return routes, nil
   140  }
   141  
   142  // GetIPv6DefaultRoutes obtains the IPv6 routes, and filters out non-default routes.
   143  func GetIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
   144  	routes := []Route{}
   145  	scanner := bufio.NewReader(input)
   146  	for {
   147  		line, err := scanner.ReadString('\n')
   148  		if err == io.EOF {
   149  			break
   150  		}
   151  		fields := strings.Fields(line)
   152  		// Interested in fields:
   153  		//  0 - destination address
   154  		//  4 - gateway
   155  		//  9 - interface name
   156  		dest, err := ParseIP(fields[0], familyIPv6)
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  		gw, err := ParseIP(fields[4], familyIPv6)
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  		if !dest.Equal(net.IPv6zero) {
   165  			continue
   166  		}
   167  		if gw.Equal(net.IPv6zero) {
   168  			continue // loopback
   169  		}
   170  		routes = append(routes, Route{
   171  			Interface:   fields[9],
   172  			Destination: dest,
   173  			Gateway:     gw,
   174  			Family:      familyIPv6,
   175  		})
   176  	}
   177  	return routes, nil
   178  }
   179  
   180  // ParseIP takes the hex IP address string from route file and converts it
   181  // to a net.IP address. For IPv4, the value must be converted to big endian.
   182  func ParseIP(str string, family AddressFamily) (net.IP, error) {
   183  	if str == "" {
   184  		return nil, fmt.Errorf("input is nil")
   185  	}
   186  	bytes, err := hex.DecodeString(str)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	if family == familyIPv4 {
   191  		if len(bytes) != net.IPv4len {
   192  			return nil, fmt.Errorf("invalid IPv4 address in route")
   193  		}
   194  		return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
   195  	}
   196  	// Must be IPv6
   197  	if len(bytes) != net.IPv6len {
   198  		return nil, fmt.Errorf("invalid IPv6 address in route")
   199  	}
   200  	return net.IP(bytes), nil
   201  }
   202  
   203  //IsInterfaceUp checks the interface is running or not
   204  func IsInterfaceUp(intf *net.Interface) bool {
   205  	if intf == nil {
   206  		return false
   207  	}
   208  	if intf.Flags&net.FlagUp != 0 {
   209  		klog.Infof("Interface %v is up", intf.Name)
   210  		return true
   211  	}
   212  	return false
   213  }
   214  
   215  //IsLoopbackOrPointToPoint checks interface is loopback or point to point
   216  func IsLoopbackOrPointToPoint(intf *net.Interface) bool {
   217  	return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
   218  }
   219  
   220  // GetMatchingGlobalIP returns the first valid global unicast address of the given
   221  // 'family' from the list of 'addrs'.
   222  func GetMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
   223  	if len(addrs) > 0 {
   224  		for i := range addrs {
   225  			klog.Infof("Checking addr  %s.", addrs[i].String())
   226  			ip, _, err := net.ParseCIDR(addrs[i].String())
   227  			if err != nil {
   228  				return nil, err
   229  			}
   230  			if MemberOf(ip, family) {
   231  				if ip.IsGlobalUnicast() {
   232  					klog.Infof("IP found %v", ip)
   233  					return ip, nil
   234  				}
   235  				klog.Infof("Non-global unicast address found %v", ip)
   236  			} else {
   237  				klog.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.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.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  // MemberOf tells if the IP is of the desired family. Used for checking interface addresses.
   271  func MemberOf(ip net.IP, family AddressFamily) bool {
   272  	if ip.To4() != nil {
   273  		return family == familyIPv4
   274  	}
   275  	//else
   276  	return family == familyIPv6
   277  }
   278  
   279  // ChooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
   280  // has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
   281  // Searches for IPv4 addresses, and then IPv6 addresses.
   282  func ChooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
   283  	intfs, err := nw.Interfaces()
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  	if len(intfs) == 0 {
   288  		return nil, fmt.Errorf("no interfaces found on host")
   289  	}
   290  	for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
   291  		klog.Infof("Looking for system interface with a global IPv%d address", uint(family))
   292  		for _, intf := range intfs {
   293  			if !IsInterfaceUp(&intf) {
   294  				klog.Infof("Skipping: down interface %q", intf.Name)
   295  				continue
   296  			}
   297  			if IsLoopbackOrPointToPoint(&intf) {
   298  				klog.Infof("Skipping: LB or P2P interface %q", intf.Name)
   299  				continue
   300  			}
   301  			addrs, err := nw.Addrs(&intf)
   302  			if err != nil {
   303  				return nil, err
   304  			}
   305  			if len(addrs) == 0 {
   306  				klog.Infof("Skipping: no addresses on interface %q", intf.Name)
   307  				continue
   308  			}
   309  			for _, addr := range addrs {
   310  				ip, _, err := net.ParseCIDR(addr.String())
   311  				if err != nil {
   312  					return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
   313  				}
   314  				if !MemberOf(ip, family) {
   315  					klog.Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
   316  					continue
   317  				}
   318  				// TODO: Decide if should open up to allow IPv6 LLAs in future.
   319  				if !ip.IsGlobalUnicast() {
   320  					klog.Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
   321  					continue
   322  				}
   323  				klog.Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
   324  				return ip, nil
   325  			}
   326  		}
   327  	}
   328  	return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
   329  }
   330  
   331  // ChooseHostInterface is a method used fetch an IP for a daemon.
   332  // If there is no routing info file, it will choose a global IP from the system
   333  // interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
   334  // IP of the interface with a gateway on it (with priority given to IPv4). For a node
   335  // with no internet connection, it returns error.
   336  func ChooseHostInterface() (net.IP, error) {
   337  	var nw networkInterfacer = networkInterface{}
   338  	if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
   339  		return ChooseIPFromHostInterfaces(nw)
   340  	}
   341  	routes, err := GetAllDefaultRoutes()
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	return ChooseHostInterfaceFromRoute(routes, nw)
   346  }
   347  
   348  // networkInterfacer defines an interface for several net library functions. Production
   349  // code will forward to net library functions, and unit tests will override the methods
   350  // for testing purposes.
   351  type networkInterfacer interface {
   352  	InterfaceByName(intfName string) (*net.Interface, error)
   353  	Addrs(intf *net.Interface) ([]net.Addr, error)
   354  	Interfaces() ([]net.Interface, error)
   355  }
   356  
   357  // networkInterface implements the networkInterfacer interface for production code, just
   358  // wrapping the underlying net library function calls.
   359  type networkInterface struct{}
   360  
   361  func (ni networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   362  	return net.InterfaceByName(intfName)
   363  }
   364  
   365  func (ni networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   366  	return intf.Addrs()
   367  }
   368  
   369  func (ni networkInterface) Interfaces() ([]net.Interface, error) {
   370  	return net.Interfaces()
   371  }
   372  
   373  // GetAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
   374  // to read the IPv4 routing info file, we return an error. If unable to read the IPv6
   375  // routing info file (which is optional), we'll just use the IPv4 route information.
   376  // Using all the routing info, if no default routes are found, an error is returned.
   377  func GetAllDefaultRoutes() ([]Route, error) {
   378  	routes, err := v4File.extract()
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  	v6Routes, _ := v6File.extract()
   383  	routes = append(routes, v6Routes...)
   384  	if len(routes) == 0 {
   385  		return nil, fmt.Errorf("No default routes")
   386  	}
   387  	return routes, nil
   388  }
   389  
   390  // ChooseHostInterfaceFromRoute cycles through each default route provided, looking for a
   391  // global IP address from the interface for the route. Will first look all each IPv4 route for
   392  // an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
   393  func ChooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
   394  	for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
   395  		klog.Infof("Looking for default routes with IPv%d addresses", uint(family))
   396  		for _, route := range routes {
   397  			if route.Family != family {
   398  				continue
   399  			}
   400  			klog.Infof("Default route transits interface %q", route.Interface)
   401  			finalIP, err := GetIPFromInterface(route.Interface, family, nw)
   402  			if err != nil {
   403  				return nil, err
   404  			}
   405  			if finalIP != nil {
   406  				klog.Infof("Found active IP %v ", finalIP)
   407  				return finalIP, nil
   408  			}
   409  		}
   410  	}
   411  	klog.Infof("No active IP found by looking at default routes")
   412  	return nil, fmt.Errorf("unable to select an IP from default routes")
   413  }
   414  
   415  // ValidateNodeIP validates given node IP belongs to the current host
   416  func ValidateNodeIP(nodeIP net.IP) error {
   417  	// Honor IP limitations set in setNodeStatus()
   418  	if nodeIP.To4() == nil && nodeIP.To16() == nil {
   419  		return fmt.Errorf("nodeIP must be a valid IP address")
   420  	}
   421  	if nodeIP.IsLoopback() {
   422  		return fmt.Errorf("nodeIP can't be loopback address")
   423  	}
   424  	if nodeIP.IsMulticast() {
   425  		return fmt.Errorf("nodeIP can't be a multicast address")
   426  	}
   427  	if nodeIP.IsLinkLocalUnicast() {
   428  		return fmt.Errorf("nodeIP can't be a link-local unicast address")
   429  	}
   430  	if nodeIP.IsUnspecified() {
   431  		return fmt.Errorf("nodeIP can't be an all zeros address")
   432  	}
   433  
   434  	addrs, err := net.InterfaceAddrs()
   435  	if err != nil {
   436  		return err
   437  	}
   438  	for _, addr := range addrs {
   439  		var ip net.IP
   440  		switch v := addr.(type) {
   441  		case *net.IPNet:
   442  			ip = v.IP
   443  		case *net.IPAddr:
   444  			ip = v.IP
   445  		}
   446  		if ip != nil && ip.Equal(nodeIP) {
   447  			return nil
   448  		}
   449  	}
   450  	return fmt.Errorf("Node IP: %q not found in the host's network interfaces", nodeIP.String())
   451  }
   452  
   453  //Command executes command and returns output
   454  func Command(name string, arg []string) (string, error) {
   455  	cmd := exec.Command(name, arg...)
   456  	ret, err := cmd.Output()
   457  	if err != nil {
   458  		klog.Errorf("exec command failed: %v", err)
   459  		return string(ret), err
   460  	}
   461  	return strings.Trim(string(ret), "\n"), nil
   462  }
   463  
   464  //GetCurPath returns filepath
   465  func GetCurPath() string {
   466  	file, _ := exec.LookPath(os.Args[0])
   467  	path, _ := filepath.Abs(file)
   468  	rst := filepath.Dir(path)
   469  	return rst
   470  }
   471  
   472  //ConvertStrToTime converts time string object to inbuilt time object
   473  func ConvertStrToTime(strTime string) (time.Time, error) {
   474  	loc, _ := time.LoadLocation("Local")
   475  	t, err := time.ParseInLocation("2006-01-02T15:04:05", strTime, loc)
   476  	if err != nil {
   477  		return time.Time{}, err
   478  	}
   479  	return t, nil
   480  }
   481  
   482  //ParseTimeErrorCode is constant set to -1 to show error
   483  const ParseTimeErrorCode = -1
   484  
   485  //ParseTimestampStr2Int64 returns given string time into int64 type
   486  func ParseTimestampStr2Int64(s string) (int64, error) {
   487  	timeStamp, err := time.Parse(time.RFC3339Nano, s)
   488  	if err != nil {
   489  		return ParseTimeErrorCode, err
   490  	}
   491  	return timeStamp.Unix(), nil
   492  }
   493  
   494  //ParseTimestampInt64 returns given int64 type time into matav1 type time
   495  func ParseTimestampInt64(timestamp int64) metav1.Time {
   496  	if timestamp == ParseTimeErrorCode {
   497  		return metav1.Time{}
   498  	}
   499  	return metav1.NewTime(time.Unix(timestamp, 0))
   500  }
   501  
   502  // ReadDirNoStat returns a string of files/directories contained
   503  // in dirname without calling lstat on them.
   504  func ReadDirNoStat(dirname string) ([]string, error) {
   505  	if dirname == "" {
   506  		dirname = "."
   507  	}
   508  
   509  	f, err := os.Open(dirname)
   510  	if err != nil {
   511  		return nil, err
   512  	}
   513  	defer f.Close()
   514  
   515  	return f.Readdirnames(-1)
   516  }
   517  
   518  func SpliceErrors(errors []error) string {
   519  	if len(errors) == 0 {
   520  		return ""
   521  	}
   522  	var stb strings.Builder
   523  	stb.WriteString("[\n")
   524  	for _, err := range errors {
   525  		stb.WriteString(fmt.Sprintf("  %s\n", err.Error()))
   526  	}
   527  	stb.WriteString("]\n")
   528  	return stb.String()
   529  }