mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-08 05:47:07 +09:00
GO-3908 fix iface filtering and sorting
This commit is contained in:
parent
ce23acf64c
commit
9d70c79fa2
6 changed files with 264 additions and 78 deletions
|
@ -1,16 +1,22 @@
|
|||
package addrs
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/logging"
|
||||
"github.com/anyproto/anytype-heart/util/slice"
|
||||
)
|
||||
|
||||
var log = logging.Logger("anytype-net")
|
||||
|
||||
type Interface struct {
|
||||
net.Interface
|
||||
Addrs []InterfaceAddr
|
||||
|
@ -55,7 +61,7 @@ func (i NetInterfaceWithAddrCache) GetAddr() []net.Addr {
|
|||
if i.cachedErr != nil {
|
||||
return nil
|
||||
}
|
||||
i.cachedAddrs, i.cachedErr = i.Addrs()
|
||||
i.cachedAddrs, i.cachedErr = i.Interface.Addrs()
|
||||
if i.cachedErr != nil {
|
||||
log.Warn("interface GetAddr error: %v", i.cachedErr)
|
||||
}
|
||||
|
@ -80,35 +86,110 @@ func (i InterfacesAddrs) Equal(other InterfacesAddrs) bool {
|
|||
}
|
||||
myStr := getStrings(i)
|
||||
otherStr := getStrings(other)
|
||||
return slices.Equal(myStr, otherStr)
|
||||
// compare slices without order
|
||||
if !slices.Equal(myStr, otherStr) {
|
||||
log.Debug(fmt.Sprintf("addrs compare: strings mismatch: %v != %v", myStr, otherStr))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
ifaceRe = regexp.MustCompile(`^([a-z]*?)([0-9]+)$`)
|
||||
ifaceRe = regexp.MustCompile(`^([a-z]*?)([0-9]+)$`)
|
||||
ifaceWindowsRe = regexp.MustCompile(`^(.*?)([0-9]*)$`)
|
||||
|
||||
// ifaceReBusSlot used for prefixBusSlot naming schema used in newer linux distros https://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-net_id.c#n20
|
||||
ifaceReBusSlot = regexp.MustCompile(`^([a-z]*?)p([0-9]+)s([0-9a-f]+)$`)
|
||||
ifaceReBusSlot = regexp.MustCompile(`^(?P<type>enp|eno|ens|enx|wlp|wlx)(?P<bus>[0-9a-fA-F]*)s?(?P<slot>[0-9a-fA-F]*)?$`)
|
||||
)
|
||||
|
||||
func parseInterfaceName(name string) (prefix string, bus int, num int64) {
|
||||
func cleanInterfaceName(name string) (clean string, namingType NamingType) {
|
||||
if strings.HasPrefix(name, "en") ||
|
||||
strings.HasPrefix(name, "wl") ||
|
||||
strings.HasPrefix(name, "eth") {
|
||||
|
||||
lastSymbol := name[len(name)-1]
|
||||
switch NamingType(lastSymbol) {
|
||||
case NamingTypeBusSlot, NamingTypeHotplug, NamingTypeMac, NamingTypeOnboard:
|
||||
return name[0 : len(name)-1], NamingType(lastSymbol)
|
||||
}
|
||||
}
|
||||
|
||||
return name, NamingTypeOld
|
||||
}
|
||||
|
||||
type NamingType string
|
||||
|
||||
const (
|
||||
NamingTypeOld NamingType = ""
|
||||
NamingTypeOnboard NamingType = "o"
|
||||
NamingTypeBusSlot NamingType = "p"
|
||||
NamingTypeMac NamingType = "x"
|
||||
NamingTypeHotplug NamingType = "s"
|
||||
)
|
||||
|
||||
func (n NamingType) Priority() int {
|
||||
switch n {
|
||||
case NamingTypeOld:
|
||||
return 0
|
||||
case NamingTypeOnboard:
|
||||
return 1
|
||||
case NamingTypeBusSlot:
|
||||
return 2
|
||||
case NamingTypeMac:
|
||||
return 3
|
||||
case NamingTypeHotplug:
|
||||
return 4
|
||||
default:
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
// parseInterfaceName parses interface name and returns prefix, naming type, bus number and slot number
|
||||
// e.g. enp0s3 -> en, NamingTypeBusSlot, 0, 3
|
||||
// bus and slot are interpreted as hex numbers
|
||||
// bus is also used for mac address
|
||||
// in case of enx001122334455 -> en, NamingTypeMac, 0x001122334455, 0
|
||||
func parseInterfaceName(name string) (iface string, namingType NamingType, busNum int64, num int64) {
|
||||
if runtime.GOOS == "windows" {
|
||||
name, num = parseInterfaceWindowsName(name)
|
||||
return
|
||||
}
|
||||
// try new-style naming schema first (enp0s3, wlp2s0, ...)
|
||||
res := ifaceReBusSlot.FindStringSubmatch(name)
|
||||
if len(res) > 0 {
|
||||
if len(res) > 1 {
|
||||
prefix = res[1]
|
||||
}
|
||||
if len(res) > 2 {
|
||||
bus, _ = strconv.Atoi(res[2])
|
||||
}
|
||||
if len(res) > 3 {
|
||||
numHex := res[3]
|
||||
num, _ = strconv.ParseInt(numHex, 16, 32)
|
||||
|
||||
for i, subName := range ifaceReBusSlot.SubexpNames() {
|
||||
if i > 0 && res[i] != "" {
|
||||
switch subName {
|
||||
case "type":
|
||||
iface, namingType = cleanInterfaceName(res[i])
|
||||
case "bus":
|
||||
busNum, _ = strconv.ParseInt(res[i], 16, 64)
|
||||
case "slot": // or mac
|
||||
num, _ = strconv.ParseInt(res[i], 16, 64)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// try old-style naming schema (eth0, wlan0, ...)
|
||||
res = ifaceRe.FindStringSubmatch(name)
|
||||
if len(res) > 1 {
|
||||
prefix = res[1]
|
||||
iface = res[1]
|
||||
}
|
||||
if len(res) > 2 {
|
||||
num, _ = strconv.ParseInt(res[2], 10, 32)
|
||||
}
|
||||
if iface == "" {
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseInterfaceWindowsName(name string) (iface string, num int64) {
|
||||
res := ifaceWindowsRe.FindStringSubmatch(name)
|
||||
if len(res) > 1 {
|
||||
iface = res[1]
|
||||
}
|
||||
if len(res) > 2 {
|
||||
num, _ = strconv.ParseInt(res[2], 10, 32)
|
||||
|
@ -116,42 +197,54 @@ func parseInterfaceName(name string) (prefix string, bus int, num int64) {
|
|||
return
|
||||
}
|
||||
|
||||
func (i InterfacesAddrs) SortWithPriority(priority []string) {
|
||||
less := func(a, b NetInterfaceWithAddrCache) bool {
|
||||
aPrefix, aBus, aNum := parseInterfaceName(a.Name)
|
||||
bPrefix, bBus, bNum := parseInterfaceName(b.Name)
|
||||
type interfaceComparer struct {
|
||||
priority []string
|
||||
}
|
||||
|
||||
aPrioirity := slice.FindPos(priority, aPrefix)
|
||||
bPrioirity := slice.FindPos(priority, bPrefix)
|
||||
func (i interfaceComparer) Compare(a, b string) int {
|
||||
aPrefix, aType, aBus, aNum := parseInterfaceName(a)
|
||||
bPrefix, bType, bBus, bNum := parseInterfaceName(b)
|
||||
|
||||
if aPrefix == bPrefix {
|
||||
return aNum < bNum
|
||||
} else if aPrioirity == -1 && bPrioirity == -1 {
|
||||
// sort alphabetically
|
||||
return aPrefix < bPrefix
|
||||
} else if aPrioirity != -1 && bPrioirity != -1 {
|
||||
// in case we have [eth, wlan]
|
||||
if aPrioirity == bPrioirity {
|
||||
// prioritize eth0 over wlan0
|
||||
return aPrioirity < bPrioirity
|
||||
aPrioirity := slice.FindPos(i.priority, aPrefix)
|
||||
bPrioirity := slice.FindPos(i.priority, bPrefix)
|
||||
|
||||
if aPrioirity != -1 && bPrioirity != -1 || aPrioirity == -1 && bPrioirity == -1 {
|
||||
if aPrefix != bPrefix {
|
||||
if aPrioirity != -1 && bPrioirity != -1 {
|
||||
// prioritize by priority
|
||||
return cmp.Compare(aPrioirity, bPrioirity)
|
||||
} else {
|
||||
// prioritize by prefix
|
||||
return cmp.Compare(aPrefix, bPrefix)
|
||||
}
|
||||
// prioritise wlan1 over eth8
|
||||
if aBus != bBus {
|
||||
return aBus < bBus
|
||||
}
|
||||
return aNum < bNum
|
||||
} else if aPrioirity != -1 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
if aType != bType {
|
||||
return cmp.Compare(aType.Priority(), bType.Priority())
|
||||
}
|
||||
if aBus != bBus {
|
||||
return cmp.Compare(aBus, bBus)
|
||||
}
|
||||
if aNum != bNum {
|
||||
return cmp.Compare(aNum, bNum)
|
||||
}
|
||||
// shouldn't be a case
|
||||
return cmp.Compare(a, b)
|
||||
}
|
||||
slices.SortFunc(i.Interfaces, func(a, b NetInterfaceWithAddrCache) int {
|
||||
if less(a, b) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if aPrioirity == -1 {
|
||||
return 1
|
||||
})
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
func (i InterfacesAddrs) SortInterfacesWithPriority(priority []string) {
|
||||
sorter := interfaceComparer{priority: priority}
|
||||
|
||||
compare := func(a, b NetInterfaceWithAddrCache) int {
|
||||
return sorter.Compare(a.Name, b.Name)
|
||||
}
|
||||
slices.SortFunc(i.Interfaces, compare)
|
||||
}
|
||||
|
||||
func (i InterfacesAddrs) NetInterfaces() []net.Interface {
|
||||
|
@ -223,3 +316,14 @@ func (i InterfacesAddrs) findInterfacePosByIP(ip net.IP) (pos int, equal bool) {
|
|||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
func filterInterfaces(ifaces []NetInterfaceWithAddrCache) []NetInterfaceWithAddrCache {
|
||||
return slice.Filter(ifaces, func(iface NetInterfaceWithAddrCache) bool {
|
||||
if iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagMulticast != 0 && iface.Flags&net.FlagLoopback == 0 {
|
||||
if len(iface.GetAddr()) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package addrs
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_parseInterfaceName(t *testing.T) {
|
||||
type args struct {
|
||||
|
@ -10,18 +15,27 @@ func Test_parseInterfaceName(t *testing.T) {
|
|||
name string
|
||||
args args
|
||||
wantPrefix string
|
||||
wantBus int
|
||||
wantType NamingType
|
||||
wantBus int64
|
||||
wantNum int64
|
||||
}{
|
||||
{"eth0", args{"eth0"}, "eth", 0, 0},
|
||||
{"eth1", args{"eth1"}, "eth", 0, 1},
|
||||
{"eth10", args{"eth10"}, "eth", 0, 10},
|
||||
{"enp0s10", args{"enp0s10"}, "en", 0, 16},
|
||||
{"wlp0s20f3", args{"wlp0s20f3"}, "wl", 0, 8435},
|
||||
{"eth0", args{"eth0"}, "eth", NamingTypeOld, 0, 0},
|
||||
{"eth1", args{"eth1"}, "eth", NamingTypeOld, 0, 1},
|
||||
{"eth10", args{"eth10"}, "eth", NamingTypeOld, 0, 10},
|
||||
{"enp0s10", args{"enp0s10"}, "en", NamingTypeBusSlot, 0, 0x10},
|
||||
{"wlp0s20f3", args{"wlp0s20f3"}, "wl", NamingTypeBusSlot, 0, 0x20f3},
|
||||
{"tun0", args{"tun0"}, "tun", NamingTypeOld, 0, 0},
|
||||
{"tap0", args{"tap0"}, "tap", NamingTypeOld, 0, 0},
|
||||
{"lo0", args{"lo0"}, "lo", NamingTypeOld, 0, 0},
|
||||
{"lo1", args{"lo1"}, "lo", NamingTypeOld, 0, 1},
|
||||
{"lo10", args{"lo10"}, "lo", NamingTypeOld, 0, 10},
|
||||
{"wlx001122334455", args{"wlx001122334455"}, "wl", NamingTypeMac, 0x001122334455, 0},
|
||||
{"wlxffffffffffff", args{"wlxffffffffffff"}, "wl", NamingTypeMac, 0xffffffffffff, 0},
|
||||
{"eno16777736", args{"eno16777736"}, "en", NamingTypeOnboard, 0x16777736, 0},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPrefix, gotBus, gotNum := parseInterfaceName(tt.args.name)
|
||||
gotPrefix, gotType, gotBus, gotNum := parseInterfaceName(tt.args.name)
|
||||
if gotPrefix != tt.wantPrefix {
|
||||
t.Errorf("parseInterfaceName() gotPrefix = %v, want %v", gotPrefix, tt.wantPrefix)
|
||||
}
|
||||
|
@ -31,6 +45,54 @@ func Test_parseInterfaceName(t *testing.T) {
|
|||
if gotNum != tt.wantNum {
|
||||
t.Errorf("parseInterfaceName() gotNum = %v, want %v", gotNum, tt.wantNum)
|
||||
}
|
||||
if gotType != tt.wantType {
|
||||
t.Errorf("parseInterfaceName() gotType = %v, want %v", gotType, tt.wantType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestInterfaceSorterSorting tests sorting a list of interface names using the Compare method.
|
||||
func TestInterfaceSorterSorting(t *testing.T) {
|
||||
sorter := interfaceComparer{
|
||||
priority: []string{"wl", "wlan", "en", "eth", "tun", "tap", "utun"},
|
||||
}
|
||||
|
||||
// List of interfaces to sort
|
||||
interfaces := []string{
|
||||
"tap0",
|
||||
"awdl0",
|
||||
"eth0",
|
||||
"eno1",
|
||||
"wlp2s0",
|
||||
"ens2",
|
||||
"enp0s3",
|
||||
"tun0",
|
||||
"wlan0",
|
||||
"enx001122334455",
|
||||
"wlx001122334455",
|
||||
}
|
||||
|
||||
// Expected order after sorting
|
||||
expected := []string{
|
||||
"wlp2s0", // Wireless LAN on PCI bus
|
||||
"wlx001122334455", // Wireless LAN with MAC address
|
||||
"wlan0", // Old-style Wireless LAN
|
||||
"eno1", // Highest priority (onboard Ethernet)
|
||||
"enp0s3", // PCI bus Ethernet
|
||||
"enx001122334455", // Ethernet with MAC address
|
||||
"ens2", // Hotplug Ethernet
|
||||
"eth0", // Old-style Ethernet
|
||||
"tun0", // VPN TUN interface
|
||||
"tap0", // VPN TAP interface
|
||||
"awdl0",
|
||||
}
|
||||
|
||||
// Sorting the interfaces using the Compare method
|
||||
sort.Slice(interfaces, func(i, j int) bool {
|
||||
return sorter.Compare(interfaces[i], interfaces[j]) < 0
|
||||
})
|
||||
|
||||
// Assert the sorted order matches the expected order
|
||||
assert.Equal(t, expected, interfaces, "The interfaces should be sorted correctly according to priority.")
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@ package addrs
|
|||
import (
|
||||
"net"
|
||||
"slices"
|
||||
|
||||
"github.com/anyproto/anytype-heart/util/slice"
|
||||
)
|
||||
|
||||
func SetInterfaceAddrsGetter(getter InterfaceAddrsGetter) {}
|
||||
|
@ -32,11 +30,7 @@ func GetInterfacesAddrs() (iAddrs InterfacesAddrs, err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
iAddrs.Interfaces = WrapInterfaces(ifaces)
|
||||
|
||||
iAddrs.Interfaces = slice.Filter(iAddrs.Interfaces, func(iface NetInterfaceWithAddrCache) bool {
|
||||
return iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagMulticast != 0
|
||||
})
|
||||
iAddrs.Interfaces = filterInterfaces(WrapInterfaces(ifaces))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ func GetInterfacesAddrs() (addrs InterfacesAddrs, err error) {
|
|||
lock.Unlock()
|
||||
for _, iface := range interfaceGetter.Interfaces() {
|
||||
ifaceWrapped := WrapInterface(iface.Interface)
|
||||
addrs.Interfaces = append(addrs.Interfaces, WrapInterface(iface.Interface))
|
||||
unmaskedAddrs := iface.Addrs
|
||||
ifaceAddrs := make([]net.Addr, 0, len(unmaskedAddrs))
|
||||
for _, addr := range unmaskedAddrs {
|
||||
|
@ -76,6 +75,9 @@ func GetInterfacesAddrs() (addrs InterfacesAddrs, err error) {
|
|||
// inject cached addresses, because we can't get them from net.Interface's Addrs() on android
|
||||
ifaceWrapped.cachedAddrs = ifaceAddrs
|
||||
addrs.Addrs = append(addrs.Addrs, ifaceAddrs...)
|
||||
addrs.Interfaces = append(addrs.Interfaces, ifaceWrapped)
|
||||
}
|
||||
|
||||
addrs.Interfaces = filterInterfaces(addrs.Interfaces)
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue