package collector import ( "bee/audit/internal/schema" "log/slog" "os" "path/filepath" "regexp" "strconv" "strings" ) var ( ethtoolModuleQuery = func(iface string) (string, error) { out, err := commandOutputWithTimeout(nicProbeTimeout, "ethtool", "-m", iface) if err != nil { return "", err } return string(out), nil } readNetAddressFile = func(iface string) (string, error) { path := filepath.Join("/sys/class/net", iface, "address") raw, err := os.ReadFile(path) if err != nil { return "", err } return strings.TrimSpace(string(raw)), nil } ) func enrichPCIeWithNICTelemetry(devs []schema.HardwarePCIeDevice) []schema.HardwarePCIeDevice { enriched := 0 for i := range devs { if !isNICDevice(devs[i]) || devs[i].BDF == nil { continue } bdf := normalizePCIeBDF(*devs[i].BDF) if bdf == "" { continue } ifaces := netIfacesByBDF(bdf) if len(ifaces) == 0 { continue } iface := ifaces[0] devs[i].MacAddresses = collectInterfaceMACs(ifaces) if devs[i].SerialNumber == nil { if serial := queryPCIDeviceSerial(bdf); serial != "" { devs[i].SerialNumber = &serial } } if devs[i].Firmware == nil { if out, err := ethtoolInfoQuery(iface); err == nil { if fw := parseEthtoolFirmwareInfo(out); fw != "" { devs[i].Firmware = &fw } } } if out, err := ethtoolModuleQuery(iface); err == nil { if injectSFPDOMTelemetry(&devs[i], out) { enriched++ continue } } if len(devs[i].MacAddresses) > 0 || devs[i].Firmware != nil { enriched++ } } slog.Info("nic: telemetry enriched", "count", enriched) return devs } func isNICDevice(dev schema.HardwarePCIeDevice) bool { if dev.DeviceClass == nil { return false } c := strings.TrimSpace(*dev.DeviceClass) return isNICClass(c) || strings.EqualFold(c, "FibreChannelController") } func collectInterfaceMACs(ifaces []string) []string { seen := map[string]struct{}{} var out []string for _, iface := range ifaces { mac, err := readNetAddressFile(iface) if err != nil || mac == "" { continue } mac = strings.ToLower(strings.TrimSpace(mac)) if _, ok := seen[mac]; ok { continue } seen[mac] = struct{}{} out = append(out, mac) } return out } var floatRe = regexp.MustCompile(`[-+]?[0-9]*\.?[0-9]+`) func injectSFPDOMTelemetry(dev *schema.HardwarePCIeDevice, raw string) bool { var changed bool for _, line := range strings.Split(raw, "\n") { trimmed := strings.TrimSpace(line) if trimmed == "" { continue } idx := strings.Index(trimmed, ":") if idx < 0 { continue } key := strings.ToLower(strings.TrimSpace(trimmed[:idx])) val := strings.TrimSpace(trimmed[idx+1:]) if val == "" || strings.EqualFold(val, "not supported") || strings.EqualFold(val, "unknown") { continue } switch { case key == "identifier": s := parseSFPIdentifier(val) dev.SFPIdentifier = &s t := true dev.SFPPresent = &t changed = true case key == "connector": s := parseSFPConnector(val) dev.SFPConnector = &s changed = true case key == "vendor name": s := strings.TrimSpace(val) dev.SFPVendor = &s changed = true case key == "vendor pn": s := strings.TrimSpace(val) dev.SFPPartNumber = &s changed = true case key == "vendor sn": s := strings.TrimSpace(val) dev.SFPSerialNumber = &s changed = true case strings.Contains(key, "laser wavelength"): if f, ok := firstFloat(val); ok { dev.SFPWavelengthNM = &f changed = true } case strings.Contains(key, "module temperature"): if f, ok := firstFloat(val); ok { dev.SFPTemperatureC = &f changed = true } case strings.Contains(key, "laser output power"): if f, ok := dbmValue(val); ok { dev.SFPTXPowerDBM = &f changed = true } case strings.Contains(key, "receiver signal"): if f, ok := dbmValue(val); ok { dev.SFPRXPowerDBM = &f changed = true } case strings.Contains(key, "module voltage"): if f, ok := firstFloat(val); ok { dev.SFPVoltageV = &f changed = true } case strings.Contains(key, "laser bias current"): if f, ok := firstFloat(val); ok { dev.SFPBiasMA = &f changed = true } } } return changed } // parseSFPIdentifier extracts the human-readable transceiver type from the // raw ethtool identifier line, e.g. "0x03 (SFP)" → "SFP". func parseSFPIdentifier(val string) string { if s := extractParens(val); s != "" { return s } return val } // parseSFPConnector extracts the connector type from the raw ethtool line, // e.g. "0x07 (LC)" → "LC". func parseSFPConnector(val string) string { if s := extractParens(val); s != "" { return s } return val } var parenRe = regexp.MustCompile(`\(([^)]+)\)`) func extractParens(s string) string { m := parenRe.FindStringSubmatch(s) if len(m) < 2 { return "" } return strings.TrimSpace(m[1]) } func parseSFPDOM(raw string) map[string]any { dev := schema.HardwarePCIeDevice{} if !injectSFPDOMTelemetry(&dev, raw) { return map[string]any{} } out := map[string]any{} if dev.SFPPresent != nil { out["sfp_present"] = *dev.SFPPresent } if dev.SFPIdentifier != nil { out["sfp_identifier"] = *dev.SFPIdentifier } if dev.SFPConnector != nil { out["sfp_connector"] = *dev.SFPConnector } if dev.SFPVendor != nil { out["sfp_vendor"] = *dev.SFPVendor } if dev.SFPPartNumber != nil { out["sfp_part_number"] = *dev.SFPPartNumber } if dev.SFPSerialNumber != nil { out["sfp_serial_number"] = *dev.SFPSerialNumber } if dev.SFPWavelengthNM != nil { out["sfp_wavelength_nm"] = *dev.SFPWavelengthNM } if dev.SFPTemperatureC != nil { out["sfp_temperature_c"] = *dev.SFPTemperatureC } if dev.SFPTXPowerDBM != nil { out["sfp_tx_power_dbm"] = *dev.SFPTXPowerDBM } if dev.SFPRXPowerDBM != nil { out["sfp_rx_power_dbm"] = *dev.SFPRXPowerDBM } if dev.SFPVoltageV != nil { out["sfp_voltage_v"] = *dev.SFPVoltageV } if dev.SFPBiasMA != nil { out["sfp_bias_ma"] = *dev.SFPBiasMA } return out } func firstFloat(raw string) (float64, bool) { m := floatRe.FindString(raw) if m == "" { return 0, false } v, err := strconv.ParseFloat(m, 64) if err != nil { return 0, false } return v, true } func dbmValue(raw string) (float64, bool) { parts := strings.Split(strings.ToLower(raw), "dbm") if len(parts) == 0 { return 0, false } for i := len(parts) - 1; i >= 0; i-- { candidate := parts[i] matches := floatRe.FindAllString(candidate, -1) if len(matches) == 0 { continue } v, err := strconv.ParseFloat(matches[len(matches)-1], 64) if err == nil { return v, true } } return 0, false }