From 7fc3f8367241fb445d26d3c04a0e24968375d337 Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Thu, 1 Oct 2015 17:31:47 +0200 Subject: [PATCH 1/6] Client config param added to specify net iface to use for fingerprinting. Added a Golang-native method for determining the interface IP address. --- client/config/config.go | 3 ++ client/fingerprint/network_unix.go | 50 +++++++++++++++++++++++++++++- command/agent/agent.go | 3 ++ command/agent/config.go | 6 ++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/client/config/config.go b/client/config/config.go index 40a01385def..66542b5e782 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 b02040367ef..81852523b14 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 c05bc0ed950..a33280c8342 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 22fd36bc5b3..86960ca28aa 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...) From a70493f855df0fe7bd8a2accf5e4c8404550c6c2 Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Thu, 1 Oct 2015 17:33:40 +0200 Subject: [PATCH 2/6] Forgot some error checking. --- client/fingerprint/network_unix.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/fingerprint/network_unix.go b/client/fingerprint/network_unix.go index 81852523b14..bb47170c457 100644 --- a/client/fingerprint/network_unix.go +++ b/client/fingerprint/network_unix.go @@ -154,7 +154,11 @@ func (f *NetworkFingerprint) nativeIpAddress(device string) (string, error) { // TODO: should we handle IPv6 here? How do we determine precedence? for _, i := range ifaces { if i.Name == device { - addrs, _ := i.Addrs() + 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: From 776d42c2e08cdf14c7a4f8093178daaa4b33ceca Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Thu, 1 Oct 2015 17:42:20 +0200 Subject: [PATCH 3/6] Updated documentation for the `iface` client configuration option. --- website/source/docs/agent/config.html.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/source/docs/agent/config.html.md b/website/source/docs/agent/config.html.md index bd34a1e504a..8a96b8c8362 100644 --- a/website/source/docs/agent/config.html.md +++ b/website/source/docs/agent/config.html.md @@ -203,6 +203,8 @@ configured on server nodes. and has no default. * `meta`: This is a key/value mapping of metadata pairs. This is a free-form map and can contain any string values. + * `iface`: This is a string to force network fingerprinting to use a specific + network interface ## Atlas Options From f639ad10ab0728a8db95901e88b60c8e7c0dba3a Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Thu, 1 Oct 2015 20:39:10 +0200 Subject: [PATCH 4/6] Comment some undocumented code. --- client/fingerprint/network_unix.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/fingerprint/network_unix.go b/client/fingerprint/network_unix.go index bb47170c457..0d8212ca849 100644 --- a/client/fingerprint/network_unix.go +++ b/client/fingerprint/network_unix.go @@ -39,6 +39,7 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) if "darwin" == runtime.GOOS { defaultDevice = "en0" } + // User-defined override for the default interface if cfg.Iface != "" { defaultDevice = cfg.Iface } From 2a08cbaeb20f4750afbcdcc8519678461f731eda Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Thu, 1 Oct 2015 21:16:39 +0200 Subject: [PATCH 5/6] Refactored code. --- client/fingerprint/network_unix.go | 32 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/client/fingerprint/network_unix.go b/client/fingerprint/network_unix.go index 0d8212ca849..b1592ce1d7e 100644 --- a/client/fingerprint/network_unix.go +++ b/client/fingerprint/network_unix.go @@ -154,22 +154,24 @@ func (f *NetworkFingerprint) nativeIpAddress(device string) (string, error) { // TODO: should we handle IPv6 here? How do we determine precedence? for _, i := range ifaces { - if i.Name == device { - addrs, err := i.Addrs() - if err != nil { - return "", errors.New("could not retrieve interface IP addresses") - } + if i.Name != device { + continue + } - 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() - } + 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() } } } From 256d390789d11e5a9760b78a086829e4e479156f Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Fri, 2 Oct 2015 09:29:18 +0200 Subject: [PATCH 6/6] Renamed all instances of `Iface` to `NetworkInterface`. --- client/config/config.go | 2 +- client/fingerprint/network_unix.go | 4 ++-- command/agent/agent.go | 4 ++-- command/agent/config.go | 6 +++--- website/source/docs/agent/config.html.md | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client/config/config.go b/client/config/config.go index 66542b5e782..185b31012c5 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -32,7 +32,7 @@ type Config struct { Region string // Network interface to be used in network fingerprinting - Iface string + NetworkInterface 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 b1592ce1d7e..b74062848f2 100644 --- a/client/fingerprint/network_unix.go +++ b/client/fingerprint/network_unix.go @@ -40,8 +40,8 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) defaultDevice = "en0" } // User-defined override for the default interface - if cfg.Iface != "" { - defaultDevice = cfg.Iface + if cfg.NetworkInterface != "" { + defaultDevice = cfg.NetworkInterface } newNetwork.Device = defaultDevice diff --git a/command/agent/agent.go b/command/agent/agent.go index a33280c8342..f5e99c6c6b5 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -192,8 +192,8 @@ 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 + if a.config.Client.NetworkInterface != "" { + conf.NetworkInterface = a.config.Client.NetworkInterface } // Setup the node diff --git a/command/agent/config.go b/command/agent/config.go index 86960ca28aa..781ee0441ba 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -141,7 +141,7 @@ type ClientConfig struct { Meta map[string]string `hcl:"meta"` // Interface to use for network fingerprinting - Iface string `hcl:"iface"` + NetworkInterface string `hcl:"network_interface"` } // ServerConfig is configuration specific to the server mode @@ -387,8 +387,8 @@ func (a *ClientConfig) Merge(b *ClientConfig) *ClientConfig { if b.NodeClass != "" { result.NodeClass = b.NodeClass } - if b.Iface != "" { - result.Iface = b.Iface + if b.NetworkInterface != "" { + result.NetworkInterface = b.NetworkInterface } // Add the servers diff --git a/website/source/docs/agent/config.html.md b/website/source/docs/agent/config.html.md index 8a96b8c8362..c6bad8156d8 100644 --- a/website/source/docs/agent/config.html.md +++ b/website/source/docs/agent/config.html.md @@ -203,8 +203,8 @@ configured on server nodes. and has no default. * `meta`: This is a key/value mapping of metadata pairs. This is a free-form map and can contain any string values. - * `iface`: This is a string to force network fingerprinting to use a specific - network interface + * `network_interface`: This is a string to force network fingerprinting to use + a specific network interface ## Atlas Options