From f3c14cd893136af3bc8ccebd179763b3657d133f Mon Sep 17 00:00:00 2001 From: Mikhail Chusavitin Date: Sat, 4 Apr 2026 15:23:15 +0300 Subject: [PATCH] Harden NIC probing for empty SFP ports --- audit/internal/collector/nic_mellanox.go | 29 +++++++++++++- audit/internal/collector/nic_telemetry.go | 12 +++--- .../internal/collector/nic_telemetry_test.go | 39 +++++++++++++++++++ 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/audit/internal/collector/nic_mellanox.go b/audit/internal/collector/nic_mellanox.go index 28f4821..80e4f9c 100644 --- a/audit/internal/collector/nic_mellanox.go +++ b/audit/internal/collector/nic_mellanox.go @@ -2,18 +2,21 @@ package collector import ( "bee/audit/internal/schema" + "context" "log/slog" "os" "os/exec" "path/filepath" "strings" + "time" ) const mellanoxVendorID = 0x15b3 +const nicProbeTimeout = 2 * time.Second var ( mstflintQuery = func(bdf string) (string, error) { - out, err := exec.Command("mstflint", "-d", bdf, "q").Output() + out, err := commandOutputWithTimeout(nicProbeTimeout, "mstflint", "-d", bdf, "q") if err != nil { return "", err } @@ -21,7 +24,7 @@ var ( } ethtoolInfoQuery = func(iface string) (string, error) { - out, err := exec.Command("ethtool", "-i", iface).Output() + out, err := commandOutputWithTimeout(nicProbeTimeout, "ethtool", "-i", iface) if err != nil { return "", err } @@ -29,6 +32,14 @@ var ( } netIfacesByBDF = listNetIfacesByBDF + readNetCarrierFile = func(iface string) (string, error) { + path := filepath.Join("/sys/class/net", iface, "carrier") + raw, err := os.ReadFile(path) + if err != nil { + return "", err + } + return strings.TrimSpace(string(raw)), nil + } ) // enrichPCIeWithMellanox enriches Mellanox/NVIDIA Networking devices with @@ -162,3 +173,17 @@ func listNetIfacesByBDF(bdf string) []string { } return ifaces } + +func commandOutputWithTimeout(timeout time.Duration, name string, args ...string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + return exec.CommandContext(ctx, name, args...).Output() +} + +func interfaceHasCarrier(iface string) bool { + raw, err := readNetCarrierFile(iface) + if err != nil { + return false + } + return strings.TrimSpace(raw) == "1" +} diff --git a/audit/internal/collector/nic_telemetry.go b/audit/internal/collector/nic_telemetry.go index 2ddfabf..ee82199 100644 --- a/audit/internal/collector/nic_telemetry.go +++ b/audit/internal/collector/nic_telemetry.go @@ -12,7 +12,7 @@ import ( var ( ethtoolModuleQuery = func(iface string) (string, error) { - out, err := raidToolQuery("ethtool", "-m", iface) + out, err := commandOutputWithTimeout(nicProbeTimeout, "ethtool", "-m", iface) if err != nil { return "", err } @@ -58,10 +58,12 @@ func enrichPCIeWithNICTelemetry(devs []schema.HardwarePCIeDevice) []schema.Hardw } } - if out, err := ethtoolModuleQuery(iface); err == nil { - if injectSFPDOMTelemetry(&devs[i], out) { - enriched++ - continue + if interfaceHasCarrier(iface) { + if out, err := ethtoolModuleQuery(iface); err == nil { + if injectSFPDOMTelemetry(&devs[i], out) { + enriched++ + continue + } } } if len(devs[i].MacAddresses) > 0 || devs[i].Firmware != nil { diff --git a/audit/internal/collector/nic_telemetry_test.go b/audit/internal/collector/nic_telemetry_test.go index 6873a49..22f9a2e 100644 --- a/audit/internal/collector/nic_telemetry_test.go +++ b/audit/internal/collector/nic_telemetry_test.go @@ -57,6 +57,7 @@ func TestEnrichPCIeWithNICTelemetryAddsSerialFallback(t *testing.T) { origReadMAC := readNetAddressFile origEth := ethtoolInfoQuery origModule := ethtoolModuleQuery + origCarrier := readNetCarrierFile t.Cleanup(func() { queryPCILSPCIDetail = origDetail readPCIVPDFile = origVPD @@ -64,6 +65,7 @@ func TestEnrichPCIeWithNICTelemetryAddsSerialFallback(t *testing.T) { readNetAddressFile = origReadMAC ethtoolInfoQuery = origEth ethtoolModuleQuery = origModule + readNetCarrierFile = origCarrier }) queryPCILSPCIDetail = func(bdf string) (string, error) { @@ -82,6 +84,7 @@ func TestEnrichPCIeWithNICTelemetryAddsSerialFallback(t *testing.T) { } return "aa:bb:cc:dd:ee:ff", nil } + readNetCarrierFile = func(string) (string, error) { return "1", nil } ethtoolInfoQuery = func(string) (string, error) { return "", fmt.Errorf("skip firmware") } ethtoolModuleQuery = func(string) (string, error) { return "", fmt.Errorf("skip optics") } @@ -101,6 +104,42 @@ func TestEnrichPCIeWithNICTelemetryAddsSerialFallback(t *testing.T) { } } +func TestEnrichPCIeWithNICTelemetrySkipsModuleQueryWithoutCarrier(t *testing.T) { + origIfaces := netIfacesByBDF + origReadMAC := readNetAddressFile + origEth := ethtoolInfoQuery + origModule := ethtoolModuleQuery + origCarrier := readNetCarrierFile + t.Cleanup(func() { + netIfacesByBDF = origIfaces + readNetAddressFile = origReadMAC + ethtoolInfoQuery = origEth + ethtoolModuleQuery = origModule + readNetCarrierFile = origCarrier + }) + + netIfacesByBDF = func(string) []string { return []string{"eth0"} } + readNetAddressFile = func(string) (string, error) { return "aa:bb:cc:dd:ee:ff", nil } + readNetCarrierFile = func(string) (string, error) { return "0", nil } + ethtoolInfoQuery = func(string) (string, error) { return "", fmt.Errorf("skip firmware") } + ethtoolModuleQuery = func(string) (string, error) { + t.Fatal("ethtool -m should not be called without carrier") + return "", nil + } + + class := "EthernetController" + bdf := "0000:18:00.0" + devs := []schema.HardwarePCIeDevice{{ + DeviceClass: &class, + BDF: &bdf, + }} + + out := enrichPCIeWithNICTelemetry(devs) + if len(out[0].MacAddresses) != 1 || out[0].MacAddresses[0] != "aa:bb:cc:dd:ee:ff" { + t.Fatalf("mac_addresses=%v", out[0].MacAddresses) + } +} + func TestDBMValue(t *testing.T) { tests := []struct { in string