diff --git a/client/config/config.go b/client/config/config.go index 40a01385d..66542b5e7 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -31,6 +31,9 @@ type Config struct { // Region is the clients region Region string + // Network interface to be used in network fingerprinting + Iface string + // Servers is a list of known server addresses. These are as "host:port" Servers []string diff --git a/client/fingerprint/network_unix.go b/client/fingerprint/network_unix.go index b02040367..81852523b 100644 --- a/client/fingerprint/network_unix.go +++ b/client/fingerprint/network_unix.go @@ -3,6 +3,7 @@ package fingerprint import ( + "errors" "fmt" "io/ioutil" "log" @@ -38,10 +39,13 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) if "darwin" == runtime.GOOS { defaultDevice = "en0" } + if cfg.Iface != "" { + defaultDevice = cfg.Iface + } newNetwork.Device = defaultDevice - if ip := f.ifConfig(defaultDevice); ip != "" { + if ip := f.ipAddress(defaultDevice); ip != "" { node.Attributes["network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" @@ -129,6 +133,50 @@ 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 + } + + return f.ifConfig(device) +} + +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 { + addrs, _ := i.Addrs() + 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 +} + // ifConfig returns the IP Address for this node according to ifConfig, for the // specified device. func (f *NetworkFingerprint) ifConfig(device string) string { diff --git a/command/agent/agent.go b/command/agent/agent.go index c05bc0ed9..a33280c83 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -192,6 +192,9 @@ func (a *Agent) setupClient() error { conf.AllocDir = a.config.Client.AllocDir } conf.Servers = a.config.Client.Servers + if a.config.Client.Iface != "" { + conf.Iface = a.config.Client.Iface + } // Setup the node conf.Node = new(structs.Node) diff --git a/command/agent/config.go b/command/agent/config.go index 22fd36bc5..86960ca28 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -139,6 +139,9 @@ type ClientConfig struct { // Metadata associated with the node Meta map[string]string `hcl:"meta"` + + // Interface to use for network fingerprinting + Iface string `hcl:"iface"` } // ServerConfig is configuration specific to the server mode @@ -384,6 +387,9 @@ func (a *ClientConfig) Merge(b *ClientConfig) *ClientConfig { if b.NodeClass != "" { result.NodeClass = b.NodeClass } + if b.Iface != "" { + result.Iface = b.Iface + } // Add the servers result.Servers = append(result.Servers, b.Servers...)