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 }