cmd/cli: extend list of valid interfaces for MacOS

By parsing "networksetup -listallhardwareports" output to get list of
available hardware ports.
This commit is contained in:
Cuong Manh Le authored and Cuong Manh Le committed 2024-08-07 15:51:11 +07:00
1 parent 82e0d4b0c4
commit 5f4a399850
5 files changed
+80 -20

No files matched your search

+27 -17
View File
@@ -43,22 +43,32 @@ func networkServiceName(ifaceName string, r io.Reader) string {
return "" return ""
} }
// validInterface reports whether the *net.Interface is a valid one, which includes en0 -> en10. // validInterface reports whether the *net.Interface is a valid one.
// - en0: physical wireless func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bool {
// - en1: Thunderbolt 1 _, ok := validIfacesMap[iface.Name]
// - en2: Thunderbolt 2 return ok
// - en3: Thunderbolt 3 }
// - en4: Thunderbolt 4
// - ... func validInterfacesMap() map[string]struct{} {
// - en10: iPad b, err := exec.Command("networksetup", "-listallhardwareports").Output()
// if err != nil {
// For full list, see: https://unix.stackexchange.com/questions/603506/what-are-these-ifconfig-interfaces-on-macos return nil
func validInterface(iface *net.Interface) bool {
switch iface.Name {
case "en0", "en1", "en2", "en3", "en4", "en5",
"en6", "en7", "en8", "en9", "en10":
return true
default:
return false
} }
return parseListAllHardwarePorts(bytes.NewReader(b))
}
// parseListAllHardwarePorts parses output of "networksetup -listallhardwareports"
// and returns map presents all hardware ports.
func parseListAllHardwarePorts(r io.Reader) map[string]struct{} {
m := make(map[string]struct{})
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
after, ok := strings.CutPrefix(line, "Device: ")
if !ok {
continue
}
m[after] = struct{}{}
}
return m
} }
+45
View File
@@ -1,6 +1,7 @@
package cli package cli
import ( import (
"maps"
"strings" "strings"
"testing" "testing"
@@ -57,3 +58,47 @@ func Test_networkServiceName(t *testing.T) {
}) })
} }
} }
const listallhardwareportsOutput = `
Hardware Port: Ethernet Adapter (en6)
Device: en6
Ethernet Address: 3a:3e:fc:1e:ab:41
Hardware Port: Ethernet Adapter (en7)
Device: en7
Ethernet Address: 3a:3e:fc:1e:ab:42
Hardware Port: Thunderbolt Bridge
Device: bridge0
Ethernet Address: 36:21:bb:3a:7a:40
Hardware Port: Wi-Fi
Device: en0
Ethernet Address: a0:78:17:68:56:3f
Hardware Port: Thunderbolt 1
Device: en1
Ethernet Address: 36:21:bb:3a:7a:40
Hardware Port: Thunderbolt 2
Device: en2
Ethernet Address: 36:21:bb:3a:7a:44
VLAN Configurations
===================
`
func Test_parseListAllHardwarePorts(t *testing.T) {
expected := map[string]struct{}{
"en0": {},
"en1": {},
"en2": {},
"en6": {},
"en7": {},
"bridge0": {},
}
m := parseListAllHardwarePorts(strings.NewReader(listallhardwareportsOutput))
if !maps.Equal(m, expected) {
t.Errorf("unexpected output, want: %v, got: %v", expected, m)
}
}
+3 -1
View File
@@ -6,4 +6,6 @@ import "net"
func patchNetIfaceName(iface *net.Interface) error { return nil } func patchNetIfaceName(iface *net.Interface) error { return nil }
func validInterface(iface *net.Interface) bool { return true } func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bool { return true }
func validInterfacesMap() map[string]struct{} { return nil }
+3 -1
View File
@@ -10,7 +10,7 @@ func patchNetIfaceName(iface *net.Interface) error {
// validInterface reports whether the *net.Interface is a valid one. // validInterface reports whether the *net.Interface is a valid one.
// On Windows, only physical interfaces are considered valid. // On Windows, only physical interfaces are considered valid.
func validInterface(iface *net.Interface) bool { func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bool {
if iface == nil { if iface == nil {
return false return false
} }
@@ -19,3 +19,5 @@ func validInterface(iface *net.Interface) bool {
} }
return false return false
} }
func validInterfacesMap() map[string]struct{} { return nil }
+2 -1
View File
@@ -868,13 +868,14 @@ func canBeLocalUpstream(addr string) bool {
// the interface that matches excludeIfaceName. The context is used to clarify the // the interface that matches excludeIfaceName. The context is used to clarify the
// log message when error happens. // log message when error happens.
func withEachPhysicalInterfaces(excludeIfaceName, context string, f func(i *net.Interface) error) { func withEachPhysicalInterfaces(excludeIfaceName, context string, f func(i *net.Interface) error) {
validIfacesMap := validInterfacesMap()
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) { interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
// Skip loopback/virtual interface. // Skip loopback/virtual interface.
if i.IsLoopback() || len(i.HardwareAddr) == 0 { if i.IsLoopback() || len(i.HardwareAddr) == 0 {
return return
} }
// Skip invalid interface. // Skip invalid interface.
if !validInterface(i.Interface) { if !validInterface(i.Interface, validIfacesMap) {
return return
} }
netIface := i.Interface netIface := i.Interface