| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685 |
- /*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
- // Package ethtool aims to provide a library giving a simple access to the
- // Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations
- // from a network device like statistics, driver related informations or
- // even the peer of a VETH interface.
- package ethtool
- import (
- "bytes"
- "encoding/hex"
- "fmt"
- "strings"
- "unsafe"
- "golang.org/x/sys/unix"
- )
- // Maximum size of an interface name
- const (
- IFNAMSIZ = 16
- )
- // ioctl ethtool request
- const (
- SIOCETHTOOL = 0x8946
- )
- // ethtool stats related constants.
- 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_GCHANNELS = 0x0000003c /* Get no of channels */
- ETHTOOL_SCHANNELS = 0x0000003d /* Set no of channels */
- ETHTOOL_GCOALESCE = 0x0000000e /* Get coalesce config */
- /* 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 = 16384
- MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32
- EEPROM_LEN = 640
- PERMADDR_LEN = 32
- )
- type ifreq struct {
- ifr_name [IFNAMSIZ]byte
- 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
- version [32]byte
- fw_version [32]byte
- bus_info [32]byte
- erom_version [32]byte
- reserved2 [12]byte
- n_priv_flags uint32
- n_stats uint32
- testinfo_len uint32
- eedump_len uint32
- regdump_len uint32
- }
- // DrvInfo contains driver information
- // ethtool.h v3.5: struct ethtool_drvinfo
- type DrvInfo struct {
- Cmd uint32
- Driver string
- Version string
- FwVersion string
- BusInfo string
- EromVersion string
- Reserved2 string
- NPrivFlags uint32
- NStats uint32
- TestInfoLen uint32
- EedumpLen uint32
- RegdumpLen uint32
- }
- // Channels contains the number of channels for a given interface.
- type Channels struct {
- Cmd uint32
- MaxRx uint32
- MaxTx uint32
- MaxOther uint32
- MaxCombined uint32
- RxCount uint32
- TxCount uint32
- OtherCount uint32
- CombinedCount uint32
- }
- // Coalesce is a coalesce config for an interface
- type Coalesce struct {
- Cmd uint32
- RxCoalesceUsecs uint32
- RxMaxCoalescedFrames uint32
- RxCoalesceUsecsIrq uint32
- RxMaxCoalescedFramesIrq uint32
- TxCoalesceUsecs uint32
- TxMaxCoalescedFrames uint32
- TxCoalesceUsecsIrq uint32
- TxMaxCoalescedFramesIrq uint32
- StatsBlockCoalesceUsecs uint32
- UseAdaptiveRxCoalesce uint32
- UseAdaptiveTxCoalesce uint32
- PktRateLow uint32
- RxCoalesceUsecsLow uint32
- RxMaxCoalescedFramesLow uint32
- TxCoalesceUsecsLow uint32
- TxMaxCoalescedFramesLow uint32
- PktRateHigh uint32
- RxCoalesceUsecsHigh uint32
- RxMaxCoalescedFramesHigh uint32
- TxCoalesceUsecsHigh uint32
- TxMaxCoalescedFramesHigh uint32
- RateSampleInterval uint32
- }
- type ethtoolGStrings struct {
- cmd uint32
- string_set uint32
- len uint32
- data [MAX_GSTRINGS * ETH_GSTRING_LEN]byte
- }
- type ethtoolStats struct {
- cmd uint32
- n_stats uint32
- 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 name.
- func (e *Ethtool) DriverName(intf string) (string, error) {
- info, err := e.getDriverInfo(intf)
- if err != nil {
- return "", err
- }
- return string(bytes.Trim(info.driver[:], "\x00")), nil
- }
- // 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 {
- return "", err
- }
- return string(bytes.Trim(info.bus_info[:], "\x00")), nil
- }
- // 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) (DrvInfo, error) {
- i, err := e.getDriverInfo(intf)
- if err != nil {
- return DrvInfo{}, err
- }
- drvInfo := DrvInfo{
- Cmd: i.cmd,
- Driver: string(bytes.Trim(i.driver[:], "\x00")),
- Version: string(bytes.Trim(i.version[:], "\x00")),
- FwVersion: string(bytes.Trim(i.fw_version[:], "\x00")),
- BusInfo: string(bytes.Trim(i.bus_info[:], "\x00")),
- EromVersion: string(bytes.Trim(i.erom_version[:], "\x00")),
- Reserved2: string(bytes.Trim(i.reserved2[:], "\x00")),
- NPrivFlags: i.n_priv_flags,
- NStats: i.n_stats,
- TestInfoLen: i.testinfo_len,
- EedumpLen: i.eedump_len,
- RegdumpLen: i.regdump_len,
- }
- return drvInfo, nil
- }
- // GetChannels returns the number of channels for the given interface name.
- func (e *Ethtool) GetChannels(intf string) (Channels, error) {
- channels, err := e.getChannels(intf)
- if err != nil {
- return Channels{}, err
- }
- return channels, nil
- }
- // SetChannels sets the number of channels for the given interface name and
- // returns the new number of channels.
- func (e *Ethtool) SetChannels(intf string, channels Channels) (Channels, error) {
- channels, err := e.setChannels(intf, channels)
- if err != nil {
- return Channels{}, err
- }
- return channels, nil
- }
- // GetCoalesce returns the coalesce config for the given interface name.
- func (e *Ethtool) GetCoalesce(intf string) (Coalesce, error) {
- coalesce, err := e.getCoalesce(intf)
- if err != nil {
- return Coalesce{}, err
- }
- return coalesce, 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: data,
- }
- _, _, ep := unix.Syscall(unix.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
- if ep != 0 {
- return 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) getChannels(intf string) (Channels, error) {
- channels := Channels{
- Cmd: ETHTOOL_GCHANNELS,
- }
- if err := e.ioctl(intf, uintptr(unsafe.Pointer(&channels))); err != nil {
- return Channels{}, err
- }
- return channels, nil
- }
- func (e *Ethtool) setChannels(intf string, channels Channels) (Channels, error) {
- channels.Cmd = ETHTOOL_SCHANNELS
- if err := e.ioctl(intf, uintptr(unsafe.Pointer(&channels))); err != nil {
- return Channels{}, err
- }
- return channels, nil
- }
- func (e *Ethtool) getCoalesce(intf string) (Coalesce, error) {
- coalesce := Coalesce{
- Cmd: ETHTOOL_GCOALESCE,
- }
- if err := e.ioctl(intf, uintptr(unsafe.Pointer(&coalesce))); err != nil {
- return Coalesce{}, err
- }
- return coalesce, 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,
- }
- 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 {
- return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, drvinfo.n_stats)
- }
- gstrings := ethtoolGStrings{
- cmd: ETHTOOL_GSTRINGS,
- string_set: ETH_SS_STATS,
- len: drvinfo.n_stats,
- data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
- }
- if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil {
- return nil, err
- }
- stats := ethtoolStats{
- cmd: ETHTOOL_GSTATS,
- n_stats: drvinfo.n_stats,
- data: [MAX_GSTRINGS]uint64{},
- }
- 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]
- strEnd := strings.Index(string(b), "\x00")
- if strEnd == -1 {
- strEnd = ETH_GSTRING_LEN
- }
- key := string(b[:strEnd])
- if len(key) != 0 {
- result[key] = stats.data[i]
- }
- }
- return result, nil
- }
- // Close closes the ethool handler
- func (e *Ethtool) Close() {
- unix.Close(e.fd)
- }
- // NewEthtool returns a new ethtool handler
- func NewEthtool() (*Ethtool, error) {
- fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
- if err != nil {
- return nil, err
- }
- return &Ethtool{
- fd: int(fd),
- }, nil
- }
- // BusInfo returns bus information of the given interface name.
- func BusInfo(intf string) (string, error) {
- e, err := NewEthtool()
- if err != nil {
- return "", err
- }
- defer e.Close()
- 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 {
- return "", err
- }
- defer e.Close()
- 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 {
- return nil, err
- }
- 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)
- }
|