Pārlūkot izejas kodu

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 7 gadi atpakaļ
vecāks
revīzija
f155f828a2

+ 1 - 1
hack/dockerfile/install/proxy.installer

@@ -3,7 +3,7 @@
 # LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
 # updating the binary version, consider updating github.com/docker/libnetwork
 # in vendor.conf accordingly
-LIBNETWORK_COMMIT=19279f0492417475b6bfbd0aa529f73e8f178fb5
+LIBNETWORK_COMMIT=430c00a6a6b3dfdd774f21e1abd4ad6b0216c629
 
 install_proxy() {
 	case "$1" in

+ 2 - 2
vendor.conf

@@ -37,14 +37,14 @@ github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
 #get libnetwork packages
 
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
-github.com/docker/libnetwork 19279f0492417475b6bfbd0aa529f73e8f178fb5
+github.com/docker/libnetwork 430c00a6a6b3dfdd774f21e1abd4ad6b0216c629
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
 github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
 github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
 github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
-github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
+github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
 github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
 github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
 github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef

+ 2 - 1
vendor/github.com/docker/libnetwork/controller.go

@@ -69,6 +69,7 @@ import (
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/osl"
 	"github.com/docker/libnetwork/types"
+	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
@@ -1252,7 +1253,7 @@ func (c *controller) loadDriver(networkType string) error {
 	}
 
 	if err != nil {
-		if err == plugins.ErrNotFound {
+		if errors.Cause(err) == plugins.ErrNotFound {
 			return types.NotFoundErrorf(err.Error())
 		}
 		return err

+ 33 - 9
vendor/github.com/docker/libnetwork/ipam/allocator.go

@@ -29,7 +29,10 @@ const (
 // Allocator provides per address space ipv4/ipv6 book keeping
 type Allocator struct {
 	// Predefined pools for default address spaces
-	predefined map[string][]*net.IPNet
+	// Separate from the addrSpace because they should not be serialized
+	predefined             map[string][]*net.IPNet
+	predefinedStartIndices map[string]int
+	// The (potentially serialized) address spaces
 	addrSpaces map[string]*addrSpace
 	// stores        []datastore.Datastore
 	// Allocated addresses in each address space's subnet
@@ -47,6 +50,9 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
 		globalAddressSpace: ipamutils.PredefinedGranularNetworks,
 	}
 
+	// Initialize asIndices map
+	a.predefinedStartIndices = make(map[string]int)
+
 	// Initialize bitseq map
 	a.addresses = make(map[SubnetKey]*bitseq.Handle)
 
@@ -374,11 +380,24 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
 func (a *Allocator) getPredefineds(as string) []*net.IPNet {
 	a.Lock()
 	defer a.Unlock()
-	l := make([]*net.IPNet, 0, len(a.predefined[as]))
-	for _, pool := range a.predefined[as] {
-		l = append(l, pool)
+
+	p := a.predefined[as]
+	i := a.predefinedStartIndices[as]
+	// defensive in case the list changed since last update
+	if i >= len(p) {
+		i = 0
 	}
-	return l
+	return append(p[i:], p[:i]...)
+}
+
+func (a *Allocator) updateStartIndex(as string, amt int) {
+	a.Lock()
+	i := a.predefinedStartIndices[as] + amt
+	if i < 0 || i >= len(a.predefined[as]) {
+		i = 0
+	}
+	a.predefinedStartIndices[as] = i
+	a.Unlock()
 }
 
 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)
 		return nil, err
 	}
 
-	for _, nw := range a.getPredefineds(as) {
+	predefined := a.getPredefineds(as)
+
+	aSpace.Lock()
+	for i, nw := range predefined {
 		if v != getAddressVersion(nw.IP) {
 			continue
 		}
-		aSpace.Lock()
+		// Checks whether pool has already been allocated
 		if _, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]; ok {
-			aSpace.Unlock()
 			continue
 		}
+		// Shouldn't be necessary, but check prevents IP collisions should
+		// predefined pools overlap for any reason.
 		if !aSpace.contains(as, nw) {
 			aSpace.Unlock()
+			a.updateStartIndex(as, i+1)
 			return nw, nil
 		}
-		aSpace.Unlock()
 	}
+	aSpace.Unlock()
 
 	return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
 }

+ 4 - 3
vendor/github.com/docker/libnetwork/ipam/structures.go

@@ -262,12 +262,13 @@ func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *Addr
 	defer aSpace.Unlock()
 
 	// Check if already allocated
-	if p, ok := aSpace.subnets[k]; ok {
+	if _, ok := aSpace.subnets[k]; ok {
 		if pdf {
 			return nil, types.InternalMaskableErrorf("predefined pool %s is already reserved", nw)
 		}
-		aSpace.incRefCount(p, 1)
-		return func() error { return nil }, nil
+		// This means the same pool is already allocated. updatePoolDBOnAdd is called when there
+		// is request for a pool/subpool. It should ensure there is no overlap with existing pools
+		return nil, ipamapi.ErrPoolOverlap
 	}
 
 	// If master pool, check for overlap

+ 9 - 8
vendor/github.com/docker/libnetwork/network.go

@@ -1156,26 +1156,27 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi
 			ep.releaseAddress()
 		}
 	}()
-	// Moving updateToSTore before calling addEndpoint so that we shall clean up VETH interfaces in case
-	// DockerD get killed between addEndpoint and updateSTore call
-	if err = n.getController().updateToStore(ep); err != nil {
+
+	if err = n.addEndpoint(ep); err != nil {
 		return nil, err
 	}
 	defer func() {
 		if err != nil {
-			if e := n.getController().deleteFromStore(ep); e != nil {
-				logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
+			if e := ep.deleteEndpoint(false); e != nil {
+				logrus.Warnf("cleaning up endpoint failed %s : %v", name, e)
 			}
 		}
 	}()
 
-	if err = n.addEndpoint(ep); err != nil {
+	// We should perform updateToStore call right after addEndpoint
+	// in order to have iface properly configured
+	if err = n.getController().updateToStore(ep); err != nil {
 		return nil, err
 	}
 	defer func() {
 		if err != nil {
-			if e := ep.deleteEndpoint(false); e != nil {
-				logrus.Warnf("cleaning up endpoint failed %s : %v", name, e)
+			if e := n.getController().deleteFromStore(ep); e != nil {
+				logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
 			}
 		}
 	}()

+ 1 - 1
vendor/github.com/docker/libnetwork/networkdb/delegate.go

@@ -41,7 +41,7 @@ func (nDB *NetworkDB) handleNodeEvent(nEvent *NodeEvent) bool {
 	// If the node is not known from memberlist we cannot process save any state of it else if it actually
 	// dies we won't receive any notification and we will remain stuck with it
 	if _, ok := nDB.nodes[nEvent.NodeName]; !ok {
-		logrus.Error("node: %s is unknown to memberlist", nEvent.NodeName)
+		logrus.Errorf("node: %s is unknown to memberlist", nEvent.NodeName)
 		return false
 	}
 

+ 14 - 11
vendor/github.com/docker/libnetwork/types/types.go

@@ -145,7 +145,12 @@ func (p *PortBinding) String() string {
 	return ret
 }
 
-// FromString reads the PortBinding structure from string
+// FromString reads the PortBinding structure from string s.
+// String s is a triple of "protocol/containerIP:port/hostIP:port"
+// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form.
+// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported.
+// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString
+// returns an error.
 func (p *PortBinding) FromString(s string) error {
 	ps := strings.Split(s, "/")
 	if len(ps) != 3 {
@@ -167,21 +172,19 @@ func (p *PortBinding) FromString(s string) error {
 }
 
 func parseIPPort(s string) (net.IP, uint16, error) {
-	pp := strings.Split(s, ":")
-	if len(pp) != 2 {
-		return nil, 0, BadRequestErrorf("invalid format: %s", s)
+	hoststr, portstr, err := net.SplitHostPort(s)
+	if err != nil {
+		return nil, 0, err
 	}
 
-	var ip net.IP
-	if pp[0] != "" {
-		if ip = net.ParseIP(pp[0]); ip == nil {
-			return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
-		}
+	ip := net.ParseIP(hoststr)
+	if ip == nil {
+		return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
 	}
 
-	port, err := strconv.ParseUint(pp[1], 10, 16)
+	port, err := strconv.ParseUint(portstr, 10, 16)
 	if err != nil {
-		return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
+		return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
 	}
 
 	return ip, uint16(port), nil

+ 31 - 20
vendor/github.com/docker/libnetwork/vendor.conf

@@ -1,27 +1,28 @@
 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
-github.com/Microsoft/go-winio v0.4.5
-github.com/Microsoft/hcsshim v0.6.5
+github.com/Microsoft/go-winio v0.4.7
+github.com/Microsoft/hcsshim v0.6.11
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
 github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904
-github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
-github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
+github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
+github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
 github.com/coreos/etcd v3.2.1
 github.com/coreos/go-semver v0.2.0
-github.com/coreos/go-systemd v4
+github.com/coreos/go-systemd v17
+github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
 github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
 
-github.com/docker/docker a3efe9722f34af5cf4443fe3a5c4e4e3e0457b54
-github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
+github.com/docker/docker 162ba6016def672690ee4a1f3978368853a1e149
+github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
 github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
 github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
 
 github.com/godbus/dbus v4.0.0
-github.com/gogo/protobuf v0.4
-github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
+github.com/gogo/protobuf v1.0.0
+github.com/golang/protobuf v1.1.0
 github.com/gorilla/context v1.1
 github.com/gorilla/mux v1.1
 github.com/hashicorp/consul v0.5.2
@@ -29,27 +30,37 @@ github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
 github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
 github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
 github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
-github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
+github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
 github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
 github.com/mattn/go-shellwords v1.0.3
-github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
+github.com/miekg/dns v1.0.7
 github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
-github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
-github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
-github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
-github.com/opencontainers/runtime-spec v1.0.0
-github.com/opencontainers/selinux v1.0.0-rc1
+github.com/opencontainers/go-digest v1.0.0-rc1
+github.com/opencontainers/image-spec v1.0.1
+github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
+github.com/opencontainers/runtime-spec v1.0.1
+github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
 github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
 github.com/sirupsen/logrus v1.0.3
 github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a
-github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
+github.com/syndtr/gocapability 33e07d32887e1e06b7c025f27ce52f62c7990bc0
 github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
 github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e
 github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
-golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
-golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
-golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
+golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491
+golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd
+golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd
 golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
 github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
 github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
+
+github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
+github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
+github.com/cyphar/filepath-securejoin v0.2.1
+github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
+github.com/hashicorp/go-immutable-radix 7f3cd4390caab3250a57f30efdb2a65dd7649ecf
+github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
+github.com/hashicorp/go-cleanhttp d5fe4b57a186c716b0e00b8c301cbd9b4182694d
+github.com/hashicorp/go-rootcerts 6bb64b370b90e7ef1fa532be9e591a81c3493e00
+github.com/mitchellh/go-homedir 3864e76763d94a6df2f9960b16a20a33da9f9a66

+ 128 - 0
vendor/github.com/hashicorp/go-sockaddr/ifaddr.go

@@ -1,5 +1,7 @@
 package sockaddr
 
+import "strings"
+
 // ifAddrAttrMap is a map of the IfAddr type-specific attributes.
 var ifAddrAttrMap map[AttrName]func(IfAddr) string
 var ifAddrAttrs []AttrName
@@ -30,6 +32,53 @@ func GetPrivateIP() (string, error) {
 	return ip.NetIP().String(), nil
 }
 
+// GetPrivateIPs returns a string with all IP addresses that are part of RFC
+// 6890 (regardless of whether or not there is a default route, unlike
+// GetPublicIP).  If the system can't find any RFC 6890 IP addresses, an empty
+// string will be returned instead.  This function is the `eval` equivalent of:
+//
+// ```
+// $ sockaddr eval -r '{{GetAllInterfaces | include "RFC" "6890" | join "address" " "}}'
+/// ```
+func GetPrivateIPs() (string, error) {
+	ifAddrs, err := GetAllInterfaces()
+	if err != nil {
+		return "", err
+	} else if len(ifAddrs) < 1 {
+		return "", nil
+	}
+
+	ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
+	if len(ifAddrs) == 0 {
+		return "", nil
+	}
+
+	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
+
+	ifAddrs, _, err = IfByRFC("6890", ifAddrs)
+	if err != nil {
+		return "", err
+	} else if len(ifAddrs) == 0 {
+		return "", nil
+	}
+
+	_, ifAddrs, err = IfByRFC(ForwardingBlacklistRFC, ifAddrs)
+	if err != nil {
+		return "", err
+	} else if len(ifAddrs) == 0 {
+		return "", nil
+	}
+
+	ips := make([]string, 0, len(ifAddrs))
+	for _, ifAddr := range ifAddrs {
+		ip := *ToIPAddr(ifAddr.SockAddr)
+		s := ip.NetIP().String()
+		ips = append(ips, s)
+	}
+
+	return strings.Join(ips, " "), nil
+}
+
 // GetPublicIP returns a string with a single IP address that is NOT part of RFC
 // 6890 and has a default route.  If the system can't determine its IP address
 // or find a non RFC 6890 IP address, an empty string will be returned instead.
@@ -51,6 +100,47 @@ func GetPublicIP() (string, error) {
 	return ip.NetIP().String(), nil
 }
 
+// GetPublicIPs returns a string with all IP addresses that are NOT part of RFC
+// 6890 (regardless of whether or not there is a default route, unlike
+// GetPublicIP).  If the system can't find any non RFC 6890 IP addresses, an
+// empty string will be returned instead.  This function is the `eval`
+// equivalent of:
+//
+// ```
+// $ sockaddr eval -r '{{GetAllInterfaces | exclude "RFC" "6890" | join "address" " "}}'
+/// ```
+func GetPublicIPs() (string, error) {
+	ifAddrs, err := GetAllInterfaces()
+	if err != nil {
+		return "", err
+	} else if len(ifAddrs) < 1 {
+		return "", nil
+	}
+
+	ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
+	if len(ifAddrs) == 0 {
+		return "", nil
+	}
+
+	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
+
+	_, ifAddrs, err = IfByRFC("6890", ifAddrs)
+	if err != nil {
+		return "", err
+	} else if len(ifAddrs) == 0 {
+		return "", nil
+	}
+
+	ips := make([]string, 0, len(ifAddrs))
+	for _, ifAddr := range ifAddrs {
+		ip := *ToIPAddr(ifAddr.SockAddr)
+		s := ip.NetIP().String()
+		ips = append(ips, s)
+	}
+
+	return strings.Join(ips, " "), nil
+}
+
 // GetInterfaceIP returns a string with a single IP address sorted by the size
 // of the network (i.e. IP addresses with a smaller netmask, larger network
 // size, are sorted first).  This function is the `eval` equivalent of:
@@ -91,6 +181,44 @@ func GetInterfaceIP(namedIfRE string) (string, error) {
 	return IPAddrAttr(*ip, "address"), nil
 }
 
+// GetInterfaceIPs returns a string with all IPs, sorted by the size of the
+// network (i.e. IP addresses with a smaller netmask, larger network size, are
+// sorted first), on a named interface.  This function is the `eval` equivalent
+// of:
+//
+// ```
+// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | join "address" " "}}'
+/// ```
+func GetInterfaceIPs(namedIfRE string) (string, error) {
+	ifAddrs, err := GetAllInterfaces()
+	if err != nil {
+		return "", err
+	}
+
+	ifAddrs, _, err = IfByName(namedIfRE, ifAddrs)
+	if err != nil {
+		return "", err
+	}
+
+	ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
+	if err != nil {
+		return "", err
+	}
+
+	if len(ifAddrs) == 0 {
+		return "", err
+	}
+
+	ips := make([]string, 0, len(ifAddrs))
+	for _, ifAddr := range ifAddrs {
+		ip := *ToIPAddr(ifAddr.SockAddr)
+		s := ip.NetIP().String()
+		ips = append(ips, s)
+	}
+
+	return strings.Join(ips, " "), nil
+}
+
 // IfAddrAttrs returns a list of attributes supported by the IfAddr type
 func IfAddrAttrs() []AttrName {
 	return ifAddrAttrs

+ 327 - 15
vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go

@@ -1,8 +1,10 @@
 package sockaddr
 
 import (
+	"encoding/binary"
 	"errors"
 	"fmt"
+	"math/big"
 	"net"
 	"regexp"
 	"sort"
@@ -10,6 +12,14 @@ import (
 	"strings"
 )
 
+var (
+	// Centralize all regexps and regexp.Copy() where necessary.
+	signRE       *regexp.Regexp = regexp.MustCompile(`^[\s]*[+-]`)
+	whitespaceRE *regexp.Regexp = regexp.MustCompile(`[\s]+`)
+	ifNameRE     *regexp.Regexp = regexp.MustCompile(`^(?:Ethernet|Wireless LAN) adapter ([^:]+):`)
+	ipAddrRE     *regexp.Regexp = regexp.MustCompile(`^   IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
+)
+
 // IfAddrs is a slice of IfAddr
 type IfAddrs []IfAddr
 
@@ -91,6 +101,40 @@ func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int {
 	return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
 }
 
+// AscIfDefault is a sorting function to sort IfAddrs by whether or not they
+// have a default route or not.  Non-equal types are deferred in the sort.
+//
+// FIXME: This is a particularly expensive sorting operation because of the
+// non-memoized calls to NewRouteInfo().  In an ideal world the routeInfo data
+// once at the start of the sort and pass it along as a context or by wrapping
+// the IfAddr type with this information (this would also solve the inability to
+// return errors and the possibility of failing silently).  Fortunately,
+// N*log(N) where N = 3 is only ~6.2 invocations.  Not ideal, but not worth
+// optimizing today.  The common case is this gets called once or twice.
+// Patches welcome.
+func AscIfDefault(p1Ptr, p2Ptr *IfAddr) int {
+	ri, err := NewRouteInfo()
+	if err != nil {
+		return sortDeferDecision
+	}
+
+	defaultIfName, err := ri.GetDefaultInterfaceName()
+	if err != nil {
+		return sortDeferDecision
+	}
+
+	switch {
+	case p1Ptr.Interface.Name == defaultIfName && p2Ptr.Interface.Name == defaultIfName:
+		return sortDeferDecision
+	case p1Ptr.Interface.Name == defaultIfName:
+		return sortReceiverBeforeArg
+	case p2Ptr.Interface.Name == defaultIfName:
+		return sortArgBeforeReceiver
+	default:
+		return sortDeferDecision
+	}
+}
+
 // AscIfName is a sorting function to sort IfAddrs by their interface names.
 func AscIfName(p1Ptr, p2Ptr *IfAddr) int {
 	return strings.Compare(p1Ptr.Name, p2Ptr.Name)
@@ -127,6 +171,11 @@ func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int {
 	return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
 }
 
+// DescIfDefault is identical to AscIfDefault but reverse ordered.
+func DescIfDefault(p1Ptr, p2Ptr *IfAddr) int {
+	return -1 * AscIfDefault(p1Ptr, p2Ptr)
+}
+
 // DescIfName is identical to AscIfName but reverse ordered.
 func DescIfName(p1Ptr, p2Ptr *IfAddr) int {
 	return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name)
@@ -169,7 +218,15 @@ func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIf
 
 // IfAttr forwards the selector to IfAttr.Attr() for resolution.  If there is
 // more than one IfAddr, only the first IfAddr is used.
-func IfAttr(selectorName string, ifAddrs IfAddrs) (string, error) {
+func IfAttr(selectorName string, ifAddr IfAddr) (string, error) {
+	attrName := AttrName(strings.ToLower(selectorName))
+	attrVal, err := ifAddr.Attr(attrName)
+	return attrVal, err
+}
+
+// IfAttrs forwards the selector to IfAttrs.Attr() for resolution.  If there is
+// more than one IfAddr, only the first IfAddr is used.
+func IfAttrs(selectorName string, ifAddrs IfAddrs) (string, error) {
 	if len(ifAddrs) == 0 {
 		return "", nil
 	}
@@ -243,10 +300,10 @@ func GetDefaultInterfaces() (IfAddrs, error) {
 // the `eval` equivalent of:
 //
 // ```
-// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}'
+// $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | include "RFC" "6890" }}'
 /// ```
 func GetPrivateInterfaces() (IfAddrs, error) {
-	privateIfs, err := GetDefaultInterfaces()
+	privateIfs, err := GetAllInterfaces()
 	if err != nil {
 		return IfAddrs{}, err
 	}
@@ -259,15 +316,21 @@ func GetPrivateInterfaces() (IfAddrs, error) {
 		return IfAddrs{}, nil
 	}
 
-	privateIfs, _, err = IfByFlag("forwardable|up", privateIfs)
+	privateIfs, _, err = IfByFlag("forwardable", privateIfs)
+	if err != nil {
+		return IfAddrs{}, err
+	}
+
+	privateIfs, _, err = IfByFlag("up", privateIfs)
 	if err != nil {
 		return IfAddrs{}, err
 	}
+
 	if len(privateIfs) == 0 {
 		return IfAddrs{}, nil
 	}
 
-	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(privateIfs)
+	OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(privateIfs)
 
 	privateIfs, _, err = IfByRFC("6890", privateIfs)
 	if err != nil {
@@ -285,10 +348,10 @@ func GetPrivateInterfaces() (IfAddrs, error) {
 // function is the `eval` equivalent of:
 //
 // ```
-// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}'
+// $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | exclude "RFC" "6890" }}'
 /// ```
 func GetPublicInterfaces() (IfAddrs, error) {
-	publicIfs, err := GetDefaultInterfaces()
+	publicIfs, err := GetAllInterfaces()
 	if err != nil {
 		return IfAddrs{}, err
 	}
@@ -301,15 +364,21 @@ func GetPublicInterfaces() (IfAddrs, error) {
 		return IfAddrs{}, nil
 	}
 
-	publicIfs, _, err = IfByFlag("forwardable|up", publicIfs)
+	publicIfs, _, err = IfByFlag("forwardable", publicIfs)
 	if err != nil {
 		return IfAddrs{}, err
 	}
+
+	publicIfs, _, err = IfByFlag("up", publicIfs)
+	if err != nil {
+		return IfAddrs{}, err
+	}
+
 	if len(publicIfs) == 0 {
 		return IfAddrs{}, nil
 	}
 
-	OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(publicIfs)
+	OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(publicIfs)
 
 	_, publicIfs, err = IfByRFC("6890", publicIfs)
 	if err != nil {
@@ -652,6 +721,245 @@ func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs,
 	return includedIfs, excludedIfs, nil
 }
 
+// IfAddrMath will return a new IfAddr struct with a mutated value.
+func IfAddrMath(operation, value string, inputIfAddr IfAddr) (IfAddr, error) {
+	// Regexp used to enforce the sign being a required part of the grammar for
+	// some values.
+	signRe := signRE.Copy()
+
+	switch strings.ToLower(operation) {
+	case "address":
+		// "address" operates on the IP address and is allowed to overflow or
+		// underflow networks, however it will wrap along the underlying address's
+		// underlying type.
+
+		if !signRe.MatchString(value) {
+			return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation)
+		}
+
+		switch sockType := inputIfAddr.SockAddr.Type(); sockType {
+		case TypeIPv4:
+			// 33 == Accept any uint32 value
+			// TODO(seanc@): Add the ability to parse hex
+			i, err := strconv.ParseInt(value, 10, 33)
+			if err != nil {
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
+			}
+
+			ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
+			ipv4Uint32 := uint32(ipv4.Address)
+			ipv4Uint32 += uint32(i)
+			return IfAddr{
+				SockAddr: IPv4Addr{
+					Address: IPv4Address(ipv4Uint32),
+					Mask:    ipv4.Mask,
+				},
+				Interface: inputIfAddr.Interface,
+			}, nil
+		case TypeIPv6:
+			// 64 == Accept any int32 value
+			// TODO(seanc@): Add the ability to parse hex.  Also parse a bignum int.
+			i, err := strconv.ParseInt(value, 10, 64)
+			if err != nil {
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
+			}
+
+			ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
+			ipv6BigIntA := new(big.Int)
+			ipv6BigIntA.Set(ipv6.Address)
+			ipv6BigIntB := big.NewInt(i)
+
+			ipv6Addr := ipv6BigIntA.Add(ipv6BigIntA, ipv6BigIntB)
+			ipv6Addr.And(ipv6Addr, ipv6HostMask)
+
+			return IfAddr{
+				SockAddr: IPv6Addr{
+					Address: IPv6Address(ipv6Addr),
+					Mask:    ipv6.Mask,
+				},
+				Interface: inputIfAddr.Interface,
+			}, nil
+		default:
+			return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
+		}
+	case "network":
+		// "network" operates on the network address.  Positive values start at the
+		// network address and negative values wrap at the network address, which
+		// means a "-1" value on a network will be the broadcast address after
+		// wrapping is applied.
+
+		if !signRe.MatchString(value) {
+			return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation)
+		}
+
+		switch sockType := inputIfAddr.SockAddr.Type(); sockType {
+		case TypeIPv4:
+			// 33 == Accept any uint32 value
+			// TODO(seanc@): Add the ability to parse hex
+			i, err := strconv.ParseInt(value, 10, 33)
+			if err != nil {
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
+			}
+
+			ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
+			ipv4Uint32 := uint32(ipv4.NetworkAddress())
+
+			// Wrap along network mask boundaries.  EZ-mode wrapping made possible by
+			// use of int64 vs a uint.
+			var wrappedMask int64
+			if i >= 0 {
+				wrappedMask = i
+			} else {
+				wrappedMask = 1 + i + int64(^uint32(ipv4.Mask))
+			}
+
+			ipv4Uint32 = ipv4Uint32 + (uint32(wrappedMask) &^ uint32(ipv4.Mask))
+
+			return IfAddr{
+				SockAddr: IPv4Addr{
+					Address: IPv4Address(ipv4Uint32),
+					Mask:    ipv4.Mask,
+				},
+				Interface: inputIfAddr.Interface,
+			}, nil
+		case TypeIPv6:
+			// 64 == Accept any int32 value
+			// TODO(seanc@): Add the ability to parse hex.  Also parse a bignum int.
+			i, err := strconv.ParseInt(value, 10, 64)
+			if err != nil {
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
+			}
+
+			ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
+			ipv6BigInt := new(big.Int)
+			ipv6BigInt.Set(ipv6.NetworkAddress())
+
+			mask := new(big.Int)
+			mask.Set(ipv6.Mask)
+			if i > 0 {
+				wrappedMask := new(big.Int)
+				wrappedMask.SetInt64(i)
+
+				wrappedMask.AndNot(wrappedMask, mask)
+				ipv6BigInt.Add(ipv6BigInt, wrappedMask)
+			} else {
+				// Mask off any bits that exceed the network size.  Subtract the
+				// wrappedMask from the last usable - 1
+				wrappedMask := new(big.Int)
+				wrappedMask.SetInt64(-1 * i)
+				wrappedMask.Sub(wrappedMask, big.NewInt(1))
+
+				wrappedMask.AndNot(wrappedMask, mask)
+
+				lastUsable := new(big.Int)
+				lastUsable.Set(ipv6.LastUsable().(IPv6Addr).Address)
+
+				ipv6BigInt = lastUsable.Sub(lastUsable, wrappedMask)
+			}
+
+			return IfAddr{
+				SockAddr: IPv6Addr{
+					Address: IPv6Address(ipv6BigInt),
+					Mask:    ipv6.Mask,
+				},
+				Interface: inputIfAddr.Interface,
+			}, nil
+		default:
+			return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
+		}
+	case "mask":
+		// "mask" operates on the IP address and returns the IP address on
+		// which the given integer mask has been applied. If the applied mask
+		// corresponds to a larger network than the mask of the IP address,
+		// the latter will be replaced by the former.
+		switch sockType := inputIfAddr.SockAddr.Type(); sockType {
+		case TypeIPv4:
+			i, err := strconv.ParseUint(value, 10, 32)
+			if err != nil {
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
+			}
+
+			if i > 32 {
+				return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv4 addresses must be between 0 and 32", operation)
+			}
+
+			ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
+
+			ipv4Mask := net.CIDRMask(int(i), 32)
+			ipv4MaskUint32 := binary.BigEndian.Uint32(ipv4Mask)
+
+			maskedIpv4 := ipv4.NetIP().Mask(ipv4Mask)
+			maskedIpv4Uint32 := binary.BigEndian.Uint32(maskedIpv4)
+
+			maskedIpv4MaskUint32 := uint32(ipv4.Mask)
+
+			if ipv4MaskUint32 < maskedIpv4MaskUint32 {
+				maskedIpv4MaskUint32 = ipv4MaskUint32
+			}
+
+			return IfAddr{
+				SockAddr: IPv4Addr{
+					Address: IPv4Address(maskedIpv4Uint32),
+					Mask:    IPv4Mask(maskedIpv4MaskUint32),
+				},
+				Interface: inputIfAddr.Interface,
+			}, nil
+		case TypeIPv6:
+			i, err := strconv.ParseUint(value, 10, 32)
+			if err != nil {
+				return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
+			}
+
+			if i > 128 {
+				return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv6 addresses must be between 0 and 64", operation)
+			}
+
+			ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
+
+			ipv6Mask := net.CIDRMask(int(i), 128)
+			ipv6MaskBigInt := new(big.Int)
+			ipv6MaskBigInt.SetBytes(ipv6Mask)
+
+			maskedIpv6 := ipv6.NetIP().Mask(ipv6Mask)
+			maskedIpv6BigInt := new(big.Int)
+			maskedIpv6BigInt.SetBytes(maskedIpv6)
+
+			maskedIpv6MaskBigInt := new(big.Int)
+			maskedIpv6MaskBigInt.Set(ipv6.Mask)
+
+			if ipv6MaskBigInt.Cmp(maskedIpv6MaskBigInt) == -1 {
+				maskedIpv6MaskBigInt = ipv6MaskBigInt
+			}
+
+			return IfAddr{
+				SockAddr: IPv6Addr{
+					Address: IPv6Address(maskedIpv6BigInt),
+					Mask:    IPv6Mask(maskedIpv6MaskBigInt),
+				},
+				Interface: inputIfAddr.Interface,
+			}, nil
+		default:
+			return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
+		}
+	default:
+		return IfAddr{}, fmt.Errorf("unsupported math operation: %q", operation)
+	}
+}
+
+// IfAddrsMath will apply an IfAddrMath operation each IfAddr struct.  Any
+// failure will result in zero results.
+func IfAddrsMath(operation, value string, inputIfAddrs IfAddrs) (IfAddrs, error) {
+	outputAddrs := make(IfAddrs, 0, len(inputIfAddrs))
+	for _, ifAddr := range inputIfAddrs {
+		result, err := IfAddrMath(operation, value, ifAddr)
+		if err != nil {
+			return IfAddrs{}, fmt.Errorf("unable to perform an IPMath operation on %s: %v", ifAddr, err)
+		}
+		outputAddrs = append(outputAddrs, result)
+	}
+	return outputAddrs, nil
+}
+
 // IncludeIfs returns an IfAddrs based on the passed in selector.
 func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
 	var includedIfs IfAddrs
@@ -736,6 +1044,10 @@ func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
 			sortFuncs[i] = AscIfAddress
 		case "-address":
 			sortFuncs[i] = DescIfAddress
+		case "+default", "default":
+			sortFuncs[i] = AscIfDefault
+		case "-default":
+			sortFuncs[i] = DescIfDefault
 		case "+name", "name":
 			// The "name" selector returns an array of IfAddrs
 			// ordered by the interface name.
@@ -886,7 +1198,7 @@ func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
 // Linux.
 func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
 	lines := strings.Split(routeOut, "\n")
-	re := regexp.MustCompile(`[\s]+`)
+	re := whitespaceRE.Copy()
 	for _, line := range lines {
 		kvs := re.Split(line, -1)
 		if len(kvs) < 5 {
@@ -929,7 +1241,7 @@ func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
 // support added.
 func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
 	lines := strings.Split(routeOut, "\n")
-	re := regexp.MustCompile(`[\s]+`)
+	re := whitespaceRE.Copy()
 	for _, line := range lines {
 		kvs := re.Split(strings.TrimSpace(line), -1)
 		if len(kvs) < 3 {
@@ -949,17 +1261,17 @@ func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
 // interface name forwarding traffic to the default gateway.
 func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
 	lines := strings.Split(routeOut, "\n")
-	ifNameRE := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`)
-	ipAddrRE := regexp.MustCompile(`^   IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
+	ifNameRe := ifNameRE.Copy()
+	ipAddrRe := ipAddrRE.Copy()
 	var ifName string
 	for _, line := range lines {
-		switch ifNameMatches := ifNameRE.FindStringSubmatch(line); {
+		switch ifNameMatches := ifNameRe.FindStringSubmatch(line); {
 		case len(ifNameMatches) > 1:
 			ifName = ifNameMatches[1]
 			continue
 		}
 
-		switch ipAddrMatches := ipAddrRE.FindStringSubmatch(line); {
+		switch ipAddrMatches := ipAddrRe.FindStringSubmatch(line); {
 		case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr:
 			return ifName, nil
 		}

+ 2 - 1
vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go

@@ -58,7 +58,8 @@ func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) {
 	// Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go.  In
 	// particular, clients with the Barracuda VPN client will see something like:
 	// `192.168.3.51/00ffffff` as their IP address.
-	if match := trailingHexNetmaskRE.FindStringIndex(ipv4Str); match != nil {
+	trailingHexNetmaskRe := trailingHexNetmaskRE.Copy()
+	if match := trailingHexNetmaskRe.FindStringIndex(ipv4Str); match != nil {
 		ipv4Str = ipv4Str[:match[0]]
 	}
 

+ 1 - 0
vendor/github.com/hashicorp/go-sockaddr/rfc.go

@@ -3,6 +3,7 @@ package sockaddr
 // ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
 // blocks.
 const ForwardingBlacklist = 4294967295
+const ForwardingBlacklistRFC = "4294967295"
 
 // IsRFC tests to see if an SockAddr matches the specified RFC
 func IsRFC(rfcNum uint, sa SockAddr) bool {

+ 9 - 6
vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go

@@ -5,10 +5,6 @@ import (
 	"os/exec"
 )
 
-var cmds map[string][]string = map[string][]string{
-	"ip": {"/sbin/ip", "route"},
-}
-
 type routeInfo struct {
 	cmds map[string][]string
 }
@@ -16,15 +12,22 @@ type routeInfo struct {
 // NewRouteInfo returns a Linux-specific implementation of the RouteInfo
 // interface.
 func NewRouteInfo() (routeInfo, error) {
+	// CoreOS Container Linux moved ip to /usr/bin/ip, so look it up on
+	// $PATH and fallback to /sbin/ip on error.
+	path, _ := exec.LookPath("ip")
+	if path == "" {
+		path = "/sbin/ip"
+	}
+
 	return routeInfo{
-		cmds: cmds,
+		cmds: map[string][]string{"ip": {path, "route"}},
 	}, nil
 }
 
 // GetDefaultInterfaceName returns the interface name attached to the default
 // route on the default interface.
 func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
-	out, err := exec.Command(cmds["ip"][0], cmds["ip"][1:]...).Output()
+	out, err := exec.Command(ri.cmds["ip"][0], ri.cmds["ip"][1:]...).Output()
 	if err != nil {
 		return "", err
 	}

+ 28 - 0
vendor/github.com/hashicorp/go-sockaddr/sockaddr.go

@@ -1,6 +1,7 @@
 package sockaddr
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 )
@@ -176,3 +177,30 @@ func sockAddrInit() {
 func SockAddrAttrs() []AttrName {
 	return sockAddrAttrs
 }
+
+// Although this is pretty trivial to do in a program, having the logic here is
+// useful all around. Note that this marshals into a *string* -- the underlying
+// string representation of the sockaddr. If you then unmarshal into this type
+// in Go, all will work as expected, but externally you can take what comes out
+// and use the string value directly.
+type SockAddrMarshaler struct {
+	SockAddr
+}
+
+func (s *SockAddrMarshaler) MarshalJSON() ([]byte, error) {
+	return json.Marshal(s.SockAddr.String())
+}
+
+func (s *SockAddrMarshaler) UnmarshalJSON(in []byte) error {
+	var str string
+	err := json.Unmarshal(in, &str)
+	if err != nil {
+		return err
+	}
+	sa, err := NewSockAddr(str)
+	if err != nil {
+		return err
+	}
+	s.SockAddr = sa
+	return nil
+}