2 Copyright 2016 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
32 type AddressFamily uint
35 familyIPv4 AddressFamily = 4
36 familyIPv6 AddressFamily = 6
40 ipv4RouteFile = "/proc/net/route"
41 ipv6RouteFile = "/proc/net/ipv6_route"
51 type RouteFile struct {
53 parse func(input io.Reader) ([]Route, error)
56 // noRoutesError can be returned by ChooseBindAddress() in case of no routes
57 type noRoutesError struct {
61 func (e noRoutesError) Error() string {
65 // IsNoRoutesError checks if an error is of type noRoutesError
66 func IsNoRoutesError(err error) bool {
79 v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
80 v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
83 func (rf RouteFile) extract() ([]Route, error) {
84 file, err := os.Open(rf.name)
92 // getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
93 func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
95 scanner := bufio.NewReader(input)
97 line, err := scanner.ReadString('\n')
101 //ignore the headers in the route info
102 if strings.HasPrefix(line, "Iface") {
105 fields := strings.Fields(line)
106 // Interested in fields:
107 // 0 - interface name
108 // 1 - destination address
110 dest, err := parseIP(fields[1], familyIPv4)
114 gw, err := parseIP(fields[2], familyIPv4)
118 if !dest.Equal(net.IPv4zero) {
121 routes = append(routes, Route{
122 Interface: fields[0],
131 func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
133 scanner := bufio.NewReader(input)
135 line, err := scanner.ReadString('\n')
139 fields := strings.Fields(line)
140 // Interested in fields:
141 // 0 - destination address
143 // 9 - interface name
144 dest, err := parseIP(fields[0], familyIPv6)
148 gw, err := parseIP(fields[4], familyIPv6)
152 if !dest.Equal(net.IPv6zero) {
155 if gw.Equal(net.IPv6zero) {
158 routes = append(routes, Route{
159 Interface: fields[9],
168 // parseIP takes the hex IP address string from route file and converts it
169 // to a net.IP address. For IPv4, the value must be converted to big endian.
170 func parseIP(str string, family AddressFamily) (net.IP, error) {
172 return nil, fmt.Errorf("input is nil")
174 bytes, err := hex.DecodeString(str)
178 if family == familyIPv4 {
179 if len(bytes) != net.IPv4len {
180 return nil, fmt.Errorf("invalid IPv4 address in route")
182 return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
185 if len(bytes) != net.IPv6len {
186 return nil, fmt.Errorf("invalid IPv6 address in route")
188 return net.IP(bytes), nil
191 func isInterfaceUp(intf *net.Interface) bool {
195 if intf.Flags&net.FlagUp != 0 {
196 klog.V(4).Infof("Interface %v is up", intf.Name)
202 func isLoopbackOrPointToPoint(intf *net.Interface) bool {
203 return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
206 // getMatchingGlobalIP returns the first valid global unicast address of the given
207 // 'family' from the list of 'addrs'.
208 func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
210 for i := range addrs {
211 klog.V(4).Infof("Checking addr %s.", addrs[i].String())
212 ip, _, err := net.ParseCIDR(addrs[i].String())
216 if memberOf(ip, family) {
217 if ip.IsGlobalUnicast() {
218 klog.V(4).Infof("IP found %v", ip)
221 klog.V(4).Infof("Non-global unicast address found %v", ip)
224 klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
232 // getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
233 // interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
234 func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
235 intf, err := nw.InterfaceByName(intfName)
239 if isInterfaceUp(intf) {
240 addrs, err := nw.Addrs(intf)
244 klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
245 matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
249 if matchingIP != nil {
250 klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
251 return matchingIP, nil
257 // memberOF tells if the IP is of the desired family. Used for checking interface addresses.
258 func memberOf(ip net.IP, family AddressFamily) bool {
260 return family == familyIPv4
262 return family == familyIPv6
266 // chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
267 // has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
268 // Searches for IPv4 addresses, and then IPv6 addresses.
269 func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
270 intfs, err := nw.Interfaces()
275 return nil, fmt.Errorf("no interfaces found on host.")
277 for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
278 klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
279 for _, intf := range intfs {
280 if !isInterfaceUp(&intf) {
281 klog.V(4).Infof("Skipping: down interface %q", intf.Name)
284 if isLoopbackOrPointToPoint(&intf) {
285 klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
288 addrs, err := nw.Addrs(&intf)
293 klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
296 for _, addr := range addrs {
297 ip, _, err := net.ParseCIDR(addr.String())
299 return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
301 if !memberOf(ip, family) {
302 klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
305 // TODO: Decide if should open up to allow IPv6 LLAs in future.
306 if !ip.IsGlobalUnicast() {
307 klog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
310 klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
315 return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
318 // ChooseHostInterface is a method used fetch an IP for a daemon.
319 // If there is no routing info file, it will choose a global IP from the system
320 // interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
321 // IP of the interface with a gateway on it (with priority given to IPv4). For a node
322 // with no internet connection, it returns error.
323 func ChooseHostInterface() (net.IP, error) {
324 var nw networkInterfacer = networkInterface{}
325 if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
326 return chooseIPFromHostInterfaces(nw)
328 routes, err := getAllDefaultRoutes()
332 return chooseHostInterfaceFromRoute(routes, nw)
335 // networkInterfacer defines an interface for several net library functions. Production
336 // code will forward to net library functions, and unit tests will override the methods
337 // for testing purposes.
338 type networkInterfacer interface {
339 InterfaceByName(intfName string) (*net.Interface, error)
340 Addrs(intf *net.Interface) ([]net.Addr, error)
341 Interfaces() ([]net.Interface, error)
344 // networkInterface implements the networkInterfacer interface for production code, just
345 // wrapping the underlying net library function calls.
346 type networkInterface struct{}
348 func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
349 return net.InterfaceByName(intfName)
352 func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
356 func (_ networkInterface) Interfaces() ([]net.Interface, error) {
357 return net.Interfaces()
360 // getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
361 // to read the IPv4 routing info file, we return an error. If unable to read the IPv6
362 // routing info file (which is optional), we'll just use the IPv4 route information.
363 // Using all the routing info, if no default routes are found, an error is returned.
364 func getAllDefaultRoutes() ([]Route, error) {
365 routes, err := v4File.extract()
369 v6Routes, _ := v6File.extract()
370 routes = append(routes, v6Routes...)
371 if len(routes) == 0 {
372 return nil, noRoutesError{
373 message: fmt.Sprintf("no default routes found in %q or %q", v4File.name, v6File.name),
379 // chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
380 // global IP address from the interface for the route. Will first look all each IPv4 route for
381 // an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
382 func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
383 for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
384 klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
385 for _, route := range routes {
386 if route.Family != family {
389 klog.V(4).Infof("Default route transits interface %q", route.Interface)
390 finalIP, err := getIPFromInterface(route.Interface, family, nw)
395 klog.V(4).Infof("Found active IP %v ", finalIP)
400 klog.V(4).Infof("No active IP found by looking at default routes")
401 return nil, fmt.Errorf("unable to select an IP from default routes.")
404 // If bind-address is usable, return it directly
405 // If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
407 func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
408 if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
409 hostIP, err := ChooseHostInterface()
415 return bindAddress, nil