github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/pkg/util/util.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 util 18 19 import ( 20 "bufio" 21 "encoding/hex" 22 "fmt" 23 "io" 24 "net" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "strings" 29 "time" 30 31 "k8s.io/klog" 32 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 ) 35 36 //AddressFamily is uint type var to describe family ips 37 type AddressFamily uint 38 39 const ( 40 familyIPv4 AddressFamily = 4 41 familyIPv6 AddressFamily = 6 42 ) 43 44 const ( 45 ipv4RouteFile = "/proc/net/route" 46 ipv6RouteFile = "/proc/net/ipv6_route" 47 ) 48 49 //Route is to define object of connection route 50 type Route struct { 51 Interface string 52 Destination net.IP 53 Gateway net.IP 54 Family AddressFamily 55 } 56 57 //RouteFile is object of file of routs 58 type RouteFile struct { 59 name string 60 parse func(input io.Reader) ([]Route, error) 61 } 62 63 var ( 64 v4File = RouteFile{name: ipv4RouteFile, parse: GetIPv4DefaultRoutes} 65 v6File = RouteFile{name: ipv6RouteFile, parse: GetIPv6DefaultRoutes} 66 ) 67 68 func GetLocalIP(hostName string) (string, error) { 69 var ipAddr net.IP 70 var err error 71 addrs, _ := net.LookupIP(hostName) 72 for _, addr := range addrs { 73 if err := ValidateNodeIP(addr); err == nil { 74 if addr.To4() != nil { 75 ipAddr = addr 76 break 77 } 78 if addr.To16() != nil && ipAddr == nil { 79 ipAddr = addr 80 } 81 } 82 } 83 if ipAddr == nil { 84 ipAddr, err = ChooseHostInterface() 85 } 86 87 if err != nil { 88 return "", err 89 } 90 91 return ipAddr.String(), nil 92 } 93 94 func (rf RouteFile) extract() ([]Route, error) { 95 file, err := os.Open(rf.name) 96 if err != nil { 97 return nil, err 98 } 99 defer file.Close() 100 return rf.parse(file) 101 } 102 103 // GetIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes. 104 func GetIPv4DefaultRoutes(input io.Reader) ([]Route, error) { 105 routes := []Route{} 106 scanner := bufio.NewReader(input) 107 for { 108 line, err := scanner.ReadString('\n') 109 if err == io.EOF { 110 break 111 } 112 //ignore the headers in the route info 113 if strings.HasPrefix(line, "Iface") { 114 continue 115 } 116 fields := strings.Fields(line) 117 // Interested in fields: 118 // 0 - interface name 119 // 1 - destination address 120 // 2 - gateway 121 dest, err := ParseIP(fields[1], familyIPv4) 122 if err != nil { 123 return nil, err 124 } 125 gw, err := ParseIP(fields[2], familyIPv4) 126 if err != nil { 127 return nil, err 128 } 129 if !dest.Equal(net.IPv4zero) { 130 continue 131 } 132 routes = append(routes, Route{ 133 Interface: fields[0], 134 Destination: dest, 135 Gateway: gw, 136 Family: familyIPv4, 137 }) 138 } 139 return routes, nil 140 } 141 142 // GetIPv6DefaultRoutes obtains the IPv6 routes, and filters out non-default routes. 143 func GetIPv6DefaultRoutes(input io.Reader) ([]Route, error) { 144 routes := []Route{} 145 scanner := bufio.NewReader(input) 146 for { 147 line, err := scanner.ReadString('\n') 148 if err == io.EOF { 149 break 150 } 151 fields := strings.Fields(line) 152 // Interested in fields: 153 // 0 - destination address 154 // 4 - gateway 155 // 9 - interface name 156 dest, err := ParseIP(fields[0], familyIPv6) 157 if err != nil { 158 return nil, err 159 } 160 gw, err := ParseIP(fields[4], familyIPv6) 161 if err != nil { 162 return nil, err 163 } 164 if !dest.Equal(net.IPv6zero) { 165 continue 166 } 167 if gw.Equal(net.IPv6zero) { 168 continue // loopback 169 } 170 routes = append(routes, Route{ 171 Interface: fields[9], 172 Destination: dest, 173 Gateway: gw, 174 Family: familyIPv6, 175 }) 176 } 177 return routes, nil 178 } 179 180 // ParseIP takes the hex IP address string from route file and converts it 181 // to a net.IP address. For IPv4, the value must be converted to big endian. 182 func ParseIP(str string, family AddressFamily) (net.IP, error) { 183 if str == "" { 184 return nil, fmt.Errorf("input is nil") 185 } 186 bytes, err := hex.DecodeString(str) 187 if err != nil { 188 return nil, err 189 } 190 if family == familyIPv4 { 191 if len(bytes) != net.IPv4len { 192 return nil, fmt.Errorf("invalid IPv4 address in route") 193 } 194 return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil 195 } 196 // Must be IPv6 197 if len(bytes) != net.IPv6len { 198 return nil, fmt.Errorf("invalid IPv6 address in route") 199 } 200 return net.IP(bytes), nil 201 } 202 203 //IsInterfaceUp checks the interface is running or not 204 func IsInterfaceUp(intf *net.Interface) bool { 205 if intf == nil { 206 return false 207 } 208 if intf.Flags&net.FlagUp != 0 { 209 klog.Infof("Interface %v is up", intf.Name) 210 return true 211 } 212 return false 213 } 214 215 //IsLoopbackOrPointToPoint checks interface is loopback or point to point 216 func IsLoopbackOrPointToPoint(intf *net.Interface) bool { 217 return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0 218 } 219 220 // GetMatchingGlobalIP returns the first valid global unicast address of the given 221 // 'family' from the list of 'addrs'. 222 func GetMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) { 223 if len(addrs) > 0 { 224 for i := range addrs { 225 klog.Infof("Checking addr %s.", addrs[i].String()) 226 ip, _, err := net.ParseCIDR(addrs[i].String()) 227 if err != nil { 228 return nil, err 229 } 230 if MemberOf(ip, family) { 231 if ip.IsGlobalUnicast() { 232 klog.Infof("IP found %v", ip) 233 return ip, nil 234 } 235 klog.Infof("Non-global unicast address found %v", ip) 236 } else { 237 klog.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.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.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 // MemberOf tells if the IP is of the desired family. Used for checking interface addresses. 271 func MemberOf(ip net.IP, family AddressFamily) bool { 272 if ip.To4() != nil { 273 return family == familyIPv4 274 } 275 //else 276 return family == familyIPv6 277 } 278 279 // ChooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that 280 // has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP. 281 // Searches for IPv4 addresses, and then IPv6 addresses. 282 func ChooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) { 283 intfs, err := nw.Interfaces() 284 if err != nil { 285 return nil, err 286 } 287 if len(intfs) == 0 { 288 return nil, fmt.Errorf("no interfaces found on host") 289 } 290 for _, family := range []AddressFamily{familyIPv4, familyIPv6} { 291 klog.Infof("Looking for system interface with a global IPv%d address", uint(family)) 292 for _, intf := range intfs { 293 if !IsInterfaceUp(&intf) { 294 klog.Infof("Skipping: down interface %q", intf.Name) 295 continue 296 } 297 if IsLoopbackOrPointToPoint(&intf) { 298 klog.Infof("Skipping: LB or P2P interface %q", intf.Name) 299 continue 300 } 301 addrs, err := nw.Addrs(&intf) 302 if err != nil { 303 return nil, err 304 } 305 if len(addrs) == 0 { 306 klog.Infof("Skipping: no addresses on interface %q", intf.Name) 307 continue 308 } 309 for _, addr := range addrs { 310 ip, _, err := net.ParseCIDR(addr.String()) 311 if err != nil { 312 return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err) 313 } 314 if !MemberOf(ip, family) { 315 klog.Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name) 316 continue 317 } 318 // TODO: Decide if should open up to allow IPv6 LLAs in future. 319 if !ip.IsGlobalUnicast() { 320 klog.Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name) 321 continue 322 } 323 klog.Infof("Found global unicast address %q on interface %q.", ip, intf.Name) 324 return ip, nil 325 } 326 } 327 } 328 return nil, fmt.Errorf("no acceptable interface with global unicast address found on host") 329 } 330 331 // ChooseHostInterface is a method used fetch an IP for a daemon. 332 // If there is no routing info file, it will choose a global IP from the system 333 // interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the 334 // IP of the interface with a gateway on it (with priority given to IPv4). For a node 335 // with no internet connection, it returns error. 336 func ChooseHostInterface() (net.IP, error) { 337 var nw networkInterfacer = networkInterface{} 338 if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) { 339 return ChooseIPFromHostInterfaces(nw) 340 } 341 routes, err := GetAllDefaultRoutes() 342 if err != nil { 343 return nil, err 344 } 345 return ChooseHostInterfaceFromRoute(routes, nw) 346 } 347 348 // networkInterfacer defines an interface for several net library functions. Production 349 // code will forward to net library functions, and unit tests will override the methods 350 // for testing purposes. 351 type networkInterfacer interface { 352 InterfaceByName(intfName string) (*net.Interface, error) 353 Addrs(intf *net.Interface) ([]net.Addr, error) 354 Interfaces() ([]net.Interface, error) 355 } 356 357 // networkInterface implements the networkInterfacer interface for production code, just 358 // wrapping the underlying net library function calls. 359 type networkInterface struct{} 360 361 func (ni networkInterface) InterfaceByName(intfName string) (*net.Interface, error) { 362 return net.InterfaceByName(intfName) 363 } 364 365 func (ni networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) { 366 return intf.Addrs() 367 } 368 369 func (ni networkInterface) Interfaces() ([]net.Interface, error) { 370 return net.Interfaces() 371 } 372 373 // GetAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable 374 // to read the IPv4 routing info file, we return an error. If unable to read the IPv6 375 // routing info file (which is optional), we'll just use the IPv4 route information. 376 // Using all the routing info, if no default routes are found, an error is returned. 377 func GetAllDefaultRoutes() ([]Route, error) { 378 routes, err := v4File.extract() 379 if err != nil { 380 return nil, err 381 } 382 v6Routes, _ := v6File.extract() 383 routes = append(routes, v6Routes...) 384 if len(routes) == 0 { 385 return nil, fmt.Errorf("No default routes") 386 } 387 return routes, nil 388 } 389 390 // ChooseHostInterfaceFromRoute cycles through each default route provided, looking for a 391 // global IP address from the interface for the route. Will first look all each IPv4 route for 392 // an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP. 393 func ChooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) { 394 for _, family := range []AddressFamily{familyIPv4, familyIPv6} { 395 klog.Infof("Looking for default routes with IPv%d addresses", uint(family)) 396 for _, route := range routes { 397 if route.Family != family { 398 continue 399 } 400 klog.Infof("Default route transits interface %q", route.Interface) 401 finalIP, err := GetIPFromInterface(route.Interface, family, nw) 402 if err != nil { 403 return nil, err 404 } 405 if finalIP != nil { 406 klog.Infof("Found active IP %v ", finalIP) 407 return finalIP, nil 408 } 409 } 410 } 411 klog.Infof("No active IP found by looking at default routes") 412 return nil, fmt.Errorf("unable to select an IP from default routes") 413 } 414 415 // ValidateNodeIP validates given node IP belongs to the current host 416 func ValidateNodeIP(nodeIP net.IP) error { 417 // Honor IP limitations set in setNodeStatus() 418 if nodeIP.To4() == nil && nodeIP.To16() == nil { 419 return fmt.Errorf("nodeIP must be a valid IP address") 420 } 421 if nodeIP.IsLoopback() { 422 return fmt.Errorf("nodeIP can't be loopback address") 423 } 424 if nodeIP.IsMulticast() { 425 return fmt.Errorf("nodeIP can't be a multicast address") 426 } 427 if nodeIP.IsLinkLocalUnicast() { 428 return fmt.Errorf("nodeIP can't be a link-local unicast address") 429 } 430 if nodeIP.IsUnspecified() { 431 return fmt.Errorf("nodeIP can't be an all zeros address") 432 } 433 434 addrs, err := net.InterfaceAddrs() 435 if err != nil { 436 return err 437 } 438 for _, addr := range addrs { 439 var ip net.IP 440 switch v := addr.(type) { 441 case *net.IPNet: 442 ip = v.IP 443 case *net.IPAddr: 444 ip = v.IP 445 } 446 if ip != nil && ip.Equal(nodeIP) { 447 return nil 448 } 449 } 450 return fmt.Errorf("Node IP: %q not found in the host's network interfaces", nodeIP.String()) 451 } 452 453 //Command executes command and returns output 454 func Command(name string, arg []string) (string, error) { 455 cmd := exec.Command(name, arg...) 456 ret, err := cmd.Output() 457 if err != nil { 458 klog.Errorf("exec command failed: %v", err) 459 return string(ret), err 460 } 461 return strings.Trim(string(ret), "\n"), nil 462 } 463 464 //GetCurPath returns filepath 465 func GetCurPath() string { 466 file, _ := exec.LookPath(os.Args[0]) 467 path, _ := filepath.Abs(file) 468 rst := filepath.Dir(path) 469 return rst 470 } 471 472 //ConvertStrToTime converts time string object to inbuilt time object 473 func ConvertStrToTime(strTime string) (time.Time, error) { 474 loc, _ := time.LoadLocation("Local") 475 t, err := time.ParseInLocation("2006-01-02T15:04:05", strTime, loc) 476 if err != nil { 477 return time.Time{}, err 478 } 479 return t, nil 480 } 481 482 //ParseTimeErrorCode is constant set to -1 to show error 483 const ParseTimeErrorCode = -1 484 485 //ParseTimestampStr2Int64 returns given string time into int64 type 486 func ParseTimestampStr2Int64(s string) (int64, error) { 487 timeStamp, err := time.Parse(time.RFC3339Nano, s) 488 if err != nil { 489 return ParseTimeErrorCode, err 490 } 491 return timeStamp.Unix(), nil 492 } 493 494 //ParseTimestampInt64 returns given int64 type time into matav1 type time 495 func ParseTimestampInt64(timestamp int64) metav1.Time { 496 if timestamp == ParseTimeErrorCode { 497 return metav1.Time{} 498 } 499 return metav1.NewTime(time.Unix(timestamp, 0)) 500 } 501 502 // ReadDirNoStat returns a string of files/directories contained 503 // in dirname without calling lstat on them. 504 func ReadDirNoStat(dirname string) ([]string, error) { 505 if dirname == "" { 506 dirname = "." 507 } 508 509 f, err := os.Open(dirname) 510 if err != nil { 511 return nil, err 512 } 513 defer f.Close() 514 515 return f.Readdirnames(-1) 516 } 517 518 func SpliceErrors(errors []error) string { 519 if len(errors) == 0 { 520 return "" 521 } 522 var stb strings.Builder 523 stb.WriteString("[\n") 524 for _, err := range errors { 525 stb.WriteString(fmt.Sprintf(" %s\n", err.Error())) 526 } 527 stb.WriteString("]\n") 528 return stb.String() 529 }