|
@@ -19,17 +19,18 @@
|
|
|
*
|
|
*
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
-// 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.
|
|
|
|
|
|
|
+// The ethtool package aims to provide a library that provides easy access
|
|
|
|
|
+// to the Linux SIOCETHTOOL ioctl operations. It can be used to retrieve information
|
|
|
|
|
+// from a network device such as statistics, driver related information or even
|
|
|
|
|
+// the peer of a VETH interface.
|
|
|
package ethtool
|
|
package ethtool
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"bytes"
|
|
"bytes"
|
|
|
"encoding/hex"
|
|
"encoding/hex"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
- "strings"
|
|
|
|
|
|
|
+ "sync"
|
|
|
|
|
+ "time"
|
|
|
"unsafe"
|
|
"unsafe"
|
|
|
|
|
|
|
|
"golang.org/x/sys/unix"
|
|
"golang.org/x/sys/unix"
|
|
@@ -47,41 +48,118 @@ const (
|
|
|
|
|
|
|
|
// ethtool stats related constants.
|
|
// ethtool stats related constants.
|
|
|
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_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 */
|
|
|
|
|
|
|
+ ETH_GSTRING_LEN = 32
|
|
|
|
|
+ ETH_SS_STATS = 1
|
|
|
|
|
+ ETH_SS_PRIV_FLAGS = 2
|
|
|
|
|
+ ETH_SS_FEATURES = 4
|
|
|
|
|
+
|
|
|
|
|
+ // CMD supported
|
|
|
|
|
+ ETHTOOL_GSET = 0x00000001 /* Get settings. */
|
|
|
|
|
+ ETHTOOL_SSET = 0x00000002 /* Set settings. */
|
|
|
|
|
+ ETHTOOL_GWOL = 0x00000005 /* Get wake-on-lan options. */
|
|
|
|
|
+ ETHTOOL_SWOL = 0x00000006 /* Set wake-on-lan options. */
|
|
|
|
|
+ ETHTOOL_GDRVINFO = 0x00000003 /* Get driver info. */
|
|
|
|
|
+ ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */
|
|
|
|
|
+ ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */
|
|
|
|
|
+ ETHTOOL_GLINKSETTINGS = unix.ETHTOOL_GLINKSETTINGS // 0x4c
|
|
|
|
|
+ ETHTOOL_SLINKSETTINGS = unix.ETHTOOL_SLINKSETTINGS // 0x4d
|
|
|
|
|
+
|
|
|
|
|
+ // 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_GCOALESCE = 0x0000000e /* Get coalesce config */
|
|
|
|
|
+ ETHTOOL_SCOALESCE = 0x0000000f /* Set coalesce config */
|
|
|
|
|
+ ETHTOOL_GRINGPARAM = 0x00000010 /* Get ring parameters */
|
|
|
|
|
+ ETHTOOL_SRINGPARAM = 0x00000011 /* Set ring parameters. */
|
|
|
|
|
+ ETHTOOL_GPAUSEPARAM = 0x00000012 /* Get pause parameters */
|
|
|
|
|
+ ETHTOOL_SPAUSEPARAM = 0x00000013 /* Set pause parameters. */
|
|
|
|
|
+ ETHTOOL_GSTRINGS = 0x0000001b /* Get specified string set */
|
|
|
|
|
+ ETHTOOL_PHYS_ID = 0x0000001c /* Identify the NIC */
|
|
|
|
|
+ ETHTOOL_GSTATS = 0x0000001d /* Get NIC-specific statistics */
|
|
|
|
|
+ ETHTOOL_GPERMADDR = 0x00000020 /* Get permanent hardware address */
|
|
|
|
|
+ ETHTOOL_GFLAGS = 0x00000025 /* Get flags bitmap(ethtool_value) */
|
|
|
|
|
+ ETHTOOL_GPFLAGS = 0x00000027 /* Get driver-private flags bitmap */
|
|
|
|
|
+ ETHTOOL_SPFLAGS = 0x00000028 /* Set driver-private flags bitmap */
|
|
|
|
|
+ ETHTOOL_GSSET_INFO = 0x00000037 /* Get string set info */
|
|
|
|
|
+ ETHTOOL_GFEATURES = 0x0000003a /* Get device offload settings */
|
|
|
|
|
+ ETHTOOL_SFEATURES = 0x0000003b /* Change device offload settings */
|
|
|
|
|
+ ETHTOOL_GCHANNELS = 0x0000003c /* Get no of channels */
|
|
|
|
|
+ ETHTOOL_SCHANNELS = 0x0000003d /* Set no of channels */
|
|
|
|
|
+ ETHTOOL_GET_TS_INFO = 0x00000041 /* Get time stamping and PHC info */
|
|
|
|
|
+ ETHTOOL_GMODULEINFO = 0x00000042 /* Get plug-in module information */
|
|
|
|
|
+ ETHTOOL_GMODULEEEPROM = 0x00000043 /* Get plug-in module eeprom */
|
|
|
|
|
+ ETHTOOL_GRXFHINDIR = 0x00000038 /* Get RX flow hash indir'n table */
|
|
|
|
|
+ ETHTOOL_SRXFHINDIR = 0x00000039 /* Set RX flow hash indir'n table */
|
|
|
|
|
+ ETH_RXFH_INDIR_NO_CHANGE = 0xFFFFFFFF
|
|
|
|
|
+
|
|
|
|
|
+ // Speed and Duplex unknowns/constants (Manually defined based on <linux/ethtool.h>)
|
|
|
|
|
+ SPEED_UNKNOWN = 0xffffffff // ((__u32)-1) SPEED_UNKNOWN
|
|
|
|
|
+ DUPLEX_HALF = 0x00 // DUPLEX_HALF
|
|
|
|
|
+ DUPLEX_FULL = 0x01 // DUPLEX_FULL
|
|
|
|
|
+ DUPLEX_UNKNOWN = 0xff // DUPLEX_UNKNOWN
|
|
|
|
|
+
|
|
|
|
|
+ // Port types (Manually defined based on <linux/ethtool.h>)
|
|
|
|
|
+ PORT_TP = 0x00 // PORT_TP
|
|
|
|
|
+ PORT_AUI = 0x01 // PORT_AUI
|
|
|
|
|
+ PORT_MII = 0x02 // PORT_MII
|
|
|
|
|
+ PORT_FIBRE = 0x03 // PORT_FIBRE
|
|
|
|
|
+ PORT_BNC = 0x04 // PORT_BNC
|
|
|
|
|
+ PORT_DA = 0x05 // PORT_DA
|
|
|
|
|
+ PORT_NONE = 0xef // PORT_NONE
|
|
|
|
|
+ PORT_OTHER = 0xff // PORT_OTHER
|
|
|
|
|
+
|
|
|
|
|
+ // Autoneg settings (Manually defined based on <linux/ethtool.h>)
|
|
|
|
|
+ AUTONEG_DISABLE = 0x00 // AUTONEG_DISABLE
|
|
|
|
|
+ AUTONEG_ENABLE = 0x01 // AUTONEG_ENABLE
|
|
|
|
|
+
|
|
|
|
|
+ // MDIX states (Manually defined based on <linux/ethtool.h>)
|
|
|
|
|
+ ETH_TP_MDI_INVALID = 0x00 // ETH_TP_MDI_INVALID
|
|
|
|
|
+ ETH_TP_MDI = 0x01 // ETH_TP_MDI
|
|
|
|
|
+ ETH_TP_MDI_X = 0x02 // ETH_TP_MDI_X
|
|
|
|
|
+ ETH_TP_MDI_AUTO = 0x03 // Control value ETH_TP_MDI_AUTO
|
|
|
|
|
+
|
|
|
|
|
+ // Link mode mask bits count (Manually defined based on ethtool.h)
|
|
|
|
|
+ ETHTOOL_LINK_MODE_MASK_NBITS = 92 // __ETHTOOL_LINK_MODE_MASK_NBITS
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate max nwords based on NBITS using the manually defined constant
|
|
|
|
|
+ MAX_LINK_MODE_MASK_NWORDS = (ETHTOOL_LINK_MODE_MASK_NBITS + 31) / 32 // = 3
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
// MAX_GSTRINGS maximum number of stats entries that ethtool can
|
|
// MAX_GSTRINGS maximum number of stats entries that ethtool can
|
|
|
// retrieve currently.
|
|
// retrieve currently.
|
|
|
const (
|
|
const (
|
|
|
- MAX_GSTRINGS = 16384
|
|
|
|
|
|
|
+ MAX_GSTRINGS = 32768
|
|
|
MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32
|
|
MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32
|
|
|
EEPROM_LEN = 640
|
|
EEPROM_LEN = 640
|
|
|
PERMADDR_LEN = 32
|
|
PERMADDR_LEN = 32
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+// ethtool sset_info related constants
|
|
|
|
|
+const (
|
|
|
|
|
+ MAX_SSET_INFO = 64
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const (
|
|
|
|
|
+ DEFAULT_BLINK_DURATION = 60 * time.Second
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+var (
|
|
|
|
|
+ gstringsPool = sync.Pool{
|
|
|
|
|
+ New: func() interface{} {
|
|
|
|
|
+ // new() will allocate and zero-initialize the struct.
|
|
|
|
|
+ // The large data array within ethtoolGStrings will be zeroed.
|
|
|
|
|
+ return new(EthtoolGStrings)
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ statsPool = sync.Pool{
|
|
|
|
|
+ New: func() interface{} {
|
|
|
|
|
+ // new() will allocate and zero-initialize the struct.
|
|
|
|
|
+ // The large data array within ethtoolStats will be zeroed.
|
|
|
|
|
+ return new(EthtoolStats)
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
type ifreq struct {
|
|
type ifreq struct {
|
|
|
ifr_name [IFNAMSIZ]byte
|
|
ifr_name [IFNAMSIZ]byte
|
|
|
ifr_data uintptr
|
|
ifr_data uintptr
|
|
@@ -91,8 +169,8 @@ type ifreq struct {
|
|
|
type ethtoolSsetInfo struct {
|
|
type ethtoolSsetInfo struct {
|
|
|
cmd uint32
|
|
cmd uint32
|
|
|
reserved uint32
|
|
reserved uint32
|
|
|
- sset_mask uint32
|
|
|
|
|
- data uintptr
|
|
|
|
|
|
|
+ sset_mask uint64
|
|
|
|
|
+ data [MAX_SSET_INFO]uint32
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
type ethtoolGetFeaturesBlock struct {
|
|
type ethtoolGetFeaturesBlock struct {
|
|
@@ -191,14 +269,135 @@ type Coalesce struct {
|
|
|
RateSampleInterval uint32
|
|
RateSampleInterval uint32
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-type ethtoolGStrings struct {
|
|
|
|
|
|
|
+// IdentityConf is an identity config for an interface
|
|
|
|
|
+type IdentityConf struct {
|
|
|
|
|
+ Cmd uint32
|
|
|
|
|
+ Duration uint32
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// WoL options
|
|
|
|
|
+const (
|
|
|
|
|
+ WAKE_PHY = 1 << 0
|
|
|
|
|
+ WAKE_UCAST = 1 << 1
|
|
|
|
|
+ WAKE_MCAST = 1 << 2
|
|
|
|
|
+ WAKE_BCAST = 1 << 3
|
|
|
|
|
+ WAKE_ARP = 1 << 4
|
|
|
|
|
+ WAKE_MAGIC = 1 << 5
|
|
|
|
|
+ WAKE_MAGICSECURE = 1 << 6 // only meaningful if WAKE_MAGIC
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+var WoLMap = map[uint32]string{
|
|
|
|
|
+ WAKE_PHY: "p", // Wake on PHY activity
|
|
|
|
|
+ WAKE_UCAST: "u", // Wake on unicast messages
|
|
|
|
|
+ WAKE_MCAST: "m", // Wake on multicast messages
|
|
|
|
|
+ WAKE_BCAST: "b", // Wake on broadcast messages
|
|
|
|
|
+ WAKE_ARP: "a", // Wake on ARP
|
|
|
|
|
+ WAKE_MAGIC: "g", // Wake on MagicPacket™
|
|
|
|
|
+ WAKE_MAGICSECURE: "s", // Enable SecureOn™ password for MagicPacket™
|
|
|
|
|
+ // f Wake on filter(s)
|
|
|
|
|
+ // d Disable (wake on nothing). This option clears all previous options.
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// WakeOnLan contains WoL config for an interface
|
|
|
|
|
+type WakeOnLan struct {
|
|
|
|
|
+ Cmd uint32 // ETHTOOL_GWOL or ETHTOOL_SWOL
|
|
|
|
|
+ Supported uint32 // r/o bitmask of WAKE_* flags for supported WoL modes
|
|
|
|
|
+ Opts uint32 // Bitmask of WAKE_* flags for enabled WoL modes
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Timestamping options
|
|
|
|
|
+// see: https://www.kernel.org/doc/Documentation/networking/timestamping.txt
|
|
|
|
|
+const (
|
|
|
|
|
+ SOF_TIMESTAMPING_TX_HARDWARE = (1 << 0) /* Request tx timestamps generated by the network adapter. */
|
|
|
|
|
+ SOF_TIMESTAMPING_TX_SOFTWARE = (1 << 1) /* Request tx timestamps when data leaves the kernel. */
|
|
|
|
|
+ SOF_TIMESTAMPING_RX_HARDWARE = (1 << 2) /* Request rx timestamps generated by the network adapter. */
|
|
|
|
|
+ SOF_TIMESTAMPING_RX_SOFTWARE = (1 << 3) /* Request rx timestamps when data enters the kernel. */
|
|
|
|
|
+ SOF_TIMESTAMPING_SOFTWARE = (1 << 4) /* Report any software timestamps when available. */
|
|
|
|
|
+ SOF_TIMESTAMPING_SYS_HARDWARE = (1 << 5) /* This option is deprecated and ignored. */
|
|
|
|
|
+ SOF_TIMESTAMPING_RAW_HARDWARE = (1 << 6) /* Report hardware timestamps. */
|
|
|
|
|
+ SOF_TIMESTAMPING_OPT_ID = (1 << 7) /* Generate a unique identifier along with each packet. */
|
|
|
|
|
+ SOF_TIMESTAMPING_TX_SCHED = (1 << 8) /* Request tx timestamps prior to entering the packet scheduler. */
|
|
|
|
|
+ SOF_TIMESTAMPING_TX_ACK = (1 << 9) /* Request tx timestamps when all data in the send buffer has been acknowledged. */
|
|
|
|
|
+ SOF_TIMESTAMPING_OPT_CMSG = (1 << 10) /* Support recv() cmsg for all timestamped packets. */
|
|
|
|
|
+ SOF_TIMESTAMPING_OPT_TSONLY = (1 << 11) /* Applies to transmit timestamps only. */
|
|
|
|
|
+ SOF_TIMESTAMPING_OPT_STATS = (1 << 12) /* Optional stats that are obtained along with the transmit timestamps. */
|
|
|
|
|
+ SOF_TIMESTAMPING_OPT_PKTINFO = (1 << 13) /* Enable the SCM_TIMESTAMPING_PKTINFO control message for incoming packets with hardware timestamps. */
|
|
|
|
|
+ SOF_TIMESTAMPING_OPT_TX_SWHW = (1 << 14) /* Request both hardware and software timestamps for outgoing packets when SOF_TIMESTAMPING_TX_HARDWARE and SOF_TIMESTAMPING_TX_SOFTWARE are enabled at the same time. */
|
|
|
|
|
+ SOF_TIMESTAMPING_BIND_PHC = (1 << 15) /* Bind the socket to a specific PTP Hardware Clock. */
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const (
|
|
|
|
|
+ /*
|
|
|
|
|
+ * No outgoing packet will need hardware time stamping;
|
|
|
|
|
+ * should a packet arrive which asks for it, no hardware
|
|
|
|
|
+ * time stamping will be done.
|
|
|
|
|
+ */
|
|
|
|
|
+ HWTSTAMP_TX_OFF = iota
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * Enables hardware time stamping for outgoing packets;
|
|
|
|
|
+ * the sender of the packet decides which are to be
|
|
|
|
|
+ * time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE
|
|
|
|
|
+ * before sending the packet.
|
|
|
|
|
+ */
|
|
|
|
|
+ HWTSTAMP_TX_ON
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * Enables time stamping for outgoing packets just as
|
|
|
|
|
+ * HWTSTAMP_TX_ON does, but also enables time stamp insertion
|
|
|
|
|
+ * directly into Sync packets. In this case, transmitted Sync
|
|
|
|
|
+ * packets will not received a time stamp via the socket error
|
|
|
|
|
+ * queue.
|
|
|
|
|
+ */
|
|
|
|
|
+ HWTSTAMP_TX_ONESTEP_SYNC
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * Same as HWTSTAMP_TX_ONESTEP_SYNC, but also enables time
|
|
|
|
|
+ * stamp insertion directly into PDelay_Resp packets. In this
|
|
|
|
|
+ * case, neither transmitted Sync nor PDelay_Resp packets will
|
|
|
|
|
+ * receive a time stamp via the socket error queue.
|
|
|
|
|
+ */
|
|
|
|
|
+ HWTSTAMP_TX_ONESTEP_P2P
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const (
|
|
|
|
|
+ HWTSTAMP_FILTER_NONE = iota /* time stamp no incoming packet at all */
|
|
|
|
|
+ HWTSTAMP_FILTER_ALL /* time stamp any incoming packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_SOME /* return value: time stamp all packets requested plus some others */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V1_L4_EVENT /* PTP v1, UDP, any kind of event packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V1_L4_SYNC /* PTP v1, UDP, Sync packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ /* PTP v1, UDP, Delay_req packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_L4_EVENT /* PTP v2, UDP, any kind of event packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_L4_SYNC /* PTP v2, UDP, Sync packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ /* PTP v2, UDP, Delay_req packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_L2_EVENT /* 802.AS1, Ethernet, any kind of event packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_L2_SYNC /* 802.AS1, Ethernet, Sync packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ /* 802.AS1, Ethernet, Delay_req packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_EVENT /* PTP v2/802.AS1, any layer, any kind of event packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_SYNC /* PTP v2/802.AS1, any layer, Sync packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_PTP_V2_DELAY_REQ /* PTP v2/802.AS1, any layer, Delay_req packet */
|
|
|
|
|
+ HWTSTAMP_FILTER_NTP_ALL /* NTP, UDP, all versions and packet modes */
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// TimestampingInformation contains PTP timetstapming information
|
|
|
|
|
+type TimestampingInformation struct {
|
|
|
|
|
+ Cmd uint32
|
|
|
|
|
+ SoTimestamping uint32 /* SOF_TIMESTAMPING_* bitmask */
|
|
|
|
|
+ PhcIndex int32
|
|
|
|
|
+ TxTypes uint32 /* HWTSTAMP_TX_* */
|
|
|
|
|
+ txReserved [3]uint32
|
|
|
|
|
+ RxFilters uint32 /* HWTSTAMP_FILTER_ */
|
|
|
|
|
+ rxReserved [3]uint32
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type EthtoolGStrings struct {
|
|
|
cmd uint32
|
|
cmd uint32
|
|
|
string_set uint32
|
|
string_set uint32
|
|
|
len uint32
|
|
len uint32
|
|
|
data [MAX_GSTRINGS * ETH_GSTRING_LEN]byte
|
|
data [MAX_GSTRINGS * ETH_GSTRING_LEN]byte
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-type ethtoolStats struct {
|
|
|
|
|
|
|
+type EthtoolStats struct {
|
|
|
cmd uint32
|
|
cmd uint32
|
|
|
n_stats uint32
|
|
n_stats uint32
|
|
|
data [MAX_GSTRINGS]uint64
|
|
data [MAX_GSTRINGS]uint64
|
|
@@ -230,17 +429,64 @@ type ethtoolPermAddr struct {
|
|
|
data [PERMADDR_LEN]byte
|
|
data [PERMADDR_LEN]byte
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Ring is a ring config for an interface
|
|
|
|
|
+type Ring struct {
|
|
|
|
|
+ Cmd uint32
|
|
|
|
|
+ RxMaxPending uint32
|
|
|
|
|
+ RxMiniMaxPending uint32
|
|
|
|
|
+ RxJumboMaxPending uint32
|
|
|
|
|
+ TxMaxPending uint32
|
|
|
|
|
+ RxPending uint32
|
|
|
|
|
+ RxMiniPending uint32
|
|
|
|
|
+ RxJumboPending uint32
|
|
|
|
|
+ TxPending uint32
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Pause is a pause config for an interface
|
|
|
|
|
+type Pause struct {
|
|
|
|
|
+ Cmd uint32
|
|
|
|
|
+ Autoneg uint32
|
|
|
|
|
+ RxPause uint32
|
|
|
|
|
+ TxPause uint32
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Ethtool is a struct that contains the file descriptor for the ethtool
|
|
|
type Ethtool struct {
|
|
type Ethtool struct {
|
|
|
fd int
|
|
fd int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// max values for my setup dont know how to make this dynamic
|
|
|
|
|
+const MAX_INDIR_SIZE = 256
|
|
|
|
|
+const MAX_CORES = 32
|
|
|
|
|
+
|
|
|
|
|
+type Indir struct {
|
|
|
|
|
+ Cmd uint32
|
|
|
|
|
+ Size uint32
|
|
|
|
|
+ RingIndex [MAX_INDIR_SIZE]uint32 // statically definded otherwise crash
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type SetIndir struct {
|
|
|
|
|
+ Equal uint8 // used to set number of cores
|
|
|
|
|
+ Weight []uint32 // used to select cores
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Convert zero-terminated array of chars (string in C) to a Go string.
|
|
|
|
|
+func goString(s []byte) string {
|
|
|
|
|
+ strEnd := bytes.IndexByte(s, 0)
|
|
|
|
|
+ if strEnd == -1 {
|
|
|
|
|
+ return string(s)
|
|
|
|
|
+ }
|
|
|
|
|
+ return string(s[:strEnd])
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// DriverName returns the driver name of the given interface name.
|
|
// DriverName returns the driver name of the given interface name.
|
|
|
func (e *Ethtool) DriverName(intf string) (string, error) {
|
|
func (e *Ethtool) DriverName(intf string) (string, error) {
|
|
|
info, err := e.getDriverInfo(intf)
|
|
info, err := e.getDriverInfo(intf)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return "", err
|
|
return "", err
|
|
|
}
|
|
}
|
|
|
- return string(bytes.Trim(info.driver[:], "\x00")), nil
|
|
|
|
|
|
|
+ return goString(info.driver[:]), nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// BusInfo returns the bus information of the given interface name.
|
|
// BusInfo returns the bus information of the given interface name.
|
|
@@ -249,7 +495,7 @@ func (e *Ethtool) BusInfo(intf string) (string, error) {
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return "", err
|
|
return "", err
|
|
|
}
|
|
}
|
|
|
- return string(bytes.Trim(info.bus_info[:], "\x00")), nil
|
|
|
|
|
|
|
+ return goString(info.bus_info[:]), nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ModuleEeprom returns Eeprom information of the given interface name.
|
|
// ModuleEeprom returns Eeprom information of the given interface name.
|
|
@@ -262,7 +508,7 @@ func (e *Ethtool) ModuleEeprom(intf string) ([]byte, error) {
|
|
|
return eeprom.data[:eeprom.len], nil
|
|
return eeprom.data[:eeprom.len], nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ModuleEeprom returns Eeprom information of the given interface name.
|
|
|
|
|
|
|
+// ModuleEepromHex returns Eeprom information as hexadecimal string
|
|
|
func (e *Ethtool) ModuleEepromHex(intf string) (string, error) {
|
|
func (e *Ethtool) ModuleEepromHex(intf string) (string, error) {
|
|
|
eeprom, _, err := e.getModuleEeprom(intf)
|
|
eeprom, _, err := e.getModuleEeprom(intf)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -281,12 +527,12 @@ func (e *Ethtool) DriverInfo(intf string) (DrvInfo, error) {
|
|
|
|
|
|
|
|
drvInfo := DrvInfo{
|
|
drvInfo := DrvInfo{
|
|
|
Cmd: i.cmd,
|
|
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")),
|
|
|
|
|
|
|
+ Driver: goString(i.driver[:]),
|
|
|
|
|
+ Version: goString(i.version[:]),
|
|
|
|
|
+ FwVersion: goString(i.fw_version[:]),
|
|
|
|
|
+ BusInfo: goString(i.bus_info[:]),
|
|
|
|
|
+ EromVersion: goString(i.erom_version[:]),
|
|
|
|
|
+ Reserved2: goString(i.reserved2[:]),
|
|
|
NPrivFlags: i.n_priv_flags,
|
|
NPrivFlags: i.n_priv_flags,
|
|
|
NStats: i.n_stats,
|
|
NStats: i.n_stats,
|
|
|
TestInfoLen: i.testinfo_len,
|
|
TestInfoLen: i.testinfo_len,
|
|
@@ -297,6 +543,36 @@ func (e *Ethtool) DriverInfo(intf string) (DrvInfo, error) {
|
|
|
return drvInfo, nil
|
|
return drvInfo, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// GetIndir retrieves the indirection table of the given interface name.
|
|
|
|
|
+func (e *Ethtool) GetIndir(intf string) (Indir, error) {
|
|
|
|
|
+ indir, err := e.getIndir(intf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return indir, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SetIndir sets the indirection table of the given interface from the SetIndir struct
|
|
|
|
|
+func (e *Ethtool) SetIndir(intf string, setIndir SetIndir) (Indir, error) {
|
|
|
|
|
+
|
|
|
|
|
+ if setIndir.Equal != 0 && setIndir.Weight != nil {
|
|
|
|
|
+ return Indir{}, fmt.Errorf("equal and weight options are mutually exclusive")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ indir, err := e.GetIndir(intf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ newindir, err := e.setIndir(intf, indir, setIndir)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return newindir, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// GetChannels returns the number of channels for the given interface name.
|
|
// GetChannels returns the number of channels for the given interface name.
|
|
|
func (e *Ethtool) GetChannels(intf string) (Channels, error) {
|
|
func (e *Ethtool) GetChannels(intf string) (Channels, error) {
|
|
|
channels, err := e.getChannels(intf)
|
|
channels, err := e.getChannels(intf)
|
|
@@ -327,6 +603,24 @@ func (e *Ethtool) GetCoalesce(intf string) (Coalesce, error) {
|
|
|
return coalesce, nil
|
|
return coalesce, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// SetCoalesce sets the coalesce config for the given interface name.
|
|
|
|
|
+func (e *Ethtool) SetCoalesce(intf string, coalesce Coalesce) (Coalesce, error) {
|
|
|
|
|
+ coalesce, err := e.setCoalesce(intf, coalesce)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return Coalesce{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return coalesce, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GetTimestampingInformation returns the PTP timestamping information for the given interface name.
|
|
|
|
|
+func (e *Ethtool) GetTimestampingInformation(intf string) (TimestampingInformation, error) {
|
|
|
|
|
+ ts, err := e.getTimestampingInformation(intf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return TimestampingInformation{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return ts, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// PermAddr returns permanent address of the given interface name.
|
|
// PermAddr returns permanent address of the given interface name.
|
|
|
func (e *Ethtool) PermAddr(intf string) (string, error) {
|
|
func (e *Ethtool) PermAddr(intf string) (string, error) {
|
|
|
permAddr, err := e.getPermAddr(intf)
|
|
permAddr, err := e.getPermAddr(intf)
|
|
@@ -350,6 +644,31 @@ func (e *Ethtool) PermAddr(intf string) (string, error) {
|
|
|
), nil
|
|
), nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// GetWakeOnLan returns the WoL config for the given interface name.
|
|
|
|
|
+func (e *Ethtool) GetWakeOnLan(intf string) (WakeOnLan, error) {
|
|
|
|
|
+ wol := WakeOnLan{
|
|
|
|
|
+ Cmd: ETHTOOL_GWOL,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&wol))); err != nil {
|
|
|
|
|
+ return WakeOnLan{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return wol, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SetWakeOnLan sets the WoL config for the given interface name and
|
|
|
|
|
+// returns the new WoL config.
|
|
|
|
|
+func (e *Ethtool) SetWakeOnLan(intf string, wol WakeOnLan) (WakeOnLan, error) {
|
|
|
|
|
+ wol.Cmd = ETHTOOL_SWOL
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&wol))); err != nil {
|
|
|
|
|
+ return WakeOnLan{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return wol, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (e *Ethtool) ioctl(intf string, data uintptr) error {
|
|
func (e *Ethtool) ioctl(intf string, data uintptr) error {
|
|
|
var name [IFNAMSIZ]byte
|
|
var name [IFNAMSIZ]byte
|
|
|
copy(name[:], []byte(intf))
|
|
copy(name[:], []byte(intf))
|
|
@@ -379,6 +698,92 @@ func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) {
|
|
|
return drvinfo, nil
|
|
return drvinfo, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// parsing of do_grxfhindir from ethtool.c
|
|
|
|
|
+func (e *Ethtool) getIndir(intf string) (Indir, error) {
|
|
|
|
|
+ indir_head := Indir{
|
|
|
|
|
+ Cmd: ETHTOOL_GRXFHINDIR,
|
|
|
|
|
+ Size: 0,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&indir_head))); err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ indir := Indir{
|
|
|
|
|
+ Cmd: ETHTOOL_GRXFHINDIR,
|
|
|
|
|
+ Size: indir_head.Size,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&indir))); err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return indir, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// parsing of do_srxfhindir from ethtool.c
|
|
|
|
|
+func (e *Ethtool) setIndir(intf string, indir Indir, setIndir SetIndir) (Indir, error) {
|
|
|
|
|
+
|
|
|
|
|
+ err := fillIndirTable(&indir.Size, indir.RingIndex[:], 0, 0, int(setIndir.Equal), setIndir.Weight, uint32(len(setIndir.Weight)))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if indir.Size == ETH_RXFH_INDIR_NO_CHANGE {
|
|
|
|
|
+ indir.Size = MAX_INDIR_SIZE
|
|
|
|
|
+ return indir, nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ indir.Cmd = ETHTOOL_SRXFHINDIR
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&indir))); err != nil {
|
|
|
|
|
+ return Indir{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return indir, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func fillIndirTable(indirSize *uint32, indir []uint32, rxfhindirDefault int,
|
|
|
|
|
+ rxfhindirStart int, rxfhindirEqual int, rxfhindirWeight []uint32,
|
|
|
|
|
+ numWeights uint32) error {
|
|
|
|
|
+
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case rxfhindirEqual != 0:
|
|
|
|
|
+ for i := uint32(0); i < *indirSize; i++ {
|
|
|
|
|
+ indir[i] = uint32(rxfhindirStart) + (i % uint32(rxfhindirEqual))
|
|
|
|
|
+ }
|
|
|
|
|
+ case rxfhindirWeight != nil:
|
|
|
|
|
+ var sum, partial uint32 = 0, 0
|
|
|
|
|
+ var j, weight uint32
|
|
|
|
|
+ for j = range numWeights {
|
|
|
|
|
+ weight = rxfhindirWeight[j]
|
|
|
|
|
+ sum += weight
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if sum == 0 {
|
|
|
|
|
+ return fmt.Errorf("at least one weight must be non-zero")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if sum > *indirSize {
|
|
|
|
|
+ return fmt.Errorf("total weight exceeds the size of the indirection table")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ j = ^uint32(0) // equivalent to -1 for unsigned
|
|
|
|
|
+ for i := uint32(0); i < *indirSize; i++ {
|
|
|
|
|
+ for i >= (*indirSize*partial)/sum {
|
|
|
|
|
+ j++
|
|
|
|
|
+ weight = rxfhindirWeight[j]
|
|
|
|
|
+ partial += weight
|
|
|
|
|
+ }
|
|
|
|
|
+ indir[i] = uint32(rxfhindirStart) + j
|
|
|
|
|
+ }
|
|
|
|
|
+ case rxfhindirDefault != 0:
|
|
|
|
|
+ *indirSize = 0
|
|
|
|
|
+ default:
|
|
|
|
|
+ *indirSize = ETH_RXFH_INDIR_NO_CHANGE
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (e *Ethtool) getChannels(intf string) (Channels, error) {
|
|
func (e *Ethtool) getChannels(intf string) (Channels, error) {
|
|
|
channels := Channels{
|
|
channels := Channels{
|
|
|
Cmd: ETHTOOL_GCHANNELS,
|
|
Cmd: ETHTOOL_GCHANNELS,
|
|
@@ -413,6 +818,28 @@ func (e *Ethtool) getCoalesce(intf string) (Coalesce, error) {
|
|
|
return coalesce, nil
|
|
return coalesce, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (e *Ethtool) setCoalesce(intf string, coalesce Coalesce) (Coalesce, error) {
|
|
|
|
|
+ coalesce.Cmd = ETHTOOL_SCOALESCE
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&coalesce))); err != nil {
|
|
|
|
|
+ return Coalesce{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return coalesce, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (e *Ethtool) getTimestampingInformation(intf string) (TimestampingInformation, error) {
|
|
|
|
|
+ ts := TimestampingInformation{
|
|
|
|
|
+ Cmd: ETHTOOL_GET_TS_INFO,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ts))); err != nil {
|
|
|
|
|
+ return TimestampingInformation{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ts, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (e *Ethtool) getPermAddr(intf string) (ethtoolPermAddr, error) {
|
|
func (e *Ethtool) getPermAddr(intf string) (ethtoolPermAddr, error) {
|
|
|
permAddr := ethtoolPermAddr{
|
|
permAddr := ethtoolPermAddr{
|
|
|
cmd: ETHTOOL_GPERMADDR,
|
|
cmd: ETHTOOL_GPERMADDR,
|
|
@@ -452,10 +879,75 @@ func (e *Ethtool) getModuleEeprom(intf string) (ethtoolEeprom, ethtoolModInfo, e
|
|
|
return eeprom, modInfo, nil
|
|
return eeprom, modInfo, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// GetRing retrieves ring parameters of the given interface name.
|
|
|
|
|
+func (e *Ethtool) GetRing(intf string) (Ring, error) {
|
|
|
|
|
+ ring := Ring{
|
|
|
|
|
+ Cmd: ETHTOOL_GRINGPARAM,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ring))); err != nil {
|
|
|
|
|
+ return Ring{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ring, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SetRing sets ring parameters of the given interface name.
|
|
|
|
|
+func (e *Ethtool) SetRing(intf string, ring Ring) (Ring, error) {
|
|
|
|
|
+ ring.Cmd = ETHTOOL_SRINGPARAM
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ring))); err != nil {
|
|
|
|
|
+ return Ring{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ring, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GetPause retrieves pause parameters of the given interface name.
|
|
|
|
|
+func (e *Ethtool) GetPause(intf string) (Pause, error) {
|
|
|
|
|
+ pause := Pause{
|
|
|
|
|
+ Cmd: ETHTOOL_GPAUSEPARAM,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&pause))); err != nil {
|
|
|
|
|
+ return Pause{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return pause, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SetPause sets pause parameters of the given interface name.
|
|
|
|
|
+func (e *Ethtool) SetPause(intf string, pause Pause) (Pause, error) {
|
|
|
|
|
+ pause.Cmd = ETHTOOL_SPAUSEPARAM
|
|
|
|
|
+
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&pause))); err != nil {
|
|
|
|
|
+ return Pause{}, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return pause, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) bool {
|
|
func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) bool {
|
|
|
return (blocks)[index/32].active&(1<<(index%32)) != 0
|
|
return (blocks)[index/32].active&(1<<(index%32)) != 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// FeatureState contains the state of a feature.
|
|
|
|
|
+type FeatureState struct {
|
|
|
|
|
+ Available bool
|
|
|
|
|
+ Requested bool
|
|
|
|
|
+ Active bool
|
|
|
|
|
+ NeverChanged bool
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func getFeatureStateBits(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) FeatureState {
|
|
|
|
|
+ return FeatureState{
|
|
|
|
|
+ Available: (blocks)[index/32].available&(1<<(index%32)) != 0,
|
|
|
|
|
+ Requested: (blocks)[index/32].requested&(1<<(index%32)) != 0,
|
|
|
|
|
+ Active: (blocks)[index/32].active&(1<<(index%32)) != 0,
|
|
|
|
|
+ NeverChanged: (blocks)[index/32].never_changed&(1<<(index%32)) != 0,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) {
|
|
func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) {
|
|
|
blockIndex, bitIndex := index/32, index%32
|
|
blockIndex, bitIndex := index/32, index%32
|
|
|
|
|
|
|
@@ -468,27 +960,28 @@ func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index ui
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// FeatureNames shows supported features by their name.
|
|
|
|
|
-func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) {
|
|
|
|
|
|
|
+func (e *Ethtool) getNames(intf string, mask int) (map[string]uint, error) {
|
|
|
ssetInfo := ethtoolSsetInfo{
|
|
ssetInfo := ethtoolSsetInfo{
|
|
|
cmd: ETHTOOL_GSSET_INFO,
|
|
cmd: ETHTOOL_GSSET_INFO,
|
|
|
- sset_mask: 1 << ETH_SS_FEATURES,
|
|
|
|
|
|
|
+ sset_mask: 1 << mask,
|
|
|
|
|
+ data: [MAX_SSET_INFO]uint32{},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ssetInfo))); err != nil {
|
|
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ssetInfo))); err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- length := uint32(ssetInfo.data)
|
|
|
|
|
|
|
+ /* we only read data on first index because single bit was set in sset_mask(0x10) */
|
|
|
|
|
+ length := ssetInfo.data[0]
|
|
|
if length == 0 {
|
|
if length == 0 {
|
|
|
return map[string]uint{}, nil
|
|
return map[string]uint{}, nil
|
|
|
} else if length > MAX_GSTRINGS {
|
|
} else if length > MAX_GSTRINGS {
|
|
|
return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, length)
|
|
return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, length)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- gstrings := ethtoolGStrings{
|
|
|
|
|
|
|
+ gstrings := EthtoolGStrings{
|
|
|
cmd: ETHTOOL_GSTRINGS,
|
|
cmd: ETHTOOL_GSTRINGS,
|
|
|
- string_set: ETH_SS_FEATURES,
|
|
|
|
|
|
|
+ string_set: uint32(mask),
|
|
|
len: length,
|
|
len: length,
|
|
|
data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
|
|
data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
|
|
|
}
|
|
}
|
|
@@ -497,10 +990,10 @@ func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var result = make(map[string]uint)
|
|
|
|
|
|
|
+ result := make(map[string]uint)
|
|
|
for i := 0; i != int(length); i++ {
|
|
for i := 0; i != int(length); i++ {
|
|
|
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
|
|
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
|
|
|
- key := string(bytes.Trim(b, "\x00"))
|
|
|
|
|
|
|
+ key := goString(b)
|
|
|
if key != "" {
|
|
if key != "" {
|
|
|
result[key] = uint(i)
|
|
result[key] = uint(i)
|
|
|
}
|
|
}
|
|
@@ -509,6 +1002,11 @@ func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) {
|
|
|
return result, nil
|
|
return result, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// FeatureNames shows supported features by their name.
|
|
|
|
|
+func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) {
|
|
|
|
|
+ return e.getNames(intf, ETH_SS_FEATURES)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Features retrieves features of the given interface name.
|
|
// Features retrieves features of the given interface name.
|
|
|
func (e *Ethtool) Features(intf string) (map[string]bool, error) {
|
|
func (e *Ethtool) Features(intf string) (map[string]bool, error) {
|
|
|
names, err := e.FeatureNames(intf)
|
|
names, err := e.FeatureNames(intf)
|
|
@@ -530,7 +1028,7 @@ func (e *Ethtool) Features(intf string) (map[string]bool, error) {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var result = make(map[string]bool, length)
|
|
|
|
|
|
|
+ result := make(map[string]bool, length)
|
|
|
for key, index := range names {
|
|
for key, index := range names {
|
|
|
result[key] = isFeatureBitSet(features.blocks, index)
|
|
result[key] = isFeatureBitSet(features.blocks, index)
|
|
|
}
|
|
}
|
|
@@ -538,6 +1036,36 @@ func (e *Ethtool) Features(intf string) (map[string]bool, error) {
|
|
|
return result, nil
|
|
return result, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// FeaturesWithState retrieves features of the given interface name,
|
|
|
|
|
+// with extra flags to explain if they can be enabled
|
|
|
|
|
+func (e *Ethtool) FeaturesWithState(intf string) (map[string]FeatureState, error) {
|
|
|
|
|
+ names, err := e.FeatureNames(intf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ length := uint32(len(names))
|
|
|
|
|
+ if length == 0 {
|
|
|
|
|
+ return map[string]FeatureState{}, 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]FeatureState, length)
|
|
|
|
|
+ for key, index := range names {
|
|
|
|
|
+ result[key] = getFeatureStateBits(features.blocks, index)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Change requests a change in the given device's features.
|
|
// Change requests a change in the given device's features.
|
|
|
func (e *Ethtool) Change(intf string, config map[string]bool) error {
|
|
func (e *Ethtool) Change(intf string, config map[string]bool) error {
|
|
|
names, err := e.FeatureNames(intf)
|
|
names, err := e.FeatureNames(intf)
|
|
@@ -563,7 +1091,69 @@ func (e *Ethtool) Change(intf string, config map[string]bool) error {
|
|
|
return e.ioctl(intf, uintptr(unsafe.Pointer(&features)))
|
|
return e.ioctl(intf, uintptr(unsafe.Pointer(&features)))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Get state of a link.
|
|
|
|
|
|
|
+// PrivFlagsNames shows supported private flags by their name.
|
|
|
|
|
+func (e *Ethtool) PrivFlagsNames(intf string) (map[string]uint, error) {
|
|
|
|
|
+ return e.getNames(intf, ETH_SS_PRIV_FLAGS)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// PrivFlags retrieves private flags of the given interface name.
|
|
|
|
|
+func (e *Ethtool) PrivFlags(intf string) (map[string]bool, error) {
|
|
|
|
|
+ names, err := e.PrivFlagsNames(intf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ length := uint32(len(names))
|
|
|
|
|
+ if length == 0 {
|
|
|
|
|
+ return map[string]bool{}, nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var val ethtoolLink
|
|
|
|
|
+ val.cmd = ETHTOOL_GPFLAGS
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&val))); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ result := make(map[string]bool, length)
|
|
|
|
|
+ for name, mask := range names {
|
|
|
|
|
+ result[name] = val.data&(1<<mask) != 0
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// UpdatePrivFlags requests a change in the given device's private flags.
|
|
|
|
|
+func (e *Ethtool) UpdatePrivFlags(intf string, config map[string]bool) error {
|
|
|
|
|
+ names, err := e.PrivFlagsNames(intf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var curr ethtoolLink
|
|
|
|
|
+ curr.cmd = ETHTOOL_GPFLAGS
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(&curr))); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var update ethtoolLink
|
|
|
|
|
+ update.cmd = ETHTOOL_SPFLAGS
|
|
|
|
|
+ update.data = curr.data
|
|
|
|
|
+ for name, value := range config {
|
|
|
|
|
+ if index, ok := names[name]; ok {
|
|
|
|
|
+ if value {
|
|
|
|
|
+ update.data |= 1 << index
|
|
|
|
|
+ } else {
|
|
|
|
|
+ update.data &= ^(1 << index)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return fmt.Errorf("unsupported priv flag %q", name)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return e.ioctl(intf, uintptr(unsafe.Pointer(&update)))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LinkState get the state of a link.
|
|
|
func (e *Ethtool) LinkState(intf string) (uint32, error) {
|
|
func (e *Ethtool) LinkState(intf string) (uint32, error) {
|
|
|
x := ethtoolLink{
|
|
x := ethtoolLink{
|
|
|
cmd: ETHTOOL_GLINK,
|
|
cmd: ETHTOOL_GLINK,
|
|
@@ -577,7 +1167,23 @@ func (e *Ethtool) LinkState(intf string) (uint32, error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Stats retrieves stats of the given interface name.
|
|
// Stats retrieves stats of the given interface name.
|
|
|
|
|
+// This maintains backward compatibility with existing code.
|
|
|
func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
|
func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
|
|
|
|
+ // Create temporary buffers and delegate to StatsWithBuffer
|
|
|
|
|
+ gstrings := gstringsPool.Get().(*EthtoolGStrings)
|
|
|
|
|
+ stats := statsPool.Get().(*EthtoolStats)
|
|
|
|
|
+ defer func() {
|
|
|
|
|
+ gstringsPool.Put(gstrings)
|
|
|
|
|
+ statsPool.Put(stats)
|
|
|
|
|
+ }()
|
|
|
|
|
+
|
|
|
|
|
+ return e.StatsWithBuffer(intf, gstrings, stats)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// StatsWithBuffer retrieves stats of the given interface name using pre-allocated buffers.
|
|
|
|
|
+// This allows the caller to control where the large structures are allocated,
|
|
|
|
|
+// which can be useful to avoid heap allocations in Go 1.24+.
|
|
|
|
|
+func (e *Ethtool) StatsWithBuffer(intf string, gstringsPtr *EthtoolGStrings, statsPtr *EthtoolStats) (map[string]uint64, error) {
|
|
|
drvinfo := ethtoolDrvInfo{
|
|
drvinfo := ethtoolDrvInfo{
|
|
|
cmd: ETHTOOL_GDRVINFO,
|
|
cmd: ETHTOOL_GDRVINFO,
|
|
|
}
|
|
}
|
|
@@ -586,41 +1192,37 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN {
|
|
|
|
|
|
|
+ if drvinfo.n_stats > MAX_GSTRINGS {
|
|
|
return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, drvinfo.n_stats)
|
|
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{},
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ gstringsPtr.cmd = ETHTOOL_GSTRINGS
|
|
|
|
|
+ gstringsPtr.string_set = ETH_SS_STATS
|
|
|
|
|
+ gstringsPtr.len = drvinfo.n_stats
|
|
|
|
|
|
|
|
- if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil {
|
|
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(gstringsPtr))); err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- stats := ethtoolStats{
|
|
|
|
|
- cmd: ETHTOOL_GSTATS,
|
|
|
|
|
- n_stats: drvinfo.n_stats,
|
|
|
|
|
- data: [MAX_GSTRINGS]uint64{},
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ statsPtr.cmd = ETHTOOL_GSTATS
|
|
|
|
|
+ statsPtr.n_stats = drvinfo.n_stats
|
|
|
|
|
|
|
|
- if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil {
|
|
|
|
|
|
|
+ if err := e.ioctl(intf, uintptr(unsafe.Pointer(statsPtr))); err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var result = make(map[string]uint64)
|
|
|
|
|
|
|
+ result := make(map[string]uint64, drvinfo.n_stats)
|
|
|
for i := 0; i != int(drvinfo.n_stats); i++ {
|
|
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")
|
|
|
|
|
|
|
+ b := gstringsPtr.data[i*ETH_GSTRING_LEN : (i+1)*ETH_GSTRING_LEN]
|
|
|
|
|
+
|
|
|
|
|
+ strEnd := bytes.IndexByte(b, 0)
|
|
|
if strEnd == -1 {
|
|
if strEnd == -1 {
|
|
|
strEnd = ETH_GSTRING_LEN
|
|
strEnd = ETH_GSTRING_LEN
|
|
|
}
|
|
}
|
|
|
key := string(b[:strEnd])
|
|
key := string(b[:strEnd])
|
|
|
|
|
+
|
|
|
if len(key) != 0 {
|
|
if len(key) != 0 {
|
|
|
- result[key] = stats.data[i]
|
|
|
|
|
|
|
+ result[key] = statsPtr.data[i]
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -632,9 +1234,23 @@ func (e *Ethtool) Close() {
|
|
|
unix.Close(e.fd)
|
|
unix.Close(e.fd)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Identity the nic with blink duration, if not specify blink for 60 seconds
|
|
|
|
|
+func (e *Ethtool) Identity(intf string, duration *time.Duration) error {
|
|
|
|
|
+ dur := uint32(DEFAULT_BLINK_DURATION.Seconds())
|
|
|
|
|
+ if duration != nil {
|
|
|
|
|
+ dur = uint32(duration.Seconds())
|
|
|
|
|
+ }
|
|
|
|
|
+ return e.identity(intf, IdentityConf{Duration: dur})
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (e *Ethtool) identity(intf string, identity IdentityConf) error {
|
|
|
|
|
+ identity.Cmd = ETHTOOL_PHYS_ID
|
|
|
|
|
+ return e.ioctl(intf, uintptr(unsafe.Pointer(&identity)))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// NewEthtool returns a new ethtool handler
|
|
// NewEthtool returns a new ethtool handler
|
|
|
func NewEthtool() (*Ethtool, error) {
|
|
func NewEthtool() (*Ethtool, error) {
|
|
|
- fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
|
|
|
|
|
|
|
+ fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, unix.IPPROTO_IP)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
@@ -683,3 +1299,46 @@ func PermAddr(intf string) (string, error) {
|
|
|
defer e.Close()
|
|
defer e.Close()
|
|
|
return e.PermAddr(intf)
|
|
return e.PermAddr(intf)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+// Identity the nic with blink duration, if not specify blink infinity
|
|
|
|
|
+func Identity(intf string, duration *time.Duration) error {
|
|
|
|
|
+ e, err := NewEthtool()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ defer e.Close()
|
|
|
|
|
+ return e.Identity(intf, duration)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func supportedSpeeds(mask uint64) (ret []struct {
|
|
|
|
|
+ name string
|
|
|
|
|
+ mask uint64
|
|
|
|
|
+ speed uint64
|
|
|
|
|
+}) {
|
|
|
|
|
+ for _, mode := range supportedCapabilities {
|
|
|
|
|
+ if mode.speed > 0 && ((1<<mode.mask)&mask) != 0 {
|
|
|
|
|
+ ret = append(ret, mode)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return ret
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SupportedLinkModes returns the names of the link modes supported by the interface.
|
|
|
|
|
+func SupportedLinkModes(mask uint64) []string {
|
|
|
|
|
+ var ret []string
|
|
|
|
|
+ for _, mode := range supportedSpeeds(mask) {
|
|
|
|
|
+ ret = append(ret, mode.name)
|
|
|
|
|
+ }
|
|
|
|
|
+ return ret
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SupportedSpeed returns the maximum capacity of this interface.
|
|
|
|
|
+func SupportedSpeed(mask uint64) uint64 {
|
|
|
|
|
+ var ret uint64
|
|
|
|
|
+ for _, mode := range supportedSpeeds(mask) {
|
|
|
|
|
+ if mode.speed > ret {
|
|
|
|
|
+ ret = mode.speed
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return ret
|
|
|
|
|
+}
|