Merge pull request #2536 from hashicorp/f-ipv6-fingerprint

Support IPv6 addresses during fingerprinting
This commit is contained in:
Diptanu Choudhury
2017-04-10 11:45:59 -07:00
committed by GitHub
5 changed files with 77 additions and 47 deletions

View File

@@ -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]

View File

@@ -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() {

View File

@@ -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,52 +69,57 @@ 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{}
// Create the network resources from the interface
nwResources, err := f.createNetworkResources(mbits, intf)
if err != nil {
return false, err
}
node.Resources.Networks = append(node.Resources.Networks, newNetwork)
// Add the network resources to the node
node.Resources.Networks = nwResources
for _, nwResource := range nwResources {
f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v", intf.Name, nwResource.IP)
}
// Deprecated, setting the first IP as unique IP for the node
if len(nwResources) > 0 {
node.Attributes["unique.network.ip-address"] = nwResources[0].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) {
var addrs []net.Addr
var err error
if addrs, err = f.interfaceDetector.Addrs(intf); err != nil {
return "", err
}
if len(addrs) == 0 {
return "", errors.New(fmt.Sprintf("Interface %s has no IP address", intf.Name))
// 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, 0)
for _, addr := range addrs {
// Create a new network resource
newNetwork := &structs.NetworkResource{
Device: intf.Name,
MBits: throughput,
}
// Find the IP Addr and the CIDR from the Address
var ip net.IP
switch v := (addr).(type) {
case *net.IPNet:
@@ -122,13 +127,21 @@ func (f *NetworkFingerprint) ipAddress(intf *net.Interface) (string, error) {
case *net.IPAddr:
ip = v.IP
}
if ip.To4() != nil {
return ip.String(), nil
// 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 "", fmt.Errorf("Couldn't parse IP address for interface %s", intf.Name)
return nwResources, nil
}
// Checks if the device is marked UP by the operator
@@ -138,8 +151,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.ipAddress(intf)
return err == nil
addrs, err := f.interfaceDetector.Addrs(intf)
return err == nil && len(addrs) != 0
}
func (n *NetworkFingerprint) isDeviceLoopBackOrPointToPoint(intf *net.Interface) bool {

View File

@@ -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))
}
}

View File

@@ -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. 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