2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2016-05-09 06:25:01 +00:00
|
|
|
package fingerprint
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-03-08 19:25:10 +00:00
|
|
|
"os"
|
2016-05-09 06:25:01 +00:00
|
|
|
"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
|
2023-03-08 19:25:10 +00:00
|
|
|
content, err := os.ReadFile(path)
|
2016-05-09 06:25:01 +00:00
|
|
|
if err != nil {
|
2021-09-16 14:48:31 +00:00
|
|
|
f.logger.Debug("unable to read link speed", "path", path, "device", device)
|
2016-05-09 06:25:01 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
lines := strings.Split(string(content), "\n")
|
|
|
|
mbs, err := strconv.Atoi(lines[0])
|
|
|
|
if err != nil || mbs <= 0 {
|
2021-09-16 14:48:31 +00:00
|
|
|
f.logger.Debug("unable to parse link speed", "path", path, "device", device)
|
2016-05-09 06:25:01 +00:00
|
|
|
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 {
|
2018-09-18 00:44:25 +00:00
|
|
|
f.logger.Warn("error calling ethtool", "error", err, "path", path, "device", device)
|
2016-05-09 06:25:01 +00:00
|
|
|
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
|
2018-09-18 00:44:25 +00:00
|
|
|
f.logger.Warn("unable to parse speed", "path", path, "device", device)
|
2016-05-09 06:25:01 +00:00
|
|
|
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 {
|
2018-09-18 00:44:25 +00:00
|
|
|
f.logger.Warn("unable to parse Mb/s", "path", path, "device", device)
|
2016-05-09 06:25:01 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return mbs
|
|
|
|
}
|