Merge pull request #2729 from hashicorp/b-2696-lookup-ip-on-path

Update go-sockaddr to lookup ip on $PATH
This commit is contained in:
Michael Schurter 2017-06-22 14:08:26 -07:00 committed by GitHub
commit 641bb7e031
8 changed files with 396 additions and 89 deletions

View file

@ -1,63 +0,0 @@
TOOLS= golang.org/x/tools/cover
GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp
GOCOVER_FILE?= .cover.out
GOCOVERHTML?= coverage.html
test:: $(GOCOVER_FILE)
@$(MAKE) -C cmd/sockaddr test
cover:: coverage_report
$(GOCOVER_FILE)::
@find . -type d ! -path '*cmd*' ! -path '*.git*' -print0 | xargs -0 -I % sh -ec "cd % && rm -f $(GOCOVER_TMPFILE) && go test -coverprofile=$(GOCOVER_TMPFILE)"
@echo 'mode: set' > $(GOCOVER_FILE)
@find . -type f ! -path '*cmd*' ! -path '*.git*' -name "$(GOCOVER_TMPFILE)" -print0 | xargs -0 -n1 cat $(GOCOVER_TMPFILE) | grep -v '^mode: ' >> ${PWD}/$(GOCOVER_FILE)
$(GOCOVERHTML): $(GOCOVER_FILE)
go tool cover -html=$(GOCOVER_FILE) -o $(GOCOVERHTML)
coverage_report:: $(GOCOVER_FILE)
go tool cover -html=$(GOCOVER_FILE)
audit_tools::
@go get -u github.com/golang/lint/golint && echo "Installed golint:"
@go get -u github.com/fzipp/gocyclo && echo "Installed gocyclo:"
@go get -u github.com/remyoudompheng/go-misc/deadcode && echo "Installed deadcode:"
@go get -u github.com/client9/misspell/cmd/misspell && echo "Installed misspell:"
@go get -u github.com/gordonklaus/ineffassign && echo "Installed ineffassign:"
audit::
deadcode
go tool vet -all *.go
go tool vet -shadow=true *.go
golint *.go
ineffassign .
gocyclo -over 65 *.go
misspell *.go
clean::
rm -f $(GOCOVER_FILE) $(GOCOVERHTML)
dev::
@go build
@make -B -C cmd/sockaddr sockaddr
install::
@go install
@make -C cmd/sockaddr install
doc::
echo Visit: http://127.0.0.1:6060/pkg/github.com/hashicorp/go-sockaddr/
godoc -http=:6060 -goroot $GOROOT
world::
@set -e; \
for os in solaris darwin freebsd linux windows; do \
for arch in amd64; do \
printf "Building on %s-%s\n" "$${os}" "$${arch}" ; \
env GOOS="$${os}" GOARCH="$${arch}" go build -o /dev/null; \
done; \
done
make -C cmd/sockaddr world

View file

