Browse code

bump libnetwork to 430c00a

Bump libnetwork to 430c00a6a6b3dfdd774f21e1abd4ad6b0216c629. This
includes the following moby-affecting changes:

* Update vendoring for go-sockaddr (8df9f31a)
* Fix inconsistent subnet allocation by preventing allocation of
overlapping subnets (8579c5d2)
* Handle IPv6 literals correctly in port bindings (474fcaf4)
* Update vendoring for miekg/dns (8f307ac8)
* Avoid subnet reallocation until required (9756ff7ed)
* Bump libnetwork build to use go version 1.10.2 (603d2c1a)
* Unwrap error type returned by PluginGetter (aacec8e1)
* Update vendored components to match moby (d768021dd)
* Add retry field to cluster-peers probe (dbbd06a7)
* Fix net driver response loss on createEndpoint (1ab6e506)
(fixes https://github.com/docker/for-linux/issues/348)

Signed-off-by: Chris Telfer <ctelfer@docker.com>

Chris Telfer authored on 2018/06/29 23:38:44
Showing 15 changed files
... ...
@@ -3,7 +3,7 @@
3 3
 # LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
4 4
 # updating the binary version, consider updating github.com/docker/libnetwork
5 5
 # in vendor.conf accordingly
6
-LIBNETWORK_COMMIT=19279f0492417475b6bfbd0aa529f73e8f178fb5
6
+LIBNETWORK_COMMIT=430c00a6a6b3dfdd774f21e1abd4ad6b0216c629
7 7
 
8 8
 install_proxy() {
9 9
 	case "$1" in
... ...
@@ -37,14 +37,14 @@ github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
37 37
 #get libnetwork packages
38 38
 
39 39
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
40
-github.com/docker/libnetwork 19279f0492417475b6bfbd0aa529f73e8f178fb5
40
+github.com/docker/libnetwork 430c00a6a6b3dfdd774f21e1abd4ad6b0216c629
41 41
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
42 42
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
43 43
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
44 44
 github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
45 45
 github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
46 46
 github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
47
-github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
47
+github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
48 48
 github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
49 49
 github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
50 50
 github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
... ...
@@ -69,6 +69,7 @@ import (
69 69
 	"github.com/docker/libnetwork/netlabel"
70 70
 	"github.com/docker/libnetwork/osl"
71 71
 	"github.com/docker/libnetwork/types"
72
+	"github.com/pkg/errors"
72 73
 	"github.com/sirupsen/logrus"
73 74
 )
74 75
 
... ...
@@ -1252,7 +1253,7 @@ func (c *controller) loadDriver(networkType string) error {
1252 1252
 	}
1253 1253
 
1254 1254
 	if err != nil {
1255
-		if err == plugins.ErrNotFound {
1255
+		if errors.Cause(err) == plugins.ErrNotFound {
1256 1256
 			return types.NotFoundErrorf(err.Error())
1257 1257
 		}
1258 1258
 		return err
... ...
@@ -29,7 +29,10 @@ const (
29 29
 // Allocator provides per address space ipv4/ipv6 book keeping
30 30
 type Allocator struct {
31 31
 	// Predefined pools for default address spaces
32
-	predefined map[string][]*net.IPNet
32
+	// Separate from the addrSpace because they should not be serialized
33
+	predefined             map[string][]*net.IPNet
34
+	predefinedStartIndices map[string]int
35
+	// The (potentially serialized) address spaces
33 36
 	addrSpaces map[string]*addrSpace
34 37
 	// stores        []datastore.Datastore
35 38
 	// Allocated addresses in each address space's subnet
... ...
@@ -47,6 +50,9 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
47 47
 		globalAddressSpace: ipamutils.PredefinedGranularNetworks,
48 48
 	}
49 49
 
50
+	// Initialize asIndices map
51
+	a.predefinedStartIndices = make(map[string]int)
52
+
50 53
 	// Initialize bitseq map
51 54
 	a.addresses = make(map[SubnetKey]*bitseq.Handle)
52 55
 
... ...
@@ -374,11 +380,24 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
374 374
 func (a *Allocator) getPredefineds(as string) []*net.IPNet {
375 375
 	a.Lock()
376 376
 	defer a.Unlock()
377
-	l := make([]*net.IPNet, 0, len(a.predefined[as]))
378
-	for _, pool := range a.predefined[as] {
379
-		l = append(l, pool)
377
+
378
+	p := a.predefined[as]
379
+	i := a.predefinedStartIndices[as]
380
+	// defensive in case the list changed since last update
381
+	if i >= len(p) {
382
+		i = 0
380 383
 	}
381
-	return l
384
+	return append(p[i:], p[:i]...)
385
+}
386
+
387
+func (a *Allocator) updateStartIndex(as string, amt int) {
388
+	a.Lock()
389
+	i := a.predefinedStartIndices[as] + amt
390
+	if i < 0 || i >= len(a.predefined[as]) {
391
+		i = 0
392
+	}
393
+	a.predefinedStartIndices[as] = i
394
+	a.Unlock()
382 395
 }
383 396
 
384 397
 func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) {
... ...
@@ -397,21 +416,26 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
397 397
 		return nil, err
398 398
 	}
399 399
 
400
-	for _, nw := range a.getPredefineds(as) {
400
+	predefined := a.getPredefineds(as)
401
+
402
+	aSpace.Lock()
403
+	for i, nw := range predefined {
401 404
 		if v != getAddressVersion(nw.IP) {
402 405
 			continue
403 406
 		}
404
-		aSpace.Lock()
407
+		// Checks whether pool has already been allocated
405 408
 		if _, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]; ok {
406
-			aSpace.Unlock()
407 409
 			continue
408 410
 		}
411
+		// Shouldn't be necessary, but check prevents IP collisions should
412
+		// predefined pools overlap for any reason.
409 413
 		if !aSpace.contains(as, nw) {
410 414
 			aSpace.Unlock()
415
+			a.updateStartIndex(as, i+1)
411 416
 			return nw, nil
412 417
 		}
413
-		aSpace.Unlock()
414 418
 	}
419
+	aSpace.Unlock()
415 420
 
416 421
 	return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
417 422
 }
... ...
@@ -262,12 +262,13 @@ func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *Addr
262 262
 	defer aSpace.Unlock()
263 263
 
264 264
 	// Check if already allocated
265
-	if p, ok := aSpace.subnets[k]; ok {
265
+	if _, ok := aSpace.subnets[k]; ok {
266 266
 		if pdf {
267 267
 			return nil, types.InternalMaskableErrorf("predefined pool %s is already reserved", nw)
268 268
 		}
269
-		aSpace.incRefCount(p, 1)
270
-		return func() error { return nil }, nil
269
+		// This means the same pool is already allocated. updatePoolDBOnAdd is called when there
270
+		// is request for a pool/subpool. It should ensure there is no overlap with existing pools
271
+		return nil, ipamapi.ErrPoolOverlap
271 272
 	}
272 273
 
273 274
 	// If master pool, check for overlap
... ...
@@ -1156,26 +1156,27 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi
1156 1156
 			ep.releaseAddress()
1157 1157
 		}
1158 1158
 	}()
1159
-	// Moving updateToSTore before calling addEndpoint so that we shall clean up VETH interfaces in case
1160
-	// DockerD get killed between addEndpoint and updateSTore call
1161
-	if err = n.getController().updateToStore(ep); err != nil {
1159
+
1160
+	if err = n.addEndpoint(ep); err != nil {
1162 1161
 		return nil, err
1163 1162
 	}
1164 1163
 	defer func() {
1165 1164
 		if err != nil {
1166
-			if e := n.getController().deleteFromStore(ep); e != nil {
1167
-				logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
1165
+			if e := ep.deleteEndpoint(false); e != nil {
1166
+				logrus.Warnf("cleaning up endpoint failed %s : %v", name, e)
1168 1167
 			}
1169 1168
 		}
1170 1169
 	}()
1171 1170
 
1172
-	if err = n.addEndpoint(ep); err != nil {
1171
+	// We should perform updateToStore call right after addEndpoint
1172
+	// in order to have iface properly configured
1173
+	if err = n.getController().updateToStore(ep); err != nil {
1173 1174
 		return nil, err
1174 1175
 	}
1175 1176
 	defer func() {
1176 1177
 		if err != nil {
1177
-			if e := ep.deleteEndpoint(false); e != nil {
1178
-				logrus.Warnf("cleaning up endpoint failed %s : %v", name, e)
1178
+			if e := n.getController().deleteFromStore(ep); e != nil {
1179
+				logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
1179 1180
 			}
1180 1181
 		}
1181 1182
 	}()
... ...
@@ -41,7 +41,7 @@ func (nDB *NetworkDB) handleNodeEvent(nEvent *NodeEvent) bool {
41 41
 	// If the node is not known from memberlist we cannot process save any state of it else if it actually
42 42
 	// dies we won't receive any notification and we will remain stuck with it
43 43
 	if _, ok := nDB.nodes[nEvent.NodeName]; !ok {
44
-		logrus.Error("node: %s is unknown to memberlist", nEvent.NodeName)
44
+		logrus.Errorf("node: %s is unknown to memberlist", nEvent.NodeName)
45 45
 		return false
46 46
 	}
47 47
 
... ...
@@ -145,7 +145,12 @@ func (p *PortBinding) String() string {
145 145
 	return ret
146 146
 }
147 147
 
148
-// FromString reads the PortBinding structure from string
148
+// FromString reads the PortBinding structure from string s.
149
+// String s is a triple of "protocol/containerIP:port/hostIP:port"
150
+// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form.
151
+// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported.
152
+// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString
153
+// returns an error.
149 154
 func (p *PortBinding) FromString(s string) error {
150 155
 	ps := strings.Split(s, "/")
151 156
 	if len(ps) != 3 {
... ...
@@ -167,21 +172,19 @@ func (p *PortBinding) FromString(s string) error {
167 167
 }
168 168
 
169 169
 func parseIPPort(s string) (net.IP, uint16, error) {
170
-	pp := strings.Split(s, ":")
171
-	if len(pp) != 2 {
172
-		return nil, 0, BadRequestErrorf("invalid format: %s", s)
170
+	hoststr, portstr, err := net.SplitHostPort(s)
171
+	if err != nil {
172
+		return nil, 0, err
173 173
 	}
174 174
 
175
-	var ip net.IP
176
-	if pp[0] != "" {
177
-		if ip = net.ParseIP(pp[0]); ip == nil {
178
-			return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
179
-		}
175
+	ip := net.ParseIP(hoststr)
176
+	if ip == nil {
177
+		return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
180 178
 	}
181 179
 
182
-	port, err := strconv.ParseUint(pp[1], 10, 16)
180
+	port, err := strconv.ParseUint(portstr, 10, 16)
183 181
 	if err != nil {
184
-		return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
182
+		return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
185 183
 	}
186 184
 
187 185
 	return ip, uint16(port), nil
... ...
@@ -1,27 +1,28 @@
1 1
 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
2 2
 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
3
-github.com/Microsoft/go-winio v0.4.5
4
-github.com/Microsoft/hcsshim v0.6.5
3
+github.com/Microsoft/go-winio v0.4.7
4
+github.com/Microsoft/hcsshim v0.6.11
5 5
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
6 6
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
7 7
 github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
8 8
 github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904
9
-github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
10
-github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
9
+github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
10
+github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
11 11
 github.com/coreos/etcd v3.2.1
12 12
 github.com/coreos/go-semver v0.2.0
13
-github.com/coreos/go-systemd v4
13
+github.com/coreos/go-systemd v17
14
+github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
14 15
 github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
15 16
 
16
-github.com/docker/docker a3efe9722f34af5cf4443fe3a5c4e4e3e0457b54
17
-github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
17
+github.com/docker/docker 162ba6016def672690ee4a1f3978368853a1e149
18
+github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
18 19
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
19 20
 github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
20 21
 github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
21 22
 
22 23
 github.com/godbus/dbus v4.0.0
23
-github.com/gogo/protobuf v0.4
24
-github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
24
+github.com/gogo/protobuf v1.0.0
25
+github.com/golang/protobuf v1.1.0
25 26
 github.com/gorilla/context v1.1
26 27
 github.com/gorilla/mux v1.1
27 28
 github.com/hashicorp/consul v0.5.2
... ...
@@ -29,27 +30,37 @@ github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
29 29
 github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
30 30
 github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
31 31
 github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
32
-github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
32
+github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
33 33
 github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
34 34
 github.com/mattn/go-shellwords v1.0.3
35
-github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
35
+github.com/miekg/dns v1.0.7
36 36
 github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
37
-github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
38
-github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
39
-github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
40
-github.com/opencontainers/runtime-spec v1.0.0
41
-github.com/opencontainers/selinux v1.0.0-rc1
37
+github.com/opencontainers/go-digest v1.0.0-rc1
38
+github.com/opencontainers/image-spec v1.0.1
39
+github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
40
+github.com/opencontainers/runtime-spec v1.0.1
41
+github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
42 42
 github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
43 43
 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
44 44
 github.com/sirupsen/logrus v1.0.3
45 45
 github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a
46
-github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
46
+github.com/syndtr/gocapability 33e07d32887e1e06b7c025f27ce52f62c7990bc0
47 47
 github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
48 48
 github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e
49 49
 github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
50
-golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
51
-golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
52
-golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
50
+golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491
51
+golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd
52
+golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd
53 53
 golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
54 54
 github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
55 55
 github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
56
+
57
+github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
58
+github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
59
+github.com/cyphar/filepath-securejoin v0.2.1
60
+github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
61
+github.com/hashicorp/go-immutable-radix 7f3cd4390caab3250a57f30efdb2a65dd7649ecf
62
+github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
63
+github.com/hashicorp/go-cleanhttp d5fe4b57a186c716b0e00b8c301cbd9b4182694d
64
+github.com/hashicorp/go-rootcerts 6bb64b370b90e7ef1fa532be9e591a81c3493e00
65
+github.com/mitchellh/go-homedir 3864e76763d94a6df2f9960b16a20a33da9f9a66
... ...
@@ -1,5 +1,7 @@
1 1
 package sockaddr
2 2
 
3
+import "strings"
4
+
3 5
 // ifAddrAttrMap is a map of the IfAddr type-specific attributes.
4 6
 var ifAddrAttrMap map[AttrName]func(IfAddr) string
5 7
 var ifAddrAttrs []AttrName
... ...
@@ -30,6 +32,53 @@ func GetPrivateIP() (string, error) {
30 30
 	return ip.NetIP().String(), nil
31 31
 }
32 32
 
33
+// GetPrivateIPs returns a string with all IP addresses that are part of RFC
34
+// 6890 (regardless of whether or not there is a default route, unlike
35
+// GetPublicIP).  If the system can't find any RFC 6890 IP addresses, an empty
36
+// string will be returned instead.  This function is the `eval` equivalent of:
37
+//
38
+// ```
39
+// $ sockaddr eval -r '{{GetAllInterfaces | include "RFC" "6890" | join "address" " "}}'
40
+/// ```
41
+func GetPrivateIPs() (string, error) {
42
+	ifAddrs, err := GetAllInterfaces()
43
+	if err != nil {
44
+		return "", err
45
+	} else if len(ifAddrs) < 1 {
46
+		return "", nil
47
+	}
48
+
49
+	ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
50
+	if len(ifAddrs) == 0 {
51
+		return "", nil
52
+	}
53
+
54
+	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
55
+
56
+	ifAddrs, _, err = IfByRFC("6890", ifAddrs)
57
+	if err != nil {
58
+		return "", err
59
+	} else if len(ifAddrs) == 0 {
60
+		return "", nil
61
+	}
62
+
63
+	_, ifAddrs, err = IfByRFC(ForwardingBlacklistRFC, ifAddrs)
64
+	if err != nil {
65
+		return "", err
66
+	} else if len(ifAddrs) == 0 {
67
+		return "", nil
68
+	}
69
+
70
+	ips := make([]string, 0, len(ifAddrs))
71
+	for _, ifAddr := range ifAddrs {
72
+		ip := *ToIPAddr(ifAddr.SockAddr)
73
+		s := ip.NetIP().String()
74
+		ips = append(ips, s)
75
+	}
76
+
77
+	return strings.Join(ips, " "), nil
78
+}
79
+
33 80
 // GetPublicIP returns a string with a single IP address that is NOT part of RFC
34 81
 // 6890 and has a default route.  If the system can't determine its IP address
35 82
 // or find a non RFC 6890 IP address, an empty string will be returned instead.
... ...
@@ -51,6 +100,47 @@ func GetPublicIP() (string, error) {
51 51
 	return ip.NetIP().String(), nil
52 52
 }
53 53
 
54
+// GetPublicIPs returns a string with all IP addresses that are NOT part of RFC
55
+// 6890 (regardless of whether or not there is a default route, unlike
56
+// GetPublicIP).  If the system can't find any non RFC 6890 IP addresses, an
57
+// empty string will be returned instead.  This function is the `eval`
58
+// equivalent of:
59
+//
60
+// ```
61
+// $ sockaddr eval -r '{{GetAllInterfaces | exclude "RFC" "6890" | join "address" " "}}'
62
+/// ```
63
+func GetPublicIPs() (string, error) {
64
+	ifAddrs, err := GetAllInterfaces()
65
+	if err != nil {
66
+		return "", err
67
+	} else if len(ifAddrs) < 1 {
68
+		return "", nil
69
+	}
70
+
71
+	ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
72
+	if len(ifAddrs) == 0 {
73
+		return "", nil
74
+	}
75
+
76
+	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
77
+
78
+	_, ifAddrs, err = IfByRFC("6890", ifAddrs)
79
+	if err != nil {
80
+		return "", err
81
+	} else if len(ifAddrs) == 0 {
82
+		return "", nil
83
+	}
84
+
85
+	ips := make([]string, 0, len(ifAddrs))
86
+	for _, ifAddr := range ifAddrs {
87
+		ip := *ToIPAddr(ifAddr.SockAddr)
88
+		s := ip.NetIP().String()
89
+		ips = append(ips, s)
90
+	}
91
+
92
+	return strings.Join(ips, " "), nil
93
+}
94
+
54 95
 // GetInterfaceIP returns a string with a single IP address sorted by the size
55 96
 // of the network (i.e. IP addresses with a smaller netmask, larger network
56 97
 // size, are sorted first).  This function is the `eval` equivalent of:
... ...
@@ -91,6 +181,44 @@ func GetInterfaceIP(namedIfRE string) (string, error) {
91 91
 	return IPAddrAttr(*ip, "address"), nil
92 92
 }
93 93
 
94
+// GetInterfaceIPs returns a string with all IPs, sorted by the size of the
95
+// network (i.e. IP addresses with a smaller netmask, larger network size, are
96
+// sorted first), on a named interface.  This function is the `eval` equivalent
97
+// of:
98
+//
99
+// ```
100
+// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | join "address" " "}}'
101
+/// ```
102
+func GetInterfaceIPs(namedIfRE string) (string, error) {
103
+	ifAddrs, err := GetAllInterfaces()
104
+	if err != nil {
105
+		return "", err
106
+	}
107
+
108
+	ifAddrs, _, err = IfByName(namedIfRE, ifAddrs)
109
+	if err != nil {
110
+		return "", err
111
+	}
112
+
113
+	ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
114
+	if err != nil {
115
+		return "", err
116
+	}
117
+
118
+	if len(ifAddrs) == 0 {
119
+		return "", err
120
+	}
121
+
122
+	ips := make([]string, 0, len(ifAddrs))
123
+	for _, ifAddr := range ifAddrs {
124
+		ip := *ToIPAddr(ifAddr.SockAddr)
125
+		s := ip.NetIP().String()
126
+		ips = append(ips, s)
127
+	}
128
+
129
+	return strings.Join(ips, " "), nil
130
+}
131
+
94 132
 // IfAddrAttrs returns a list of attributes supported by the IfAddr type
95 133
 func IfAddrAttrs() []AttrName {
96 134
 	return ifAddrAttrs
... ...
@@ -1,8 +1,10 @@
1 1
 package sockaddr
2 2
 
3 3
 import (
4
+	"encoding/binary"
4 5
 	"errors"
5 6
 	"fmt"
7
+	"math/big"
6 8
 	"net"
7 9
 	"regexp"
8 10
 	"sort"
... ...
@@ -10,6 +12,14 @@ import (
10 10
 	"strings"
11 11
 )
12 12
 
13
+var (
14
+	// Centralize all regexps and regexp.Copy() where necessary.
15
+	signRE       *regexp.Regexp = regexp.MustCompile(`^[\s]*[+-]`)
16
+	whitespaceRE *regexp.Regexp = regexp.MustCompile(`[\s]+`)
17
+	ifNameRE     *regexp.Regexp = regexp.MustCompile(`^(?:Ethernet|Wireless LAN) adapter ([^:]+):`)
18
+	ipAddrRE     *regexp.Regexp = regexp.MustCompile(`^   IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
19
+)
20
+
13 21
 // IfAddrs is a slice of IfAddr
14 22
 type IfAddrs []IfAddr
15 23
 
... ...
@@ -91,6 +101,40 @@ func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int {
91 91
 	return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
92 92
 }
93 93
 
94
+// AscIfDefault is a sorting function to sort IfAddrs by whether or not they
95
+// have a default route or not.  Non-equal types are deferred in the sort.
96
+//
97
+// FIXME: This is a particularly expensive sorting operation because of the
98
+// non-memoized calls to NewRouteInfo().  In an ideal world the routeInfo data
99
+// once at the start of the sort and pass it along as a context or by wrapping
100
+// the IfAddr type with this information (this would also solve the inability to
101
+// return errors and the possibility of failing silently).  Fortunately,
102
+// N*log(N) where N = 3 is only ~6.2 invocations.  Not ideal, but not worth
103
+// optimizing today.  The common case is this gets called once or twice.
104
+// Patches welcome.
105
+func AscIfDefault(p1Ptr, p2Ptr *IfAddr) int {
106
+	ri, err := NewRouteInfo()
107
+	if err != nil {
108
+		return sortDeferDecision
109
+	}
110
+
111
+	defaultIfName, err := ri.GetDefaultInterfaceName()
112
+	if err != nil {
113
+		return sortDeferDecision
114
+	}
115
+
116
+	switch {
117
+	case p1Ptr.Interface.Name == defaultIfName && p2Ptr.Interface.Name == defaultIfName:
118
+		return sortDeferDecision
119
+	case p1Ptr.Interface.Name == defaultIfName:
120
+		return sortReceiverBeforeArg
121
+	case p2Ptr.Interface.Name == defaultIfName:
122
+		return sortArgBeforeReceiver
123
+	default:
124
+		return sortDeferDecision
125
+	}
126
+}
127
+
94 128
 // AscIfName is a sorting function to sort IfAddrs by their interface names.
95 129
 func AscIfName(p1Ptr, p2Ptr *IfAddr) int {
96 130
 	return strings.Compare(p1Ptr.Name, p2Ptr.Name)
... ...
@@ -127,6 +171,11 @@ func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int {
127 127
 	return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
128 128
 }
129 129
 
130
+// DescIfDefault is identical to AscIfDefault but reverse ordered.
131
+func DescIfDefault(p1Ptr, p2Ptr *IfAddr) int {
132
+	return -1 * AscIfDefault(p1Ptr, p2Ptr)
133
+}
134
+
130 135
 // DescIfName is identical to AscIfName but reverse ordered.
131 136
 func DescIfName(p1Ptr, p2Ptr *IfAddr) int {
132 137
 	return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name)
... ...
@@ -169,7 +218,15 @@ func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIf
169 169
 
170 170
 // IfAttr forwards the selector to IfAttr.Attr() for resolution.  If there is
171 171
 // more than one IfAddr, only the first IfAddr is used.
172
-func IfAttr(selectorName string, ifAddrs IfAddrs) (string, error) {
172
+func IfAttr(selectorName string, ifAddr IfAddr) (string, error) {
173
+	attrName := AttrName(strings.ToLower(selectorName))
174
+	attrVal, err := ifAddr.Attr(attrName)
175
+	return attrVal, err
176
+}
177
+
178
+// IfAttrs forwards the selector to IfAttrs.Attr() for resolution.  If there is
179
+// more than one IfAddr, only the first IfAddr is used.
180
+func IfAttrs(selectorName string, ifAddrs IfAddrs) (string, error) {
173 181
 	if len(ifAddrs) == 0 {
174 182
 		return "", nil
175 183
 	}
... ...
@@ -243,10 +300,10 @@ func GetDefaultInterfaces() (IfAddrs, error) {
243 243
 // the `eval` equivalent of:
244 244
 //
245 245
 // ```
246
-// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}'
246
+// $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | include "RFC" "6890" }}'
247 247
 /// ```
248 248
 func GetPrivateInterfaces() (IfAddrs, error) {
249
-	privateIfs, err := GetDefaultInterfaces()
249
+	privateIfs, err := GetAllInterfaces()
250 250
 	if err != nil {
251 251
 		return IfAddrs{}, err
252 252
 	}
... ...
@@ -259,15 +316,21 @@ func GetPrivateInterfaces() (IfAddrs, error) {
259 259
 		return IfAddrs{}, nil
260 260
 	}
261 261
 
262
-	privateIfs, _, err = IfByFlag("forwardable|up", privateIfs)
262
+	privateIfs, _, err = IfByFlag("forwardable", privateIfs)
263
+	if err != nil {
264
+		return IfAddrs{}, err
265
+	}
266
+
267
+	privateIfs, _, err = IfByFlag("up", privateIfs)
263 268
 	if err != nil {
264 269
 		return IfAddrs{}, err
265 270
 	}
271
+
266 272
 	if len(privateIfs) == 0 {
267 273
 		return IfAddrs{}, nil
268 274
 	}
269 275
 
270
-	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(privateIfs)
276
+	OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(privateIfs)
271 277
 
272 278
 	privateIfs, _, err = IfByRFC("6890", privateIfs)
273 279
 	if err != nil {
... ...
@@ -285,10 +348,10 @@ func GetPrivateInterfaces() (IfAddrs, error) {
285 285
 // function is the `eval` equivalent of:
286 286
 //
287 287
 // ```
288
-// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}'
288
+// $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | exclude "RFC" "6890" }}'
289 289
 /// ```
290 290
 func GetPublicInterfaces() (IfAddrs, error) {
291
-	publicIfs, err := GetDefaultInterfaces()
291
+	publicIfs, err := GetAllInterfaces()
292 292
 	if err != nil {
293 293
 		return IfAddrs{}, err
294 294
 	}
... ...
@@ -301,15 +364,21 @@ func GetPublicInterfaces() (IfAddrs, error) {
301 301
 		return IfAddrs{}, nil
302 302
 	}
303 303
 
304
-	publicIfs, _, err = IfByFlag("forwardable|up", publicIfs)
304
+	publicIfs, _, err = IfByFlag("forwardable", publicIfs)
305 305
 	if err != nil {
306 306
 		return IfAddrs{}, err
307 307
 	}
308
+
309
+	publicIfs, _, err = IfByFlag("up", publicIfs)
310
+	if err != nil {
311
+		return IfAddrs{}, err
312
+	}
313
+
308 314
 	if len(publicIfs) == 0 {
309 315
 		return IfAddrs{}, nil
310 316
 	}
311 317
 
312
-	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(publicIfs)
318
+	OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(publicIfs)
313 319
 
314 320
 	_, publicIfs, err = IfByRFC("6890", publicIfs)
315 321
 	if err != nil {
... ...
@@ -652,6 +721,245 @@ func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs,
652 652
 	return includedIfs, excludedIfs, nil
653 653
 }
654 654
 
655
+// IfAddrMath will return a new IfAddr struct with a mutated value.
656
+func IfAddrMath(operation, value string, inputIfAddr IfAddr) (IfAddr, error) {
657
+	// Regexp used to enforce the sign being a required part of the grammar for
658
+	// some values.
659
+	signRe := signRE.Copy()
660
+
661
+	switch strings.ToLower(operation) {
662
+	case "address":
663
+		// "address" operates on the IP address and is allowed to overflow or
664
+		// underflow networks, however it will wrap along the underlying address's
665
+		// underlying type.
666
+
667
+		if !signRe.MatchString(value) {
668
+			return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation)
669
+		}
670
+
671
+		switch sockType := inputIfAddr.SockAddr.Type(); sockType {
672
+		case TypeIPv4:
673
+			// 33 == Accept any uint32 value
674
+			// TODO(seanc@): Add the ability to parse hex
675
+			i, err := strconv.ParseInt(value, 10, 33)
676
+			if err != nil {
677
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
678
+			}
679
+
680
+			ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
681
+			ipv4Uint32 := uint32(ipv4.Address)
682
+			ipv4Uint32 += uint32(i)
683
+			return IfAddr{
684
+				SockAddr: IPv4Addr{
685
+					Address: IPv4Address(ipv4Uint32),
686
+					Mask:    ipv4.Mask,
687
+				},
688
+				Interface: inputIfAddr.Interface,
689
+			}, nil
690
+		case TypeIPv6:
691
+			// 64 == Accept any int32 value
692
+			// TODO(seanc@): Add the ability to parse hex.  Also parse a bignum int.
693
+			i, err := strconv.ParseInt(value, 10, 64)
694
+			if err != nil {
695
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
696
+			}
697
+
698
+			ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
699
+			ipv6BigIntA := new(big.Int)
700
+			ipv6BigIntA.Set(ipv6.Address)
701
+			ipv6BigIntB := big.NewInt(i)
702
+
703
+			ipv6Addr := ipv6BigIntA.Add(ipv6BigIntA, ipv6BigIntB)
704
+			ipv6Addr.And(ipv6Addr, ipv6HostMask)
705
+
706
+			return IfAddr{
707
+				SockAddr: IPv6Addr{
708
+					Address: IPv6Address(ipv6Addr),
709
+					Mask:    ipv6.Mask,
710
+				},
711
+				Interface: inputIfAddr.Interface,
712
+			}, nil
713
+		default:
714
+			return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
715
+		}
716
+	case "network":
717
+		// "network" operates on the network address.  Positive values start at the
718
+		// network address and negative values wrap at the network address, which
719
+		// means a "-1" value on a network will be the broadcast address after
720
+		// wrapping is applied.
721
+
722
+		if !signRe.MatchString(value) {
723
+			return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation)
724
+		}
725
+
726
+		switch sockType := inputIfAddr.SockAddr.Type(); sockType {
727
+		case TypeIPv4:
728
+			// 33 == Accept any uint32 value
729
+			// TODO(seanc@): Add the ability to parse hex
730
+			i, err := strconv.ParseInt(value, 10, 33)
731
+			if err != nil {
732
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
733
+			}
734
+
735
+			ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
736
+			ipv4Uint32 := uint32(ipv4.NetworkAddress())
737
+
738
+			// Wrap along network mask boundaries.  EZ-mode wrapping made possible by
739
+			// use of int64 vs a uint.
740
+			var wrappedMask int64
741
+			if i >= 0 {
742
+				wrappedMask = i
743
+			} else {
744
+				wrappedMask = 1 + i + int64(^uint32(ipv4.Mask))
745
+			}
746
+
747
+			ipv4Uint32 = ipv4Uint32 + (uint32(wrappedMask) &^ uint32(ipv4.Mask))
748
+
749
+			return IfAddr{
750
+				SockAddr: IPv4Addr{
751
+					Address: IPv4Address(ipv4Uint32),
752
+					Mask:    ipv4.Mask,
753
+				},
754
+				Interface: inputIfAddr.Interface,
755
+			}, nil
756
+		case TypeIPv6:
757
+			// 64 == Accept any int32 value
758
+			// TODO(seanc@): Add the ability to parse hex.  Also parse a bignum int.
759
+			i, err := strconv.ParseInt(value, 10, 64)
760
+			if err != nil {
761
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
762
+			}
763
+
764
+			ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
765
+			ipv6BigInt := new(big.Int)
766
+			ipv6BigInt.Set(ipv6.NetworkAddress())
767
+
768
+			mask := new(big.Int)
769
+			mask.Set(ipv6.Mask)
770
+			if i > 0 {
771
+				wrappedMask := new(big.Int)
772
+				wrappedMask.SetInt64(i)
773
+
774
+				wrappedMask.AndNot(wrappedMask, mask)
775
+				ipv6BigInt.Add(ipv6BigInt, wrappedMask)
776
+			} else {
777
+				// Mask off any bits that exceed the network size.  Subtract the
778
+				// wrappedMask from the last usable - 1
779
+				wrappedMask := new(big.Int)
780
+				wrappedMask.SetInt64(-1 * i)
781
+				wrappedMask.Sub(wrappedMask, big.NewInt(1))
782
+
783
+				wrappedMask.AndNot(wrappedMask, mask)
784
+
785
+				lastUsable := new(big.Int)
786
+				lastUsable.Set(ipv6.LastUsable().(IPv6Addr).Address)
787
+
788
+				ipv6BigInt = lastUsable.Sub(lastUsable, wrappedMask)
789
+			}
790
+
791
+			return IfAddr{
792
+				SockAddr: IPv6Addr{
793
+					Address: IPv6Address(ipv6BigInt),
794
+					Mask:    ipv6.Mask,
795
+				},
796
+				Interface: inputIfAddr.Interface,
797
+			}, nil
798
+		default:
799
+			return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
800
+		}
801
+	case "mask":
802
+		// "mask" operates on the IP address and returns the IP address on
803
+		// which the given integer mask has been applied. If the applied mask
804
+		// corresponds to a larger network than the mask of the IP address,
805
+		// the latter will be replaced by the former.
806
+		switch sockType := inputIfAddr.SockAddr.Type(); sockType {
807
+		case TypeIPv4:
808
+			i, err := strconv.ParseUint(value, 10, 32)
809
+			if err != nil {
810
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
811
+			}
812
+
813
+			if i > 32 {
814
+				return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv4 addresses must be between 0 and 32", operation)
815
+			}
816
+
817
+			ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
818
+
819
+			ipv4Mask := net.CIDRMask(int(i), 32)
820
+			ipv4MaskUint32 := binary.BigEndian.Uint32(ipv4Mask)
821
+
822
+			maskedIpv4 := ipv4.NetIP().Mask(ipv4Mask)
823
+			maskedIpv4Uint32 := binary.BigEndian.Uint32(maskedIpv4)
824
+
825
+			maskedIpv4MaskUint32 := uint32(ipv4.Mask)
826
+
827
+			if ipv4MaskUint32 < maskedIpv4MaskUint32 {
828
+				maskedIpv4MaskUint32 = ipv4MaskUint32
829
+			}
830
+
831
+			return IfAddr{
832
+				SockAddr: IPv4Addr{
833
+					Address: IPv4Address(maskedIpv4Uint32),
834
+					Mask:    IPv4Mask(maskedIpv4MaskUint32),
835
+				},
836
+				Interface: inputIfAddr.Interface,
837
+			}, nil
838
+		case TypeIPv6:
839
+			i, err := strconv.ParseUint(value, 10, 32)
840
+			if err != nil {
841
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
842
+			}
843
+
844
+			if i > 128 {
845
+				return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv6 addresses must be between 0 and 64", operation)
846
+			}
847
+
848
+			ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
849
+
850
+			ipv6Mask := net.CIDRMask(int(i), 128)
851
+			ipv6MaskBigInt := new(big.Int)
852
+			ipv6MaskBigInt.SetBytes(ipv6Mask)
853
+
854
+			maskedIpv6 := ipv6.NetIP().Mask(ipv6Mask)
855
+			maskedIpv6BigInt := new(big.Int)
856
+			maskedIpv6BigInt.SetBytes(maskedIpv6)
857
+
858
+			maskedIpv6MaskBigInt := new(big.Int)
859
+			maskedIpv6MaskBigInt.Set(ipv6.Mask)
860
+
861
+			if ipv6MaskBigInt.Cmp(maskedIpv6MaskBigInt) == -1 {
862
+				maskedIpv6MaskBigInt = ipv6MaskBigInt
863
+			}
864
+
865
+			return IfAddr{
866
+				SockAddr: IPv6Addr{
867
+					Address: IPv6Address(maskedIpv6BigInt),
868
+					Mask:    IPv6Mask(maskedIpv6MaskBigInt),
869
+				},
870
+				Interface: inputIfAddr.Interface,
871
+			}, nil
872
+		default:
873
+			return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
874
+		}
875
+	default:
876
+		return IfAddr{}, fmt.Errorf("unsupported math operation: %q", operation)
877
+	}
878
+}
879
+
880
+// IfAddrsMath will apply an IfAddrMath operation each IfAddr struct.  Any
881
+// failure will result in zero results.
882
+func IfAddrsMath(operation, value string, inputIfAddrs IfAddrs) (IfAddrs, error) {
883
+	outputAddrs := make(IfAddrs, 0, len(inputIfAddrs))
884
+	for _, ifAddr := range inputIfAddrs {
885
+		result, err := IfAddrMath(operation, value, ifAddr)
886
+		if err != nil {
887
+			return IfAddrs{}, fmt.Errorf("unable to perform an IPMath operation on %s: %v", ifAddr, err)
888
+		}
889
+		outputAddrs = append(outputAddrs, result)
890
+	}
891
+	return outputAddrs, nil
892
+}
893
+
655 894
 // IncludeIfs returns an IfAddrs based on the passed in selector.
656 895
 func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
657 896
 	var includedIfs IfAddrs
... ...
@@ -736,6 +1044,10 @@ func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
736 736
 			sortFuncs[i] = AscIfAddress
737 737
 		case "-address":
738 738
 			sortFuncs[i] = DescIfAddress
739
+		case "+default", "default":
740
+			sortFuncs[i] = AscIfDefault
741
+		case "-default":
742
+			sortFuncs[i] = DescIfDefault
739 743
 		case "+name", "name":
740 744
 			// The "name" selector returns an array of IfAddrs
741 745
 			// ordered by the interface name.
... ...
@@ -886,7 +1198,7 @@ func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
886 886
 // Linux.
887 887
 func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
888 888
 	lines := strings.Split(routeOut, "\n")
889
-	re := regexp.MustCompile(`[\s]+`)
889
+	re := whitespaceRE.Copy()
890 890
 	for _, line := range lines {
891 891
 		kvs := re.Split(line, -1)
892 892
 		if len(kvs) < 5 {
... ...
@@ -929,7 +1241,7 @@ func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
929 929
 // support added.
930 930
 func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
931 931
 	lines := strings.Split(routeOut, "\n")
932
-	re := regexp.MustCompile(`[\s]+`)
932
+	re := whitespaceRE.Copy()
933 933
 	for _, line := range lines {
934 934
 		kvs := re.Split(strings.TrimSpace(line), -1)
935 935
 		if len(kvs) < 3 {
... ...
@@ -949,17 +1261,17 @@ func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
949 949
 // interface name forwarding traffic to the default gateway.
950 950
 func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
951 951
 	lines := strings.Split(routeOut, "\n")
952
-	ifNameRE := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`)
953
-	ipAddrRE := regexp.MustCompile(`^   IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
952
+	ifNameRe := ifNameRE.Copy()
953
+	ipAddrRe := ipAddrRE.Copy()
954 954
 	var ifName string
955 955
 	for _, line := range lines {
956
-		switch ifNameMatches := ifNameRE.FindStringSubmatch(line); {
956
+		switch ifNameMatches := ifNameRe.FindStringSubmatch(line); {
957 957
 		case len(ifNameMatches) > 1:
958 958
 			ifName = ifNameMatches[1]
959 959
 			continue
960 960
 		}
961 961
 
962
-		switch ipAddrMatches := ipAddrRE.FindStringSubmatch(line); {
962
+		switch ipAddrMatches := ipAddrRe.FindStringSubmatch(line); {
963 963
 		case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr:
964 964
 			return ifName, nil
965 965
 		}
... ...
@@ -58,7 +58,8 @@ func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) {
58 58
 	// Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go.  In
59 59
 	// particular, clients with the Barracuda VPN client will see something like:
60 60
 	// `192.168.3.51/00ffffff` as their IP address.
61
-	if match := trailingHexNetmaskRE.FindStringIndex(ipv4Str); match != nil {
61
+	trailingHexNetmaskRe := trailingHexNetmaskRE.Copy()
62
+	if match := trailingHexNetmaskRe.FindStringIndex(ipv4Str); match != nil {
62 63
 		ipv4Str = ipv4Str[:match[0]]
63 64
 	}
64 65
 
... ...
@@ -3,6 +3,7 @@ package sockaddr
3 3
 // ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
4 4
 // blocks.
5 5
 const ForwardingBlacklist = 4294967295
6
+const ForwardingBlacklistRFC = "4294967295"
6 7
 
7 8
 // IsRFC tests to see if an SockAddr matches the specified RFC
8 9
 func IsRFC(rfcNum uint, sa SockAddr) bool {
... ...
@@ -5,10 +5,6 @@ import (
5 5
 	"os/exec"
6 6
 )
7 7
 
8
-var cmds map[string][]string = map[string][]string{
9
-	"ip": {"/sbin/ip", "route"},
10
-}
11
-
12 8
 type routeInfo struct {
13 9
 	cmds map[string][]string
14 10
 }
... ...
@@ -16,15 +12,22 @@ type routeInfo struct {
16 16
 // NewRouteInfo returns a Linux-specific implementation of the RouteInfo
17 17
 // interface.
18 18
 func NewRouteInfo() (routeInfo, error) {
19
+	// CoreOS Container Linux moved ip to /usr/bin/ip, so look it up on
20
+	// $PATH and fallback to /sbin/ip on error.
21
+	path, _ := exec.LookPath("ip")
22
+	if path == "" {
23
+		path = "/sbin/ip"
24
+	}
25
+
19 26
 	return routeInfo{
20
-		cmds: cmds,
27
+		cmds: map[string][]string{"ip": {path, "route"}},
21 28
 	}, nil
22 29
 }
23 30
 
24 31
 // GetDefaultInterfaceName returns the interface name attached to the default
25 32
 // route on the default interface.
26 33
 func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
27
-	out, err := exec.Command(cmds["ip"][0], cmds["ip"][1:]...).Output()
34
+	out, err := exec.Command(ri.cmds["ip"][0], ri.cmds["ip"][1:]...).Output()
28 35
 	if err != nil {
29 36
 		return "", err
30 37
 	}
... ...
@@ -1,6 +1,7 @@
1 1
 package sockaddr
2 2
 
3 3
 import (
4
+	"encoding/json"
4 5
 	"fmt"
5 6
 	"strings"
6 7
 )
... ...
@@ -176,3 +177,30 @@ func sockAddrInit() {
176 176
 func SockAddrAttrs() []AttrName {
177 177
 	return sockAddrAttrs
178 178
 }
179
+
180
+// Although this is pretty trivial to do in a program, having the logic here is
181
+// useful all around. Note that this marshals into a *string* -- the underlying
182
+// string representation of the sockaddr. If you then unmarshal into this type
183
+// in Go, all will work as expected, but externally you can take what comes out
184
+// and use the string value directly.
185
+type SockAddrMarshaler struct {
186
+	SockAddr
187
+}
188
+
189
+func (s *SockAddrMarshaler) MarshalJSON() ([]byte, error) {
190
+	return json.Marshal(s.SockAddr.String())
191
+}
192
+
193
+func (s *SockAddrMarshaler) UnmarshalJSON(in []byte) error {
194
+	var str string
195
+	err := json.Unmarshal(in, &str)
196
+	if err != nil {
197
+		return err
198
+	}
199
+	sa, err := NewSockAddr(str)
200
+	if err != nil {
201
+		return err
202
+	}
203
+	s.SockAddr = sa
204
+	return nil
205
+}