-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnetwork.go
626 lines (540 loc) · 19.2 KB
/
network.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package network
import (
"bufio"
"fmt"
"net"
"os"
"regexp"
"sort"
"strings"
"github.com/juju/errors"
"github.com/juju/loggo"
"github.com/juju/utils/set"
)
var logger = loggo.GetLogger("juju.network")
// SpaceInvalidChars is a regexp for validating that space names contain no
// invalid characters.
var SpaceInvalidChars = regexp.MustCompile("[^0-9a-z-]")
// noAddress represents an error when an address is requested but not available.
type noAddress struct {
errors.Err
}
// NoAddressError returns an error which satisfies IsNoAddressError(). The given
// addressKind specifies what kind of address(es) is(are) missing, usually
// "private" or "public".
func NoAddressError(addressKind string) error {
newErr := errors.NewErr("no %s address(es)", addressKind)
newErr.SetLocation(1)
return &noAddress{newErr}
}
// IsNoAddressError reports whether err was created with NoAddressError().
func IsNoAddressError(err error) bool {
err = errors.Cause(err)
_, ok := err.(*noAddress)
return ok
}
// Id defines a provider-specific network id.
type Id string
// AnySubnet when passed as a subnet id should be interpreted by the
// providers as "the subnet id does not matter". It's up to the
// provider how to handle this case - it might return an error.
const AnySubnet Id = ""
// UnknownId can be used whenever an Id is needed but not known.
const UnknownId = ""
// DefaultLXCBridge is the bridge that gets used for LXC containers
const DefaultLXCBridge = "lxcbr0"
// DefaultLXDBridge is the bridge that gets used for LXD containers
const DefaultLXDBridge = "lxdbr0"
// DefaultKVMBridge is the bridge that is set up by installing libvirt-bin
// Note: we don't import this from 'container' to avoid import loops
const DefaultKVMBridge = "virbr0"
var dashPrefix = regexp.MustCompile("^-*")
var dashSuffix = regexp.MustCompile("-*$")
var multipleDashes = regexp.MustCompile("--+")
// ConvertSpaceName converts names between provider space names and valid juju
// space names.
// TODO(mfoord): once MAAS space name rules are in sync with juju space name
// rules this can go away.
func ConvertSpaceName(name string, existing set.Strings) string {
// First lower case and replace spaces with dashes.
name = strings.Replace(name, " ", "-", -1)
name = strings.ToLower(name)
// Replace any character that isn't in the set "-", "a-z", "0-9".
name = SpaceInvalidChars.ReplaceAllString(name, "")
// Get rid of any dashes at the start as that isn't valid.
name = dashPrefix.ReplaceAllString(name, "")
// And any at the end.
name = dashSuffix.ReplaceAllString(name, "")
// Repleace multiple dashes with a single dash.
name = multipleDashes.ReplaceAllString(name, "-")
// Special case of when the space name was only dashes or invalid
// characters!
if name == "" {
name = "empty"
}
// If this name is in use add a numerical suffix.
if existing.Contains(name) {
counter := 2
for existing.Contains(name + fmt.Sprintf("-%d", counter)) {
counter += 1
}
name = name + fmt.Sprintf("-%d", counter)
}
return name
}
// SubnetInfo describes the bare minimum information for a subnet,
// which the provider knows about but juju might not yet.
type SubnetInfo struct {
// CIDR of the network, in 123.45.67.89/24 format. Can be empty if
// unknown.
CIDR string
// ProviderId is a provider-specific subnet id. This the only
// required field.
ProviderId Id
// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
// normal networks. It's defined by IEEE 802.1Q standard, and used
// to define a VLAN network. For more information, see:
// http://en.wikipedia.org/wiki/IEEE_802.1Q.
VLANTag int
// AvailabilityZones describes which availability zone(s) this
// subnet is in. It can be empty if the provider does not support
// availability zones.
AvailabilityZones []string
// SpaceProviderId holds the provider Id of the space associated
// with this subnet. Can be empty if not supported.
// TODO(babbageclunk): change this to ProviderSpaceId to be
// consistent with the InterfaceInfo.Provider*Id fields.
SpaceProviderId Id
// ProviderNetworkId holds the provider id of the network
// containing this subnet, for example VPC id for EC2.
ProviderNetworkId Id
}
type SpaceInfo struct {
Name string
ProviderId Id
Subnets []SubnetInfo
}
// InterfaceConfigType defines valid network interface configuration
// types. See interfaces(5) for details
type InterfaceConfigType string
const (
ConfigUnknown InterfaceConfigType = ""
ConfigDHCP InterfaceConfigType = "dhcp"
ConfigStatic InterfaceConfigType = "static"
ConfigManual InterfaceConfigType = "manual"
ConfigLoopback InterfaceConfigType = "loopback"
)
// InterfaceType defines valid network interface types.
type InterfaceType string
const (
UnknownInterface InterfaceType = ""
LoopbackInterface InterfaceType = "loopback"
EthernetInterface InterfaceType = "ethernet"
VLAN_8021QInterface InterfaceType = "802.1q"
BondInterface InterfaceType = "bond"
BridgeInterface InterfaceType = "bridge"
)
// InterfaceInfo describes a single network interface available on an
// instance. For providers that support networks, this will be
// available at StartInstance() time.
// TODO(mue): Rename to InterfaceConfig due to consistency later.
type InterfaceInfo struct {
// DeviceIndex specifies the order in which the network interface
// appears on the host. The primary interface has an index of 0.
DeviceIndex int
// MACAddress is the network interface's hardware MAC address
// (e.g. "aa:bb:cc:dd:ee:ff").
MACAddress string
// CIDR of the network, in 123.45.67.89/24 format.
CIDR string
// ProviderId is a provider-specific NIC id.
ProviderId Id
// ProviderSubnetId is the provider-specific id for the associated
// subnet.
ProviderSubnetId Id
// ProviderNetworkId is the provider-specific id for the
// associated network.
ProviderNetworkId Id
// ProviderSpaceId is the provider-specific id for the associated space, if
// known and supported.
ProviderSpaceId Id
// ProviderVLANId is the provider-specific id of the VLAN for this
// interface.
ProviderVLANId Id
// ProviderAddressId is the provider-specific id of the assigned address.
ProviderAddressId Id
// AvailabilityZones describes the availability zones the associated
// subnet is in.
AvailabilityZones []string
// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
// normal networks. It's defined by IEEE 802.1Q standard.
VLANTag int
// InterfaceName is the raw OS-specific network device name (e.g.
// "eth1", even for a VLAN eth1.42 virtual interface).
InterfaceName string
// ParentInterfaceName is the name of the parent interface to use, if known.
ParentInterfaceName string
// InterfaceType is the type of the interface.
InterfaceType InterfaceType
// Disabled is true when the interface needs to be disabled on the
// machine, e.g. not to configure it.
Disabled bool
// NoAutoStart is true when the interface should not be configured
// to start automatically on boot. By default and for
// backwards-compatibility, interfaces are configured to
// auto-start.
NoAutoStart bool
// ConfigType determines whether the interface should be
// configured via DHCP, statically, manually, etc. See
// interfaces(5) for more information.
ConfigType InterfaceConfigType
// Address contains an optional static IP address to configure for
// this network interface. The subnet mask to set will be inferred
// from the CIDR value.
Address Address
// DNSServers contains an optional list of IP addresses and/or
// hostnames to configure as DNS servers for this network
// interface.
DNSServers []Address
// MTU is the Maximum Transmission Unit controlling the maximum size of the
// protocol packats that the interface can pass through. It is only used
// when > 0.
MTU int
// DNSSearchDomains contains the default DNS domain to use for non-FQDN
// lookups.
DNSSearchDomains []string
// Gateway address, if set, defines the default gateway to
// configure for this network interface. For containers this
// usually is (one of) the host address(es).
GatewayAddress Address
// Routes defines a list of routes that should be added when this interface
// is brought up, and removed when this interface is stopped.
Routes []Route
}
// Route defines a single route to a subnet via a defined gateway.
type Route struct {
// DestinationCIDR is the subnet that we want a controlled route to.
DestinationCIDR string
// GatewayIP is the IP (v4 or v6) that should be used for traffic that is
// bound for DestinationCIDR
GatewayIP string
// Metric is the weight to apply to this route.
Metric int
}
// InterfaceAddress represents a single address attached to the interface.
type InterfaceAddress struct {
Address string
CIDR string
}
// NetworkInfo describes one interface with assigned IP addresses, it's a mirror of params.NetworkInfo.
type NetworkInfo struct {
// MACAddress is the network interface's hardware MAC address
// (e.g. "aa:bb:cc:dd:ee:ff").
MACAddress string
// InterfaceName is the OS-specific interface name, eg. "eth0" or "eno1.412"
InterfaceName string
// Addresses contains a list of addresses configured on the interface.
Addresses []InterfaceAddress
}
// Validate that this Route is properly formed.
func (r Route) Validate() error {
// Make sure the CIDR is actually a CIDR not just an IP or hostname
destinationIP, _, err := net.ParseCIDR(r.DestinationCIDR)
if err != nil {
return errors.Annotate(err, "DestinationCIDR not valid")
}
// Make sure the Gateway is just an IP, not a CIDR, etc.
gatewayIP := net.ParseIP(r.GatewayIP)
if gatewayIP == nil {
return errors.Errorf("GatewayIP is not a valid IP address: %q", r.GatewayIP)
}
if r.Metric < 0 {
return errors.Errorf("Metric is negative: %d", r.Metric)
}
// Make sure that either both are IPv4 or both are IPv6, not mixed.
destIP4 := destinationIP.To4()
gatewayIP4 := gatewayIP.To4()
if destIP4 != nil {
if gatewayIP4 == nil {
return errors.Errorf("DestinationCIDR is IPv4 (%s) but GatewayIP is IPv6 (%s)", r.DestinationCIDR, r.GatewayIP)
}
} else {
if gatewayIP4 != nil {
return errors.Errorf("DestinationCIDR is IPv6 (%s) but GatewayIP is IPv4 (%s)", r.DestinationCIDR, r.GatewayIP)
}
}
return nil
}
type interfaceInfoSlice []InterfaceInfo
func (s interfaceInfoSlice) Len() int { return len(s) }
func (s interfaceInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s interfaceInfoSlice) Less(i, j int) bool {
iface1 := s[i]
iface2 := s[j]
return iface1.DeviceIndex < iface2.DeviceIndex
}
// SortInterfaceInfo sorts a slice of InterfaceInfo on DeviceIndex in ascending
// order.
func SortInterfaceInfo(interfaces []InterfaceInfo) {
sort.Sort(interfaceInfoSlice(interfaces))
}
// ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and
// virtual interface name for virtual interface (e.g. "eth0.42")
func (i *InterfaceInfo) ActualInterfaceName() string {
if i.VLANTag > 0 {
return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag)
}
return i.InterfaceName
}
// IsVirtual returns true when the interface is a virtual device, as
// opposed to a physical device (e.g. a VLAN or a network alias)
func (i *InterfaceInfo) IsVirtual() bool {
return i.VLANTag > 0
}
// IsVLAN returns true when the interface is a VLAN interface.
func (i *InterfaceInfo) IsVLAN() bool {
return i.VLANTag > 0
}
// CIDRAddress returns Address.Value combined with CIDR mask.
func (i *InterfaceInfo) CIDRAddress() string {
if i.CIDR == "" || i.Address.Value == "" {
return ""
}
_, ipNet, err := net.ParseCIDR(i.CIDR)
if err != nil {
return errors.Trace(err).Error()
}
ip := net.ParseIP(i.Address.Value)
if ip == nil {
return errors.Errorf("cannot parse IP address %q", i.Address.Value).Error()
}
ipNet.IP = ip
return ipNet.String()
}
// ProviderInterfaceInfo holds enough information to identify an
// interface or link layer device to a provider so that it can be
// queried or manipulated. Its initial purpose is to pass to
// provider.ReleaseContainerAddresses.
type ProviderInterfaceInfo struct {
// InterfaceName is the raw OS-specific network device name (e.g.
// "eth1", even for a VLAN eth1.42 virtual interface).
InterfaceName string
// ProviderId is a provider-specific NIC id.
ProviderId Id
// MACAddress is the network interface's hardware MAC address
// (e.g. "aa:bb:cc:dd:ee:ff").
MACAddress string
}
// DeviceToBridge gives the information about a particular device that
// should be bridged.
type DeviceToBridge struct {
// DeviceName is the name of the device on the machine that should
// be bridged.
DeviceName string
// BridgeName is the name of the bridge that we want created.
BridgeName string
// MACAddress is the MAC address of the device to be bridged
MACAddress string
}
// LXCNetDefaultConfig is the location of the default network config
// of the lxc package. It's exported to allow cross-package testing.
var LXCNetDefaultConfig = "/etc/default/lxc-net"
// InterfaceByNameAddrs returns the addresses for the given interface
// name. It's exported to facilitate cross-package testing.
var InterfaceByNameAddrs = func(name string) ([]net.Addr, error) {
iface, err := net.InterfaceByName(name)
if err != nil {
return nil, err
}
return iface.Addrs()
}
type ipNetAndName struct {
ipnet *net.IPNet
name string
}
func addrMapToIPNetAndName(bridgeToAddrs map[string][]net.Addr) []ipNetAndName {
ipNets := make([]ipNetAndName, 0, len(bridgeToAddrs))
for bridgeName, addrList := range bridgeToAddrs {
for _, ifaceAddr := range addrList {
ip, ipNet, err := net.ParseCIDR(ifaceAddr.String())
if err != nil {
// Not a valid CIDR, check as an IP
ip = net.ParseIP(ifaceAddr.String())
}
if ip == nil {
logger.Debugf("cannot parse %q as IP, ignoring", ifaceAddr)
continue
}
if ipNet == nil {
// convert the IP into an IPNet
if ip.To4() != nil {
_, ipNet, err = net.ParseCIDR(ip.String() + "/32")
if err != nil {
logger.Debugf("error creating a /32 CIDR for %q", ifaceAddr)
}
} else if ip.To16() != nil {
_, ipNet, err = net.ParseCIDR(ip.String() + "/128")
if err != nil {
logger.Debugf("error creating a /128 CIDR for %q", ifaceAddr)
}
} else {
logger.Debugf("failed to convert %q to a v4 or v6 address, ignoring", ifaceAddr)
}
}
ipNets = append(ipNets, ipNetAndName{ipnet: ipNet, name: bridgeName})
}
}
return ipNets
}
// filterAddrs looks at all of the addresses in allAddresses and removes ones
// that line up with removeAddresses. Note that net.Addr may be just an IP or
// may be a CIDR. removeAddresses should be a map of 'bridge name' to list of
// addresses, so that we can report why the address was filtered.
func filterAddrs(allAddresses []Address, removeAddresses map[string][]net.Addr) []Address {
filtered := make([]Address, 0, len(allAddresses))
// Convert all
ipNets := addrMapToIPNetAndName(removeAddresses)
for _, addr := range allAddresses {
bridgeName := ""
// Then check if it is in one of the CIDRs
ip := net.ParseIP(addr.Value)
if ip == nil {
logger.Debugf("not filtering invalid IP: %q", addr.Value)
} else {
for _, ipNetName := range ipNets {
if ipNetName.ipnet.Contains(ip) {
bridgeName = ipNetName.name
break
}
}
}
if bridgeName == "" {
logger.Debugf("including address %v for machine", addr)
filtered = append(filtered, addr)
} else {
logger.Debugf("filtering %q address %s for machine", bridgeName, addr.String())
}
}
return filtered
}
// gatherLXCAddresses tries to discover the default lxc bridge name
// and all of its addresses. See LP bug #1416928.
func gatherLXCAddresses(toRemove map[string][]net.Addr) {
file, err := os.Open(LXCNetDefaultConfig)
if os.IsNotExist(err) {
// No lxc-net config found, nothing to do.
logger.Debugf("no lxc bridge addresses to filter for machine")
return
} else if err != nil {
// Just log it, as it's not fatal.
logger.Errorf("cannot open %q: %v", LXCNetDefaultConfig, err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
switch {
case strings.HasPrefix(line, "#"):
// Skip comments.
case strings.HasPrefix(line, "LXC_BRIDGE"):
// Extract <name> from LXC_BRIDGE="<name>".
parts := strings.Split(line, `"`)
if len(parts) < 2 {
logger.Debugf("ignoring invalid line '%s' in %q", line, LXCNetDefaultConfig)
continue
}
bridgeName := strings.TrimSpace(parts[1])
gatherBridgeAddresses(bridgeName, toRemove)
return
}
}
if err := scanner.Err(); err != nil {
logger.Debugf("failed to read %q: %v (ignoring)", LXCNetDefaultConfig, err)
}
return
}
func gatherBridgeAddresses(bridgeName string, toRemove map[string][]net.Addr) {
addrs, err := InterfaceByNameAddrs(bridgeName)
if err != nil {
logger.Debugf("cannot get %q addresses: %v (ignoring)", bridgeName, err)
return
}
logger.Debugf("%q has addresses %v", bridgeName, addrs)
toRemove[bridgeName] = addrs
return
}
// FilterBridgeAddresses removes addresses seen as a Bridge address (the IP
// address used only to connect to local containers), rather than a remote
// accessible address.
func FilterBridgeAddresses(addresses []Address) []Address {
addressesToRemove := make(map[string][]net.Addr)
gatherLXCAddresses(addressesToRemove)
gatherBridgeAddresses(DefaultLXDBridge, addressesToRemove)
gatherBridgeAddresses(DefaultKVMBridge, addressesToRemove)
filtered := filterAddrs(addresses, addressesToRemove)
logger.Debugf("addresses after filtering: %v", filtered)
return filtered
}
// QuoteSpaces takes a slice of space names, and returns a nicely formatted
// form so they show up legible in log messages, etc.
func QuoteSpaces(vals []string) string {
out := []string{}
if len(vals) == 0 {
return "<none>"
}
for _, space := range vals {
out = append(out, fmt.Sprintf("%q", space))
}
return strings.Join(out, ", ")
}
// QuoteSpaceSet is the same as QuoteSpaces, but ensures that a set.Strings
// gets sorted values output.
func QuoteSpaceSet(vals set.Strings) string {
return QuoteSpaces(vals.SortedValues())
}
// firstLastAddresses returns the first and last addresses of the subnet.
func firstLastAddresses(subnet *net.IPNet) (net.IP, net.IP) {
firstIP := subnet.IP
lastIP := make([]byte, len(firstIP))
copy(lastIP, firstIP)
for i, b := range lastIP {
lastIP[i] = b ^ (^subnet.Mask[i])
}
return firstIP, lastIP
}
func cidrContains(cidr *net.IPNet, subnet *net.IPNet) bool {
first, last := firstLastAddresses(subnet)
return cidr.Contains(first) && cidr.Contains(last)
}
// SubnetInAnyRange returns true if the subnet's address range is fully
// contained in any of the specified subnet blocks.
func SubnetInAnyRange(cidrs []*net.IPNet, subnet *net.IPNet) bool {
for _, cidr := range cidrs {
if cidrContains(cidr, subnet) {
return true
}
}
return false
}
// FormatAsCIDR converts the specified IP addresses to
// a slice of CIDRs.
func FormatAsCIDR(addresses []string) []string {
result := make([]string, len(addresses))
for i, a := range addresses {
cidr := a
// If address is not already a cidr, add a /32 (ipv4) or /128 (ipv6).
if _, _, err := net.ParseCIDR(a); err != nil {
ip := net.ParseIP(a)
if ip.To4() != nil {
cidr = a + "/32"
} else {
cidr = a + "/128"
}
}
result[i] = cidr
}
return result
}