Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / apimachinery / pkg / util / net / interface.go
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"
30 )
31
32 type AddressFamily uint
33
34 const (
35         familyIPv4 AddressFamily = 4
36         familyIPv6 AddressFamily = 6
37 )
38
39 const (
40         ipv4RouteFile = "/proc/net/route"
41         ipv6RouteFile = "/proc/net/ipv6_route"
42 )
43
44 type Route struct {
45         Interface   string
46         Destination net.IP
47         Gateway     net.IP
48         Family      AddressFamily
49 }
50
51 type RouteFile struct {
52         name  string
53         parse func(input io.Reader) ([]Route, error)
54 }
55
56 // noRoutesError can be returned by ChooseBindAddress() in case of no routes
57 type noRoutesError struct {
58         message string
59 }
60
61 func (e noRoutesError) Error() string {
62         return e.message
63 }
64
65 // IsNoRoutesError checks if an error is of type noRoutesError
66 func IsNoRoutesError(err error) bool {
67         if err == nil {
68                 return false
69         }
70         switch err.(type) {
71         case noRoutesError:
72                 return true
73         default:
74                 return false
75         }
76 }
77
78 var (
79         v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
80         v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
81 )
82
83 func (rf RouteFile) extract() ([]Route, error) {
84         file, err := os.Open(rf.name)
85         if err != nil {
86                 return nil, err
87         }
88         defer file.Close()
89         return rf.parse(file)
90 }
91
92 // getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
93 func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
94         routes := []Route{}
95         scanner := bufio.NewReader(input)
96         for {
97                 line, err := scanner.ReadString('\n')
98                 if err == io.EOF {
99                         break
100                 }
101                 //ignore the headers in the route info
102                 if strings.HasPrefix(line, "Iface") {
103                         continue
104                 }
105                 fields := strings.Fields(line)
106                 // Interested in fields:
107                 //  0 - interface name
108                 //  1 - destination address
109                 //  2 - gateway
110                 dest, err := parseIP(fields[1], familyIPv4)
111                 if err != nil {
112                         return nil, err
113                 }
114                 gw, err := parseIP(fields[2], familyIPv4)
115                 if err != nil {
116                         return nil, err
117                 }
118                 if !dest.Equal(net.IPv4zero) {
119                         continue
120                 }
121                 routes = append(routes, Route{
122                         Interface:   fields[0],
123                         Destination: dest,
124                         Gateway:     gw,
125                         Family:      familyIPv4,
126                 })
127         }
128         return routes, nil
129 }
130
131 func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
132         routes := []Route{}
133         scanner := bufio.NewReader(input)
134         for {
135                 line, err := scanner.ReadString('\n')
136                 if err == io.EOF {
137                         break
138                 }
139                 fields := strings.Fields(line)
140                 // Interested in fields:
141                 //  0 - destination address
142                 //  4 - gateway
143                 //  9 - interface name
144                 dest, err := parseIP(fields[0], familyIPv6)
145                 if err != nil {
146                         return nil, err
147                 }
148                 gw, err := parseIP(fields[4], familyIPv6)
149                 if err != nil {
150                         return nil, err
151                 }
152                 if !dest.Equal(net.IPv6zero) {
153                         continue
154                 }
155                 if gw.Equal(net.IPv6zero) {
156                         continue // loopback
157                 }
158                 routes = append(routes, Route{
159                         Interface:   fields[9],
160                         Destination: dest,
161                         Gateway:     gw,
162                         Family:      familyIPv6,
163                 })
164         }
165         return routes, nil
166 }
167
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) {
171         if str == "" {
172                 return nil, fmt.Errorf("input is nil")
173         }
174         bytes, err := hex.DecodeString(str)
175         if err != nil {
176                 return nil, err
177         }
178         if family == familyIPv4 {
179                 if len(bytes) != net.IPv4len {
180                         return nil, fmt.Errorf("invalid IPv4 address in route")
181                 }
182                 return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
183         }
184         // Must be IPv6
185         if len(bytes) != net.IPv6len {
186                 return nil, fmt.Errorf("invalid IPv6 address in route")
187         }
188         return net.IP(bytes), nil
189 }
190
191 func isInterfaceUp(intf *net.Interface) bool {
192         if intf == nil {
193                 return false
194         }
195         if intf.Flags&net.FlagUp != 0 {
196                 klog.V(4).Infof("Interface %v is up", intf.Name)
197                 return true
198         }
199         return false
200 }
201
202 func isLoopbackOrPointToPoint(intf *net.Interface) bool {
203         return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
204 }
205
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) {
209         if len(addrs) > 0 {
210                 for i := range addrs {
211                         klog.V(4).Infof("Checking addr  %s.", addrs[i].String())
212                         ip, _, err := net.ParseCIDR(addrs[i].String())
213                         if err != nil {
214                                 return nil, err
215                         }
216                         if memberOf(ip, family) {
217                                 if ip.IsGlobalUnicast() {
218                                         klog.V(4).Infof("IP found %v", ip)
219                                         return ip, nil
220                                 } else {
221                                         klog.V(4).Infof("Non-global unicast address found %v", ip)
222                                 }
223                         } else {
224                                 klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
225                         }
226
227                 }
228         }
229         return nil, nil
230 }
231
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)
236         if err != nil {
237                 return nil, err
238         }
239         if isInterfaceUp(intf) {
240                 addrs, err := nw.Addrs(intf)
241                 if err != nil {
242                         return nil, err
243                 }
244                 klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
245                 matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
246                 if err != nil {
247                         return nil, err
248                 }
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
252                 }
253         }
254         return nil, nil
255 }
256
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 {
259         if ip.To4() != nil {
260                 return family == familyIPv4
261         } else {
262                 return family == familyIPv6
263         }
264 }
265
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()
271         if err != nil {
272                 return nil, err
273         }
274         if len(intfs) == 0 {
275                 return nil, fmt.Errorf("no interfaces found on host.")
276         }
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)
282                                 continue
283                         }
284                         if isLoopbackOrPointToPoint(&intf) {
285                                 klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
286                                 continue
287                         }
288                         addrs, err := nw.Addrs(&intf)
289                         if err != nil {
290                                 return nil, err
291                         }
292                         if len(addrs) == 0 {
293                                 klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
294                                 continue
295                         }
296                         for _, addr := range addrs {
297                                 ip, _, err := net.ParseCIDR(addr.String())
298                                 if err != nil {
299                                         return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
300                                 }
301                                 if !memberOf(ip, family) {
302                                         klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
303                                         continue
304                                 }
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)
308                                         continue
309                                 }
310                                 klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
311                                 return ip, nil
312                         }
313                 }
314         }
315         return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
316 }
317
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)
327         }
328         routes, err := getAllDefaultRoutes()
329         if err != nil {
330                 return nil, err
331         }
332         return chooseHostInterfaceFromRoute(routes, nw)
333 }
334
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)
342 }
343
344 // networkInterface implements the networkInterfacer interface for production code, just
345 // wrapping the underlying net library function calls.
346 type networkInterface struct{}
347
348 func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
349         return net.InterfaceByName(intfName)
350 }
351
352 func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
353         return intf.Addrs()
354 }
355
356 func (_ networkInterface) Interfaces() ([]net.Interface, error) {
357         return net.Interfaces()
358 }
359
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()
366         if err != nil {
367                 return nil, err
368         }
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),
374                 }
375         }
376         return routes, nil
377 }
378
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 {
387                                 continue
388                         }
389                         klog.V(4).Infof("Default route transits interface %q", route.Interface)
390                         finalIP, err := getIPFromInterface(route.Interface, family, nw)
391                         if err != nil {
392                                 return nil, err
393                         }
394                         if finalIP != nil {
395                                 klog.V(4).Infof("Found active IP %v ", finalIP)
396                                 return finalIP, nil
397                         }
398                 }
399         }
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.")
402 }
403
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
406 // interface.
407 func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
408         if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
409                 hostIP, err := ChooseHostInterface()
410                 if err != nil {
411                         return nil, err
412                 }
413                 bindAddress = hostIP
414         }
415         return bindAddress, nil
416 }