github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/interface.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  	"github.com/golang/glog"
    30  )
    31  
    32  type Route struct {
    33  	Interface   string
    34  	Destination net.IP
    35  	Gateway     net.IP
    36  	// TODO: add more fields here if needed
    37  }
    38  
    39  func getRoutes(input io.Reader) ([]Route, error) {
    40  	routes := []Route{}
    41  	if input == nil {
    42  		return nil, fmt.Errorf("input is nil")
    43  	}
    44  	scanner := bufio.NewReader(input)
    45  	for {
    46  		line, err := scanner.ReadString('\n')
    47  		if err == io.EOF {
    48  			break
    49  		}
    50  		//ignore the headers in the route info
    51  		if strings.HasPrefix(line, "Iface") {
    52  			continue
    53  		}
    54  		fields := strings.Fields(line)
    55  		routes = append(routes, Route{})
    56  		route := &routes[len(routes)-1]
    57  		route.Interface = fields[0]
    58  		ip, err := parseIP(fields[1])
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  		route.Destination = ip
    63  		ip, err = parseIP(fields[2])
    64  		if err != nil {
    65  			return nil, err
    66  		}
    67  		route.Gateway = ip
    68  	}
    69  	return routes, nil
    70  }
    71  
    72  func parseIP(str string) (net.IP, error) {
    73  	if str == "" {
    74  		return nil, fmt.Errorf("input is nil")
    75  	}
    76  	bytes, err := hex.DecodeString(str)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	//TODO add ipv6 support
    81  	if len(bytes) != net.IPv4len {
    82  		return nil, fmt.Errorf("only IPv4 is supported")
    83  	}
    84  	bytes[0], bytes[1], bytes[2], bytes[3] = bytes[3], bytes[2], bytes[1], bytes[0]
    85  	return net.IP(bytes), nil
    86  }
    87  
    88  func isInterfaceUp(intf *net.Interface) bool {
    89  	if intf == nil {
    90  		return false
    91  	}
    92  	if intf.Flags&net.FlagUp != 0 {
    93  		glog.V(4).Infof("Interface %v is up", intf.Name)
    94  		return true
    95  	}
    96  	return false
    97  }
    98  
    99  //getFinalIP method receives all the IP addrs of a Interface
   100  //and returns a nil if the address is Loopback, Ipv6, link-local or nil.
   101  //It returns a valid IPv4 if an Ipv4 address is found in the array.
   102  func getFinalIP(addrs []net.Addr) (net.IP, error) {
   103  	if len(addrs) > 0 {
   104  		for i := range addrs {
   105  			glog.V(4).Infof("Checking addr  %s.", addrs[i].String())
   106  			ip, _, err := net.ParseCIDR(addrs[i].String())
   107  			if err != nil {
   108  				return nil, err
   109  			}
   110  			//Only IPv4
   111  			//TODO : add IPv6 support
   112  			if ip.To4() != nil {
   113  				if !ip.IsLoopback() && !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() {
   114  					glog.V(4).Infof("IP found %v", ip)
   115  					return ip, nil
   116  				} else {
   117  					glog.V(4).Infof("Loopback/link-local found %v", ip)
   118  				}
   119  			} else {
   120  				glog.V(4).Infof("%v is not a valid IPv4 address", ip)
   121  			}
   122  
   123  		}
   124  	}
   125  	return nil, nil
   126  }
   127  
   128  func getIPFromInterface(intfName string, nw networkInterfacer) (net.IP, error) {
   129  	intf, err := nw.InterfaceByName(intfName)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	if isInterfaceUp(intf) {
   134  		addrs, err := nw.Addrs(intf)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		glog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
   139  		finalIP, err := getFinalIP(addrs)
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  		if finalIP != nil {
   144  			glog.V(4).Infof("valid IPv4 address for interface %q found as %v.", intfName, finalIP)
   145  			return finalIP, nil
   146  		}
   147  	}
   148  
   149  	return nil, nil
   150  }
   151  
   152  func flagsSet(flags net.Flags, test net.Flags) bool {
   153  	return flags&test != 0
   154  }
   155  
   156  func flagsClear(flags net.Flags, test net.Flags) bool {
   157  	return flags&test == 0
   158  }
   159  
   160  func chooseHostInterfaceNativeGo() (net.IP, error) {
   161  	intfs, err := net.Interfaces()
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	i := 0
   166  	var ip net.IP
   167  	for i = range intfs {
   168  		if flagsSet(intfs[i].Flags, net.FlagUp) && flagsClear(intfs[i].Flags, net.FlagLoopback|net.FlagPointToPoint) {
   169  			addrs, err := intfs[i].Addrs()
   170  			if err != nil {
   171  				return nil, err
   172  			}
   173  			if len(addrs) > 0 {
   174  				for _, addr := range addrs {
   175  					if addrIP, _, err := net.ParseCIDR(addr.String()); err == nil {
   176  						if addrIP.To4() != nil {
   177  							ip = addrIP.To4()
   178  							if !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() {
   179  								break
   180  							}
   181  						}
   182  					}
   183  				}
   184  				if ip != nil {
   185  					// This interface should suffice.
   186  					break
   187  				}
   188  			}
   189  		}
   190  	}
   191  	if ip == nil {
   192  		return nil, fmt.Errorf("no acceptable interface from host")
   193  	}
   194  	glog.V(4).Infof("Choosing interface %s (IP %v) as default", intfs[i].Name, ip)
   195  	return ip, nil
   196  }
   197  
   198  //ChooseHostInterface is a method used fetch an IP for a daemon.
   199  //It uses data from /proc/net/route file.
   200  //For a node with no internet connection ,it returns error
   201  //For a multi n/w interface node it returns the IP of the interface with gateway on it.
   202  func ChooseHostInterface() (net.IP, error) {
   203  	inFile, err := os.Open("/proc/net/route")
   204  	if err != nil {
   205  		if os.IsNotExist(err) {
   206  			return chooseHostInterfaceNativeGo()
   207  		}
   208  		return nil, err
   209  	}
   210  	defer inFile.Close()
   211  	var nw networkInterfacer = networkInterface{}
   212  	return chooseHostInterfaceFromRoute(inFile, nw)
   213  }
   214  
   215  type networkInterfacer interface {
   216  	InterfaceByName(intfName string) (*net.Interface, error)
   217  	Addrs(intf *net.Interface) ([]net.Addr, error)
   218  }
   219  
   220  type networkInterface struct{}
   221  
   222  func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   223  	intf, err := net.InterfaceByName(intfName)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	return intf, nil
   228  }
   229  
   230  func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   231  	addrs, err := intf.Addrs()
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	return addrs, nil
   236  }
   237  
   238  func chooseHostInterfaceFromRoute(inFile io.Reader, nw networkInterfacer) (net.IP, error) {
   239  	routes, err := getRoutes(inFile)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  	zero := net.IP{0, 0, 0, 0}
   244  	var finalIP net.IP
   245  	for i := range routes {
   246  		//find interface with gateway
   247  		if routes[i].Destination.Equal(zero) {
   248  			glog.V(4).Infof("Default route transits interface %q", routes[i].Interface)
   249  			finalIP, err := getIPFromInterface(routes[i].Interface, nw)
   250  			if err != nil {
   251  				return nil, err
   252  			}
   253  			if finalIP != nil {
   254  				glog.V(4).Infof("Choosing IP %v ", finalIP)
   255  				return finalIP, nil
   256  			}
   257  		}
   258  	}
   259  	glog.V(4).Infof("No valid IP found")
   260  	if finalIP == nil {
   261  		return nil, fmt.Errorf("Unable to select an IP.")
   262  	}
   263  	return nil, nil
   264  }
   265  
   266  // If bind-address is usable, return it directly
   267  // If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
   268  // interface.
   269  func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
   270  	if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
   271  		hostIP, err := ChooseHostInterface()
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  		bindAddress = hostIP
   276  	}
   277  	return bindAddress, nil
   278  }