blob: 42816bd7059448d39b498099198373c757b9e212 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package net
18
19import (
20 "bufio"
21 "encoding/hex"
22 "fmt"
23 "io"
24 "net"
25 "os"
26
27 "strings"
28
29 "github.com/golang/glog"
30)
31
32type AddressFamily uint
33
34const (
35 familyIPv4 AddressFamily = 4
36 familyIPv6 AddressFamily = 6
37)
38
39const (
40 ipv4RouteFile = "/proc/net/route"
41 ipv6RouteFile = "/proc/net/ipv6_route"
42)
43
44type Route struct {
45 Interface string
46 Destination net.IP
47 Gateway net.IP
48 Family AddressFamily
49}
50
51type RouteFile struct {
52 name string
53 parse func(input io.Reader) ([]Route, error)
54}
55
56var (
57 v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
58 v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
59)
60
61func (rf RouteFile) extract() ([]Route, error) {
62 file, err := os.Open(rf.name)
63 if err != nil {
64 return nil, err
65 }
66 defer file.Close()
67 return rf.parse(file)
68}
69
70// getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
71func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
72 routes := []Route{}
73 scanner := bufio.NewReader(input)
74 for {
75 line, err := scanner.ReadString('\n')
76 if err == io.EOF {
77 break
78 }
79 //ignore the headers in the route info
80 if strings.HasPrefix(line, "Iface") {
81 continue
82 }
83 fields := strings.Fields(line)
84 // Interested in fields:
85 // 0 - interface name
86 // 1 - destination address
87 // 2 - gateway
88 dest, err := parseIP(fields[1], familyIPv4)
89 if err != nil {
90 return nil, err
91 }
92 gw, err := parseIP(fields[2], familyIPv4)
93 if err != nil {
94 return nil, err
95 }
96 if !dest.Equal(net.IPv4zero) {
97 continue
98 }
99 routes = append(routes, Route{
100 Interface: fields[0],
101 Destination: dest,
102 Gateway: gw,
103 Family: familyIPv4,
104 })
105 }
106 return routes, nil
107}
108
109func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
110 routes := []Route{}
111 scanner := bufio.NewReader(input)
112 for {
113 line, err := scanner.ReadString('\n')
114 if err == io.EOF {
115 break
116 }
117 fields := strings.Fields(line)
118 // Interested in fields:
119 // 0 - destination address
120 // 4 - gateway
121 // 9 - interface name
122 dest, err := parseIP(fields[0], familyIPv6)
123 if err != nil {
124 return nil, err
125 }
126 gw, err := parseIP(fields[4], familyIPv6)
127 if err != nil {
128 return nil, err
129 }
130 if !dest.Equal(net.IPv6zero) {
131 continue
132 }
133 if gw.Equal(net.IPv6zero) {
134 continue // loopback
135 }
136 routes = append(routes, Route{
137 Interface: fields[9],
138 Destination: dest,
139 Gateway: gw,
140 Family: familyIPv6,
141 })
142 }
143 return routes, nil
144}
145
146// parseIP takes the hex IP address string from route file and converts it
147// to a net.IP address. For IPv4, the value must be converted to big endian.
148func parseIP(str string, family AddressFamily) (net.IP, error) {
149 if str == "" {
150 return nil, fmt.Errorf("input is nil")
151 }
152 bytes, err := hex.DecodeString(str)
153 if err != nil {
154 return nil, err
155 }
156 if family == familyIPv4 {
157 if len(bytes) != net.IPv4len {
158 return nil, fmt.Errorf("invalid IPv4 address in route")
159 }
160 return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
161 }
162 // Must be IPv6
163 if len(bytes) != net.IPv6len {
164 return nil, fmt.Errorf("invalid IPv6 address in route")
165 }
166 return net.IP(bytes), nil
167}
168
169func isInterfaceUp(intf *net.Interface) bool {
170 if intf == nil {
171 return false
172 }
173 if intf.Flags&net.FlagUp != 0 {
174 glog.V(4).Infof("Interface %v is up", intf.Name)
175 return true
176 }
177 return false
178}
179
180func isLoopbackOrPointToPoint(intf *net.Interface) bool {
181 return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
182}
183
184// getMatchingGlobalIP returns the first valid global unicast address of the given
185// 'family' from the list of 'addrs'.
186func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
187 if len(addrs) > 0 {
188 for i := range addrs {
189 glog.V(4).Infof("Checking addr %s.", addrs[i].String())
190 ip, _, err := net.ParseCIDR(addrs[i].String())
191 if err != nil {
192 return nil, err
193 }
194 if memberOf(ip, family) {
195 if ip.IsGlobalUnicast() {
196 glog.V(4).Infof("IP found %v", ip)
197 return ip, nil
198 } else {
199 glog.V(4).Infof("Non-global unicast address found %v", ip)
200 }
201 } else {
202 glog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
203 }
204
205 }
206 }
207 return nil, nil
208}
209
210// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
211// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
212func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
213 intf, err := nw.InterfaceByName(intfName)
214 if err != nil {
215 return nil, err
216 }
217 if isInterfaceUp(intf) {
218 addrs, err := nw.Addrs(intf)
219 if err != nil {
220 return nil, err
221 }
222 glog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
223 matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
224 if err != nil {
225 return nil, err
226 }
227 if matchingIP != nil {
228 glog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
229 return matchingIP, nil
230 }
231 }
232 return nil, nil
233}
234
235// memberOF tells if the IP is of the desired family. Used for checking interface addresses.
236func memberOf(ip net.IP, family AddressFamily) bool {
237 if ip.To4() != nil {
238 return family == familyIPv4
239 } else {
240 return family == familyIPv6
241 }
242}
243
244// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
245// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
246// Searches for IPv4 addresses, and then IPv6 addresses.
247func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
248 intfs, err := nw.Interfaces()
249 if err != nil {
250 return nil, err
251 }
252 if len(intfs) == 0 {
253 return nil, fmt.Errorf("no interfaces found on host.")
254 }
255 for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
256 glog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
257 for _, intf := range intfs {
258 if !isInterfaceUp(&intf) {
259 glog.V(4).Infof("Skipping: down interface %q", intf.Name)
260 continue
261 }
262 if isLoopbackOrPointToPoint(&intf) {
263 glog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
264 continue
265 }
266 addrs, err := nw.Addrs(&intf)
267 if err != nil {
268 return nil, err
269 }
270 if len(addrs) == 0 {
271 glog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
272 continue
273 }
274 for _, addr := range addrs {
275 ip, _, err := net.ParseCIDR(addr.String())
276 if err != nil {
277 return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
278 }
279 if !memberOf(ip, family) {
280 glog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
281 continue
282 }
283 // TODO: Decide if should open up to allow IPv6 LLAs in future.
284 if !ip.IsGlobalUnicast() {
285 glog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
286 continue
287 }
288 glog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
289 return ip, nil
290 }
291 }
292 }
293 return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
294}
295
296// ChooseHostInterface is a method used fetch an IP for a daemon.
297// If there is no routing info file, it will choose a global IP from the system
298// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
299// IP of the interface with a gateway on it (with priority given to IPv4). For a node
300// with no internet connection, it returns error.
301func ChooseHostInterface() (net.IP, error) {
302 var nw networkInterfacer = networkInterface{}
303 if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
304 return chooseIPFromHostInterfaces(nw)
305 }
306 routes, err := getAllDefaultRoutes()
307 if err != nil {
308 return nil, err
309 }
310 return chooseHostInterfaceFromRoute(routes, nw)
311}
312
313// networkInterfacer defines an interface for several net library functions. Production
314// code will forward to net library functions, and unit tests will override the methods
315// for testing purposes.
316type networkInterfacer interface {
317 InterfaceByName(intfName string) (*net.Interface, error)
318 Addrs(intf *net.Interface) ([]net.Addr, error)
319 Interfaces() ([]net.Interface, error)
320}
321
322// networkInterface implements the networkInterfacer interface for production code, just
323// wrapping the underlying net library function calls.
324type networkInterface struct{}
325
326func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
327 return net.InterfaceByName(intfName)
328}
329
330func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
331 return intf.Addrs()
332}
333
334func (_ networkInterface) Interfaces() ([]net.Interface, error) {
335 return net.Interfaces()
336}
337
338// getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
339// to read the IPv4 routing info file, we return an error. If unable to read the IPv6
340// routing info file (which is optional), we'll just use the IPv4 route information.
341// Using all the routing info, if no default routes are found, an error is returned.
342func getAllDefaultRoutes() ([]Route, error) {
343 routes, err := v4File.extract()
344 if err != nil {
345 return nil, err
346 }
347 v6Routes, _ := v6File.extract()
348 routes = append(routes, v6Routes...)
349 if len(routes) == 0 {
350 return nil, fmt.Errorf("No default routes.")
351 }
352 return routes, nil
353}
354
355// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
356// global IP address from the interface for the route. Will first look all each IPv4 route for
357// an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
358func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
359 for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
360 glog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
361 for _, route := range routes {
362 if route.Family != family {
363 continue
364 }
365 glog.V(4).Infof("Default route transits interface %q", route.Interface)
366 finalIP, err := getIPFromInterface(route.Interface, family, nw)
367 if err != nil {
368 return nil, err
369 }
370 if finalIP != nil {
371 glog.V(4).Infof("Found active IP %v ", finalIP)
372 return finalIP, nil
373 }
374 }
375 }
376 glog.V(4).Infof("No active IP found by looking at default routes")
377 return nil, fmt.Errorf("unable to select an IP from default routes.")
378}
379
380// If bind-address is usable, return it directly
381// If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
382// interface.
383func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
384 if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
385 hostIP, err := ChooseHostInterface()
386 if err != nil {
387 return nil, err
388 }
389 bindAddress = hostIP
390 }
391 return bindAddress, nil
392}