Merge pull request #37371 from ctelfer/bump-libnw-430c00a

bump libnetwork to 430c00a
This commit is contained in:
Sebastiaan van Stijn 2018-07-03 19:09:41 +02:00 committed by GitHub
commit dca4cab55d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 606 additions and 92 deletions

View File

@ -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

View File

@ -610,17 +610,17 @@ func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *check.C) {
// test network with multiple subnets
// bridge network doesn't support multiple subnets. hence, use a dummy driver that supports
dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.168.0.0/16", "--subnet=192.170.0.0/16", "test6")
dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.170.0.0/16", "--subnet=192.171.0.0/16", "test6")
assertNwIsAvailable(c, "test6")
// test network with multiple subnets with valid ipam combinations
// also check same subnet across networks when the driver supports it.
dockerCmd(c, "network", "create", "-d", dummyNetworkDriver,
"--subnet=192.168.0.0/16", "--subnet=192.170.0.0/16",
"--gateway=192.168.0.100", "--gateway=192.170.0.100",
"--ip-range=192.168.1.0/24",
"--aux-address", "a=192.168.1.5", "--aux-address", "b=192.168.1.6",
"--aux-address", "c=192.170.1.5", "--aux-address", "d=192.170.1.6",
"--subnet=192.172.0.0/16", "--subnet=192.173.0.0/16",
"--gateway=192.172.0.100", "--gateway=192.173.0.100",
"--ip-range=192.172.1.0/24",
"--aux-address", "a=192.172.1.5", "--aux-address", "b=192.172.1.6",
"--aux-address", "c=192.173.1.5", "--aux-address", "d=192.173.1.6",
"test7")
assertNwIsAvailable(c, "test7")

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -1156,18 +1156,6 @@ 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 {
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 err = n.addEndpoint(ep); err != nil {
return nil, err
@ -1180,6 +1168,19 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi
}
}()
// 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 := n.getController().deleteFromStore(ep); e != nil {
logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
}
}
}()
if err = ep.assignAddress(ipam, false, n.enableIPv6 && n.postIPv6); err != nil {
return nil, err
}

View File

@ -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
}

View File

@ -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)
}
var ip net.IP
if pp[0] != "" {
if ip = net.ParseIP(pp[0]); ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
}
}
port, err := strconv.ParseUint(pp[1], 10, 16)
hoststr, portstr, err := net.SplitHostPort(s)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
return nil, 0, err
}
ip := net.ParseIP(hoststr)
if ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
}
port, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
}
return ip, uint16(port), nil

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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]]
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}