package fingerprint import ( "fmt" "io/ioutil" "os/exec" "regexp" "strconv" "strings" ) // linkSpeedSys parses link speed in Mb/s from /sys. func (f *NetworkFingerprint) linkSpeedSys(device string) int { path := fmt.Sprintf("/sys/class/net/%s/speed", device) // Read contents of the device/speed file content, err := ioutil.ReadFile(path) if err != nil { f.logger.Debug("unable to read link speed", "path", path) return 0 } lines := strings.Split(string(content), "\n") mbs, err := strconv.Atoi(lines[0]) if err != nil || mbs <= 0 { f.logger.Debug("unable to parse link speed", "path", path) return 0 } return mbs } // linkSpeed returns link speed in Mb/s, or 0 when unable to determine it. func (f *NetworkFingerprint) linkSpeed(device string) int { // Use LookPath to find the ethtool in the systems $PATH // If it's not found or otherwise errors, LookPath returns and empty string // and an error we can ignore for our purposes ethtoolPath, _ := exec.LookPath("ethtool") if ethtoolPath != "" { if speed := f.linkSpeedEthtool(ethtoolPath, device); speed > 0 { return speed } } // Fall back on checking a system file for link speed. return f.linkSpeedSys(device) } // linkSpeedEthtool determines link speed in Mb/s with 'ethtool'. func (f *NetworkFingerprint) linkSpeedEthtool(path, device string) int { outBytes, err := exec.Command(path, device).Output() if err != nil { f.logger.Warn("error calling ethtool", "error", err, "path", path, "device", device) return 0 } output := strings.TrimSpace(string(outBytes)) re := regexp.MustCompile("Speed: [0-9]+[a-zA-Z]+/s") m := re.FindString(output) if m == "" { // no matches found, output may be in a different format f.logger.Warn("unable to parse speed", "path", path, "device", device) return 0 } // Split and trim the Mb/s unit from the string output args := strings.Split(m, ": ") raw := strings.TrimSuffix(args[1], "Mb/s") // convert to Mb/s mbs, err := strconv.Atoi(raw) if err != nil || mbs <= 0 { f.logger.Warn("unable to parse Mb/s", "path", path, "device", device) return 0 } return mbs }