github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/net/interface.go (about) 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/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 // LoopbackInterfaceName is the default name of the loopback interface 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 // noRoutesError can be returned in case of no routes 70 type noRoutesError struct { 71 message string 72 } 73 74 func (e noRoutesError) Error() string { 75 return e.message 76 } 77 78 // IsNoRoutesError checks if an error is of type noRoutesError 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 // getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes. 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 //ignore the headers in the route info 115 if strings.HasPrefix(line, "Iface") { 116 continue 117 } 118 fields := strings.Fields(line) 119 // Interested in fields: 120 // 0 - interface name 121 // 1 - destination address 122 // 2 - gateway 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 // Interested in fields: 154 // 0 - destination address 155 // 4 - gateway 156 // 9 - interface name 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 // loopback 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 // parseIP takes the hex IP address string from route file and converts it 182 // to a net.IP address. For IPv4, the value must be converted to big endian. 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 // Must be IPv6 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 // getMatchingGlobalIP returns the first valid global unicast address of the given 220 // 'family' from the list of 'addrs'. 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 // getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The 246 // interface must be up, the IP must in the family requested, and the IP must be a global unicast address. 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 // getIPFromLoopbackInterface gets the IPs on a loopback interface and returns a global unicast address, if any. 271 // The loopback interface must be up, the IP must in the family requested, and the IP must be a global unicast address. 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 // memberOf tells if the IP is of the desired family. Used for checking interface addresses. 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 // chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that 310 // has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP. 311 // addressFamilies determines whether it prefers IPv4 or IPv6 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 // TODO: Decide if should open up to allow IPv6 LLAs in future. 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 // ChooseHostInterface is a method used fetch an IP for a daemon. 362 // If there is no routing info file, it will choose a global IP from the system 363 // interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the 364 // IP of the interface with a gateway on it (with priority given to IPv4). For a node 365 // with no internet connection, it returns error. 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 // networkInterfacer defines an interface for several net library functions. Production 383 // code will forward to net library functions, and unit tests will override the methods 384 // for testing purposes. 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 // networkInterface implements the networkInterfacer interface for production code, just 392 // wrapping the underlying net library function calls. 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 // getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable 408 // to read the IPv4 routing info file, we return an error. If unable to read the IPv6 409 // routing info file (which is optional), we'll just use the IPv4 route information. 410 // Using all the routing info, if no default routes are found, an error is returned. 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 // chooseHostInterfaceFromRoute cycles through each default route provided, looking for a 427 // global IP address from the interface for the route. If there are routes but no global 428 // address is obtained from the interfaces, it checks if the loopback interface has a global address. 429 // addressFamilies determines whether it prefers IPv4 or IPv6 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 // In case of network setups where default routes are present, but network 447 // interfaces use only link-local addresses (e.g. as described in RFC5549). 448 // the global IP is assigned to the loopback interface, and we should use it 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 // ResolveBindAddress returns the IP address of a daemon, based on the given bindAddress: 464 // If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface(). 465 // If bindAddress is unspecified or loopback, it returns the default IP of the same 466 // address family as bindAddress. 467 // Otherwise, it just returns bindAddress. 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 // ChooseBindAddressForInterface choose a global IP for a specific interface, with priority given to IPv4. 485 // This is required in case of network setups where default routes are present, but network 486 // interfaces use only link-local addresses (e.g. as described in RFC5549). 487 // e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface. 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 }