From 61ded1b1d57409d57465820e705491b41847aa2b Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Tue, 27 Oct 2015 14:14:25 -0700 Subject: [PATCH] Using the Go stdlib APIs to detect IP Address of a device --- client/fingerprint/network_unix.go | 118 ++++++----------------------- 1 file changed, 25 insertions(+), 93 deletions(-) diff --git a/client/fingerprint/network_unix.go b/client/fingerprint/network_unix.go index 020c99516..de06191e3 100644 --- a/client/fingerprint/network_unix.go +++ b/client/fingerprint/network_unix.go @@ -10,7 +10,6 @@ import ( "net" "os/exec" "regexp" - "runtime" "strconv" "strings" @@ -46,10 +45,10 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) return false, err } - for _, i := range intfs { - if (i.Flags&net.FlagUp != 0) && (i.Flags&(net.FlagLoopback|net.FlagPointToPoint) == 0) { - if ip := f.ipAddress(i.Name); ip != "" { - defaultDevice = i.Name + for _, intf := range intfs { + if f.isDeviceEnabled(&intf) && f.isDeviceLoopBackOrPointToPoint(&intf) && f.deviceHasIpAddress(&intf) { + if ip, err := f.ipAddress(&intf); err == nil { + defaultDevice = intf.Name node.Attributes["network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" @@ -150,99 +149,32 @@ func (f *NetworkFingerprint) linkSpeedEthtool(path, device string) int { return mbs } -// ipAddress returns the first IPv4 address on the configured default interface -// Tries Golang native functions and falls back onto ifconfig -func (f *NetworkFingerprint) ipAddress(device string) string { - if ip, err := f.nativeIpAddress(device); err == nil { - return ip +func (f *NetworkFingerprint) ipAddress(intf *net.Interface) (string, error) { + var ( + addrs []net.Addr + err error + ) + if addrs, err = intf.Addrs(); err != nil { + return "", err } - return f.ifConfig(device) + if len(addrs) == 0 { + return "", errors.New(fmt.Sprintf("Interface %s has no IP address", intf.Name)) + } + return addrs[0].String(), nil } -func (f *NetworkFingerprint) nativeIpAddress(device string) (string, error) { - // Find IP address on configured interface - var ip string - ifaces, err := net.Interfaces() - if err != nil { - return "", errors.New("could not retrieve interface list") - } - - // TODO: should we handle IPv6 here? How do we determine precedence? - for _, i := range ifaces { - if i.Name != device { - continue - } - - addrs, err := i.Addrs() - if err != nil { - return "", errors.New("could not retrieve interface IP addresses") - } - - for _, a := range addrs { - switch v := a.(type) { - case *net.IPNet: - if v.IP.To4() != nil { - ip = v.IP.String() - } - case *net.IPAddr: - if v.IP.To4() != nil { - ip = v.IP.String() - } - } - } - } - - if net.ParseIP(ip) == nil { - return "", errors.New(fmt.Sprintf("could not parse IP address `%s`", ip)) - } - - return ip, nil +func (f *NetworkFingerprint) isDeviceEnabled(intf *net.Interface) bool { + return intf.Flags&net.FlagUp != 0 } -// ifConfig returns the IP Address for this node according to ifConfig, for the -// specified device. -func (f *NetworkFingerprint) ifConfig(device string) string { - ifConfigPath, _ := exec.LookPath("ifconfig") - if ifConfigPath == "" { - f.logger.Println("[WARN] fingerprint.network: ifconfig not found") - return "" +func (f *NetworkFingerprint) deviceHasIpAddress(intf *net.Interface) bool { + if addrs, err := intf.Addrs(); err == nil { + return len(addrs) > 0 } - - outBytes, err := exec.Command(ifConfigPath, device).Output() - if err != nil { - f.logger.Printf("[WARN] fingerprint.network: Error calling ifconfig (%s %s): %v", ifConfigPath, device, err) - return "" - } - - // Parse out the IP address returned from ifconfig for this device - // Tested on Ubuntu, the matching part of ifconfig output for eth0 is like - // so: - // inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 - // For OS X and en0, we have: - // inet 192.168.0.7 netmask 0xffffff00 broadcast 192.168.0.255 - output := strings.TrimSpace(string(outBytes)) - - // re is a regular expression, which can vary based on the OS - var re *regexp.Regexp - - if "darwin" == runtime.GOOS { - re = regexp.MustCompile("inet [0-9].+") - } else { - re = regexp.MustCompile("inet addr:[0-9].+") - } - args := strings.Split(re.FindString(output), " ") - - var ip string - if len(args) > 1 { - ip = strings.TrimPrefix(args[1], "addr:") - } - - // validate what we've sliced out is a valid IP - if net.ParseIP(ip) == nil { - f.logger.Printf("[WARN] fingerprint.network: Unable to parse IP in output of '%s %s'", ifConfigPath, device) - return "" - } - - return ip + return false +} + +func (n *NetworkFingerprint) isDeviceLoopBackOrPointToPoint(intf *net.Interface) bool { + return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) == 0 }