1282 lines
38 KiB
Go
1282 lines
38 KiB
Go
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 adapter ([^\s:]+):`)
|
|
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")
|
|
}
|