github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/common/network.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "net" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/network" 14 ) 15 16 var logger = loggo.GetLogger("juju.api.common") 17 18 // NetworkConfigSource defines the necessary calls to obtain the network 19 // configuration of a machine. 20 type NetworkConfigSource interface { 21 // SysClassNetPath returns the Linux kernel userspace SYSFS path used by 22 // this source. DefaultNetworkConfigSource() uses network.SysClassNetPath. 23 SysClassNetPath() string 24 25 // Interfaces returns information about all network interfaces on the 26 // machine as []net.Interface. 27 Interfaces() ([]net.Interface, error) 28 29 // InterfaceAddresses returns information about all addresses assigned to 30 // the network interface with the given name. 31 InterfaceAddresses(name string) ([]net.Addr, error) 32 33 // DefaultRoute returns the gateway IP address and device name of the 34 // default route on the machine. If there is no default route (known), 35 // then zero values are returned. 36 DefaultRoute() (net.IP, string, error) 37 } 38 39 type netPackageConfigSource struct{} 40 41 // SysClassNetPath implements NetworkConfigSource. 42 func (n *netPackageConfigSource) SysClassNetPath() string { 43 return network.SysClassNetPath 44 } 45 46 // Interfaces implements NetworkConfigSource. 47 func (n *netPackageConfigSource) Interfaces() ([]net.Interface, error) { 48 return net.Interfaces() 49 } 50 51 // InterfaceAddresses implements NetworkConfigSource. 52 func (n *netPackageConfigSource) InterfaceAddresses(name string) ([]net.Addr, error) { 53 iface, err := net.InterfaceByName(name) 54 if err != nil { 55 return nil, errors.Trace(err) 56 } 57 return iface.Addrs() 58 } 59 60 // DefaultRoute implements NetworkConfigSource. 61 func (n *netPackageConfigSource) DefaultRoute() (net.IP, string, error) { 62 return network.GetDefaultRoute() 63 } 64 65 // DefaultNetworkConfigSource returns a NetworkConfigSource backed by the net 66 // package, to be used with GetObservedNetworkConfig(). 67 func DefaultNetworkConfigSource() NetworkConfigSource { 68 return &netPackageConfigSource{} 69 } 70 71 // GetObservedNetworkConfig uses the given source to find all available network 72 // interfaces and their assigned addresses, and returns the result as 73 // []params.NetworkConfig. In addition to what the source returns, a few 74 // additional transformations are done: 75 // 76 // * On any OS, the state (UP/DOWN) of each interface and the DeviceIndex field, 77 // will be correctly populated. Loopback interfaces are also properly detected 78 // and will have InterfaceType set LoopbackInterface. 79 // * On Linux only, the InterfaceType field will be reliably detected for a few 80 // types: BondInterface, BridgeInterface, VLAN_8021QInterface. 81 // * Also on Linux, for interfaces that are discovered to be ports on a bridge, 82 // the ParentInterfaceName will be populated with the name of the bridge. 83 // * ConfigType fields will be set to ConfigManual when no address is detected, 84 // or ConfigStatic when it is. 85 // * TODO: IPv6 link-local addresses will be ignored and treated as empty ATM. 86 // 87 // Result entries will be grouped by InterfaceName, in the same order they are 88 // returned by the given source. 89 func GetObservedNetworkConfig(source NetworkConfigSource) ([]params.NetworkConfig, error) { 90 logger.Tracef("discovering observed machine network config...") 91 92 interfaces, err := source.Interfaces() 93 if err != nil { 94 return nil, errors.Annotate(err, "cannot get network interfaces") 95 } 96 if len(interfaces) == 0 { 97 logger.Tracef("no network interfaces") 98 return nil, nil 99 } 100 101 defaultRoute, defaultRouteDevice, err := source.DefaultRoute() 102 if err != nil { 103 return nil, errors.Annotate(err, "cannot get default route") 104 } 105 var namesOrder []string 106 nameToConfigs := make(map[string][]params.NetworkConfig) 107 sysClassNetPath := source.SysClassNetPath() 108 for _, nic := range interfaces { 109 nicType := network.ParseInterfaceType(sysClassNetPath, nic.Name) 110 nicConfig := interfaceToNetworkConfig(nic, nicType) 111 if nicConfig.InterfaceName == defaultRouteDevice { 112 nicConfig.IsDefaultGateway = true 113 nicConfig.GatewayAddress = defaultRoute.String() 114 } 115 116 if nicType == network.BridgeInterface { 117 updateParentForBridgePorts(nic.Name, sysClassNetPath, nameToConfigs) 118 } 119 120 seenSoFar := false 121 if existing, ok := nameToConfigs[nic.Name]; ok { 122 nicConfig.ParentInterfaceName = existing[0].ParentInterfaceName 123 // If only ParentInterfaceName was set in a previous iteration (e.g. 124 // if the bridge appeared before the port), treat the interface as 125 // not yet seen. 126 seenSoFar = existing[0].InterfaceName != "" 127 } 128 129 if !seenSoFar { 130 nameToConfigs[nic.Name] = []params.NetworkConfig(nil) 131 namesOrder = append(namesOrder, nic.Name) 132 } 133 134 addrs, err := source.InterfaceAddresses(nic.Name) 135 if err != nil { 136 return nil, errors.Annotatef(err, "cannot get interface %q addresses", nic.Name) 137 } 138 139 if len(addrs) == 0 { 140 logger.Infof("no addresses observed on interface %q", nic.Name) 141 nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfig) 142 continue 143 } 144 145 for _, addr := range addrs { 146 addressConfig, err := interfaceAddressToNetworkConfig(nic.Name, nicConfig.ConfigType, addr) 147 if err != nil { 148 return nil, errors.Trace(err) 149 } 150 151 // Need to copy nicConfig so only the fields relevant for the 152 // current address are updated. 153 nicConfigCopy := nicConfig 154 nicConfigCopy.Address = addressConfig.Address 155 nicConfigCopy.CIDR = addressConfig.CIDR 156 nicConfigCopy.ConfigType = addressConfig.ConfigType 157 nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfigCopy) 158 } 159 } 160 161 // Return all interfaces configs in input order. 162 var observedConfig []params.NetworkConfig 163 for _, name := range namesOrder { 164 observedConfig = append(observedConfig, nameToConfigs[name]...) 165 } 166 logger.Tracef("observed network config: %+v", observedConfig) 167 return observedConfig, nil 168 } 169 170 func interfaceToNetworkConfig(nic net.Interface, nicType network.InterfaceType) params.NetworkConfig { 171 configType := network.ConfigManual // assume manual initially, until we parse the address. 172 isUp := nic.Flags&net.FlagUp > 0 173 isLoopback := nic.Flags&net.FlagLoopback > 0 174 isUnknown := nicType == network.UnknownInterface 175 176 switch { 177 case isUnknown && isLoopback: 178 nicType = network.LoopbackInterface 179 configType = network.ConfigLoopback 180 case isUnknown: 181 nicType = network.EthernetInterface 182 } 183 184 return params.NetworkConfig{ 185 DeviceIndex: nic.Index, 186 MACAddress: nic.HardwareAddr.String(), 187 ConfigType: string(configType), 188 MTU: nic.MTU, 189 InterfaceName: nic.Name, 190 InterfaceType: string(nicType), 191 NoAutoStart: !isUp, 192 Disabled: !isUp, 193 } 194 } 195 196 func updateParentForBridgePorts(bridgeName, sysClassNetPath string, nameToConfigs map[string][]params.NetworkConfig) { 197 ports := network.GetBridgePorts(sysClassNetPath, bridgeName) 198 for _, portName := range ports { 199 portConfigs, ok := nameToConfigs[portName] 200 if ok { 201 portConfigs[0].ParentInterfaceName = bridgeName 202 } else { 203 portConfigs = []params.NetworkConfig{{ParentInterfaceName: bridgeName}} 204 } 205 nameToConfigs[portName] = portConfigs 206 } 207 } 208 209 func interfaceAddressToNetworkConfig(interfaceName, configType string, address net.Addr) (params.NetworkConfig, error) { 210 config := params.NetworkConfig{ 211 ConfigType: configType, 212 } 213 214 cidrAddress := address.String() 215 if cidrAddress == "" { 216 return config, nil 217 } 218 219 ip, ipNet, err := net.ParseCIDR(cidrAddress) 220 if err != nil { 221 logger.Tracef("cannot parse %q on interface %q as CIDR, trying as IP address: %v", cidrAddress, interfaceName, err) 222 if ip = net.ParseIP(cidrAddress); ip == nil { 223 return config, errors.Errorf("cannot parse IP address %q on interface %q", cidrAddress, interfaceName) 224 } else { 225 ipNet = &net.IPNet{IP: ip} 226 } 227 } 228 if ip.To4() == nil && ip.IsLinkLocalUnicast() { 229 // TODO(macgreagoir) IPv6. Skip link-local for now until we decide how to handle them. 230 logger.Tracef("skipping observed IPv6 link-local address %q on %q", ip, interfaceName) 231 return config, nil 232 } 233 234 if ipNet.Mask != nil { 235 config.CIDR = ipNet.String() 236 } 237 config.Address = ip.String() 238 if configType != string(network.ConfigLoopback) { 239 config.ConfigType = string(network.ConfigStatic) 240 } 241 242 // TODO(dimitern): Add DNS servers, search domains, and gateway 243 // later. 244 245 return config, nil 246 }