#!/usr/bin/env python import socket import fcntl import struct import array SIOCETHTOOL = 0x8946 ETHTOOL_GSTRINGS = 0x0000001b ETHTOOL_GSSET_INFO = 0x00000037 ETHTOOL_GSTATS = 0x0000001d ETH_SS_STATS = 0x1 ETH_GSTRING_LEN = 32 class Ethtool(object): def __init__(self, ifname): self.ifname = ifname self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) def _send_ioctl(self, data): ifr = struct.pack('16sP', self.ifname.encode("utf-8"), data.buffer_info()[0]) return fcntl.ioctl(self._sock.fileno(), SIOCETHTOOL, ifr) def get_gstringset(self, set_id): sset_info = array.array('B', struct.pack("IIQI", ETHTOOL_GSSET_INFO, 0, 1 << set_id, 0)) self._send_ioctl(sset_info) sset_mask, sset_len = struct.unpack("8xQI", sset_info) if sset_mask == 0: sset_len = 0 strings = array.array("B", struct.pack("III", ETHTOOL_GSTRINGS, ETH_SS_STATS, sset_len)) strings.extend(b'\x00' * sset_len * ETH_GSTRING_LEN) self._send_ioctl(strings) for i in range(sset_len): offset = 12 + ETH_GSTRING_LEN * i s = strings[offset:offset+ETH_GSTRING_LEN].tobytes().partition(b'\x00')[0].decode("utf-8") yield s def get_nic_stats(self): strings = list(self.get_gstringset(ETH_SS_STATS)) n_stats = len(strings) stats = array.array("B", struct.pack("II", ETHTOOL_GSTATS, n_stats)) stats.extend(struct.pack('Q', 0) * n_stats) self._send_ioctl(stats) for i in range(n_stats): offset = 8 + 8 * i value = struct.unpack('Q', stats[offset:offset+8])[0] yield (strings[i], value) if __name__ == '__main__': import sys ifname = sys.argv[1] et = Ethtool(ifname) for k, v in et.get_nic_stats(): print(f"{k}: {v}")