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 }