1
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/v2"
30 netutils "k8s.io/utils/net"
31 )
32
33 type AddressFamily uint
34
35 const (
36 familyIPv4 AddressFamily = 4
37 familyIPv6 AddressFamily = 6
38 )
39
40 type AddressFamilyPreference []AddressFamily
41
42 var (
43 preferIPv4 = AddressFamilyPreference{familyIPv4, familyIPv6}
44 preferIPv6 = AddressFamilyPreference{familyIPv6, familyIPv4}
45 )
46
47 const (
48
49 LoopbackInterfaceName = "lo"
50 )
51
52 const (
53 ipv4RouteFile = "/proc/net/route"
54 ipv6RouteFile = "/proc/net/ipv6_route"
55 )
56
57 type Route struct {
58 Interface string
59 Destination net.IP
60 Gateway net.IP
61 Family AddressFamily
62 }
63
64 type RouteFile struct {
65 name string
66 parse func(input io.Reader) ([]Route, error)
67 }
68
69
70 type noRoutesError struct {
71 message string
72 }
73
74 func (e noRoutesError) Error() string {
75 return e.message
76 }
77
78
79 func IsNoRoutesError(err error) bool {
80 if err == nil {
81 return false
82 }
83 switch err.(type) {
84 case noRoutesError:
85 return true
86 default:
87 return false
88 }
89 }
90
91 var (
92 v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
93 v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
94 )
95
96 func (rf RouteFile) extract() ([]Route, error) {
97 file, err := os.Open(rf.name)
98 if err != nil {
99 return nil, err
100 }
101 defer file.Close()
102 return rf.parse(file)
103 }
104
105
106 func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
107 routes := []Route{}
108 scanner := bufio.NewReader(input)
109 for {
110 line, err := scanner.ReadString('\n')
111 if err == io.EOF {
112 break
113 }
114
115 if strings.HasPrefix(line, "Iface") {
116 continue
117 }
118 fields := strings.Fields(line)
119
120
121
122
123 dest, err := parseIP(fields[1], familyIPv4)
124 if err != nil {
125 return nil, err
126 }
127 gw, err := parseIP(fields[2], familyIPv4)
128 if err != nil {
129 return nil, err
130 }
131 if !dest.Equal(net.IPv4zero) {
132 continue
133 }
134 routes = append(routes, Route{
135 Interface: fields[0],
136 Destination: dest,
137 Gateway: gw,
138 Family: familyIPv4,
139 })
140 }
141 return routes, nil
142 }
143
144 func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
145 routes := []Route{}
146 scanner := bufio.NewReader(input)
147 for {
148 line, err := scanner.ReadString('\n')
149 if err == io.EOF {
150 break
151 }
152 fields := strings.Fields(line)
153
154
155
156
157 dest, err := parseIP(fields[0], familyIPv6)
158 if err != nil {
159 return nil, err
160 }
161 gw, err := parseIP(fields[4], familyIPv6)
162 if err != nil {
163 return nil, err
164 }
165 if !dest.Equal(net.IPv6zero) {
166 continue
167 }
168 if gw.Equal(net.IPv6zero) {
169 continue
170 }
171 routes = append(routes, Route{
172 Interface: fields[9],
173 Destination: dest,
174 Gateway: gw,
175 Family: familyIPv6,
176 })
177 }
178 return routes, nil
179 }
180
181
182
183 func parseIP(str string, family AddressFamily) (net.IP, error) {
184 if str == "" {
185 return nil, fmt.Errorf("input is nil")
186 }
187 bytes, err := hex.DecodeString(str)
188 if err != nil {
189 return nil, err
190 }
191 if family == familyIPv4 {
192 if len(bytes) != net.IPv4len {
193 return nil, fmt.Errorf("invalid IPv4 address in route")
194 }
195 return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
196 }
197
198 if len(bytes) != net.IPv6len {
199 return nil, fmt.Errorf("invalid IPv6 address in route")
200 }
201 return net.IP(bytes), nil
202 }
203
204 func isInterfaceUp(intf *net.Interface) bool {
205 if intf == nil {
206 return false
207 }
208 if intf.Flags&net.FlagUp != 0 {
209 klog.V(4).Infof("Interface %v is up", intf.Name)
210 return true
211 }
212 return false
213 }
214
215 func isLoopbackOrPointToPoint(intf *net.Interface) bool {
216 return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
217 }
218
219
220
221 func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
222 if len(addrs) > 0 {
223 for i := range addrs {
224 klog.V(4).Infof("Checking addr %s.", addrs[i].String())
225 ip, _, err := netutils.ParseCIDRSloppy(addrs[i].String())
226 if err != nil {
227 return nil, err
228 }
229 if memberOf(ip, family) {
230 if ip.IsGlobalUnicast() {
231 klog.V(4).Infof("IP found %v", ip)
232 return ip, nil
233 } else {
234 klog.V(4).Infof("Non-global unicast address found %v", ip)
235 }
236 } else {
237 klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
238 }
239
240 }
241 }
242 return nil, nil
243 }
244
245
246
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.V(4).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.V(4).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
271
272 func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
273 intfs, err := nw.Interfaces()
274 if err != nil {
275 return nil, err
276 }
277 for _, intf := range intfs {
278 if !isInterfaceUp(&intf) {
279 continue
280 }
281 if intf.Flags&(net.FlagLoopback) != 0 {
282 addrs, err := nw.Addrs(&intf)
283 if err != nil {
284 return nil, err
285 }
286 klog.V(4).Infof("Interface %q has %d addresses :%v.", intf.Name, len(addrs), addrs)
287 matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
288 if err != nil {
289 return nil, err
290 }
291 if matchingIP != nil {
292 klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intf.Name)
293 return matchingIP, nil
294 }
295 }
296 }
297 return nil, nil
298 }
299
300
301 func memberOf(ip net.IP, family AddressFamily) bool {
302 if ip.To4() != nil {
303 return family == familyIPv4
304 } else {
305 return family == familyIPv6
306 }
307 }
308
309
310
311
312 func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
313 intfs, err := nw.Interfaces()
314 if err != nil {
315 return nil, err
316 }
317 if len(intfs) == 0 {
318 return nil, fmt.Errorf("no interfaces found on host.")
319 }
320 for _, family := range addressFamilies {
321 klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
322 for _, intf := range intfs {
323 if !isInterfaceUp(&intf) {
324 klog.V(4).Infof("Skipping: down interface %q", intf.Name)
325 continue
326 }
327 if isLoopbackOrPointToPoint(&intf) {
328 klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
329 continue
330 }
331 addrs, err := nw.Addrs(&intf)
332 if err != nil {
333 return nil, err
334 }
335 if len(addrs) == 0 {
336 klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
337 continue
338 }
339 for _, addr := range addrs {
340 ip, _, err := netutils.ParseCIDRSloppy(addr.String())
341 if err != nil {
342 return nil, fmt.Errorf("unable to parse CIDR for interface %q: %s", intf.Name, err)
343 }
344 if !memberOf(ip, family) {
345 klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
346 continue
347 }
348
349 if !ip.IsGlobalUnicast() {
350 klog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
351 continue
352 }
353 klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
354 return ip, nil
355 }
356 }
357 }
358 return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
359 }
360
361
362
363
364
365
366 func ChooseHostInterface() (net.IP, error) {
367 return chooseHostInterface(preferIPv4)
368 }
369
370 func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
371 var nw networkInterfacer = networkInterface{}
372 if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
373 return chooseIPFromHostInterfaces(nw, addressFamilies)
374 }
375 routes, err := getAllDefaultRoutes()
376 if err != nil {
377 return nil, err
378 }
379 return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
380 }
381
382
383
384
385 type networkInterfacer interface {
386 InterfaceByName(intfName string) (*net.Interface, error)
387 Addrs(intf *net.Interface) ([]net.Addr, error)
388 Interfaces() ([]net.Interface, error)
389 }
390
391
392
393 type networkInterface struct{}
394
395 func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
396 return net.InterfaceByName(intfName)
397 }
398
399 func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
400 return intf.Addrs()
401 }
402
403 func (_ networkInterface) Interfaces() ([]net.Interface, error) {
404 return net.Interfaces()
405 }
406
407
408
409
410
411 func getAllDefaultRoutes() ([]Route, error) {
412 routes, err := v4File.extract()
413 if err != nil {
414 return nil, err
415 }
416 v6Routes, _ := v6File.extract()
417 routes = append(routes, v6Routes...)
418 if len(routes) == 0 {
419 return nil, noRoutesError{
420 message: fmt.Sprintf("no default routes found in %q or %q", v4File.name, v6File.name),
421 }
422 }
423 return routes, nil
424 }
425
426
427
428
429
430 func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
431 for _, family := range addressFamilies {
432 klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
433 for _, route := range routes {
434 if route.Family != family {
435 continue
436 }
437 klog.V(4).Infof("Default route transits interface %q", route.Interface)
438 finalIP, err := getIPFromInterface(route.Interface, family, nw)
439 if err != nil {
440 return nil, err
441 }
442 if finalIP != nil {
443 klog.V(4).Infof("Found active IP %v ", finalIP)
444 return finalIP, nil
445 }
446
447
448
449 loopbackIP, err := getIPFromLoopbackInterface(family, nw)
450 if err != nil {
451 return nil, err
452 }
453 if loopbackIP != nil {
454 klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
455 return loopbackIP, nil
456 }
457 }
458 }
459 klog.V(4).Infof("No active IP found by looking at default routes")
460 return nil, fmt.Errorf("unable to select an IP from default routes.")
461 }
462
463
464
465
466
467
468 func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
469 addressFamilies := preferIPv4
470 if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
471 addressFamilies = preferIPv6
472 }
473
474 if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
475 hostIP, err := chooseHostInterface(addressFamilies)
476 if err != nil {
477 return nil, err
478 }
479 bindAddress = hostIP
480 }
481 return bindAddress, nil
482 }
483
484
485
486
487
488 func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
489 var nw networkInterfacer = networkInterface{}
490 for _, family := range preferIPv4 {
491 ip, err := getIPFromInterface(intfName, family, nw)
492 if err != nil {
493 return nil, err
494 }
495 if ip != nil {
496 return ip, nil
497 }
498 }
499 return nil, fmt.Errorf("unable to select an IP from %s network interface", intfName)
500 }
501
View as plain text