173 lines
4.3 KiB
Go
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,
|
|
}
|
|
}
|