github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/fingerprint/network.go (about) 1 package fingerprint 2 3 import ( 4 "fmt" 5 "net" 6 7 log "github.com/hashicorp/go-hclog" 8 "github.com/hashicorp/nomad/nomad/structs" 9 sockaddr "github.com/samsungnext/go-sockaddr" 10 ) 11 12 const ( 13 // defaultNetworkSpeed is the speed set if the network link speed could not 14 // be detected. 15 defaultNetworkSpeed = 1000 16 17 // networkDisallowLinkLocalOption/Default are used to allow the operator to 18 // decide how the fingerprinter handles an interface that only contains link 19 // local addresses. 20 networkDisallowLinkLocalOption = "fingerprint.network.disallow_link_local" 21 networkDisallowLinkLocalDefault = false 22 ) 23 24 // NetworkFingerprint is used to fingerprint the Network capabilities of a node 25 type NetworkFingerprint struct { 26 StaticFingerprinter 27 logger log.Logger 28 interfaceDetector NetworkInterfaceDetector 29 } 30 31 // An interface to isolate calls to various api in net package 32 // This facilitates testing where we can implement 33 // fake interfaces and addresses to test varios code paths 34 type NetworkInterfaceDetector interface { 35 Interfaces() ([]net.Interface, error) 36 InterfaceByName(name string) (*net.Interface, error) 37 Addrs(intf *net.Interface) ([]net.Addr, error) 38 } 39 40 // Implements the interface detector which calls net directly 41 type DefaultNetworkInterfaceDetector struct { 42 } 43 44 func (b *DefaultNetworkInterfaceDetector) Interfaces() ([]net.Interface, error) { 45 return net.Interfaces() 46 } 47 48 func (b *DefaultNetworkInterfaceDetector) InterfaceByName(name string) (*net.Interface, error) { 49 return net.InterfaceByName(name) 50 } 51 52 func (b *DefaultNetworkInterfaceDetector) Addrs(intf *net.Interface) ([]net.Addr, error) { 53 return intf.Addrs() 54 } 55 56 // NewNetworkFingerprint returns a new NetworkFingerprinter with the given 57 // logger 58 func NewNetworkFingerprint(logger log.Logger) Fingerprint { 59 f := &NetworkFingerprint{logger: logger.Named("network"), interfaceDetector: &DefaultNetworkInterfaceDetector{}} 60 return f 61 } 62 63 func (f *NetworkFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error { 64 cfg := req.Config 65 66 // Find the named interface 67 intf, err := f.findInterface(cfg.NetworkInterface) 68 switch { 69 case err != nil: 70 return fmt.Errorf("Error while detecting network interface %s during fingerprinting: %v", 71 cfg.NetworkInterface, 72 err) 73 case intf == nil: 74 // No interface could be found 75 return nil 76 } 77 78 // Record the throughput of the interface 79 var mbits int 80 throughput := f.linkSpeed(intf.Name) 81 if cfg.NetworkSpeed != 0 { 82 mbits = cfg.NetworkSpeed 83 f.logger.Debug("setting link speed to user configured speed", "mbits", mbits) 84 } else if throughput != 0 { 85 mbits = throughput 86 f.logger.Debug("link speed detected", "interface", intf.Name, "mbits", mbits) 87 } else { 88 mbits = defaultNetworkSpeed 89 f.logger.Debug("link speed could not be detected and no speed specified by user, falling back to default speed", "mbits", defaultNetworkSpeed) 90 } 91 92 // Create the network resources from the interface 93 disallowLinkLocal := cfg.ReadBoolDefault(networkDisallowLinkLocalOption, networkDisallowLinkLocalDefault) 94 nwResources, err := f.createNetworkResources(mbits, intf, disallowLinkLocal) 95 if err != nil { 96 return err 97 } 98 99 // COMPAT(0.10): Remove in 0.10 100 resp.Resources = &structs.Resources{ 101 Networks: nwResources, 102 } 103 104 resp.NodeResources = &structs.NodeResources{ 105 Networks: nwResources, 106 } 107 108 for _, nwResource := range nwResources { 109 f.logger.Debug("detected interface IP", "interface", intf.Name, "IP", nwResource.IP) 110 } 111 112 // Deprecated, setting the first IP as unique IP for the node 113 if len(nwResources) > 0 { 114 resp.AddAttribute("unique.network.ip-address", nwResources[0].IP) 115 } 116 resp.Detected = true 117 118 return nil 119 } 120 121 // createNetworkResources creates network resources for every IP 122 func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.Interface, disallowLinkLocal bool) ([]*structs.NetworkResource, error) { 123 // Find the interface with the name 124 addrs, err := f.interfaceDetector.Addrs(intf) 125 if err != nil { 126 return nil, err 127 } 128 129 nwResources := make([]*structs.NetworkResource, 0) 130 linkLocals := make([]*structs.NetworkResource, 0) 131 132 for _, addr := range addrs { 133 // Create a new network resource 134 newNetwork := &structs.NetworkResource{ 135 Device: intf.Name, 136 MBits: throughput, 137 } 138 139 // Find the IP Addr and the CIDR from the Address 140 var ip net.IP 141 switch v := (addr).(type) { 142 case *net.IPNet: 143 ip = v.IP 144 case *net.IPAddr: 145 ip = v.IP 146 } 147 148 newNetwork.IP = ip.String() 149 if ip.To4() != nil { 150 newNetwork.CIDR = newNetwork.IP + "/32" 151 } else { 152 newNetwork.CIDR = newNetwork.IP + "/128" 153 } 154 155 // If the ip is link-local then we ignore it unless the user allows it 156 // and we detect nothing else 157 if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { 158 linkLocals = append(linkLocals, newNetwork) 159 continue 160 } 161 162 nwResources = append(nwResources, newNetwork) 163 } 164 165 if len(nwResources) == 0 && len(linkLocals) != 0 { 166 if disallowLinkLocal { 167 f.logger.Debug("ignoring detected link-local address on interface", "interface", intf.Name) 168 return nwResources, nil 169 } 170 171 return linkLocals, nil 172 } 173 174 return nwResources, nil 175 } 176 177 // Returns the interface with the name passed by user. If the name is blank, we 178 // use the interface attached to the default route. 179 func (f *NetworkFingerprint) findInterface(deviceName string) (*net.Interface, error) { 180 // If we aren't given a device, look it up by using the interface with the default route 181 if deviceName == "" { 182 ri, err := sockaddr.NewRouteInfo() 183 if err != nil { 184 return nil, err 185 } 186 187 defaultIfName, err := ri.GetDefaultInterfaceName() 188 if err != nil { 189 return nil, err 190 } 191 if defaultIfName == "" { 192 return nil, fmt.Errorf("no network_interface given and failed to determine interface attached to default route") 193 } 194 deviceName = defaultIfName 195 } 196 197 return f.interfaceDetector.InterfaceByName(deviceName) 198 }