From 5e9b4244e7c3e11509599fbfd9bff7c08dd93739 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 25 Nov 2024 18:38:40 +0700 Subject: [PATCH] cmd/cli: get physical interfaces using Windows WMI --- cmd/cli/net_windows.go | 60 ++++++++++++++++++++++++++++++------- cmd/cli/net_windows_test.go | 42 ++++++++++++++++++++++++++ go.mod | 3 ++ go.sum | 6 ++++ 4 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 cmd/cli/net_windows_test.go diff --git a/cmd/cli/net_windows.go b/cmd/cli/net_windows.go index dc13b08..7174a1f 100644 --- a/cmd/cli/net_windows.go +++ b/cmd/cli/net_windows.go @@ -1,10 +1,13 @@ package cli import ( - "bufio" - "bytes" "net" - "strings" + + "github.com/microsoft/wmi/pkg/base/host" + "github.com/microsoft/wmi/pkg/base/instance" + "github.com/microsoft/wmi/pkg/base/query" + "github.com/microsoft/wmi/pkg/constant" + "github.com/microsoft/wmi/pkg/hardware/network/netadapter" ) func patchNetIfaceName(iface *net.Interface) error { @@ -20,15 +23,52 @@ func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bo // validInterfacesMap returns a set of all physical interfaces. func validInterfacesMap() map[string]struct{} { - out, err := powershell("Get-NetAdapter -Physical | Select-Object -ExpandProperty Name") - if err != nil { - return nil - } m := make(map[string]struct{}) - scanner := bufio.NewScanner(bytes.NewReader(out)) - for scanner.Scan() { - ifaceName := strings.TrimSpace(scanner.Text()) + for _, ifaceName := range validInterfaces() { m[ifaceName] = struct{}{} } return m } + +// validInterfaces returns a list of all physical interfaces. +func validInterfaces() []string { + whost := host.NewWmiLocalHost() + q := query.NewWmiQuery("MSFT_NetAdapter") + instances, err := instance.GetWmiInstancesFromHost(whost, string(constant.StadardCimV2), q) + if err != nil { + mainLog.Load().Err(err).Msg("failed to get wmi network adapter") + return nil + } + var adapters []string + for _, i := range instances { + adapter, err := netadapter.NewNetworkAdapter(i) + if err != nil { + mainLog.Load().Err(err).Msg("failed to get network adapter") + continue + } + // From: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/hh968170(v=vs.85) + // + // "Indicates if a connector is present on the network adapter. This value is set to TRUE + // if this is a physical adapter or FALSE if this is not a physical adapter." + physical, err := adapter.GetPropertyConnectorPresent() + if err != nil { + mainLog.Load().Err(err).Msg("failed to get network adapter connector present property") + continue + } + if !physical { + continue + } + ifaceIdx, err := adapter.GetInterfaceIndex() + if err != nil { + mainLog.Load().Err(err).Msg("failed to get interface index") + continue + } + iff, err := net.InterfaceByIndex(int(ifaceIdx)) + if err != nil { + mainLog.Load().Err(err).Msg("failed to get interface") + continue + } + adapters = append(adapters, iff.Name) + } + return adapters +} diff --git a/cmd/cli/net_windows_test.go b/cmd/cli/net_windows_test.go new file mode 100644 index 0000000..a15f119 --- /dev/null +++ b/cmd/cli/net_windows_test.go @@ -0,0 +1,42 @@ +package cli + +import ( + "bufio" + "bytes" + "slices" + "strings" + "testing" + "time" +) + +func Test_validInterfaces(t *testing.T) { + verbose = 3 + initConsoleLogging() + start := time.Now() + ifaces := validInterfaces() + t.Logf("Using Windows API takes: %d", time.Since(start).Milliseconds()) + + start = time.Now() + ifacesPowershell := validInterfacesPowershell() + t.Logf("Using Powershell takes: %d", time.Since(start).Milliseconds()) + + slices.Sort(ifaces) + slices.Sort(ifacesPowershell) + if !slices.Equal(ifaces, ifacesPowershell) { + t.Fatalf("result mismatch, want: %v, got: %v", ifacesPowershell, ifaces) + } +} + +func validInterfacesPowershell() []string { + out, err := powershell("Get-NetAdapter -Physical | Select-Object -ExpandProperty Name") + if err != nil { + return nil + } + var res []string + scanner := bufio.NewScanner(bytes.NewReader(out)) + for scanner.Scan() { + ifaceName := strings.TrimSpace(scanner.Text()) + res = append(res, ifaceName) + } + return res +} diff --git a/go.mod b/go.mod index 84b58c4..58b67c5 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 github.com/kardianos/service v1.2.1 github.com/mdlayher/ndp v1.0.1 + github.com/microsoft/wmi v0.24.5 github.com/miekg/dns v1.1.58 github.com/minio/selfupdate v0.6.0 github.com/olekukonko/tablewriter v0.0.5 @@ -49,6 +50,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect @@ -72,6 +74,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect diff --git a/go.sum b/go.sum index ebb9042..cb1d9ee 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -227,6 +229,8 @@ github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= +github.com/microsoft/wmi v0.24.5 h1:NT+WqhjKbEcg3ldmDsRMarWgHGkpeW+gMopSCfON0kM= +github.com/microsoft/wmi v0.24.5/go.mod h1:1zbdSF0A+5OwTUII5p3hN7/K6KF2m3o27pSG6Y51VU8= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU= @@ -245,6 +249,7 @@ github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -478,6 +483,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=