diff --git a/go.mod b/go.mod index 998bde10..e7df0985 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/mattn/go-shellwords v1.0.3 github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a - github.com/safchain/ethtool v0.0.0-20170622225139-7ff1ba29eca2 + github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 github.com/sirupsen/logrus v1.0.6 // indirect github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect diff --git a/go.sum b/go.sum index 023052ea..a36fe564 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a h1:KfNOeFvoAssuZLT7Int github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/safchain/ethtool v0.0.0-20170622225139-7ff1ba29eca2 h1:f10KcdY8NPt2w0/M2o+O9uCiH8sHpS6OVAHcf4BPL7Y= github.com/safchain/ethtool v0.0.0-20170622225139-7ff1ba29eca2/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf h1:3J37+NPjNyGW/dbfXtj3yWuF9OEepIdGOXRaJGbORV8= diff --git a/vendor/github.com/safchain/ethtool/.gitignore b/vendor/github.com/safchain/ethtool/.gitignore index daf913b1..db6cadff 100644 --- a/vendor/github.com/safchain/ethtool/.gitignore +++ b/vendor/github.com/safchain/ethtool/.gitignore @@ -22,3 +22,6 @@ _testmain.go *.exe *.test *.prof + +# Skip compiled example binary file +/example/example diff --git a/vendor/github.com/safchain/ethtool/Makefile b/vendor/github.com/safchain/ethtool/Makefile new file mode 100644 index 00000000..67d2da39 --- /dev/null +++ b/vendor/github.com/safchain/ethtool/Makefile @@ -0,0 +1,4 @@ +all: build + +build: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build diff --git a/vendor/github.com/safchain/ethtool/README.md b/vendor/github.com/safchain/ethtool/README.md index b089edad..1f146229 100644 --- a/vendor/github.com/safchain/ethtool/README.md +++ b/vendor/github.com/safchain/ethtool/README.md @@ -33,6 +33,7 @@ func main() { if err != nil { panic(err.Error()) } + defer ethHandle.Close() // Retrieve tx from eth0 stats, err := ethHandle.Stats("eth0") diff --git a/vendor/github.com/safchain/ethtool/ethtool.go b/vendor/github.com/safchain/ethtool/ethtool.go index 1f8828fa..8dcc78c0 100644 --- a/vendor/github.com/safchain/ethtool/ethtool.go +++ b/vendor/github.com/safchain/ethtool/ethtool.go @@ -27,7 +27,9 @@ package ethtool import ( "bytes" + "encoding/hex" "fmt" + "strings" "syscall" "unsafe" ) @@ -46,20 +48,34 @@ const ( const ( ETH_GSTRING_LEN = 32 ETH_SS_STATS = 1 + ETH_SS_FEATURES = 4 ETHTOOL_GDRVINFO = 0x00000003 ETHTOOL_GSTRINGS = 0x0000001b ETHTOOL_GSTATS = 0x0000001d // other CMDs from ethtool-copy.h of ethtool-3.5 package - ETHTOOL_GSET = 0x00000001 /* Get settings. */ - ETHTOOL_SSET = 0x00000002 /* Set settings. */ - ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */ - ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */ + ETHTOOL_GSET = 0x00000001 /* Get settings. */ + ETHTOOL_SSET = 0x00000002 /* Set settings. */ + ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */ + ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */ + /* Get link status for host, i.e. whether the interface *and* the + * physical port (if there is one) are up (ethtool_value). */ + ETHTOOL_GLINK = 0x0000000a + ETHTOOL_GMODULEINFO = 0x00000042 /* Get plug-in module information */ + ETHTOOL_GMODULEEEPROM = 0x00000043 /* Get plug-in module eeprom */ + ETHTOOL_GPERMADDR = 0x00000020 + ETHTOOL_GFEATURES = 0x0000003a /* Get device offload settings */ + ETHTOOL_SFEATURES = 0x0000003b /* Change device offload settings */ + ETHTOOL_GFLAGS = 0x00000025 /* Get flags bitmap(ethtool_value) */ + ETHTOOL_GSSET_INFO = 0x00000037 /* Get string set info */ ) // MAX_GSTRINGS maximum number of stats entries that ethtool can // retrieve currently. const ( - MAX_GSTRINGS = 1000 + MAX_GSTRINGS = 1000 + MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32 + EEPROM_LEN = 640 + PERMADDR_LEN = 32 ) type ifreq struct { @@ -67,6 +83,38 @@ type ifreq struct { ifr_data uintptr } +// following structures comes from uapi/linux/ethtool.h +type ethtoolSsetInfo struct { + cmd uint32 + reserved uint32 + sset_mask uint32 + data uintptr +} + +type ethtoolGetFeaturesBlock struct { + available uint32 + requested uint32 + active uint32 + never_changed uint32 +} + +type ethtoolGfeatures struct { + cmd uint32 + size uint32 + blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock +} + +type ethtoolSetFeaturesBlock struct { + valid uint32 + requested uint32 +} + +type ethtoolSfeatures struct { + cmd uint32 + size uint32 + blocks [MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock +} + type ethtoolDrvInfo struct { cmd uint32 driver [32]byte @@ -95,11 +143,37 @@ type ethtoolStats struct { data [MAX_GSTRINGS]uint64 } +type ethtoolEeprom struct { + cmd uint32 + magic uint32 + offset uint32 + len uint32 + data [EEPROM_LEN]byte +} + +type ethtoolModInfo struct { + cmd uint32 + tpe uint32 + eeprom_len uint32 + reserved [8]uint32 +} + +type ethtoolLink struct { + cmd uint32 + data uint32 +} + +type ethtoolPermAddr struct { + cmd uint32 + size uint32 + data [PERMADDR_LEN]byte +} + type Ethtool struct { fd int } -// DriverName returns the driver name of the given interface. +// DriverName returns the driver name of the given interface name. func (e *Ethtool) DriverName(intf string) (string, error) { info, err := e.getDriverInfo(intf) if err != nil { @@ -108,7 +182,7 @@ func (e *Ethtool) DriverName(intf string) (string, error) { return string(bytes.Trim(info.driver[:], "\x00")), nil } -// BusInfo returns the bus info of the given interface. +// BusInfo returns the bus information of the given interface name. func (e *Ethtool) BusInfo(intf string) (string, error) { info, err := e.getDriverInfo(intf) if err != nil { @@ -117,44 +191,259 @@ func (e *Ethtool) BusInfo(intf string) (string, error) { return string(bytes.Trim(info.bus_info[:], "\x00")), nil } -func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) { - drvinfo := ethtoolDrvInfo{ - cmd: ETHTOOL_GDRVINFO, +// ModuleEeprom returns Eeprom information of the given interface name. +func (e *Ethtool) ModuleEeprom(intf string) ([]byte, error) { + eeprom, _, err := e.getModuleEeprom(intf) + if err != nil { + return nil, err } + return eeprom.data[:eeprom.len], nil +} + +// ModuleEeprom returns Eeprom information of the given interface name. +func (e *Ethtool) ModuleEepromHex(intf string) (string, error) { + eeprom, _, err := e.getModuleEeprom(intf) + if err != nil { + return "", err + } + + return hex.EncodeToString(eeprom.data[:eeprom.len]), nil +} + +// DriverInfo returns driver information of the given interface name. +func (e *Ethtool) DriverInfo(intf string) (ethtoolDrvInfo, error) { + drvInfo, err := e.getDriverInfo(intf) + if err != nil { + return ethtoolDrvInfo{}, err + } + + return drvInfo, nil +} + +// PermAddr returns permanent address of the given interface name. +func (e *Ethtool) PermAddr(intf string) (string, error) { + permAddr, err := e.getPermAddr(intf) + if err != nil { + return "", err + } + + if permAddr.data[0] == 0 && permAddr.data[1] == 0 && + permAddr.data[2] == 0 && permAddr.data[3] == 0 && + permAddr.data[4] == 0 && permAddr.data[5] == 0 { + return "", nil + } + + return fmt.Sprintf("%x:%x:%x:%x:%x:%x", + permAddr.data[0:1], + permAddr.data[1:2], + permAddr.data[2:3], + permAddr.data[3:4], + permAddr.data[4:5], + permAddr.data[5:6], + ), nil +} + +func (e *Ethtool) ioctl(intf string, data uintptr) error { var name [IFNAMSIZ]byte copy(name[:], []byte(intf)) ifr := ifreq{ ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(&drvinfo)), + ifr_data: data, } _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) if ep != 0 { - return ethtoolDrvInfo{}, syscall.Errno(ep) + return syscall.Errno(ep) + } + + return nil +} + +func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) { + drvinfo := ethtoolDrvInfo{ + cmd: ETHTOOL_GDRVINFO, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil { + return ethtoolDrvInfo{}, err } return drvinfo, nil } +func (e *Ethtool) getPermAddr(intf string) (ethtoolPermAddr, error) { + permAddr := ethtoolPermAddr{ + cmd: ETHTOOL_GPERMADDR, + size: PERMADDR_LEN, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&permAddr))); err != nil { + return ethtoolPermAddr{}, err + } + + return permAddr, nil +} + +func (e *Ethtool) getModuleEeprom(intf string) (ethtoolEeprom, ethtoolModInfo, error) { + modInfo := ethtoolModInfo{ + cmd: ETHTOOL_GMODULEINFO, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&modInfo))); err != nil { + return ethtoolEeprom{}, ethtoolModInfo{}, err + } + + eeprom := ethtoolEeprom{ + cmd: ETHTOOL_GMODULEEEPROM, + len: modInfo.eeprom_len, + offset: 0, + } + + if modInfo.eeprom_len > EEPROM_LEN { + return ethtoolEeprom{}, ethtoolModInfo{}, fmt.Errorf("eeprom size: %d is larger than buffer size: %d", modInfo.eeprom_len, EEPROM_LEN) + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&eeprom))); err != nil { + return ethtoolEeprom{}, ethtoolModInfo{}, err + } + + return eeprom, modInfo, nil +} + +func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) bool { + return (blocks)[index/32].active&(1<<(index%32)) != 0 +} + +func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) { + blockIndex, bitIndex := index/32, index%32 + + blocks[blockIndex].valid |= 1 << bitIndex + + if value { + blocks[blockIndex].requested |= 1 << bitIndex + } else { + blocks[blockIndex].requested &= ^(1 << bitIndex) + } +} + +// FeatureNames shows supported features by their name. +func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) { + ssetInfo := ethtoolSsetInfo{ + cmd: ETHTOOL_GSSET_INFO, + sset_mask: 1 << ETH_SS_FEATURES, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ssetInfo))); err != nil { + return nil, err + } + + length := uint32(ssetInfo.data) + if length == 0 { + return map[string]uint{}, nil + } else if length > MAX_GSTRINGS { + return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, length) + } + + gstrings := ethtoolGStrings{ + cmd: ETHTOOL_GSTRINGS, + string_set: ETH_SS_FEATURES, + len: length, + data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{}, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil { + return nil, err + } + + var result = make(map[string]uint) + for i := 0; i != int(length); i++ { + b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN] + key := string(bytes.Trim(b, "\x00")) + if key != "" { + result[key] = uint(i) + } + } + + return result, nil +} + +// Features retrieves features of the given interface name. +func (e *Ethtool) Features(intf string) (map[string]bool, error) { + names, err := e.FeatureNames(intf) + if err != nil { + return nil, err + } + + length := uint32(len(names)) + if length == 0 { + return map[string]bool{}, nil + } + + features := ethtoolGfeatures{ + cmd: ETHTOOL_GFEATURES, + size: (length + 32 - 1) / 32, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&features))); err != nil { + return nil, err + } + + var result = make(map[string]bool, length) + for key, index := range names { + result[key] = isFeatureBitSet(features.blocks, index) + } + + return result, nil +} + +// Change requests a change in the given device's features. +func (e *Ethtool) Change(intf string, config map[string]bool) error { + names, err := e.FeatureNames(intf) + if err != nil { + return err + } + + length := uint32(len(names)) + + features := ethtoolSfeatures{ + cmd: ETHTOOL_SFEATURES, + size: (length + 32 - 1) / 32, + } + + for key, value := range config { + if index, ok := names[key]; ok { + setFeatureBit(&features.blocks, index, value) + } else { + return fmt.Errorf("unsupported feature %q", key) + } + } + + return e.ioctl(intf, uintptr(unsafe.Pointer(&features))) +} + +// Get state of a link. +func (e *Ethtool) LinkState(intf string) (uint32, error) { + x := ethtoolLink{ + cmd: ETHTOOL_GLINK, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&x))); err != nil { + return 0, err + } + + return x.data, nil +} + // Stats retrieves stats of the given interface name. func (e *Ethtool) Stats(intf string) (map[string]uint64, error) { drvinfo := ethtoolDrvInfo{ cmd: ETHTOOL_GDRVINFO, } - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(&drvinfo)), - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return nil, syscall.Errno(ep) + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil { + return nil, err } if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN { @@ -167,11 +456,9 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) { len: drvinfo.n_stats, data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{}, } - ifr.ifr_data = uintptr(unsafe.Pointer(&gstrings)) - _, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return nil, syscall.Errno(ep) + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil { + return nil, err } stats := ethtoolStats{ @@ -180,31 +467,32 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) { data: [MAX_GSTRINGS]uint64{}, } - ifr.ifr_data = uintptr(unsafe.Pointer(&stats)) - - _, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return nil, syscall.Errno(ep) + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil { + return nil, err } var result = make(map[string]uint64) for i := 0; i != int(drvinfo.n_stats); i++ { b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN] - key := string(bytes.Trim(b, "\x00")) - result[key] = stats.data[i] + key := string(b[:strings.Index(string(b), "\x00")]) + if len(key) != 0 { + result[key] = stats.data[i] + } } return result, nil } +// Close closes the ethool handler func (e *Ethtool) Close() { syscall.Close(e.fd) } +// NewEthtool returns a new ethtool handler func NewEthtool() (*Ethtool, error) { - fd, _, err := syscall.RawSyscall(syscall.SYS_SOCKET, syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) - if err != 0 { - return nil, syscall.Errno(err) + fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) + if err != nil { + return nil, err } return &Ethtool{ @@ -212,6 +500,7 @@ func NewEthtool() (*Ethtool, error) { }, nil } +// BusInfo returns bus information of the given interface name. func BusInfo(intf string) (string, error) { e, err := NewEthtool() if err != nil { @@ -221,6 +510,7 @@ func BusInfo(intf string) (string, error) { return e.BusInfo(intf) } +// DriverName returns the driver name of the given interface name. func DriverName(intf string) (string, error) { e, err := NewEthtool() if err != nil { @@ -230,6 +520,7 @@ func DriverName(intf string) (string, error) { return e.DriverName(intf) } +// Stats retrieves stats of the given interface name. func Stats(intf string) (map[string]uint64, error) { e, err := NewEthtool() if err != nil { @@ -238,3 +529,13 @@ func Stats(intf string) (map[string]uint64, error) { defer e.Close() return e.Stats(intf) } + +// PermAddr returns permanent address of the given interface name. +func PermAddr(intf string) (string, error) { + e, err := NewEthtool() + if err != nil { + return "", err + } + defer e.Close() + return e.PermAddr(intf) +} diff --git a/vendor/github.com/safchain/ethtool/ethtool_cmd.go b/vendor/github.com/safchain/ethtool/ethtool_cmd.go index f5b34eeb..d0c35e47 100644 --- a/vendor/github.com/safchain/ethtool/ethtool_cmd.go +++ b/vendor/github.com/safchain/ethtool/ethtool_cmd.go @@ -26,6 +26,7 @@ package ethtool import ( + "math" "reflect" "syscall" "unsafe" @@ -128,6 +129,9 @@ func (e *Ethtool) CmdGet(ecmd *EthtoolCmd, intf string) (uint32, error) { var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | (uint32(ecmd.Speed) & 0xffff) + if speedval == math.MaxUint16 { + speedval = math.MaxUint32 + } return speedval, nil } @@ -153,6 +157,9 @@ func (e *Ethtool) CmdSet(ecmd *EthtoolCmd, intf string) (uint32, error) { var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | (uint32(ecmd.Speed) & 0xffff) + if speedval == math.MaxUint16 { + speedval = math.MaxUint32 + } return speedval, nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 64f47740..4674836c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,7 +84,7 @@ github.com/onsi/gomega/matchers/support/goraph/bipartitegraph github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util -# github.com/safchain/ethtool v0.0.0-20170622225139-7ff1ba29eca2 +# github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 github.com/safchain/ethtool # github.com/sirupsen/logrus v1.0.6 github.com/sirupsen/logrus