@ -24,7 +24,7 @@ For example, with this library it is possible to find an IP address that:
* is attached to a default route * is attached to a default route
([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces)) ([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces))
* is contained within a CIDR block (['IfByNetwork()'](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork)) * is contained within a CIDR block ([`IfByNetwork()`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork))
* is an RFC1918 address * is an RFC1918 address
([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC)) ([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
* is ordered * is ordered

View file

@ -1,5 +1,7 @@
package sockaddr package sockaddr
import "strings"
// ifAddrAttrMap is a map of the IfAddr type-specific attributes. // ifAddrAttrMap is a map of the IfAddr type-specific attributes.
var ifAddrAttrMap map[AttrName]func(IfAddr) string var ifAddrAttrMap map[AttrName]func(IfAddr) string
var ifAddrAttrs []AttrName var ifAddrAttrs []AttrName
@ -30,6 +32,53 @@ func GetPrivateIP() (string, error) {
return ip.NetIP().String(), nil 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 // 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 // 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. // 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 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 // 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 // of the network (i.e. IP addresses with a smaller netmask, larger network
// size, are sorted first). This function is the `eval` equivalent of: // 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 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 // IfAddrAttrs returns a list of attributes supported by the IfAddr type
func IfAddrAttrs() []AttrName { func IfAddrAttrs() []AttrName {
return ifAddrAttrs return ifAddrAttrs

View file

@ -3,6 +3,7 @@ package sockaddr
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"net" "net"
"regexp" "regexp"
"sort" "sort"
@ -10,6 +11,14 @@ import (
"strings" "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 adapter ([^\s:]+):`)
ipAddrRE *regexp.Regexp = regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
)
// IfAddrs is a slice of IfAddr // IfAddrs is a slice of IfAddr
type IfAddrs []IfAddr type IfAddrs []IfAddr
@ -91,6 +100,40 @@ func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int {
return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr) 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. // AscIfName is a sorting function to sort IfAddrs by their interface names.
func AscIfName(p1Ptr, p2Ptr *IfAddr) int { func AscIfName(p1Ptr, p2Ptr *IfAddr) int {
return strings.Compare(p1Ptr.Name, p2Ptr.Name) return strings.Compare(p1Ptr.Name, p2Ptr.Name)
@ -127,6 +170,11 @@ func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int {
return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr) 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. // DescIfName is identical to AscIfName but reverse ordered.
func DescIfName(p1Ptr, p2Ptr *IfAddr) int { func DescIfName(p1Ptr, p2Ptr *IfAddr) int {
return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name) return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name)
@ -169,7 +217,15 @@ func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIf
// IfAttr forwards the selector to IfAttr.Attr() for resolution. If there is // IfAttr forwards the selector to IfAttr.Attr() for resolution. If there is
// more than one IfAddr, only the first IfAddr is used. // 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 { if len(ifAddrs) == 0 {
return "", nil return "", nil
} }
@ -243,10 +299,10 @@ func GetDefaultInterfaces() (IfAddrs, error) {
// the `eval` equivalent of: // 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) { func GetPrivateInterfaces() (IfAddrs, error) {
privateIfs, err := GetDefaultInterfaces() privateIfs, err := GetAllInterfaces()
if err != nil { if err != nil {
return IfAddrs{}, err return IfAddrs{}, err
} }
@ -259,15 +315,21 @@ func GetPrivateInterfaces() (IfAddrs, error) {
return IfAddrs{}, nil return IfAddrs{}, nil
} }
privateIfs, _, err = IfByFlag("forwardable|up", privateIfs) privateIfs, _, err = IfByFlag("forwardable", privateIfs)
if err != nil { if err != nil {
return IfAddrs{}, err return IfAddrs{}, err
} }
privateIfs, _, err = IfByFlag("up", privateIfs)
if err != nil {
return IfAddrs{}, err
}
if len(privateIfs) == 0 { if len(privateIfs) == 0 {
return IfAddrs{}, nil return IfAddrs{}, nil
} }
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(privateIfs) OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(privateIfs)
privateIfs, _, err = IfByRFC("6890", privateIfs) privateIfs, _, err = IfByRFC("6890", privateIfs)
if err != nil { if err != nil {
@ -285,10 +347,10 @@ func GetPrivateInterfaces() (IfAddrs, error) {
// function is the `eval` equivalent of: // 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) { func GetPublicInterfaces() (IfAddrs, error) {
publicIfs, err := GetDefaultInterfaces() publicIfs, err := GetAllInterfaces()
if err != nil { if err != nil {
return IfAddrs{}, err return IfAddrs{}, err
} }
@ -301,15 +363,21 @@ func GetPublicInterfaces() (IfAddrs, error) {
return IfAddrs{}, nil return IfAddrs{}, nil
} }
publicIfs, _, err = IfByFlag("forwardable|up", publicIfs) publicIfs, _, err = IfByFlag("forwardable", publicIfs)
if err != nil { if err != nil {
return IfAddrs{}, err return IfAddrs{}, err
} }
publicIfs, _, err = IfByFlag("up", publicIfs)
if err != nil {
return IfAddrs{}, err
}
if len(publicIfs) == 0 { if len(publicIfs) == 0 {
return IfAddrs{}, nil return IfAddrs{}, nil
} }
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(publicIfs) OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(publicIfs)
_, publicIfs, err = IfByRFC("6890", publicIfs) _, publicIfs, err = IfByRFC("6890", publicIfs)
if err != nil { if err != nil {
@ -652,6 +720,171 @@ func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs,
return includedIfs, excludedIfs, nil 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)
}
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. // IncludeIfs returns an IfAddrs based on the passed in selector.
func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) { func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
var includedIfs IfAddrs var includedIfs IfAddrs
@ -736,6 +969,10 @@ func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
sortFuncs[i] = AscIfAddress sortFuncs[i] = AscIfAddress
case "-address": case "-address":
sortFuncs[i] = DescIfAddress sortFuncs[i] = DescIfAddress
case "+default", "default":
sortFuncs[i] = AscIfDefault
case "-default":
sortFuncs[i] = DescIfDefault
case "+name", "name": case "+name", "name":
// The "name" selector returns an array of IfAddrs // The "name" selector returns an array of IfAddrs
// ordered by the interface name. // ordered by the interface name.
@ -886,7 +1123,7 @@ func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
// Linux. // Linux.
func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) { func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n") lines := strings.Split(routeOut, "\n")
re := regexp.MustCompile(`[\s]+`) re := whitespaceRE.Copy()
for _, line := range lines { for _, line := range lines {
kvs := re.Split(line, -1) kvs := re.Split(line, -1)
if len(kvs) < 5 { if len(kvs) < 5 {
@ -929,7 +1166,7 @@ func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
// support added. // support added.
func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) { func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n") lines := strings.Split(routeOut, "\n")
re := regexp.MustCompile(`[\s]+`) re := whitespaceRE.Copy()
for _, line := range lines { for _, line := range lines {
kvs := re.Split(strings.TrimSpace(line), -1) kvs := re.Split(strings.TrimSpace(line), -1)
if len(kvs) < 3 { if len(kvs) < 3 {
@ -949,17 +1186,17 @@ func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
// interface name forwarding traffic to the default gateway. // interface name forwarding traffic to the default gateway.
func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) { func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n") lines := strings.Split(routeOut, "\n")
ifNameRE := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`) ifNameRe := ifNameRE.Copy()
ipAddrRE := regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`) ipAddrRe := ipAddrRE.Copy()
var ifName string var ifName string
for _, line := range lines { for _, line := range lines {
switch ifNameMatches := ifNameRE.FindStringSubmatch(line); { switch ifNameMatches := ifNameRe.FindStringSubmatch(line); {
case len(ifNameMatches) > 1: case len(ifNameMatches) > 1:
ifName = ifNameMatches[1] ifName = ifNameMatches[1]
continue continue
} }
switch ipAddrMatches := ipAddrRE.FindStringSubmatch(line); { switch ipAddrMatches := ipAddrRe.FindStringSubmatch(line); {
case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr: case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr:
return ifName, nil 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 // 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: // particular, clients with the Barracuda VPN client will see something like:
// `192.168.3.51/00ffffff` as their IP address. // `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]] 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 // ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
// blocks. // blocks.
const ForwardingBlacklist = 4294967295 const ForwardingBlacklist = 4294967295
const ForwardingBlacklistRFC = "4294967295"
// IsRFC tests to see if an SockAddr matches the specified RFC // IsRFC tests to see if an SockAddr matches the specified RFC
func IsRFC(rfcNum uint, sa SockAddr) bool { func IsRFC(rfcNum uint, sa SockAddr) bool {

View file

@ -5,10 +5,6 @@ import (
"os/exec" "os/exec"
) )
var cmds map[string][]string = map[string][]string{
"ip": {"/sbin/ip", "route"},
}
type routeInfo struct { type routeInfo struct {
cmds map[string][]string cmds map[string][]string
} }
@ -16,15 +12,22 @@ type routeInfo struct {
// NewRouteInfo returns a Linux-specific implementation of the RouteInfo // NewRouteInfo returns a Linux-specific implementation of the RouteInfo
// interface. // interface.
func NewRouteInfo() (routeInfo, error) { 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{ return routeInfo{
cmds: cmds, cmds: map[string][]string{"ip": {path, "route"}},
}, nil }, nil
} }
// GetDefaultInterfaceName returns the interface name attached to the default // GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface. // route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) { 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 { if err != nil {
return "", err return "", err
} }

6
vendor/vendor.json vendored
View file

@ -739,10 +739,10 @@
"revisionTime": "2016-05-03T14:34:40Z" "revisionTime": "2016-05-03T14:34:40Z"
}, },
{ {
"checksumSHA1": "BGODc7juQbdG3vNXHZG07kt+lKI=", "checksumSHA1": "X2tSlVe532du67MEIU1KHoCT68k=",
"path": "github.com/hashicorp/go-sockaddr", "path": "github.com/hashicorp/go-sockaddr",
"revision": "f910dd83c2052566cad78352c33af714358d1372", "revision": "e12d9401a74f025fe672cd1a84b2081c773990d3",
"revisionTime": "2017-02-08T07:30:35Z" "revisionTime": "2017-06-22T20:44:38Z"
}, },
{ {
"checksumSHA1": "lPzwetgfMBtpHqdTPolgejMctVQ=", "checksumSHA1": "lPzwetgfMBtpHqdTPolgejMctVQ=",