github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/fingerprint/network.go (about)

     1  package fingerprint
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  
     8  	log "github.com/hashicorp/go-hclog"
     9  	sockaddr "github.com/hashicorp/go-sockaddr"
    10  	"github.com/hashicorp/go-sockaddr/template"
    11  	"github.com/hashicorp/nomad/client/config"
    12  	"github.com/hashicorp/nomad/nomad/structs"
    13  )
    14  
    15  const (
    16  	// defaultNetworkSpeed is the speed set if the network link speed could not
    17  	// be detected.
    18  	defaultNetworkSpeed = 1000
    19  
    20  	// networkDisallowLinkLocalOption/Default are used to allow the operator to
    21  	// decide how the fingerprinter handles an interface that only contains link
    22  	// local addresses.
    23  	networkDisallowLinkLocalOption  = "fingerprint.network.disallow_link_local"
    24  	networkDisallowLinkLocalDefault = false
    25  )
    26  
    27  // NetworkFingerprint is used to fingerprint the Network capabilities of a node
    28  type NetworkFingerprint struct {
    29  	StaticFingerprinter
    30  	logger            log.Logger
    31  	interfaceDetector NetworkInterfaceDetector
    32  }
    33  
    34  // An interface to isolate calls to various api in net package
    35  // This facilitates testing where we can implement
    36  // fake interfaces and addresses to test varios code paths
    37  type NetworkInterfaceDetector interface {
    38  	Interfaces() ([]net.Interface, error)
    39  	InterfaceByName(name string) (*net.Interface, error)
    40  	Addrs(intf *net.Interface) ([]net.Addr, error)
    41  }
    42  
    43  // Implements the interface detector which calls net directly
    44  type DefaultNetworkInterfaceDetector struct {
    45  }
    46  
    47  func (b *DefaultNetworkInterfaceDetector) Interfaces() ([]net.Interface, error) {
    48  	return net.Interfaces()
    49  }
    50  
    51  func (b *DefaultNetworkInterfaceDetector) InterfaceByName(name string) (*net.Interface, error) {
    52  	return net.InterfaceByName(name)
    53  }
    54  
    55  func (b *DefaultNetworkInterfaceDetector) Addrs(intf *net.Interface) ([]net.Addr, error) {
    56  	return intf.Addrs()
    57  }
    58  
    59  // NewNetworkFingerprint returns a new NetworkFingerprinter with the given
    60  // logger
    61  func NewNetworkFingerprint(logger log.Logger) Fingerprint {
    62  	f := &NetworkFingerprint{logger: logger.Named("network"), interfaceDetector: &DefaultNetworkInterfaceDetector{}}
    63  	return f
    64  }
    65  
    66  func (f *NetworkFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error {
    67  	cfg := req.Config
    68  
    69  	// Find the named interface
    70  	intf, err := f.findInterface(cfg.NetworkInterface)
    71  	switch {
    72  	case err != nil:
    73  		return fmt.Errorf("Error while detecting network interface %s during fingerprinting: %v",
    74  			cfg.NetworkInterface,
    75  			err)
    76  	case intf == nil:
    77  		// No interface could be found
    78  		return nil
    79  	}
    80  
    81  	// Record the throughput of the interface
    82  	var mbits int
    83  	throughput := f.linkSpeed(intf.Name)
    84  	if cfg.NetworkSpeed != 0 {
    85  		mbits = cfg.NetworkSpeed
    86  		f.logger.Debug("setting link speed to user configured speed", "mbits", mbits)
    87  	} else if throughput != 0 {
    88  		mbits = throughput
    89  		f.logger.Debug("link speed detected", "interface", intf.Name, "mbits", mbits)
    90  	} else {
    91  		mbits = defaultNetworkSpeed
    92  		f.logger.Debug("link speed could not be detected and no speed specified by user, falling back to default speed", "mbits", defaultNetworkSpeed)
    93  	}
    94  
    95  	// Create the network resources from the interface
    96  	disallowLinkLocal := cfg.ReadBoolDefault(networkDisallowLinkLocalOption, networkDisallowLinkLocalDefault)
    97  	nwResources, err := f.createNetworkResources(mbits, intf, disallowLinkLocal)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	// COMPAT(0.10): Remove in 0.10
   103  	resp.Resources = &structs.Resources{
   104  		Networks: nwResources,
   105  	}
   106  
   107  	resp.NodeResources = &structs.NodeResources{
   108  		Networks: nwResources,
   109  	}
   110  
   111  	for _, nwResource := range nwResources {
   112  		f.logger.Debug("detected interface IP", "interface", intf.Name, "IP", nwResource.IP)
   113  	}
   114  
   115  	// Deprecated, setting the first IP as unique IP for the node
   116  	if len(nwResources) > 0 {
   117  		resp.AddAttribute("unique.network.ip-address", nwResources[0].IP)
   118  	}
   119  
   120  	ifaces, err := f.interfaceDetector.Interfaces()
   121  	if err != nil {
   122  		return err
   123  	}
   124  	nodeNetResources, err := f.createNodeNetworkResources(ifaces, disallowLinkLocal, req.Config)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	resp.NodeResources.NodeNetworks = nodeNetResources
   129  
   130  	resp.Detected = true
   131  
   132  	return nil
   133  }
   134  
   135  func (f *NetworkFingerprint) createNodeNetworkResources(ifaces []net.Interface, disallowLinkLocal bool, conf *config.Config) ([]*structs.NodeNetworkResource, error) {
   136  	nets := make([]*structs.NodeNetworkResource, 0)
   137  	for _, iface := range ifaces {
   138  		speed := f.linkSpeed(iface.Name)
   139  		if speed == 0 {
   140  			speed = defaultNetworkSpeed
   141  			f.logger.Debug("link speed could not be detected, falling back to default speed", "mbits", defaultNetworkSpeed)
   142  		}
   143  
   144  		newNetwork := &structs.NodeNetworkResource{
   145  			Mode:       "host",
   146  			Device:     iface.Name,
   147  			MacAddress: iface.HardwareAddr.String(),
   148  			Speed:      speed,
   149  		}
   150  		addrs, err := f.interfaceDetector.Addrs(&iface)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  		var networkAddrs, linkLocalAddrs []structs.NodeNetworkAddress
   155  		for _, addr := range addrs {
   156  			// Find the IP Addr and the CIDR from the Address
   157  			var ip net.IP
   158  			var family structs.NodeNetworkAF
   159  			switch v := (addr).(type) {
   160  			case *net.IPNet:
   161  				ip = v.IP
   162  			case *net.IPAddr:
   163  				ip = v.IP
   164  			}
   165  
   166  			if ip.To4() != nil {
   167  				family = structs.NodeNetworkAF_IPv4
   168  			} else {
   169  				family = structs.NodeNetworkAF_IPv6
   170  			}
   171  			newAddr := structs.NodeNetworkAddress{
   172  				Address: ip.String(),
   173  				Family:  family,
   174  				Alias:   deriveAddressAlias(iface, ip, conf),
   175  			}
   176  
   177  			if newAddr.Alias != "" {
   178  				if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
   179  					linkLocalAddrs = append(linkLocalAddrs, newAddr)
   180  				} else {
   181  					networkAddrs = append(networkAddrs, newAddr)
   182  				}
   183  			}
   184  		}
   185  
   186  		if len(networkAddrs) == 0 && len(linkLocalAddrs) > 0 {
   187  			if disallowLinkLocal {
   188  				f.logger.Debug("ignoring detected link-local address on interface", "interface", iface.Name)
   189  			} else {
   190  				newNetwork.Addresses = linkLocalAddrs
   191  			}
   192  		} else {
   193  			newNetwork.Addresses = networkAddrs
   194  		}
   195  
   196  		if len(newNetwork.Addresses) > 0 {
   197  			nets = append(nets, newNetwork)
   198  		}
   199  	}
   200  	return nets, nil
   201  }
   202  
   203  func deriveAddressAlias(iface net.Interface, addr net.IP, config *config.Config) string {
   204  	for name, conf := range config.HostNetworks {
   205  		var cidrMatch, ifaceMatch bool
   206  		if conf.CIDR != "" {
   207  			for _, cidr := range strings.Split(conf.CIDR, ",") {
   208  				_, ipnet, err := net.ParseCIDR(cidr)
   209  				if err != nil {
   210  					continue
   211  				}
   212  
   213  				if ipnet.Contains(addr) {
   214  					cidrMatch = true
   215  					break
   216  				}
   217  			}
   218  		} else {
   219  			cidrMatch = true
   220  		}
   221  		if conf.Interface != "" {
   222  			ifaceName, err := template.Parse(conf.Interface)
   223  			if err != nil {
   224  				continue
   225  			}
   226  
   227  			if ifaceName == iface.Name {
   228  				ifaceMatch = true
   229  			}
   230  		} else {
   231  			ifaceMatch = true
   232  		}
   233  		if cidrMatch && ifaceMatch {
   234  			return name
   235  		}
   236  	}
   237  
   238  	if config.NetworkInterface != "" {
   239  		if config.NetworkInterface == iface.Name {
   240  			return "default"
   241  		}
   242  	} else if ri, err := sockaddr.NewRouteInfo(); err == nil {
   243  		defaultIface, err := ri.GetDefaultInterfaceName()
   244  		if err == nil && iface.Name == defaultIface {
   245  			return "default"
   246  		}
   247  	}
   248  
   249  	return ""
   250  }
   251  
   252  // createNetworkResources creates network resources for every IP
   253  func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.Interface, disallowLinkLocal bool) ([]*structs.NetworkResource, error) {
   254  	// Find the interface with the name
   255  	addrs, err := f.interfaceDetector.Addrs(intf)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	nwResources := make([]*structs.NetworkResource, 0)
   261  	linkLocals := make([]*structs.NetworkResource, 0)
   262  
   263  	for _, addr := range addrs {
   264  		// Create a new network resource
   265  		newNetwork := &structs.NetworkResource{
   266  			Mode:   "host",
   267  			Device: intf.Name,
   268  			MBits:  throughput,
   269  		}
   270  
   271  		// Find the IP Addr and the CIDR from the Address
   272  		var ip net.IP
   273  		switch v := (addr).(type) {
   274  		case *net.IPNet:
   275  			ip = v.IP
   276  		case *net.IPAddr:
   277  			ip = v.IP
   278  		}
   279  
   280  		newNetwork.IP = ip.String()
   281  		if ip.To4() != nil {
   282  			newNetwork.CIDR = newNetwork.IP + "/32"
   283  		} else {
   284  			newNetwork.CIDR = newNetwork.IP + "/128"
   285  		}
   286  
   287  		// If the ip is link-local then we ignore it unless the user allows it
   288  		// and we detect nothing else
   289  		if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
   290  			linkLocals = append(linkLocals, newNetwork)
   291  			continue
   292  		}
   293  
   294  		nwResources = append(nwResources, newNetwork)
   295  	}
   296  
   297  	if len(nwResources) == 0 && len(linkLocals) != 0 {
   298  		if disallowLinkLocal {
   299  			f.logger.Debug("ignoring detected link-local address on interface", "interface", intf.Name)
   300  			return nwResources, nil
   301  		}
   302  
   303  		return linkLocals, nil
   304  	}
   305  
   306  	return nwResources, nil
   307  }
   308  
   309  // Returns the interface with the name passed by user. If the name is blank, we
   310  // use the interface attached to the default route.
   311  func (f *NetworkFingerprint) findInterface(deviceName string) (*net.Interface, error) {
   312  	// If we aren't given a device, look it up by using the interface with the default route
   313  	if deviceName == "" {
   314  		ri, err := sockaddr.NewRouteInfo()
   315  		if err != nil {
   316  			return nil, err
   317  		}
   318  
   319  		defaultIfName, err := ri.GetDefaultInterfaceName()
   320  		if err != nil {
   321  			return nil, err
   322  		}
   323  		if defaultIfName == "" {
   324  			return nil, fmt.Errorf("no network_interface given and failed to determine interface attached to default route")
   325  		}
   326  		deviceName = defaultIfName
   327  	}
   328  
   329  	return f.interfaceDetector.InterfaceByName(deviceName)
   330  }