github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/network/source.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package network
     5  
     6  import (
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/juju/collections/set"
    13  )
    14  
    15  // SysClassNetPath is the full Linux SYSFS path containing
    16  // information about each network interface on the system.
    17  // TODO (manadart 2021-02-12): This remains in the main "source.go" module
    18  // because there was previously only one ConfigSource implementation,
    19  // which presumably did not work on Windows.
    20  // When the netlinkConfigSource was introduced for use on Linux,
    21  // we retained the old universal config source for use on Windows.
    22  // If there comes a time when we properly implement a Windows source,
    23  // this should be relocated to the Linux module and an appropriate counterpart
    24  // introduced for Windows.
    25  const SysClassNetPath = "/sys/class/net"
    26  
    27  // ConfigSourceNIC describes a network interface detected on the local machine
    28  // by an implementation of ConfigSource.
    29  type ConfigSourceNIC interface {
    30  	// Name returns the name of the network interface; E.g. "eth0".
    31  	Name() string
    32  
    33  	// Type returns the type of the interface - Ethernet, VLAN, Loopback etc.
    34  	// TODO (manadart 2021-03-03): We do not recognise device types such as
    35  	// veth, tuntap, macvtap et al. Our parsing falls back to ethernet for such
    36  	// devices, which we should change in order to have a better informed
    37  	// networking model.
    38  	Type() LinkLayerDeviceType
    39  
    40  	// Index returns the index of the interface.
    41  	Index() int
    42  
    43  	// HardwareAddr returns the hardware address of the interface.
    44  	// It is the MAC address for ethernet devices.
    45  	HardwareAddr() net.HardwareAddr
    46  
    47  	// Addresses returns IP addresses associated with the network interface.
    48  	Addresses() ([]ConfigSourceAddr, error)
    49  
    50  	// MTU returns the maximum transmission unit for the interface.
    51  	MTU() int
    52  
    53  	// IsUp returns true if the interface is in the "up" state.
    54  	IsUp() bool
    55  }
    56  
    57  // ConfigSourceAddr describes addresses detected on a network interface
    58  // represented by an implementation of ConfigSourceAddr.
    59  type ConfigSourceAddr interface {
    60  	// IP returns the address in net.IP form.
    61  	IP() net.IP
    62  
    63  	// IPNet returns the subnet corresponding with the address
    64  	// provided that it can be determined.
    65  	IPNet() *net.IPNet
    66  
    67  	// IsSecondary returns true if this address can be determined not to be
    68  	// the primary address of its NIC.
    69  	// Such addresses are added by HA setups like Corosync+Pacemaker.
    70  	IsSecondary() bool
    71  
    72  	// String returns the address in string form,
    73  	// including the subnet mask if known.
    74  	String() string
    75  }
    76  
    77  // ConfigSource defines the necessary calls to obtain
    78  // the network configuration of a machine.
    79  type ConfigSource interface {
    80  	// Interfaces returns information about all
    81  	// network interfaces on the machine.
    82  	Interfaces() ([]ConfigSourceNIC, error)
    83  
    84  	// DefaultRoute returns the gateway IP address and device name of the
    85  	// default route on the machine. If there is no default route (known),
    86  	// then zero values are returned.
    87  	DefaultRoute() (net.IP, string, error)
    88  
    89  	// OvsManagedBridges returns the names of network interfaces that
    90  	// correspond to OVS-managed bridges.
    91  	OvsManagedBridges() (set.Strings, error)
    92  
    93  	// GetBridgePorts returns the names of network interfaces that are ports ot
    94  	// the bridge with the input device name.
    95  	GetBridgePorts(string) []string
    96  }
    97  
    98  // ParseInterfaceType parses the DEVTYPE attribute from the Linux kernel
    99  // userspace SYSFS location "<sysPath/<interfaceName>/uevent" and returns it as
   100  // InterfaceType. SysClassNetPath should be passed as sysPath. Returns
   101  // UnknownInterface if the type cannot be reliably determined for any reason.
   102  // Example call: network.ParseInterfaceType(network.SysClassNetPath, "br-eth1")
   103  // TODO (manadart 2021-02-12): As with SysClassNetPath above, specific
   104  // implementations should be sought for this that are OS-dependent.
   105  func ParseInterfaceType(sysPath, interfaceName string) LinkLayerDeviceType {
   106  	const deviceType = "DEVTYPE="
   107  	location := filepath.Join(sysPath, interfaceName, "uevent")
   108  
   109  	data, err := os.ReadFile(location)
   110  	if err != nil {
   111  		logger.Debugf("ignoring error reading %q: %v", location, err)
   112  		return UnknownDevice
   113  	}
   114  
   115  	var devType string
   116  	lines := strings.Fields(string(data))
   117  	for _, line := range lines {
   118  		if !strings.HasPrefix(line, deviceType) {
   119  			continue
   120  		}
   121  
   122  		devType = strings.TrimPrefix(line, deviceType)
   123  		switch devType {
   124  		case "bridge":
   125  			return BridgeDevice
   126  		case "vlan":
   127  			return VLAN8021QDevice
   128  		case "bond":
   129  			return BondDevice
   130  		case "":
   131  			// DEVTYPE is not present for some types, like Ethernet and loopback
   132  			// interfaces, so if missing do not try to guess.
   133  			break
   134  		}
   135  	}
   136  
   137  	return UnknownDevice
   138  }
   139  
   140  // GetBridgePorts extracts and returns the names of all interfaces configured as
   141  // ports of the given bridgeName from the Linux kernel userspace SYSFS location
   142  // "<sysPath/<bridgeName>/brif/*". SysClassNetPath should be passed as sysPath.
   143  // Returns an empty result if the ports cannot be determined reliably for any
   144  // reason, or if there are no configured ports for the bridge.
   145  // Example call: network.GetBridgePorts(network.SysClassNetPath, "br-eth1")
   146  // TODO (manadart 2021-02-12): As with SysClassNetPath above, specific
   147  // implementations should be sought for this that are OS-dependent.
   148  func GetBridgePorts(sysPath, bridgeName string) []string {
   149  	portsGlobPath := filepath.Join(sysPath, bridgeName, "brif", "*")
   150  	// Glob ignores I/O errors and can only return ErrBadPattern, which we treat
   151  	// as no results, but for debugging we're still logging the error.
   152  	paths, err := filepath.Glob(portsGlobPath)
   153  	if err != nil {
   154  		logger.Debugf("ignoring error traversing path %q: %v", portsGlobPath, err)
   155  	}
   156  
   157  	if len(paths) == 0 {
   158  		return nil
   159  	}
   160  
   161  	// We need to convert full paths like /sys/class/net/br-eth0/brif/eth0 to
   162  	// just names.
   163  	names := make([]string, len(paths))
   164  	for i := range paths {
   165  		names[i] = filepath.Base(paths[i])
   166  	}
   167  	return names
   168  }