1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
15
"github.com/juju/errors"
16
"github.com/juju/loggo"
17
"github.com/juju/utils/set"
20
var logger = loggo.GetLogger("juju.network")
22
// SpaceInvalidChars is a regexp for validating that space names contain no
23
// invalid characters.
24
var SpaceInvalidChars = regexp.MustCompile("[^0-9a-z-]")
26
// noAddress represents an error when an address is requested but not available.
27
type noAddress struct {
31
// NoAddressError returns an error which satisfies IsNoAddressError(). The given
32
// addressKind specifies what kind of address is missing, usually "private" or
34
func NoAddressError(addressKind string) error {
35
newErr := errors.NewErr("no %s address", addressKind)
37
return &noAddress{newErr}
40
// IsNoAddressError reports whether err was created with NoAddressError().
41
func IsNoAddressError(err error) bool {
42
err = errors.Cause(err)
43
_, ok := err.(*noAddress)
47
// Id defines a provider-specific network id.
50
// AnySubnet when passed as a subnet id should be interpreted by the
51
// providers as "the subnet id does not matter". It's up to the
52
// provider how to handle this case - it might return an error.
53
const AnySubnet Id = ""
55
// UnknownId can be used whenever an Id is needed but not known.
58
// DefaultLXDBridge is the bridge that gets used for LXD containers
59
const DefaultLXDBridge = "lxdbr0"
61
var dashPrefix = regexp.MustCompile("^-*")
62
var dashSuffix = regexp.MustCompile("-*$")
63
var multipleDashes = regexp.MustCompile("--+")
65
// ConvertSpaceName converts names between provider space names and valid juju
67
// TODO(mfoord): once MAAS space name rules are in sync with juju space name
68
// rules this can go away.
69
func ConvertSpaceName(name string, existing set.Strings) string {
70
// First lower case and replace spaces with dashes.
71
name = strings.Replace(name, " ", "-", -1)
72
name = strings.ToLower(name)
73
// Replace any character that isn't in the set "-", "a-z", "0-9".
74
name = SpaceInvalidChars.ReplaceAllString(name, "")
75
// Get rid of any dashes at the start as that isn't valid.
76
name = dashPrefix.ReplaceAllString(name, "")
77
// And any at the end.
78
name = dashSuffix.ReplaceAllString(name, "")
79
// Repleace multiple dashes with a single dash.
80
name = multipleDashes.ReplaceAllString(name, "-")
81
// Special case of when the space name was only dashes or invalid
86
// If this name is in use add a numerical suffix.
87
if existing.Contains(name) {
89
for existing.Contains(name + fmt.Sprintf("-%d", counter)) {
92
name = name + fmt.Sprintf("-%d", counter)
97
// SubnetInfo describes the bare minimum information for a subnet,
98
// which the provider knows about but juju might not yet.
99
type SubnetInfo struct {
100
// CIDR of the network, in 123.45.67.89/24 format. Can be empty if
104
// ProviderId is a provider-specific network id. This the only
108
// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
109
// normal networks. It's defined by IEEE 802.1Q standard, and used
110
// to define a VLAN network. For more information, see:
111
// http://en.wikipedia.org/wiki/IEEE_802.1Q.
114
// AvailabilityZones describes which availability zone(s) this
115
// subnet is in. It can be empty if the provider does not support
116
// availability zones.
117
AvailabilityZones []string
119
// SpaceProviderId holds the provider Id of the space associated with
120
// this subnet. Can be empty if not supported.
124
type SpaceInfo struct {
129
type BySpaceName []SpaceInfo
131
func (s BySpaceName) Len() int { return len(s) }
132
func (s BySpaceName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
133
func (s BySpaceName) Less(i, j int) bool {
134
return s[i].Name < s[j].Name
137
// InterfaceConfigType defines valid network interface configuration
138
// types. See interfaces(5) for details
139
type InterfaceConfigType string
142
ConfigUnknown InterfaceConfigType = ""
143
ConfigDHCP InterfaceConfigType = "dhcp"
144
ConfigStatic InterfaceConfigType = "static"
145
ConfigManual InterfaceConfigType = "manual"
146
ConfigLoopback InterfaceConfigType = "loopback"
149
// InterfaceType defines valid network interface types.
150
type InterfaceType string
153
UnknownInterface InterfaceType = ""
154
LoopbackInterface InterfaceType = "loopback"
155
EthernetInterface InterfaceType = "ethernet"
156
VLAN_8021QInterface InterfaceType = "802.1q"
157
BondInterface InterfaceType = "bond"
158
BridgeInterface InterfaceType = "bridge"
161
// InterfaceInfo describes a single network interface available on an
162
// instance. For providers that support networks, this will be
163
// available at StartInstance() time.
164
// TODO(mue): Rename to InterfaceConfig due to consistency later.
165
type InterfaceInfo struct {
166
// DeviceIndex specifies the order in which the network interface
167
// appears on the host. The primary interface has an index of 0.
170
// MACAddress is the network interface's hardware MAC address
171
// (e.g. "aa:bb:cc:dd:ee:ff").
174
// CIDR of the network, in 123.45.67.89/24 format.
177
// ProviderId is a provider-specific NIC id.
180
// ProviderSubnetId is the provider-specific id for the associated
184
// ProviderSpaceId is the provider-specific id for the associated space, if
185
// known and supported.
188
// ProviderVLANId is the provider-specific id of the VLAN for this
192
// ProviderAddressId is the provider-specific id of the assigned address.
195
// AvailabilityZones describes the availability zones the associated
197
AvailabilityZones []string
199
// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
200
// normal networks. It's defined by IEEE 802.1Q standard.
203
// InterfaceName is the raw OS-specific network device name (e.g.
204
// "eth1", even for a VLAN eth1.42 virtual interface).
207
// ParentInterfaceName is the name of the parent interface to use, if known.
208
ParentInterfaceName string
210
// InterfaceType is the type of the interface.
211
InterfaceType InterfaceType
213
// Disabled is true when the interface needs to be disabled on the
214
// machine, e.g. not to configure it.
217
// NoAutoStart is true when the interface should not be configured
218
// to start automatically on boot. By default and for
219
// backwards-compatibility, interfaces are configured to
223
// ConfigType determines whether the interface should be
224
// configured via DHCP, statically, manually, etc. See
225
// interfaces(5) for more information.
226
ConfigType InterfaceConfigType
228
// Address contains an optional static IP address to configure for
229
// this network interface. The subnet mask to set will be inferred
230
// from the CIDR value.
233
// DNSServers contains an optional list of IP addresses and/or
234
// hostnames to configure as DNS servers for this network
238
// MTU is the Maximum Transmission Unit controlling the maximum size of the
239
// protocol packats that the interface can pass through. It is only used
243
// DNSSearchDomains contains the default DNS domain to use for non-FQDN
245
DNSSearchDomains []string
247
// Gateway address, if set, defines the default gateway to
248
// configure for this network interface. For containers this
249
// usually is (one of) the host address(es).
250
GatewayAddress Address
253
type interfaceInfoSlice []InterfaceInfo
255
func (s interfaceInfoSlice) Len() int { return len(s) }
256
func (s interfaceInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
257
func (s interfaceInfoSlice) Less(i, j int) bool {
260
return iface1.DeviceIndex < iface2.DeviceIndex
263
// SortInterfaceInfo sorts a slice of InterfaceInfo on DeviceIndex in ascending
265
func SortInterfaceInfo(interfaces []InterfaceInfo) {
266
sort.Sort(interfaceInfoSlice(interfaces))
269
// ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and
270
// virtual interface name for virtual interface (e.g. "eth0.42")
271
func (i *InterfaceInfo) ActualInterfaceName() string {
273
return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag)
275
return i.InterfaceName
278
// IsVirtual returns true when the interface is a virtual device, as
279
// opposed to a physical device (e.g. a VLAN or a network alias)
280
func (i *InterfaceInfo) IsVirtual() bool {
284
// IsVLAN returns true when the interface is a VLAN interface.
285
func (i *InterfaceInfo) IsVLAN() bool {
289
// CIDRAddress returns Address.Value combined with CIDR mask.
290
func (i *InterfaceInfo) CIDRAddress() string {
291
if i.CIDR == "" || i.Address.Value == "" {
294
_, ipNet, err := net.ParseCIDR(i.CIDR)
296
return errors.Trace(err).Error()
298
ip := net.ParseIP(i.Address.Value)
300
return errors.Errorf("cannot parse IP address %q", i.Address.Value).Error()
303
return ipNet.String()
306
// LXCNetDefaultConfig is the location of the default network config
307
// of the lxc package. It's exported to allow cross-package testing.
308
var LXCNetDefaultConfig = "/etc/default/lxc-net"
310
// InterfaceByNameAddrs returns the addresses for the given interface
311
// name. It's exported to facilitate cross-package testing.
312
var InterfaceByNameAddrs = func(name string) ([]net.Addr, error) {
313
iface, err := net.InterfaceByName(name)
320
// filterAddrs looks at all of the addresses in allAddresses and removes ones
321
// that line up with removeAddresses. Note that net.Addr may be just an IP or
323
func filterAddrs(bridgeName string, allAddresses []Address, removeAddresses []net.Addr) []Address {
324
filtered := make([]Address, 0, len(allAddresses))
325
// TODO(jam) ips could be turned into a map[string]bool rather than
326
// iterating over all of them, as we only compare against ip.String()
327
ips := make([]net.IP, 0, len(removeAddresses))
328
ipNets := make([]*net.IPNet, 0, len(removeAddresses))
329
for _, ifaceAddr := range removeAddresses {
330
// First check if this is a CIDR, as
331
// net.InterfaceAddrs might return this instead of
333
ip, ipNet, err := net.ParseCIDR(ifaceAddr.String())
335
// It's not a CIDR, try parsing as IP.
336
ip = net.ParseIP(ifaceAddr.String())
339
logger.Debugf("cannot parse %q as IP, ignoring", ifaceAddr)
342
ips = append(ips, ip)
344
ipNets = append(ipNets, ipNet)
347
for _, addr := range allAddresses {
349
// Filter all known IPs
350
for _, ip := range ips {
351
if ip.String() == addr.Value {
357
// Then check if it is in one of the CIDRs
358
for _, ipNet := range ipNets {
359
if ipNet.Contains(net.ParseIP(addr.Value)) {
366
logger.Debugf("filtering %q address %s for machine", bridgeName, addr.String())
368
logger.Debugf("not filtering address %s for machine", addr)
369
filtered = append(filtered, addr)
372
logger.Debugf("addresses after filtering: %v", filtered)
376
// filterLXCAddresses tries to discover the default lxc bridge name
377
// and all of its addresses, then filters those addresses out of the
378
// given ones and returns the result. Any errors encountered during
379
// this process are logged, but not considered fatal. See LP bug
381
func filterLXCAddresses(addresses []Address) []Address {
382
file, err := os.Open(LXCNetDefaultConfig)
383
if os.IsNotExist(err) {
384
// No lxc-net config found, nothing to do.
385
logger.Debugf("no lxc bridge addresses to filter for machine")
387
} else if err != nil {
388
// Just log it, as it's not fatal.
389
logger.Errorf("cannot open %q: %v", LXCNetDefaultConfig, err)
394
scanner := bufio.NewScanner(file)
396
line := strings.TrimSpace(scanner.Text())
398
case strings.HasPrefix(line, "#"):
400
case strings.HasPrefix(line, "LXC_BRIDGE"):
401
// Extract <name> from LXC_BRIDGE="<name>".
402
parts := strings.Split(line, `"`)
404
logger.Debugf("ignoring invalid line '%s' in %q", line, LXCNetDefaultConfig)
407
bridgeName := strings.TrimSpace(parts[1])
408
// Discover all addresses of bridgeName interface.
409
addrs, err := InterfaceByNameAddrs(bridgeName)
411
logger.Debugf("cannot get %q addresses: %v (ignoring)", bridgeName, err)
414
logger.Debugf("%q has addresses %v", bridgeName, addrs)
415
return filterAddrs(bridgeName, addresses, addrs)
418
if err := scanner.Err(); err != nil {
419
logger.Debugf("failed to read %q: %v (ignoring)", LXCNetDefaultConfig, err)
424
// filterLXDAddresses removes addresses on the LXD bridge from the list to be
426
func filterLXDAddresses(addresses []Address) []Address {
427
// Should we be getting this from LXD instead?
428
addrs, err := InterfaceByNameAddrs(DefaultLXDBridge)
430
logger.Warningf("cannot get %q addresses: %v (ignoring)", DefaultLXDBridge, err)
433
logger.Debugf("%q has addresses %v", DefaultLXDBridge, addrs)
434
return filterAddrs(DefaultLXDBridge, addresses, addrs)
438
// FilterBridgeAddresses removes addresses seen as a Bridge address (the IP
439
// address used only to connect to local containers), rather than a remote
440
// accessible address.
441
func FilterBridgeAddresses(addresses []Address) []Address {
442
addresses = filterLXCAddresses(addresses)
443
addresses = filterLXDAddresses(addresses)