github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/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 // Create a sub-logger with common values to help with debugging 82 logger := f.logger.With("interface", intf.Name) 83 84 // Record the throughput of the interface 85 var mbits int 86 throughput := f.linkSpeed(intf.Name) 87 if cfg.NetworkSpeed != 0 { 88 mbits = cfg.NetworkSpeed 89 logger.Debug("setting link speed to user configured speed", "mbits", mbits) 90 } else if throughput != 0 { 91 mbits = throughput 92 logger.Debug("link speed detected", "mbits", mbits) 93 } else { 94 mbits = defaultNetworkSpeed 95 logger.Debug("link speed could not be detected and no speed specified by user, falling back to default speed", "mbits", defaultNetworkSpeed) 96 } 97 98 // Create the network resources from the interface 99 disallowLinkLocal := cfg.ReadBoolDefault(networkDisallowLinkLocalOption, networkDisallowLinkLocalDefault) 100 nwResources, err := f.createNetworkResources(mbits, intf, disallowLinkLocal) 101 if err != nil { 102 return err 103 } 104 105 // COMPAT(0.10): Remove in 0.10 106 resp.Resources = &structs.Resources{ 107 Networks: nwResources, 108 } 109 110 resp.NodeResources = &structs.NodeResources{ 111 Networks: nwResources, 112 } 113 114 for _, nwResource := range nwResources { 115 logger.Debug("detected interface IP", "IP", nwResource.IP) 116 } 117 118 // Deprecated, setting the first IP as unique IP for the node 119 if len(nwResources) > 0 { 120 resp.AddAttribute("unique.network.ip-address", nwResources[0].IP) 121 } 122 123 ifaces, err := f.interfaceDetector.Interfaces() 124 if err != nil { 125 return err 126 } 127 nodeNetResources, err := f.createNodeNetworkResources(ifaces, disallowLinkLocal, req.Config) 128 if err != nil { 129 return err 130 } 131 resp.NodeResources.NodeNetworks = nodeNetResources 132 133 resp.Detected = true 134 135 return nil 136 } 137 138 func (f *NetworkFingerprint) createNodeNetworkResources(ifaces []net.Interface, disallowLinkLocal bool, conf *config.Config) ([]*structs.NodeNetworkResource, error) { 139 nets := make([]*structs.NodeNetworkResource, 0) 140 for _, iface := range ifaces { 141 speed := f.linkSpeed(iface.Name) 142 if speed == 0 { 143 speed = defaultNetworkSpeed 144 f.logger.Debug("link speed could not be detected, falling back to default speed", "interface", iface.Name, "mbits", defaultNetworkSpeed) 145 } 146 147 newNetwork := &structs.NodeNetworkResource{ 148 Mode: "host", 149 Device: iface.Name, 150 MacAddress: iface.HardwareAddr.String(), 151 Speed: speed, 152 } 153 addrs, err := f.interfaceDetector.Addrs(&iface) 154 if err != nil { 155 return nil, err 156 } 157 var networkAddrs, linkLocalAddrs []structs.NodeNetworkAddress 158 for _, addr := range addrs { 159 // Find the IP Addr and the CIDR from the Address 160 var ip net.IP 161 var family structs.NodeNetworkAF 162 switch v := (addr).(type) { 163 case *net.IPNet: 164 ip = v.IP 165 case *net.IPAddr: 166 ip = v.IP 167 } 168 169 if ip.To4() != nil { 170 family = structs.NodeNetworkAF_IPv4 171 } else { 172 family = structs.NodeNetworkAF_IPv6 173 } 174 for _, alias := range deriveAddressAliases(iface, ip, conf) { 175 newAddr := structs.NodeNetworkAddress{ 176 Address: ip.String(), 177 Family: family, 178 Alias: alias, 179 } 180 181 if hostNetwork, ok := conf.HostNetworks[alias]; ok { 182 newAddr.ReservedPorts = hostNetwork.ReservedPorts 183 } 184 185 if newAddr.Alias != "" { 186 if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { 187 linkLocalAddrs = append(linkLocalAddrs, newAddr) 188 } else { 189 networkAddrs = append(networkAddrs, newAddr) 190 } 191 } 192 } 193 } 194 195 if len(networkAddrs) == 0 && len(linkLocalAddrs) > 0 { 196 if disallowLinkLocal { 197 f.logger.Debug("ignoring detected link-local address on interface", "interface", iface.Name) 198 } else { 199 newNetwork.Addresses = linkLocalAddrs 200 } 201 } else { 202 newNetwork.Addresses = networkAddrs 203 } 204 205 if len(newNetwork.Addresses) > 0 { 206 nets = append(nets, newNetwork) 207 } 208 } 209 return nets, nil 210 } 211 212 func deriveAddressAliases(iface net.Interface, addr net.IP, config *config.Config) (aliases []string) { 213 for name, conf := range config.HostNetworks { 214 var cidrMatch, ifaceMatch bool 215 if conf.CIDR != "" { 216 for _, cidr := range strings.Split(conf.CIDR, ",") { 217 _, ipnet, err := net.ParseCIDR(cidr) 218 if err != nil { 219 continue 220 } 221 222 if ipnet.Contains(addr) { 223 cidrMatch = true 224 break 225 } 226 } 227 } else { 228 cidrMatch = true 229 } 230 if conf.Interface != "" { 231 ifaceName, err := template.Parse(conf.Interface) 232 if err != nil { 233 continue 234 } 235 236 if ifaceName == iface.Name { 237 ifaceMatch = true 238 } 239 } else { 240 ifaceMatch = true 241 } 242 if cidrMatch && ifaceMatch { 243 aliases = append(aliases, name) 244 } 245 } 246 247 if len(aliases) > 0 { 248 return 249 } 250 251 if config.NetworkInterface != "" { 252 if config.NetworkInterface == iface.Name { 253 return []string{"default"} 254 } 255 } else if ri, err := sockaddr.NewRouteInfo(); err == nil { 256 defaultIface, err := ri.GetDefaultInterfaceName() 257 if err == nil && iface.Name == defaultIface { 258 return []string{"default"} 259 } 260 } 261 262 return 263 } 264 265 // createNetworkResources creates network resources for every IP 266 func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.Interface, disallowLinkLocal bool) ([]*structs.NetworkResource, error) { 267 // Find the interface with the name 268 addrs, err := f.interfaceDetector.Addrs(intf) 269 if err != nil { 270 return nil, err 271 } 272 273 nwResources := make([]*structs.NetworkResource, 0) 274 linkLocals := make([]*structs.NetworkResource, 0) 275 276 for _, addr := range addrs { 277 // Create a new network resource 278 newNetwork := &structs.NetworkResource{ 279 Mode: "host", 280 Device: intf.Name, 281 MBits: throughput, 282 } 283 284 // Find the IP Addr and the CIDR from the Address 285 var ip net.IP 286 switch v := (addr).(type) { 287 case *net.IPNet: 288 ip = v.IP 289 case *net.IPAddr: 290 ip = v.IP 291 } 292 293 newNetwork.IP = ip.String() 294 if ip.To4() != nil { 295 newNetwork.CIDR = newNetwork.IP + "/32" 296 } else { 297 newNetwork.CIDR = newNetwork.IP + "/128" 298 } 299 300 // If the ip is link-local then we ignore it unless the user allows it 301 // and we detect nothing else 302 if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { 303 linkLocals = append(linkLocals, newNetwork) 304 continue 305 } 306 307 nwResources = append(nwResources, newNetwork) 308 } 309 310 if len(nwResources) == 0 && len(linkLocals) != 0 { 311 if disallowLinkLocal { 312 f.logger.Debug("ignoring detected link-local address on interface", "interface", intf.Name) 313 return nwResources, nil 314 } 315 316 return linkLocals, nil 317 } 318 319 return nwResources, nil 320 } 321 322 // Returns the interface with the name passed by user. If the name is blank, we 323 // use the interface attached to the default route. 324 func (f *NetworkFingerprint) findInterface(deviceName string) (*net.Interface, error) { 325 // If we aren't given a device, look it up by using the interface with the default route 326 if deviceName == "" { 327 ri, err := sockaddr.NewRouteInfo() 328 if err != nil { 329 return nil, err 330 } 331 332 defaultIfName, err := ri.GetDefaultInterfaceName() 333 if err != nil { 334 return nil, err 335 } 336 if defaultIfName == "" { 337 return nil, fmt.Errorf("no network_interface given and failed to determine interface attached to default route") 338 } 339 deviceName = defaultIfName 340 } 341 342 return f.interfaceDetector.InterfaceByName(deviceName) 343 }