Files
scrutiny/vendor/github.com/jaypipes/ghw/net_linux.go
T
2020-08-21 06:31:48 +00:00

173 lines
4.3 KiB
Go

// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//
package ghw
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
)
const (
_WARN_ETHTOOL_NOT_INSTALLED = `ethtool not installed. Cannot grab NIC capabilities`
)
func (ctx *context) netFillInfo(info *NetworkInfo) error {
info.NICs = ctx.nics()
return nil
}
// NICS has been deprecated in 0.2. Please use the NetworkInfo.NICs attribute.
// TODO(jaypipes): Remove in 1.0.
func NICs() []*NIC {
msg := `
The NICs() function has been DEPRECATED and will be removed in the 1.0 release
of ghw. Please use the NetworkInfo.NICs attribute.
`
warn(msg)
ctx := contextFromEnv()
return ctx.nics()
}
func (ctx *context) nics() []*NIC {
nics := make([]*NIC, 0)
files, err := ioutil.ReadDir(ctx.pathSysClassNet())
if err != nil {
return nics
}
etInstalled := ethtoolInstalled()
if !etInstalled {
warn(_WARN_ETHTOOL_NOT_INSTALLED)
}
for _, file := range files {
filename := file.Name()
// Ignore loopback...
if filename == "lo" {
continue
}
netPath := filepath.Join(ctx.pathSysClassNet(), filename)
dest, _ := os.Readlink(netPath)
isVirtual := false
if strings.Contains(dest, "devices/virtual/net") {
isVirtual = true
}
nic := &NIC{
Name: filename,
IsVirtual: isVirtual,
}
mac := ctx.netDeviceMacAddress(filename)
nic.MacAddress = mac
if etInstalled {
nic.Capabilities = ctx.netDeviceCapabilities(filename)
} else {
nic.Capabilities = []*NICCapability{}
}
nics = append(nics, nic)
}
return nics
}
func (ctx *context) netDeviceMacAddress(dev string) string {
// Instead of use udevadm, we can get the device's MAC address by examing
// the /sys/class/net/$DEVICE/address file in sysfs. However, for devices
// that have addr_assign_type != 0, return None since the MAC address is
// random.
aatPath := filepath.Join(ctx.pathSysClassNet(), dev, "addr_assign_type")
contents, err := ioutil.ReadFile(aatPath)
if err != nil {
return ""
}
if strings.TrimSpace(string(contents)) != "0" {
return ""
}
addrPath := filepath.Join(ctx.pathSysClassNet(), dev, "address")
contents, err = ioutil.ReadFile(addrPath)
if err != nil {
return ""
}
return strings.TrimSpace(string(contents))
}
func ethtoolInstalled() bool {
_, err := exec.LookPath("ethtool")
return err == nil
}
func (ctx *context) netDeviceCapabilities(dev string) []*NICCapability {
caps := make([]*NICCapability, 0)
path, _ := exec.LookPath("ethtool")
cmd := exec.Command(path, "-k", dev)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
msg := fmt.Sprintf("could not grab NIC capabilities for %s: %s", dev, err)
warn(msg)
return caps
}
// The out variable will now contain something that looks like the
// following.
//
// Features for enp58s0f1:
// rx-checksumming: on
// tx-checksumming: off
// tx-checksum-ipv4: off
// tx-checksum-ip-generic: off [fixed]
// tx-checksum-ipv6: off
// tx-checksum-fcoe-crc: off [fixed]
// tx-checksum-sctp: off [fixed]
// scatter-gather: off
// tx-scatter-gather: off
// tx-scatter-gather-fraglist: off [fixed]
// tcp-segmentation-offload: off
// tx-tcp-segmentation: off
// tx-tcp-ecn-segmentation: off [fixed]
// tx-tcp-mangleid-segmentation: off
// tx-tcp6-segmentation: off
// < snipped >
scanner := bufio.NewScanner(&out)
// Skip the first line...
scanner.Scan()
for scanner.Scan() {
line := strings.TrimPrefix(scanner.Text(), "\t")
caps = append(caps, netParseEthtoolFeature(line))
}
return caps
}
// netParseEthtoolFeature parses a line from the ethtool -k output and returns
// a NICCapability.
//
// The supplied line will look like the following:
//
// tx-checksum-ip-generic: off [fixed]
//
// [fixed] indicates that the feature may not be turned on/off. Note: it makes
// no difference whether a privileged user runs `ethtool -k` when determining
// whether [fixed] appears for a feature.
func netParseEthtoolFeature(line string) *NICCapability {
parts := strings.Fields(line)
cap := strings.TrimSuffix(parts[0], ":")
enabled := parts[1] == "on"
fixed := len(parts) == 3 && parts[2] == "[fixed]"
return &NICCapability{
Name: cap,
IsEnabled: enabled,
CanEnable: !fixed,
}
}