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  }