From 8b93d40298937d8d2de78a3429ee29b18381a075 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 6 Apr 2017 01:06:32 -0700 Subject: [PATCH 1/8] Making the fingerprinter support ipv6 ips --- client/fingerprint/network.go | 69 ++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/client/fingerprint/network.go b/client/fingerprint/network.go index eceb213e43f7..6a5138113c9c 100644 --- a/client/fingerprint/network.go +++ b/client/fingerprint/network.go @@ -1,7 +1,6 @@ package fingerprint import ( - "errors" "fmt" "log" "net" @@ -56,10 +55,11 @@ func NewNetworkFingerprint(logger *log.Logger) Fingerprint { } func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // newNetwork is populated and addded to the Nodes resources - newNetwork := &structs.NetworkResource{} - var ip string + if node.Resources == nil { + node.Resources = &structs.Resources{} + } + // Find the named interface intf, err := f.findInterface(cfg.NetworkInterface) switch { case err != nil: @@ -69,51 +69,54 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) return false, nil } - if ip, err = f.ipAddress(intf); err != nil { - return false, fmt.Errorf("Unable to find IP address of interface: %s, err: %v", intf.Name, err) - } - - newNetwork.Device = intf.Name - node.Attributes["unique.network.ip-address"] = ip - newNetwork.IP = ip - newNetwork.CIDR = newNetwork.IP + "/32" - - f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP %v during fingerprinting", intf.Name, ip) - + // Record the throughput of the interface + var mbits int throughput := f.linkSpeed(intf.Name) if cfg.NetworkSpeed != 0 { - newNetwork.MBits = cfg.NetworkSpeed - f.logger.Printf("[DEBUG] fingerprint.network: setting link speed to user configured speed: %d", newNetwork.MBits) + mbits = cfg.NetworkSpeed + f.logger.Printf("[DEBUG] fingerprint.network: setting link speed to user configured speed: %d", mbits) } else if throughput != 0 { - newNetwork.MBits = throughput - f.logger.Printf("[DEBUG] fingerprint.network: link speed for %v set to %v", intf.Name, newNetwork.MBits) + mbits = throughput + f.logger.Printf("[DEBUG] fingerprint.network: link speed for %v set to %v", intf.Name, mbits) } else { - newNetwork.MBits = defaultNetworkSpeed + mbits = defaultNetworkSpeed f.logger.Printf("[DEBUG] fingerprint.network: link speed could not be detected and no speed specified by user. Defaulting to %d", defaultNetworkSpeed) } - if node.Resources == nil { - node.Resources = &structs.Resources{} + var ips []net.IP + if ips, err = f.ipAddrs(intf); err != nil || len(ips) == 0 { + return false, fmt.Errorf("Unable to find IP address of interface: %s, err: %v", intf.Name, err) } - node.Resources.Networks = append(node.Resources.Networks, newNetwork) + for _, ip := range ips { + newNetwork := &structs.NetworkResource{} + newNetwork.Device = intf.Name + newNetwork.IP = ip.String() + if ip.To4() != nil { + newNetwork.CIDR = newNetwork.IP + "/32" + } else { + newNetwork.CIDR = newNetwork.IP + "/64" + } + newNetwork.MBits = mbits + node.Resources.Networks = append(node.Resources.Networks, newNetwork) + + f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP %v during fingerprinting", intf.Name, ip) + } // return true, because we have a network connection return true, nil } -// Gets the ipv4 addr for a network interface -func (f *NetworkFingerprint) ipAddress(intf *net.Interface) (string, error) { +// ipAddrs gets all the ipv addresses associated with a network interface +func (f *NetworkFingerprint) ipAddrs(intf *net.Interface) ([]net.IP, error) { var addrs []net.Addr var err error if addrs, err = f.interfaceDetector.Addrs(intf); err != nil { - return "", err + return nil, err } - if len(addrs) == 0 { - return "", errors.New(fmt.Sprintf("Interface %s has no IP address", intf.Name)) - } + ips := make([]net.IP, 0) for _, addr := range addrs { var ip net.IP switch v := (addr).(type) { @@ -122,12 +125,10 @@ func (f *NetworkFingerprint) ipAddress(intf *net.Interface) (string, error) { case *net.IPAddr: ip = v.IP } - if ip.To4() != nil { - return ip.String(), nil - } + ips = append(ips, ip) } - return "", fmt.Errorf("Couldn't parse IP address for interface %s", intf.Name) + return ips, nil } @@ -138,7 +139,7 @@ func (f *NetworkFingerprint) isDeviceEnabled(intf *net.Interface) bool { // Checks if the device has any IP address configured func (f *NetworkFingerprint) deviceHasIpAddress(intf *net.Interface) bool { - _, err := f.ipAddress(intf) + _, err := f.ipAddrs(intf) return err == nil } From 94f9542a04a481e5fb6b1bb01eb9275e35c2e402 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 6 Apr 2017 13:42:32 -0700 Subject: [PATCH 2/8] Finding the appropriate cidr block --- client/fingerprint/network.go | 70 +++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/client/fingerprint/network.go b/client/fingerprint/network.go index 6a5138113c9c..48a641d717be 100644 --- a/client/fingerprint/network.go +++ b/client/fingerprint/network.go @@ -83,53 +83,59 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) f.logger.Printf("[DEBUG] fingerprint.network: link speed could not be detected and no speed specified by user. Defaulting to %d", defaultNetworkSpeed) } - var ips []net.IP - if ips, err = f.ipAddrs(intf); err != nil || len(ips) == 0 { - return false, fmt.Errorf("Unable to find IP address of interface: %s, err: %v", intf.Name, err) + // Create the network resources from the interface + nwResources, err := f.createNetworkResources(mbits, intf) + if err != nil { + return false, err } - for _, ip := range ips { - newNetwork := &structs.NetworkResource{} - newNetwork.Device = intf.Name - newNetwork.IP = ip.String() - if ip.To4() != nil { - newNetwork.CIDR = newNetwork.IP + "/32" - } else { - newNetwork.CIDR = newNetwork.IP + "/64" - } - newNetwork.MBits = mbits - node.Resources.Networks = append(node.Resources.Networks, newNetwork) + // Add the network resources to the node + for _, nwResource := range nwResources { + node.Resources.Networks = append(node.Resources.Networks, nwResource) + f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v, CIDR: %v during fingerprinting", + intf.Name, nwResource.IP, nwResource.CIDR) + } - f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP %v during fingerprinting", intf.Name, ip) + if len(nwResources) > 0 { + node.Attributes["unique.network.ip-address"] = nwResources[0].IP } // return true, because we have a network connection return true, nil } -// ipAddrs gets all the ipv addresses associated with a network interface -func (f *NetworkFingerprint) ipAddrs(intf *net.Interface) ([]net.IP, error) { - var addrs []net.Addr - var err error - - if addrs, err = f.interfaceDetector.Addrs(intf); err != nil { +// createNetworkResources creates network resources for every IP +func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.Interface) ([]*structs.NetworkResource, error) { + // Find the interface with the name + addrs, err := f.interfaceDetector.Addrs(intf) + if err != nil { return nil, err } + nwResources := make([]*structs.NetworkResource, len(addrs)) + for i, addr := range addrs { + // Create a new network resource + newNetwork := &structs.NetworkResource{ + Device: intf.Name, + MBits: throughput, + } - ips := make([]net.IP, 0) - for _, addr := range addrs { - var ip net.IP + // Find the IP Addr and the CIDR from the Address switch v := (addr).(type) { case *net.IPNet: - ip = v.IP + newNetwork.IP = v.IP.String() + newNetwork.CIDR = v.String() case *net.IPAddr: - ip = v.IP + ip := v.IP + newNetwork.IP = ip.String() + if ip.To4() != nil { + newNetwork.CIDR = newNetwork.IP + "/32" + } else { + newNetwork.CIDR = newNetwork.IP + "/128" + } } - ips = append(ips, ip) + nwResources[i] = newNetwork } - - return ips, nil - + return nwResources, nil } // Checks if the device is marked UP by the operator @@ -139,8 +145,8 @@ func (f *NetworkFingerprint) isDeviceEnabled(intf *net.Interface) bool { // Checks if the device has any IP address configured func (f *NetworkFingerprint) deviceHasIpAddress(intf *net.Interface) bool { - _, err := f.ipAddrs(intf) - return err == nil + addrs, err := f.interfaceDetector.Addrs(intf) + return err == nil && len(addrs) != 0 } func (n *NetworkFingerprint) isDeviceLoopBackOrPointToPoint(intf *net.Interface) bool { From ad00ec861b481ca1110168ed29cba138846ba1a5 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 7 Apr 2017 16:04:36 -0700 Subject: [PATCH 3/8] Ignoring link local addresses --- client/fingerprint/network.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/client/fingerprint/network.go b/client/fingerprint/network.go index 48a641d717be..8e1538e10223 100644 --- a/client/fingerprint/network.go +++ b/client/fingerprint/network.go @@ -111,8 +111,8 @@ func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.In if err != nil { return nil, err } - nwResources := make([]*structs.NetworkResource, len(addrs)) - for i, addr := range addrs { + nwResources := make([]*structs.NetworkResource, 0) + for _, addr := range addrs { // Create a new network resource newNetwork := &structs.NetworkResource{ Device: intf.Name, @@ -120,20 +120,27 @@ func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.In } // Find the IP Addr and the CIDR from the Address + var ip net.IP switch v := (addr).(type) { case *net.IPNet: + ip = v.IP newNetwork.IP = v.IP.String() - newNetwork.CIDR = v.String() case *net.IPAddr: - ip := v.IP - newNetwork.IP = ip.String() - if ip.To4() != nil { - newNetwork.CIDR = newNetwork.IP + "/32" - } else { - newNetwork.CIDR = newNetwork.IP + "/128" - } + ip = v.IP } - nwResources[i] = newNetwork + + // If the ip is link-local then we ignore it + if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { + continue + } + newNetwork.IP = ip.String() + if ip.To4() != nil { + newNetwork.CIDR = newNetwork.IP + "/32" + } else { + newNetwork.CIDR = newNetwork.IP + "/128" + } + + nwResources = append(nwResources, newNetwork) } return nwResources, nil } From bee4b57368a50694169875375bc048098cb7d686 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 7 Apr 2017 18:28:22 -0700 Subject: [PATCH 4/8] Removed redundant code --- client/fingerprint/network.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/fingerprint/network.go b/client/fingerprint/network.go index 8e1538e10223..9bb08685346a 100644 --- a/client/fingerprint/network.go +++ b/client/fingerprint/network.go @@ -90,10 +90,9 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) } // Add the network resources to the node + node.Resources.Networks = nwResources for _, nwResource := range nwResources { - node.Resources.Networks = append(node.Resources.Networks, nwResource) - f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v, CIDR: %v during fingerprinting", - intf.Name, nwResource.IP, nwResource.CIDR) + f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v", intf.Name, nwResource.IP) } if len(nwResources) > 0 { @@ -124,7 +123,6 @@ func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.In switch v := (addr).(type) { case *net.IPNet: ip = v.IP - newNetwork.IP = v.IP.String() case *net.IPAddr: ip = v.IP } From e0d165812bf10ee6d1b023c4ef4459678bc7f6c9 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Fri, 7 Apr 2017 18:36:43 -0700 Subject: [PATCH 5/8] Fix api.NetworkResource fields --- api/resources.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/resources.go b/api/resources.go index 14c6a4fd72bd..8d3f27c6cd2b 100644 --- a/api/resources.go +++ b/api/resources.go @@ -66,12 +66,12 @@ type Port struct { // NetworkResource is used to describe required network // resources of a given task. type NetworkResource struct { - Public bool + Device string CIDR string - ReservedPorts []Port - DynamicPorts []Port IP string MBits *int + ReservedPorts []Port + DynamicPorts []Port } func (n *NetworkResource) Canonicalize() { From 536fdde121acedea6c14626aabd87ac0c8f6b7bb Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 8 Apr 2017 13:43:29 -0700 Subject: [PATCH 6/8] Added a test --- client/fingerprint/network.go | 1 + client/fingerprint/network_test.go | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/client/fingerprint/network.go b/client/fingerprint/network.go index 9bb08685346a..8c2c35d6541d 100644 --- a/client/fingerprint/network.go +++ b/client/fingerprint/network.go @@ -95,6 +95,7 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v", intf.Name, nwResource.IP) } + // Deprectaed, setting the first IP as unique IP for the node if len(nwResources) > 0 { node.Attributes["unique.network.ip-address"] = nwResources[0].IP } diff --git a/client/fingerprint/network_test.go b/client/fingerprint/network_test.go index 84e0a7642287..eaa638b56783 100644 --- a/client/fingerprint/network_test.go +++ b/client/fingerprint/network_test.go @@ -126,8 +126,9 @@ func (n *NetworkInterfaceDetectorMultipleInterfaces) Addrs(intf *net.Interface) if intf.Name == "eth0" { _, ipnet1, _ := net.ParseCIDR("100.64.0.11/10") - _, ipnet2, _ := net.ParseCIDR("2005:DB6::/48") - return []net.Addr{ipnet1, ipnet2}, nil + _, ipnet2, _ := net.ParseCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64") + ipAddr, _ := net.ResolveIPAddr("ip6", "fe80::140c:9579:8037:f565") + return []net.Addr{ipnet1, ipnet2, ipAddr}, nil } if intf.Name == "eth1" { @@ -307,4 +308,16 @@ func TestNetworkFingerPrint_excludelo_down_interfaces(t *testing.T) { if net.MBits == 0 { t.Fatal("Expected Network Resource to have a non-zero bandwith") } + + // Test the CIDR of the IPs + if node.Resources.Networks[0].CIDR != "100.64.0.0/32" { + t.Fatalf("bad CIDR: %v", node.Resources.Networks[0].CIDR) + } + if node.Resources.Networks[1].CIDR != "2001:db8:85a3::/128" { + t.Fatalf("bad CIDR: %v", node.Resources.Networks[1].CIDR) + } + // Ensure that the link local address isn't fingerprinted + if len(node.Resources.Networks) != 2 { + t.Fatalf("bad number of IPs %v", len(node.Resources.Networks)) + } } From 2073eaf2bead17df842f7e6cb870d6bdb80e6dda Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 10 Apr 2017 11:27:32 -0700 Subject: [PATCH 7/8] Added docs --- CHANGELOG.md | 2 ++ website/source/docs/agent/configuration/client.html.md | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6934aee5143b..0cb429b1eadf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ IMPROVEMENTS: * driver/docker: Allow setting container IP with user defined networks [GH-2535] + * client: Fingerprint all routable addresses on an interface including IPv6 + addresses [GH-2536] BUG FIXES: * server: Reject non-TLS clients when TLS enabled [GH-2525] diff --git a/website/source/docs/agent/configuration/client.html.md b/website/source/docs/agent/configuration/client.html.md index 38cf283175eb..9bcf9c0214b4 100644 --- a/website/source/docs/agent/configuration/client.html.md +++ b/website/source/docs/agent/configuration/client.html.md @@ -51,7 +51,9 @@ client { - `network_interface` `(string: "lo | lo0")` - Specifies the name of the interface to force network fingerprinting on. This defaults to the loopback - interface. + interface. All addresses on the interface are fingerprinted except the ones + which are scoped local for IPv6. On a dual stack system the scheduler is going + to pick one of the many addresses which have been fingerprinted. - `network_speed` `(int: 0)` - Specifies an override for the network link speed. This value, if set, overrides any detected or defaulted link speed. Most From b0a20b4dc965a38b0c843f47c16685ccad7439da Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 10 Apr 2017 11:31:14 -0700 Subject: [PATCH 8/8] Fixed typo --- client/fingerprint/network.go | 2 +- website/source/docs/agent/configuration/client.html.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/fingerprint/network.go b/client/fingerprint/network.go index 8c2c35d6541d..bfc59c7e8307 100644 --- a/client/fingerprint/network.go +++ b/client/fingerprint/network.go @@ -95,7 +95,7 @@ func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v", intf.Name, nwResource.IP) } - // Deprectaed, setting the first IP as unique IP for the node + // Deprecated, setting the first IP as unique IP for the node if len(nwResources) > 0 { node.Attributes["unique.network.ip-address"] = nwResources[0].IP } diff --git a/website/source/docs/agent/configuration/client.html.md b/website/source/docs/agent/configuration/client.html.md index 9bcf9c0214b4..d622f843c82d 100644 --- a/website/source/docs/agent/configuration/client.html.md +++ b/website/source/docs/agent/configuration/client.html.md @@ -52,8 +52,8 @@ client { - `network_interface` `(string: "lo | lo0")` - Specifies the name of the interface to force network fingerprinting on. This defaults to the loopback interface. All addresses on the interface are fingerprinted except the ones - which are scoped local for IPv6. On a dual stack system the scheduler is going - to pick one of the many addresses which have been fingerprinted. + which are scoped local for IPv6. The scheduler is going to pick one of the + many addresses which have been fingerprinted. - `network_speed` `(int: 0)` - Specifies an override for the network link speed. This value, if set, overrides any detected or defaulted link speed. Most