github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/network/nic.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package network 5 6 import ( 7 "fmt" 8 "net" 9 "strings" 10 11 "github.com/juju/errors" 12 ) 13 14 // VirtualPortType defines the list of known port types for virtual NICs. 15 type VirtualPortType string 16 17 const ( 18 NonVirtualPort VirtualPortType = "" 19 OvsPort VirtualPortType = "openvswitch" 20 ) 21 22 // Route defines a single route to a subnet via a defined gateway. 23 type Route struct { 24 // DestinationCIDR is the subnet that we want a controlled route to. 25 DestinationCIDR string 26 // GatewayIP is the IP (v4 or v6) that should be used for traffic that is 27 // bound for DestinationCIDR 28 GatewayIP string 29 // Metric is the weight to apply to this route. 30 Metric int 31 } 32 33 // Validate that this Route is properly formed. 34 func (r Route) Validate() error { 35 // Make sure the CIDR is actually a CIDR not just an IP or hostname 36 destinationIP, _, err := net.ParseCIDR(r.DestinationCIDR) 37 if err != nil { 38 return errors.Annotate(err, "DestinationCIDR not valid") 39 } 40 // Make sure the Gateway is just an IP, not a CIDR, etc. 41 gatewayIP := net.ParseIP(r.GatewayIP) 42 if gatewayIP == nil { 43 return errors.Errorf("GatewayIP is not a valid IP address: %q", r.GatewayIP) 44 } 45 if r.Metric < 0 { 46 return errors.Errorf("Metric is negative: %d", r.Metric) 47 } 48 // Make sure that either both are IPv4 or both are IPv6, not mixed. 49 destIP4 := destinationIP.To4() 50 gatewayIP4 := gatewayIP.To4() 51 if destIP4 != nil { 52 if gatewayIP4 == nil { 53 return errors.Errorf("DestinationCIDR is IPv4 (%s) but GatewayIP is IPv6 (%s)", r.DestinationCIDR, r.GatewayIP) 54 } 55 } else { 56 if gatewayIP4 != nil { 57 return errors.Errorf("DestinationCIDR is IPv6 (%s) but GatewayIP is IPv4 (%s)", r.DestinationCIDR, r.GatewayIP) 58 } 59 } 60 return nil 61 } 62 63 // InterfaceInfo describes a single network interface. 64 // 65 // A note on ConfigType stored against the interface, and on members of the 66 // Addresses collection: 67 // Addresses detected for machines during discovery (on-machine or via the 68 // instance-poller) are denormalised for storage in that the configuration 69 // method (generally associated with the device) is stored for each address. 70 // So when incoming, ConfigType supplied with *addresses* is prioritised. 71 // Alternatively, when supplied to instance provisioning as network 72 // configuration for cloud-init, we are informing how a *device* should be 73 // configured for addresses and so we use the ConfigType against the interface. 74 type InterfaceInfo struct { 75 // DeviceIndex specifies the order in which the network interface 76 // appears on the host. The primary interface has an index of 0. 77 DeviceIndex int 78 79 // MACAddress is the network interface's hardware MAC address 80 // (e.g. "aa:bb:cc:dd:ee:ff"). 81 MACAddress string 82 83 // ProviderId is a provider-specific NIC id. 84 ProviderId Id 85 86 // ProviderSubnetId is the provider-specific id for the associated 87 // subnet. 88 ProviderSubnetId Id 89 90 // ProviderNetworkId is the provider-specific id for the 91 // associated network. 92 ProviderNetworkId Id 93 94 // ProviderSpaceId is the provider-specific id for the associated space, 95 // if known and supported. 96 ProviderSpaceId Id 97 98 // ProviderVLANId is the provider-specific id of the VLAN for this 99 // interface. 100 ProviderVLANId Id 101 102 // ProviderAddressId is the provider-specific id of the assigned address. 103 ProviderAddressId Id 104 105 // AvailabilityZones describes the availability zones the associated 106 // subnet is in. 107 AvailabilityZones []string 108 109 // VLANTag needs to be between 1 and 4094 for VLANs and 0 for 110 // normal networks. It's defined by IEEE 802.1Q standard. 111 VLANTag int 112 113 // InterfaceName is the raw OS-specific network device name (e.g. 114 // "eth1", even for a VLAN eth1.42 virtual interface). 115 InterfaceName string 116 117 // ParentInterfaceName is the name of the parent interface to use, 118 // if known. 119 ParentInterfaceName string 120 121 // InterfaceType is the type of the interface. 122 InterfaceType LinkLayerDeviceType 123 124 // Disabled is true when the interface needs to be disabled on the 125 // machine, e.g. not to configure it. 126 Disabled bool 127 128 // NoAutoStart is true when the interface should not be configured 129 // to start automatically on boot. 130 // By default and for backwards-compatibility, interfaces are 131 // configured to auto-start. 132 NoAutoStart bool 133 134 // ConfigType determines whether the interface should be 135 // configured via DHCP, statically, manually, etc. See 136 // interfaces(5) for more information. 137 ConfigType AddressConfigType 138 139 // Addresses contains an optional list of static IP address to 140 // configure for this network interface. The subnet mask to set will be 141 // inferred from the CIDR value of the first entry which is always 142 // assumed to be the primary IP address for the interface. 143 Addresses ProviderAddresses 144 145 // ShadowAddresses contains an optional list of additional IP addresses 146 // that the underlying network provider associates with this network 147 // interface instance. These IP addresses are not typically visible 148 // to the machine that the interface is connected to. 149 ShadowAddresses ProviderAddresses 150 151 // DNSServers contains an optional list of IP addresses and/or 152 // host names to configure as DNS servers for this network interface. 153 DNSServers ProviderAddresses 154 155 // MTU is the Maximum Transmission Unit controlling the maximum size of the 156 // protocol packets that the interface can pass through. It is only used 157 // when > 0. 158 MTU int 159 160 // DNSSearchDomains contains the default DNS domain to use for non-FQDN 161 // lookups. 162 DNSSearchDomains []string 163 164 // Gateway address, if set, defines the default gateway to 165 // configure for this network interface. For containers this 166 // usually is (one of) the host address(es). 167 GatewayAddress ProviderAddress 168 169 // Routes defines a list of routes that should be added when this interface 170 // is brought up, and removed when this interface is stopped. 171 Routes []Route 172 173 // IsDefaultGateway is set if this device is a default gw on a machine. 174 IsDefaultGateway bool 175 176 // VirtualPortType provides additional information about the type of 177 // this device if it belongs to a virtual switch (e.g. when using 178 // open-vswitch). 179 VirtualPortType VirtualPortType 180 181 // Origin represents the authoritative source of the InterfaceInfo. 182 // It is expected that either the provider gave us this info or the 183 // machine gave us this info. 184 // Giving us this information allows us to reason about when a InterfaceInfo 185 // is in use. 186 Origin Origin 187 } 188 189 // ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and 190 // virtual interface name for virtual interface (e.g. "eth0.42") 191 func (i *InterfaceInfo) ActualInterfaceName() string { 192 if i.VLANTag > 0 { 193 return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag) 194 } 195 return i.InterfaceName 196 } 197 198 // IsVirtual returns true when the interface is a virtual device, as 199 // opposed to a physical device (e.g. a VLAN, network alias or OVS-managed 200 // device). 201 func (i *InterfaceInfo) IsVirtual() bool { 202 return i.VLANTag > 0 || i.VirtualPortType != NonVirtualPort 203 } 204 205 // IsVLAN returns true when the interface is a VLAN interface. 206 func (i *InterfaceInfo) IsVLAN() bool { 207 return i.VLANTag > 0 208 } 209 210 // Validate checks that the receiver looks like a real interface. 211 // An error is returned if invalid members are detected. 212 func (i *InterfaceInfo) Validate() error { 213 if i.MACAddress != "" { 214 if _, err := net.ParseMAC(i.MACAddress); err != nil { 215 return errors.NotValidf("link-layer device hardware address %q", i.MACAddress) 216 } 217 } 218 219 if i.InterfaceName == "" { 220 return errors.NotValidf("link-layer device %q, empty name", i.MACAddress) 221 } 222 223 if !IsValidLinkLayerDeviceName(i.InterfaceName) { 224 // TODO (manadart 2020-07-07): This preserves prior behaviour. 225 // If we are waving invalid names through, I'm not sure of the value. 226 logger.Warningf("link-layer device %q has an invalid name, %q", i.MACAddress, i.InterfaceName) 227 } 228 229 if !IsValidLinkLayerDeviceType(string(i.InterfaceType)) { 230 return errors.NotValidf("link-layer device %q, type %q", i.InterfaceName, i.InterfaceType) 231 } 232 233 return nil 234 } 235 236 // PrimaryAddress returns the primary address for the interface. 237 func (i *InterfaceInfo) PrimaryAddress() ProviderAddress { 238 if len(i.Addresses) == 0 { 239 return ProviderAddress{} 240 } 241 242 // We assume that the primary IP is always listed first. The majority 243 // of providers only define a single IP so this will still work as 244 // expected. Notably, ec2 does allow multiple private IP addresses to 245 // be assigned to an interface but the provider ensures that the one 246 // flagged as primary is present at index 0. 247 return i.Addresses[0] 248 } 249 250 // InterfaceInfos is a slice of InterfaceInfo 251 // for a single host/machine/container. 252 type InterfaceInfos []InterfaceInfo 253 254 // Validate validates each interface, returning an error if any are invalid 255 func (s InterfaceInfos) Validate() error { 256 for _, dev := range s { 257 if err := dev.Validate(); err != nil { 258 return errors.Trace(err) 259 } 260 } 261 return nil 262 } 263 264 // InterfaceFilterFunc is a function that can be applied to filter a slice of 265 // InterfaceInfo instances. Calls to this function should return false if 266 // the specified InterfaceInfo should be filtered out. 267 type InterfaceFilterFunc func(InterfaceInfo) bool 268 269 // Filter applies keepFn to each entry in a InterfaceInfos list and returns 270 // back a filtered list containing the entries for which predicateFn returned 271 // true. 272 func (s InterfaceInfos) Filter(predicateFn InterfaceFilterFunc) InterfaceInfos { 273 var out InterfaceInfos 274 for _, iface := range s { 275 if !predicateFn(iface) { 276 continue 277 } 278 out = append(out, iface) 279 } 280 return out 281 } 282 283 // GetByName returns a new collection containing 284 // any interfaces with the input device name. 285 func (s InterfaceInfos) GetByName(name string) InterfaceInfos { 286 var res InterfaceInfos 287 for _, dev := range s { 288 if dev.InterfaceName == name { 289 res = append(res, dev) 290 } 291 } 292 return res 293 } 294 295 // ProviderInterfaceInfo holds enough information to identify an 296 // interface or link layer device to a provider so that it can be 297 // queried or manipulated. Its initial purpose is to pass to 298 // provider.ReleaseContainerAddresses. 299 type ProviderInterfaceInfo struct { 300 // InterfaceName is the raw OS-specific network device name (e.g. 301 // "eth1", even for a VLAN eth1.42 virtual interface). 302 InterfaceName string 303 304 // ProviderId is a provider-specific NIC id. 305 ProviderId Id 306 307 // HardwareAddress is the network interface's hardware address. The 308 // contents of this field depend on the NIC type (a MAC address for an 309 // ethernet device, a GUID for an infiniband device etc.) 310 HardwareAddress string 311 } 312 313 // NormalizeMACAddress replaces dashes with colons and lowercases the MAC 314 // address provided as input. 315 func NormalizeMACAddress(mac string) string { 316 return strings.ToLower( 317 strings.Replace(mac, "-", ":", -1), 318 ) 319 }