aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go')
-rw-r--r--vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go1281
1 files changed, 1281 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
new file mode 100644
index 0000000..2a706c3
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
@@ -0,0 +1,1281 @@
+package sockaddr
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/big"
+ "net"
+ "regexp"
+ "sort"
+ "strconv"
+ "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
+
+func (ifs IfAddrs) Len() int { return len(ifs) }
+
+// CmpIfFunc is the function signature that must be met to be used in the
+// OrderedIfAddrBy multiIfAddrSorter
+type CmpIfAddrFunc func(p1, p2 *IfAddr) int
+
+// multiIfAddrSorter implements the Sort interface, sorting the IfAddrs within.
+type multiIfAddrSorter struct {
+ ifAddrs IfAddrs
+ cmp []CmpIfAddrFunc
+}
+
+// Sort sorts the argument slice according to the Cmp functions passed to
+// OrderedIfAddrBy.
+func (ms *multiIfAddrSorter) Sort(ifAddrs IfAddrs) {
+ ms.ifAddrs = ifAddrs
+ sort.Sort(ms)
+}
+
+// OrderedIfAddrBy sorts SockAddr by the list of sort function pointers.
+func OrderedIfAddrBy(cmpFuncs ...CmpIfAddrFunc) *multiIfAddrSorter {
+ return &multiIfAddrSorter{
+ cmp: cmpFuncs,
+ }
+}
+
+// Len is part of sort.Interface.
+func (ms *multiIfAddrSorter) Len() int {
+ return len(ms.ifAddrs)
+}
+
+// Less is part of sort.Interface. It is implemented by looping along the Cmp()
+// functions until it finds a comparison that is either less than or greater
+// than. A return value of 0 defers sorting to the next function in the
+// multisorter (which means the results of sorting may leave the resutls in a
+// non-deterministic order).
+func (ms *multiIfAddrSorter) Less(i, j int) bool {
+ p, q := &ms.ifAddrs[i], &ms.ifAddrs[j]
+ // Try all but the last comparison.
+ var k int
+ for k = 0; k < len(ms.cmp)-1; k++ {
+ cmp := ms.cmp[k]
+ x := cmp(p, q)
+ switch x {
+ case -1:
+ // p < q, so we have a decision.
+ return true
+ case 1:
+ // p > q, so we have a decision.
+ return false
+ }
+ // p == q; try the next comparison.
+ }
+ // All comparisons to here said "equal", so just return whatever the
+ // final comparison reports.
+ switch ms.cmp[k](p, q) {
+ case -1:
+ return true
+ case 1:
+ return false
+ default:
+ // Still a tie! Now what?
+ return false
+ panic("undefined sort order for remaining items in the list")
+ }
+}
+
+// Swap is part of sort.Interface.
+func (ms *multiIfAddrSorter) Swap(i, j int) {
+ ms.ifAddrs[i], ms.ifAddrs[j] = ms.ifAddrs[j], ms.ifAddrs[i]
+}
+
+// AscIfAddress is a sorting function to sort IfAddrs by their respective
+// address type. Non-equal types are deferred in the sort.
+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)
+}
+
+// AscIfNetworkSize is a sorting function to sort IfAddrs by their respective
+// network mask size.
+func AscIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int {
+ return AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// AscIfPort is a sorting function to sort IfAddrs by their respective
+// port type. Non-equal types are deferred in the sort.
+func AscIfPort(p1Ptr, p2Ptr *IfAddr) int {
+ return AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// AscIfPrivate is a sorting function to sort IfAddrs by "private" values before
+// "public" values. Both IPv4 and IPv6 are compared against RFC6890 (RFC6890
+// includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and IPv6
+// includes RFC4193).
+func AscIfPrivate(p1Ptr, p2Ptr *IfAddr) int {
+ return AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// AscIfType is a sorting function to sort IfAddrs by their respective address
+// type. Non-equal types are deferred in the sort.
+func AscIfType(p1Ptr, p2Ptr *IfAddr) int {
+ return AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// DescIfAddress is identical to AscIfAddress but reverse ordered.
+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)
+}
+
+// DescIfNetworkSize is identical to AscIfNetworkSize but reverse ordered.
+func DescIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int {
+ return -1 * AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// DescIfPort is identical to AscIfPort but reverse ordered.
+func DescIfPort(p1Ptr, p2Ptr *IfAddr) int {
+ return -1 * AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// DescIfPrivate is identical to AscIfPrivate but reverse ordered.
+func DescIfPrivate(p1Ptr, p2Ptr *IfAddr) int {
+ return -1 * AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// DescIfType is identical to AscIfType but reverse ordered.
+func DescIfType(p1Ptr, p2Ptr *IfAddr) int {
+ return -1 * AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
+}
+
+// FilterIfByType filters IfAddrs and returns a list of the matching type
+func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIfs IfAddrs) {
+ excludedIfs = make(IfAddrs, 0, len(ifAddrs))
+ matchedIfs = make(IfAddrs, 0, len(ifAddrs))
+
+ for _, ifAddr := range ifAddrs {
+ if ifAddr.SockAddr.Type()&type_ != 0 {
+ matchedIfs = append(matchedIfs, ifAddr)
+ } else {
+ excludedIfs = append(excludedIfs, ifAddr)
+ }
+ }
+ return matchedIfs, excludedIfs
+}
+
+// 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, 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
+ }
+
+ attrName := AttrName(strings.ToLower(selectorName))
+ attrVal, err := ifAddrs[0].Attr(attrName)
+ return attrVal, err
+}
+
+// GetAllInterfaces iterates over all available network interfaces and finds all
+// available IP addresses on each interface and converts them to
+// sockaddr.IPAddrs, and returning the result as an array of IfAddr.
+func GetAllInterfaces() (IfAddrs, error) {
+ ifs, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+
+ ifAddrs := make(IfAddrs, 0, len(ifs))
+ for _, intf := range ifs {
+ addrs, err := intf.Addrs()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, addr := range addrs {
+ var ipAddr IPAddr
+ ipAddr, err = NewIPAddr(addr.String())
+ if err != nil {
+ return IfAddrs{}, fmt.Errorf("unable to create an IP address from %q", addr.String())
+ }
+
+ ifAddr := IfAddr{
+ SockAddr: ipAddr,
+ Interface: intf,
+ }
+ ifAddrs = append(ifAddrs, ifAddr)
+ }
+ }
+
+ return ifAddrs, nil
+}
+
+// GetDefaultInterfaces returns IfAddrs of the addresses attached to the default
+// route.
+func GetDefaultInterfaces() (IfAddrs, error) {
+ ri, err := NewRouteInfo()
+ if err != nil {
+ return nil, err
+ }
+
+ defaultIfName, err := ri.GetDefaultInterfaceName()
+ if err != nil {
+ return nil, err
+ }
+
+ var defaultIfs, ifAddrs IfAddrs
+ ifAddrs, err = GetAllInterfaces()
+ for _, ifAddr := range ifAddrs {
+ if ifAddr.Name == defaultIfName {
+ defaultIfs = append(defaultIfs, ifAddr)
+ }
+ }
+
+ return defaultIfs, nil
+}
+
+// GetPrivateInterfaces returns an IfAddrs that are part of RFC 6890 and have a
+// default route. If the system can't determine its IP address or find an RFC
+// 6890 IP address, an empty IfAddrs will be returned instead. This function is
+// the `eval` equivalent of:
+//
+// ```
+// $ 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 := GetAllInterfaces()
+ if err != nil {
+ return IfAddrs{}, err
+ }
+ if len(privateIfs) == 0 {
+ return IfAddrs{}, nil
+ }
+
+ privateIfs, _ = FilterIfByType(privateIfs, TypeIP)
+ if len(privateIfs) == 0 {
+ return IfAddrs{}, nil
+ }
+
+ 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(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(privateIfs)
+
+ privateIfs, _, err = IfByRFC("6890", privateIfs)
+ if err != nil {
+ return IfAddrs{}, err
+ } else if len(privateIfs) == 0 {
+ return IfAddrs{}, nil
+ }
+
+ return privateIfs, nil
+}
+
+// GetPublicInterfaces returns an IfAddrs that are 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 IfAddrs will be returned instead. This
+// function is the `eval` equivalent of:
+//
+// ```
+// $ 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 := GetAllInterfaces()
+ if err != nil {
+ return IfAddrs{}, err
+ }
+ if len(publicIfs) == 0 {
+ return IfAddrs{}, nil
+ }
+
+ publicIfs, _ = FilterIfByType(publicIfs, TypeIP)
+ if len(publicIfs) == 0 {
+ return IfAddrs{}, nil
+ }
+
+ 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(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(publicIfs)
+
+ _, publicIfs, err = IfByRFC("6890", publicIfs)
+ if err != nil {
+ return IfAddrs{}, err
+ } else if len(publicIfs) == 0 {
+ return IfAddrs{}, nil
+ }
+
+ return publicIfs, nil
+}
+
+// IfByAddress returns a list of matched and non-matched IfAddrs, or an error if
+// the regexp fails to compile.
+func IfByAddress(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
+ re, err := regexp.Compile(inputRe)
+ if err != nil {
+ return nil, nil, fmt.Errorf("Unable to compile address regexp %+q: %v", inputRe, err)
+ }
+
+ matchedAddrs := make(IfAddrs, 0, len(ifAddrs))
+ excludedAddrs := make(IfAddrs, 0, len(ifAddrs))
+ for _, addr := range ifAddrs {
+ if re.MatchString(addr.SockAddr.String()) {
+ matchedAddrs = append(matchedAddrs, addr)
+ } else {
+ excludedAddrs = append(excludedAddrs, addr)
+ }
+ }
+
+ return matchedAddrs, excludedAddrs, nil
+}
+
+// IfByName returns a list of matched and non-matched IfAddrs, or an error if
+// the regexp fails to compile.
+func IfByName(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
+ re, err := regexp.Compile(inputRe)
+ if err != nil {
+ return nil, nil, fmt.Errorf("Unable to compile name regexp %+q: %v", inputRe, err)
+ }
+
+ matchedAddrs := make(IfAddrs, 0, len(ifAddrs))
+ excludedAddrs := make(IfAddrs, 0, len(ifAddrs))
+ for _, addr := range ifAddrs {
+ if re.MatchString(addr.Name) {
+ matchedAddrs = append(matchedAddrs, addr)
+ } else {
+ excludedAddrs = append(excludedAddrs, addr)
+ }
+ }
+
+ return matchedAddrs, excludedAddrs, nil
+}
+
+// IfByPort returns a list of matched and non-matched IfAddrs, or an error if
+// the regexp fails to compile.
+func IfByPort(inputRe string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) {
+ re, err := regexp.Compile(inputRe)
+ if err != nil {
+ return nil, nil, fmt.Errorf("Unable to compile port regexp %+q: %v", inputRe, err)
+ }
+
+ ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP)
+ matchedIfs = make(IfAddrs, 0, len(ipIfs))
+ excludedIfs = append(IfAddrs(nil), nonIfs...)
+ for _, addr := range ipIfs {
+ ipAddr := ToIPAddr(addr.SockAddr)
+ if ipAddr == nil {
+ continue
+ }
+
+ port := strconv.FormatInt(int64((*ipAddr).IPPort()), 10)
+ if re.MatchString(port) {
+ matchedIfs = append(matchedIfs, addr)
+ } else {
+ excludedIfs = append(excludedIfs, addr)
+ }
+ }
+
+ return matchedIfs, excludedIfs, nil
+}
+
+// IfByRFC returns a list of matched and non-matched IfAddrs that contain the
+// relevant RFC-specified traits.
+func IfByRFC(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
+ inputRFC, err := strconv.ParseUint(selectorParam, 10, 64)
+ if err != nil {
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to parse RFC number %q: %v", selectorParam, err)
+ }
+
+ matchedIfAddrs := make(IfAddrs, 0, len(ifAddrs))
+ remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs))
+
+ rfcNetMap := KnownRFCs()
+ rfcNets, ok := rfcNetMap[uint(inputRFC)]
+ if !ok {
+ return nil, nil, fmt.Errorf("unsupported RFC %d", inputRFC)
+ }
+
+ for _, ifAddr := range ifAddrs {
+ var contained bool
+ for _, rfcNet := range rfcNets {
+ if rfcNet.Contains(ifAddr.SockAddr) {
+ matchedIfAddrs = append(matchedIfAddrs, ifAddr)
+ contained = true
+ break
+ }
+ }
+ if !contained {
+ remainingIfAddrs = append(remainingIfAddrs, ifAddr)
+ }
+ }
+
+ return matchedIfAddrs, remainingIfAddrs, nil
+}
+
+// IfByRFCs returns a list of matched and non-matched IfAddrs that contain the
+// relevant RFC-specified traits. Multiple RFCs can be specified and separated
+// by the `|` symbol. No protection is taken to ensure an IfAddr does not end
+// up in both the included and excluded list.
+func IfByRFCs(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
+ var includedIfs, excludedIfs IfAddrs
+ for _, rfcStr := range strings.Split(selectorParam, "|") {
+ includedRFCIfs, excludedRFCIfs, err := IfByRFC(rfcStr, ifAddrs)
+ if err != nil {
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to lookup RFC number %q: %v", rfcStr, err)
+ }
+ includedIfs = append(includedIfs, includedRFCIfs...)
+ excludedIfs = append(excludedIfs, excludedRFCIfs...)
+ }
+
+ return includedIfs, excludedIfs, nil
+}
+
+// IfByMaskSize returns a list of matched and non-matched IfAddrs that have the
+// matching mask size.
+func IfByMaskSize(selectorParam string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) {
+ maskSize, err := strconv.ParseUint(selectorParam, 10, 64)
+ if err != nil {
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("invalid exclude size argument (%q): %v", selectorParam, err)
+ }
+
+ ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP)
+ matchedIfs = make(IfAddrs, 0, len(ipIfs))
+ excludedIfs = append(IfAddrs(nil), nonIfs...)
+ for _, addr := range ipIfs {
+ ipAddr := ToIPAddr(addr.SockAddr)
+ if ipAddr == nil {
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to filter mask sizes on non-IP type %s: %v", addr.SockAddr.Type().String(), addr.SockAddr.String())
+ }
+
+ switch {
+ case (*ipAddr).Type()&TypeIPv4 != 0 && maskSize > 32:
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv4 address: %d", maskSize)
+ case (*ipAddr).Type()&TypeIPv6 != 0 && maskSize > 128:
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv6 address: %d", maskSize)
+ }
+
+ if (*ipAddr).Maskbits() == int(maskSize) {
+ matchedIfs = append(matchedIfs, addr)
+ } else {
+ excludedIfs = append(excludedIfs, addr)
+ }
+ }
+
+ return matchedIfs, excludedIfs, nil
+}
+
+// IfByType returns a list of matching and non-matching IfAddr that match the
+// specified type. For instance:
+//
+// include "type" "IPv4,IPv6"
+//
+// will include any IfAddrs that is either an IPv4 or IPv6 address. Any
+// addresses on those interfaces that don't match will be included in the
+// remainder results.
+func IfByType(inputTypes string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
+ matchingIfAddrs := make(IfAddrs, 0, len(ifAddrs))
+ remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs))
+
+ ifTypes := strings.Split(strings.ToLower(inputTypes), "|")
+ for _, ifType := range ifTypes {
+ switch ifType {
+ case "ip", "ipv4", "ipv6", "unix":
+ // Valid types
+ default:
+ return nil, nil, fmt.Errorf("unsupported type %q %q", ifType, inputTypes)
+ }
+ }
+
+ for _, ifAddr := range ifAddrs {
+ for _, ifType := range ifTypes {
+ var matched bool
+ switch {
+ case ifType == "ip" && ifAddr.SockAddr.Type()&TypeIP != 0:
+ matched = true
+ case ifType == "ipv4" && ifAddr.SockAddr.Type()&TypeIPv4 != 0:
+ matched = true
+ case ifType == "ipv6" && ifAddr.SockAddr.Type()&TypeIPv6 != 0:
+ matched = true
+ case ifType == "unix" && ifAddr.SockAddr.Type()&TypeUnix != 0:
+ matched = true
+ }
+
+ if matched {
+ matchingIfAddrs = append(matchingIfAddrs, ifAddr)
+ } else {
+ remainingIfAddrs = append(remainingIfAddrs, ifAddr)
+ }
+ }
+ }
+
+ return matchingIfAddrs, remainingIfAddrs, nil
+}
+
+// IfByFlag returns a list of matching and non-matching IfAddrs that match the
+// specified type. For instance:
+//
+// include "flag" "up,broadcast"
+//
+// will include any IfAddrs that have both the "up" and "broadcast" flags set.
+// Any addresses on those interfaces that don't match will be omitted from the
+// results.
+func IfByFlag(inputFlags string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
+ matchedAddrs := make(IfAddrs, 0, len(ifAddrs))
+ excludedAddrs := make(IfAddrs, 0, len(ifAddrs))
+
+ var wantForwardable,
+ wantGlobalUnicast,
+ wantInterfaceLocalMulticast,
+ wantLinkLocalMulticast,
+ wantLinkLocalUnicast,
+ wantLoopback,
+ wantMulticast,
+ wantUnspecified bool
+ var ifFlags net.Flags
+ var checkFlags, checkAttrs bool
+ for _, flagName := range strings.Split(strings.ToLower(inputFlags), "|") {
+ switch flagName {
+ case "broadcast":
+ checkFlags = true
+ ifFlags = ifFlags | net.FlagBroadcast
+ case "down":
+ checkFlags = true
+ ifFlags = (ifFlags &^ net.FlagUp)
+ case "forwardable":
+ checkAttrs = true
+ wantForwardable = true
+ case "global unicast":
+ checkAttrs = true
+ wantGlobalUnicast = true
+ case "interface-local multicast":
+ checkAttrs = true
+ wantInterfaceLocalMulticast = true
+ case "link-local multicast":
+ checkAttrs = true
+ wantLinkLocalMulticast = true
+ case "link-local unicast":
+ checkAttrs = true
+ wantLinkLocalUnicast = true
+ case "loopback":
+ checkAttrs = true
+ checkFlags = true
+ ifFlags = ifFlags | net.FlagLoopback
+ wantLoopback = true
+ case "multicast":
+ checkAttrs = true
+ checkFlags = true
+ ifFlags = ifFlags | net.FlagMulticast
+ wantMulticast = true
+ case "point-to-point":
+ checkFlags = true
+ ifFlags = ifFlags | net.FlagPointToPoint
+ case "unspecified":
+ checkAttrs = true
+ wantUnspecified = true
+ case "up":
+ checkFlags = true
+ ifFlags = ifFlags | net.FlagUp
+ default:
+ return nil, nil, fmt.Errorf("Unknown interface flag: %+q", flagName)
+ }
+ }
+
+ for _, ifAddr := range ifAddrs {
+ var matched bool
+ if checkFlags && ifAddr.Interface.Flags&ifFlags == ifFlags {
+ matched = true
+ }
+ if checkAttrs {
+ if ip := ToIPAddr(ifAddr.SockAddr); ip != nil {
+ netIP := (*ip).NetIP()
+ switch {
+ case wantGlobalUnicast && netIP.IsGlobalUnicast():
+ matched = true
+ case wantInterfaceLocalMulticast && netIP.IsInterfaceLocalMulticast():
+ matched = true
+ case wantLinkLocalMulticast && netIP.IsLinkLocalMulticast():
+ matched = true
+ case wantLinkLocalUnicast && netIP.IsLinkLocalUnicast():
+ matched = true
+ case wantLoopback && netIP.IsLoopback():
+ matched = true
+ case wantMulticast && netIP.IsMulticast():
+ matched = true
+ case wantUnspecified && netIP.IsUnspecified():
+ matched = true
+ case wantForwardable && !IsRFC(ForwardingBlacklist, ifAddr.SockAddr):
+ matched = true
+ }
+ }
+ }
+ if matched {
+ matchedAddrs = append(matchedAddrs, ifAddr)
+ } else {
+ excludedAddrs = append(excludedAddrs, ifAddr)
+ }
+ }
+ return matchedAddrs, excludedAddrs, nil
+}
+
+// IfByNetwork returns an IfAddrs that are equal to or included within the
+// network passed in by selector.
+func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs, error) {
+ var includedIfs, excludedIfs IfAddrs
+ for _, netStr := range strings.Split(selectorParam, "|") {
+ netAddr, err := NewIPAddr(netStr)
+ if err != nil {
+ return nil, nil, fmt.Errorf("unable to create an IP address from %+q: %v", netStr, err)
+ }
+
+ for _, ifAddr := range inputIfAddrs {
+ if netAddr.Contains(ifAddr.SockAddr) {
+ includedIfs = append(includedIfs, ifAddr)
+ } else {
+ excludedIfs = append(excludedIfs, ifAddr)
+ }
+ }
+ }
+
+ 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
+ var err error
+
+ switch strings.ToLower(selectorName) {
+ case "address":
+ includedIfs, _, err = IfByAddress(selectorParam, inputIfAddrs)
+ case "flag", "flags":
+ includedIfs, _, err = IfByFlag(selectorParam, inputIfAddrs)
+ case "name":
+ includedIfs, _, err = IfByName(selectorParam, inputIfAddrs)
+ case "network":
+ includedIfs, _, err = IfByNetwork(selectorParam, inputIfAddrs)
+ case "port":
+ includedIfs, _, err = IfByPort(selectorParam, inputIfAddrs)
+ case "rfc", "rfcs":
+ includedIfs, _, err = IfByRFCs(selectorParam, inputIfAddrs)
+ case "size":
+ includedIfs, _, err = IfByMaskSize(selectorParam, inputIfAddrs)
+ case "type":
+ includedIfs, _, err = IfByType(selectorParam, inputIfAddrs)
+ default:
+ return IfAddrs{}, fmt.Errorf("invalid include selector %q", selectorName)
+ }
+
+ if err != nil {
+ return IfAddrs{}, err
+ }
+
+ return includedIfs, nil
+}
+
+// ExcludeIfs returns an IfAddrs based on the passed in selector.
+func ExcludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
+ var excludedIfs IfAddrs
+ var err error
+
+ switch strings.ToLower(selectorName) {
+ case "address":
+ _, excludedIfs, err = IfByAddress(selectorParam, inputIfAddrs)
+ case "flag", "flags":
+ _, excludedIfs, err = IfByFlag(selectorParam, inputIfAddrs)
+ case "name":
+ _, excludedIfs, err = IfByName(selectorParam, inputIfAddrs)
+ case "network":
+ _, excludedIfs, err = IfByNetwork(selectorParam, inputIfAddrs)
+ case "port":
+ _, excludedIfs, err = IfByPort(selectorParam, inputIfAddrs)
+ case "rfc", "rfcs":
+ _, excludedIfs, err = IfByRFCs(selectorParam, inputIfAddrs)
+ case "size":
+ _, excludedIfs, err = IfByMaskSize(selectorParam, inputIfAddrs)
+ case "type":
+ _, excludedIfs, err = IfByType(selectorParam, inputIfAddrs)
+ default:
+ return IfAddrs{}, fmt.Errorf("invalid exclude selector %q", selectorName)
+ }
+
+ if err != nil {
+ return IfAddrs{}, err
+ }
+
+ return excludedIfs, nil
+}
+
+// SortIfBy returns an IfAddrs sorted based on the passed in selector. Multiple
+// sort clauses can be passed in as a comma delimited list without whitespace.
+func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
+ sortedIfs := append(IfAddrs(nil), inputIfAddrs...)
+
+ clauses := strings.Split(selectorParam, ",")
+ sortFuncs := make([]CmpIfAddrFunc, len(clauses))
+
+ for i, clause := range clauses {
+ switch strings.TrimSpace(strings.ToLower(clause)) {
+ case "+address", "address":
+ // The "address" selector returns an array of IfAddrs
+ // ordered by the network address. IfAddrs that are not
+ // comparable will be at the end of the list and in a
+ // non-deterministic order.
+ 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.
+ sortFuncs[i] = AscIfName
+ case "-name":
+ sortFuncs[i] = DescIfName
+ case "+port", "port":
+ // The "port" selector returns an array of IfAddrs
+ // ordered by the port, if included in the IfAddr.
+ // IfAddrs that are not comparable will be at the end of
+ // the list and in a non-deterministic order.
+ sortFuncs[i] = AscIfPort
+ case "-port":
+ sortFuncs[i] = DescIfPort
+ case "+private", "private":
+ // The "private" selector returns an array of IfAddrs
+ // ordered by private addresses first. IfAddrs that are
+ // not comparable will be at the end of the list and in
+ // a non-deterministic order.
+ sortFuncs[i] = AscIfPrivate
+ case "-private":
+ sortFuncs[i] = DescIfPrivate
+ case "+size", "size":
+ // The "size" selector returns an array of IfAddrs
+ // ordered by the size of the network mask, smaller mask
+ // (larger number of hosts per network) to largest
+ // (e.g. a /24 sorts before a /32).
+ sortFuncs[i] = AscIfNetworkSize
+ case "-size":
+ sortFuncs[i] = DescIfNetworkSize
+ case "+type", "type":
+ // The "type" selector returns an array of IfAddrs
+ // ordered by the type of the IfAddr. The sort order is
+ // Unix, IPv4, then IPv6.
+ sortFuncs[i] = AscIfType
+ case "-type":
+ sortFuncs[i] = DescIfType
+ default:
+ // Return an empty list for invalid sort types.
+ return IfAddrs{}, fmt.Errorf("unknown sort type: %q", clause)
+ }
+ }
+
+ OrderedIfAddrBy(sortFuncs...).Sort(sortedIfs)
+
+ return sortedIfs, nil
+}
+
+// UniqueIfAddrsBy creates a unique set of IfAddrs based on the matching
+// selector. UniqueIfAddrsBy assumes the input has already been sorted.
+func UniqueIfAddrsBy(selectorName string, inputIfAddrs IfAddrs) (IfAddrs, error) {
+ attrName := strings.ToLower(selectorName)
+
+ ifs := make(IfAddrs, 0, len(inputIfAddrs))
+ var lastMatch string
+ for _, ifAddr := range inputIfAddrs {
+ var out string
+ switch attrName {
+ case "address":
+ out = ifAddr.SockAddr.String()
+ case "name":
+ out = ifAddr.Name
+ default:
+ return nil, fmt.Errorf("unsupported unique constraint %+q", selectorName)
+ }
+
+ switch {
+ case lastMatch == "", lastMatch != out:
+ lastMatch = out
+ ifs = append(ifs, ifAddr)
+ case lastMatch == out:
+ continue
+ }
+ }
+
+ return ifs, nil
+}
+
+// JoinIfAddrs joins an IfAddrs and returns a string
+func JoinIfAddrs(selectorName string, joinStr string, inputIfAddrs IfAddrs) (string, error) {
+ outputs := make([]string, 0, len(inputIfAddrs))
+ attrName := AttrName(strings.ToLower(selectorName))
+
+ for _, ifAddr := range inputIfAddrs {
+ var attrVal string
+ var err error
+ attrVal, err = ifAddr.Attr(attrName)
+ if err != nil {
+ return "", err
+ }
+ outputs = append(outputs, attrVal)
+ }
+ return strings.Join(outputs, joinStr), nil
+}
+
+// LimitIfAddrs returns a slice of IfAddrs based on the specified limit.
+func LimitIfAddrs(lim uint, in IfAddrs) (IfAddrs, error) {
+ // Clamp the limit to the length of the array
+ if int(lim) > len(in) {
+ lim = uint(len(in))
+ }
+
+ return in[0:lim], nil
+}
+
+// OffsetIfAddrs returns a slice of IfAddrs based on the specified offset.
+func OffsetIfAddrs(off int, in IfAddrs) (IfAddrs, error) {
+ var end bool
+ if off < 0 {
+ end = true
+ off = off * -1
+ }
+
+ if off > len(in) {
+ return IfAddrs{}, fmt.Errorf("unable to seek past the end of the interface array: offset (%d) exceeds the number of interfaces (%d)", off, len(in))
+ }
+
+ if end {
+ return in[len(in)-off:], nil
+ }
+ return in[off:], nil
+}
+
+func (ifAddr IfAddr) String() string {
+ return fmt.Sprintf("%s %v", ifAddr.SockAddr, ifAddr.Interface)
+}
+
+// parseDefaultIfNameFromRoute parses standard route(8)'s output for the *BSDs
+// and Solaris.
+func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
+ lines := strings.Split(routeOut, "\n")
+ for _, line := range lines {
+ kvs := strings.SplitN(line, ":", 2)
+ if len(kvs) != 2 {
+ continue
+ }
+
+ if strings.TrimSpace(kvs[0]) == "interface" {
+ ifName := strings.TrimSpace(kvs[1])
+ return ifName, nil
+ }
+ }
+
+ return "", errors.New("No default interface found")
+}
+
+// parseDefaultIfNameFromIPCmd parses the default interface from ip(8) for
+// Linux.
+func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
+ lines := strings.Split(routeOut, "\n")
+ re := whitespaceRE.Copy()
+ for _, line := range lines {
+ kvs := re.Split(line, -1)
+ if len(kvs) < 5 {
+ continue
+ }
+
+ if kvs[0] == "default" &&
+ kvs[1] == "via" &&
+ kvs[3] == "dev" {
+ ifName := strings.TrimSpace(kvs[4])
+ return ifName, nil
+ }
+ }
+
+ return "", errors.New("No default interface found")
+}
+
+// parseDefaultIfNameWindows parses the default interface from `netstat -rn` and
+// `ipconfig` on Windows.
+func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
+ defaultIPAddr, err := parseDefaultIPAddrWindowsRoute(routeOut)
+ if err != nil {
+ return "", err
+ }
+
+ ifName, err := parseDefaultIfNameWindowsIPConfig(defaultIPAddr, ipconfigOut)
+ if err != nil {
+ return "", err
+ }
+
+ return ifName, nil
+}
+
+// parseDefaultIPAddrWindowsRoute parses the IP address on the default interface
+// `netstat -rn`.
+//
+// NOTES(sean): Only IPv4 addresses are parsed at this time. If you have an
+// IPv6 connected host, submit an issue on github.com/hashicorp/go-sockaddr with
+// the output from `netstat -rn`, `ipconfig`, and version of Windows to see IPv6
+// support added.
+func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
+ lines := strings.Split(routeOut, "\n")
+ re := whitespaceRE.Copy()
+ for _, line := range lines {
+ kvs := re.Split(strings.TrimSpace(line), -1)
+ if len(kvs) < 3 {
+ continue
+ }
+
+ if kvs[0] == "0.0.0.0" && kvs[1] == "0.0.0.0" {
+ defaultIPAddr := strings.TrimSpace(kvs[3])
+ return defaultIPAddr, nil
+ }
+ }
+
+ return "", errors.New("No IP on default interface found")
+}
+
+// parseDefaultIfNameWindowsIPConfig parses the output of `ipconfig` to find the
+// interface name forwarding traffic to the default gateway.
+func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
+ lines := strings.Split(routeOut, "\n")
+ ifNameRe := ifNameRE.Copy()
+ ipAddrRe := ipAddrRE.Copy()
+ var ifName string
+ for _, line := range lines {
+ switch ifNameMatches := ifNameRe.FindStringSubmatch(line); {
+ case len(ifNameMatches) > 1:
+ ifName = ifNameMatches[1]
+ continue
+ }
+
+ switch ipAddrMatches := ipAddrRe.FindStringSubmatch(line); {
+ case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr:
+ return ifName, nil
+ }
+ }
+
+ return "", errors.New("No default interface found with matching IP")
+}