Update hashicorp/go-sockaddr to the latest version.

* Adds plural IP helpers (e.g. `GetPrivateIPs`, `GetPublicIPs`)
  hashicorp/go-sockaddr#11
* Adds subnet math hashicorp/go-sockaddr#8
* Fixes helper functions for dual-homed hosts hashicorp/go-sockaddr#10)
This commit is contained in:
Sean Chittenden 2017-05-23 16:40:20 -07:00
parent 39aeb5e520
commit 6037c3c016
No known key found for this signature in database
GPG key ID: 4EBC9DC16C2E5E16
11 changed files with 741 additions and 31 deletions

View file

@ -2,6 +2,8 @@ TOOLS= golang.org/x/tools/cover
GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp
GOCOVER_FILE?= .cover.out GOCOVER_FILE?= .cover.out
GOCOVERHTML?= coverage.html GOCOVERHTML?= coverage.html
FIND=`/usr/bin/which 2> /dev/null gfind find | /usr/bin/grep -v ^no | /usr/bin/head -n 1`
XARGS=`/usr/bin/which 2> /dev/null gxargs xargs | /usr/bin/grep -v ^no | /usr/bin/head -n 1`
test:: $(GOCOVER_FILE) test:: $(GOCOVER_FILE)
@$(MAKE) -C cmd/sockaddr test @$(MAKE) -C cmd/sockaddr test
@ -9,10 +11,10 @@ test:: $(GOCOVER_FILE)
cover:: coverage_report cover:: coverage_report
$(GOCOVER_FILE):: $(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)" @${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) @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) @${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) $(GOCOVERHTML): $(GOCOVER_FILE)
go tool cover -html=$(GOCOVER_FILE) -o $(GOCOVERHTML) go tool cover -html=$(GOCOVER_FILE) -o $(GOCOVERHTML)
@ -41,15 +43,15 @@ clean::
dev:: dev::
@go build @go build
@make -B -C cmd/sockaddr sockaddr @$(MAKE) -B -C cmd/sockaddr sockaddr
install:: install::
@go install @go install
@make -C cmd/sockaddr install @$(MAKE) -C cmd/sockaddr install
doc:: doc::
echo Visit: http://127.0.0.1:6060/pkg/github.com/hashicorp/go-sockaddr/ @echo Visit: http://127.0.0.1:6161/pkg/github.com/hashicorp/go-sockaddr/
godoc -http=:6060 -goroot $GOROOT godoc -http=:6161 -goroot $GOROOT
world:: world::
@set -e; \ @set -e; \
@ -60,4 +62,4 @@ world::
done; \ done; \
done done
make -C cmd/sockaddr world $(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

@ -0,0 +1,2 @@
test::
go test

View file

@ -0,0 +1,6 @@
# sockaddr/template
sockaddr's template library. See
the
[sockaddr/template](https://godoc.org/github.com/hashicorp/go-sockaddr/template)
docs for details on how to use this template.

303
vendor/github.com/hashicorp/go-sockaddr/template/doc.go generated vendored Normal file
View file

@ -0,0 +1,303 @@
/*
Package sockaddr/template provides a text/template interface the SockAddr helper
functions. The primary entry point into the sockaddr/template package is
through its Parse() call. For example:
import (
"fmt"
template "github.com/hashicorp/go-sockaddr/template"
)
results, err := template.Parse(`{{ GetPrivateIP }}`)
if err != nil {
fmt.Errorf("Unable to find a private IP address: %v", err)
}
fmt.Printf("My Private IP address is: %s\n", results)
Below is a list of builtin template functions and details re: their usage. It
is possible to add additional functions by calling ParseIfAddrsTemplate
directly.
In general, the calling convention for this template library is to seed a list
of initial interfaces via one of the Get*Interfaces() calls, then filter, sort,
and extract the necessary attributes for use as string input. This template
interface is primarily geared toward resolving specific values that are only
available at runtime, but can be defined as a heuristic for execution when a
config file is parsed.
All functions, unless noted otherwise, return an array of IfAddr structs making
it possible to `sort`, `filter`, `limit`, seek (via the `offset` function), or
`unique` the list. To extract useful string information, the `attr` and `join`
functions return a single string value. See below for details.
Important note: see the
https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr utility for
more examples and for a CLI utility to experiment with the template syntax.
`GetAllInterfaces` - Returns an exhaustive set of IfAddr structs available on
the host. `GetAllInterfaces` is the initial input and accessible as the initial
"dot" in the pipeline.
Example:
{{ GetAllInterfaces }}
`GetDefaultInterfaces` - Returns one IfAddr for every IP that is on the
interface containing the default route for the host.
Example:
{{ GetDefaultInterfaces }}
`GetPrivateInterfaces` - Returns one IfAddr for every forwardable IP address
that is included in RFC 6890 and whose interface is marked as up. NOTE: RFC 6890 is a more exhaustive
version of RFC1918 because it spans IPv4 and IPv6, however, RFC6890 does permit the
inclusion of likely undesired addresses such as multicast, therefore our version
of "private" also filters out non-forwardable addresses.
Example:
{{ GetPrivateInterfaces | sort "default" | join "address" " " }}
`GetPublicInterfaces` - Returns a list of IfAddr structs whos IPs are
forwardable, do not match RFC 6890, and whose interface is marked up.
Example:
{{ GetPublicInterfaces | sort "default" | join "name" " " }}
`GetPrivateIP` - Helper function that returns a string of the first IP address
from GetPrivateInterfaces.
Example:
{{ GetPrivateIP }}
`GetPrivateIPs` - Helper function that returns a string of the all private IP
addresses on the host.
Example:
{{ GetPrivateIPs }}
`GetPublicIP` - Helper function that returns a string of the first IP from
GetPublicInterfaces.
Example:
{{ GetPublicIP }}
`GetPublicIPs` - Helper function that returns a space-delimited string of the
all public IP addresses on the host.
Example:
{{ GetPrivateIPs }}
`GetInterfaceIP` - Helper function that returns a string of the first IP from
the named interface.
Example:
{{ GetInterfaceIP "en0" }}
`GetInterfaceIPs` - Helper function that returns a space-delimited list of all
IPs on a given interface.
Example:
{{ GetInterfaceIPs "en0" }}
`sort` - Sorts the IfAddrs result based on its arguments. `sort` takes one
argument, a list of ways to sort its IfAddrs argument. The list of sort
criteria is comma separated (`,`):
- `address`, `+address`: Ascending sort of IfAddrs by Address
- `-address`: Descending sort of IfAddrs by Address
- `default`, `+default`: Ascending sort of IfAddrs, IfAddr with a default route first
- `-default`: Descending sort of IfAddrs, IfAttr with default route last
- `name`, `+name`: Ascending sort of IfAddrs by lexical ordering of interface name
- `-name`: Descending sort of IfAddrs by lexical ordering of interface name
- `port`, `+port`: Ascending sort of IfAddrs by port number
- `-port`: Descending sort of IfAddrs by port number
- `private`, `+private`: Ascending sort of IfAddrs with private addresses first
- `-private`: Descending sort IfAddrs with private addresses last
- `size`, `+size`: Ascending sort of IfAddrs by their network size as determined
by their netmask (larger networks first)
- `-size`: Descending sort of IfAddrs by their network size as determined by their
netmask (smaller networks first)
- `type`, `+type`: Ascending sort of IfAddrs by the type of the IfAddr (Unix,
IPv4, then IPv6)
- `-type`: Descending sort of IfAddrs by the type of the IfAddr (IPv6, IPv4, Unix)
Example:
{{ GetPrivateInterfaces | sort "default,-type,size,+address" }}
`exclude` and `include`: Filters IfAddrs based on the selector criteria and its
arguments. Both `exclude` and `include` take two arguments. The list of
available filtering criteria is:
- "address": Filter IfAddrs based on a regexp matching the string representation
of the address
- "flag","flags": Filter IfAddrs based on the list of flags specified. Multiple
flags can be passed together using the pipe character (`|`) to create an inclusive
bitmask of flags. The list of flags is included below.
- "name": Filter IfAddrs based on a regexp matching the interface name.
- "network": Filter IfAddrs based on whether a netowkr is included in a given
CIDR. More than one CIDR can be passed in if each network is separated by
the pipe character (`|`).
- "port": Filter IfAddrs based on an exact match of the port number (number must
be expressed as a string)
- "rfc", "rfcs": Filter IfAddrs based on the matching RFC. If more than one RFC
is specified, the list of RFCs can be joined together using the pipe character (`|`).
- "size": Filter IfAddrs based on the exact match of the mask size.
- "type": Filter IfAddrs based on their SockAddr type. Multiple types can be
specified together by using the pipe character (`|`). Valid types include:
`ip`, `ipv4`, `ipv6`, and `unix`.
Example:
{{ GetPrivateInterfaces | exclude "type" "IPv6" }}
`unique`: Removes duplicate entries from the IfAddrs list, assuming the list has
already been sorted. `unique` only takes one argument:
- "address": Removes duplicates with the same address
- "name": Removes duplicates with the same interface names
Example:
{{ GetAllInterfaces | sort "default,-type,address" | unique "name" }}
`limit`: Reduces the size of the list to the specified value.
Example:
{{ GetPrivateInterfaces | limit 1 }}
`offset`: Seeks into the list by the specified value. A negative value can be
used to seek from the end of the list.
Example:
{{ GetPrivateInterfaces | offset "-2" | limit 1 }}
`math`: Perform a "math" operation on each member of the list and return new
values. `math` takes two arguments, the attribute to operate on and the
operation's value.
Supported operations include:
- `address`: Adds the value, a positive or negative value expressed as a
decimal string, to the address. The sign is required. This value is
allowed to over or underflow networks (e.g. 127.255.255.255 `"address" "+1"`
will return "128.0.0.0"). Addresses will wrap at IPv4 or IPv6 boundaries.
- `network`: Add the value, a positive or negative value expressed as a
decimal string, to the network address. The sign is required. Positive
values are added to the network address. Negative values are subtracted
from the network's broadcast address (e.g. 127.0.0.1 `"network" "-1"` will
return "127.255.255.255"). Values that overflow the network size will
safely wrap.
Example:
{{ GetPrivateInterfaces | include "type" "IP" | math "address" "+256" | attr "address" }}
{{ GetPrivateInterfaces | include "type" "IP" | math "address" "-256" | attr "address" }}
{{ GetPrivateInterfaces | include "type" "IP" | math "network" "+2" | attr "address" }}
{{ GetPrivateInterfaces | include "type" "IP" | math "network" "-2" | attr "address" }}
{{ GetPrivateInterfaces | include "flags" "forwardable|up" | include "type" "IPv4" | math "network" "+2" | attr "address" }}
`attr`: Extracts a single attribute of the first member of the list and returns
it as a string. `attr` takes a single attribute name. The list of available
attributes is type-specific and shared between `join`. See below for a list of
supported attributes.
Example:
{{ GetAllInterfaces | exclude "flags" "up" | attr "address" }}
`Attr`: Extracts a single attribute from an `IfAttr` and in every other way
performs the same as the `attr`.
Example:
{{ with $ifAddrs := GetAllInterfaces | include "type" "IP" | sort "+type,+address" -}}
{{- range $ifAddrs -}}
{{- Attr "address" . }} -- {{ Attr "network" . }}/{{ Attr "size" . -}}
{{- end -}}
{{- end }}
`join`: Similar to `attr`, `join` extracts all matching attributes of the list
and returns them as a string joined by the separator, the second argument to
`join`. The list of available attributes is type-specific and shared between
`join`.
Example:
{{ GetAllInterfaces | include "flags" "forwardable" | join "address" " " }}
`exclude` and `include` flags:
- `broadcast`
- `down`: Is the interface down?
- `forwardable`: Is the IP forwardable?
- `global unicast`
- `interface-local multicast`
- `link-local multicast`
- `link-local unicast`
- `loopback`
- `multicast`
- `point-to-point`
- `unspecified`: Is the IfAddr the IPv6 unspecified address?
- `up`: Is the interface up?
Attributes for `attr`, `Attr`, and `join`:
SockAddr Type:
- `string`
- `type`
IPAddr Type:
- `address`
- `binary`
- `first_usable`
- `hex`
- `host`
- `last_usable`
- `mask_bits`
- `netmask`
- `network`
- `octets`: Decimal values per byte
- `port`
- `size`: Number of hosts in the network
IPv4Addr Type:
- `broadcast`
- `uint32`: unsigned integer representation of the value
IPv6Addr Type:
- `uint128`: unsigned integer representation of the value
UnixSock Type:
- `path`
*/
package template

View file

@ -64,23 +64,53 @@ func init() {
HelperFuncs = template.FuncMap{ HelperFuncs = template.FuncMap{
// Misc functions that operate on IfAddrs inputs // Misc functions that operate on IfAddrs inputs
"attr": sockaddr.IfAttr, "attr": Attr,
"join": sockaddr.JoinIfAddrs, "join": sockaddr.JoinIfAddrs,
"limit": sockaddr.LimitIfAddrs, "limit": sockaddr.LimitIfAddrs,
"offset": sockaddr.OffsetIfAddrs, "offset": sockaddr.OffsetIfAddrs,
"unique": sockaddr.UniqueIfAddrsBy, "unique": sockaddr.UniqueIfAddrsBy,
// Misc math functions that operate on a single IfAddr input
"math": sockaddr.IfAddrsMath,
// Return a Private RFC 6890 IP address string that is attached // Return a Private RFC 6890 IP address string that is attached
// to the default route and a forwardable address. // to the default route and a forwardable address.
"GetPrivateIP": sockaddr.GetPrivateIP, "GetPrivateIP": sockaddr.GetPrivateIP,
// Return all Private RFC 6890 IP addresses as a space-delimited string of
// IP addresses. Addresses returned do not have to be on the interface with
// a default route.
"GetPrivateIPs": sockaddr.GetPrivateIPs,
// Return a Public RFC 6890 IP address string that is attached // Return a Public RFC 6890 IP address string that is attached
// to the default route and a forwardable address. // to the default route and a forwardable address.
"GetPublicIP": sockaddr.GetPublicIP, "GetPublicIP": sockaddr.GetPublicIP,
// Return allPublic RFC 6890 IP addresses as a space-delimited string of IP
// addresses. Addresses returned do not have to be on the interface with a
// default route.
"GetPublicIPs": sockaddr.GetPublicIPs,
// Return the first IP address of the named interface, sorted by // Return the first IP address of the named interface, sorted by
// the largest network size. // the largest network size.
"GetInterfaceIP": sockaddr.GetInterfaceIP, "GetInterfaceIP": sockaddr.GetInterfaceIP,
// Return all IP addresses on the named interface, sorted by the largest
// network size.
"GetInterfaceIPs": sockaddr.GetInterfaceIPs,
}
}
// Attr returns the attribute from the ifAddrRaw argument. If the argument is
// an IfAddrs, only the first element will be evaluated for resolution.
func Attr(selectorName string, ifAddrsRaw interface{}) (string, error) {
switch v := ifAddrsRaw.(type) {
case sockaddr.IfAddr:
return sockaddr.IfAttr(selectorName, v)
case sockaddr.IfAddrs:
return sockaddr.IfAttrs(selectorName, v)
default:
return "", fmt.Errorf("unable to obtain attribute %s from type %T (%v)", selectorName, ifAddrsRaw, ifAddrsRaw)
} }
} }

12
vendor/vendor.json vendored
View file

@ -486,16 +486,16 @@
"revisionTime": "2016-05-03T14:34:40Z" "revisionTime": "2016-05-03T14:34:40Z"
}, },
{ {
"checksumSHA1": "BGODc7juQbdG3vNXHZG07kt+lKI=", "checksumSHA1": "GP24Vz4EmZAL1ZH2TYTkDiiCO94=",
"path": "github.com/hashicorp/go-sockaddr", "path": "github.com/hashicorp/go-sockaddr",
"revision": "f910dd83c2052566cad78352c33af714358d1372", "revision": "2d10d7c10258d11196c0ebf2943509e4afd06cd4",
"revisionTime": "2017-02-08T07:30:35Z" "revisionTime": "2017-05-23T22:50:28Z"
}, },
{ {
"checksumSHA1": "lPzwetgfMBtpHqdTPolgejMctVQ=", "checksumSHA1": "mIUCMmRHslN2bxQZ0uObMnXxk9E=",
"path": "github.com/hashicorp/go-sockaddr/template", "path": "github.com/hashicorp/go-sockaddr/template",
"revision": "af174a6fe6c9f9a049a638e1dae7bc4442c4d426", "revision": "2d10d7c10258d11196c0ebf2943509e4afd06cd4",
"revisionTime": "2016-12-02T14:18:37Z" "revisionTime": "2017-05-23T22:50:28Z"
}, },
{ {
"checksumSHA1": "xZ7Ban1x//6uUIU1xtrTbCYNHBc=", "checksumSHA1": "xZ7Ban1x//6uUIU1xtrTbCYNHBc=",