diff --git a/client/config/config.go b/client/config/config.go
index 40a01385d..185b31012 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
+ 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 b02040367..b74062848 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,14 @@ 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.NetworkInterface != "" {
+ defaultDevice = cfg.NetworkInterface
+ }
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 +134,56 @@ 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 {
+ 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
+}
+
// 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..f5e99c6c6 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.NetworkInterface != "" {
+ conf.NetworkInterface = a.config.Client.NetworkInterface
+ }
// Setup the node
conf.Node = new(structs.Node)
diff --git a/command/agent/config.go b/command/agent/config.go
index 22fd36bc5..781ee0441 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
+ NetworkInterface string `hcl:"network_interface"`
}
// 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.NetworkInterface != "" {
+ result.NetworkInterface = b.NetworkInterface
+ }
// Add the servers
result.Servers = append(result.Servers, b.Servers...)
diff --git a/website/source/docs/agent/config.html.md b/website/source/docs/agent/config.html.md
index 57b654d5d..9d3a4bf15 100644
--- a/website/source/docs/agent/config.html.md
+++ b/website/source/docs/agent/config.html.md
@@ -84,7 +84,7 @@ nodes, unless otherwise specified:
TCP and UDP should be routable between the server nodes on this port.
Defaults to `4648`. Only used on server nodes.
-* `addresses`: Controls the bind address for individual
+* `addresses`: Controls the bind address for individual
network services. Any values configured in this block take precedence over the
default [bind_addr](#bind_addr). The value is a map of IP addresses and
supports the following keys:
@@ -154,7 +154,7 @@ configured on client nodes.
* `enabled`: A boolean indicating if server mode should be enabled for the
local agent. All other server options depend on this value being set.
Defaults to `false`.
- * `bootstrap_expect`: This is an integer
+ * `bootstrap_expect`: This is an integer
representing the number of server nodes to wait for before bootstrapping. It
is most common to use the odd-numbered integers `3` or `5` for this value,
depending on the cluster size. A value of `1` does not provide any fault
@@ -205,8 +205,10 @@ configured on server nodes.
* `node_class`: A string used to logically group client
nodes by class. This can be used during job placement as a filter. This
option is not required and has no default.
- * `meta`: This is a key/value mapping of metadata pairs. This
+ * `meta`: This is a key/value mapping of metadata pairs. This
is a free-form map and can contain any string values.
+ * `network_interface`: This is a string to force network fingerprinting to use
+ a specific network interface
## Atlas Options