12 min read

On Network Interface Statistics

I’ve been recently (more) interested in testing the hardware on my personal device(s). Admittedly, I’m no expert in this, and I’ve probably always used the same (small) handful of utilities as everyone else to get a bird’s eye view of how the CPU and I/O and network devices are performing.

However, since I’m always keen on learning more, I’m not satisfied in just copying and pasting a command from some dark and dank place on the Internet to get the information that I’m after.

It’s better, in my opinion, to not only understand how to get access to the information (which is undoubtedly important), but also to understand how the kernel exposes the information to the user space tools (arguably, even more important to understand).

So, the purpose of this post is twofold:

  1. Determine where user space tools are getting their information.
  2. List a number of user space tools and the common commands they use to output this information.

That is all.



Network Interface Statistics

struct

When the kernel is installed, many header files and are installed to the /usr/include/linux/ directory. One of them is /usr/include/linux/if_link.h, which contains the following struct:

struct rtnl_link_stats64 {
    __u64 rx_packets;
    __u64 tx_packets;
    __u64 rx_bytes;
    __u64 tx_bytes;
    __u64 rx_errors;
    __u64 tx_errors;
    __u64 rx_dropped;
    __u64 tx_dropped;
    __u64 multicast;
    __u64 collisions;
    __u64 rx_length_errors;
    __u64 rx_over_errors;
    __u64 rx_crc_errors;
    __u64 rx_frame_errors;
    __u64 rx_fifo_errors;
    __u64 rx_missed_errors;
    __u64 tx_aborted_errors;
    __u64 tx_carrier_errors;
    __u64 tx_fifo_errors;
    __u64 tx_heartbeat_errors;
    __u64 tx_window_errors;
    __u64 rx_compressed;
    __u64 tx_compressed;
    __u64 rx_nohandler;
    __u64 rx_otherhost_dropped;
};

This is the struct that the kernel writes to and exposes to user space through two pseudo-filesystems. In fact, all of the tools that we’ll briefly look at in this article will internally use this struct.

There are many small scripts that use this information to quickly get some insight into the throughput for a particular network interface for a specific period of time. The following two members especially of the above struct are often used for many back-of-the-envelope calculations:

  • rx_packets

    • Number of good packets received by the interface. For hardware interfaces counts all good packets received from the device by the host, including packets which host had to drop at various stages of processing (even in the driver).
  • rx_bytes

    • Number of good received bytes, corresponding to rx_packets.
    • For IEEE 802.3 devices should count the length of Ethernet Frames excluding the FCS.
  • tx_packets

    • Number of packets successfully transmitted. For hardware interfaces counts packets which host was able to successfully hand over to the device, which does not necessarily mean that packets had been successfully transmitted out of the device, only that device acknowledged it copied them out of host memory.
  • tx_bytes

    • Number of good transmitted bytes, corresponding to tx_packets.
    • For IEEE 802.3 devices should count the length of Ethernet Frames excluding the FCS.

Let’s now see where the kernel makes this information available.

Files

As mentioned, there are shell scripts and scripting languages that will use the values in the following files to determine the throughput of a network adapter over time. The kernel writes the values of the rtnl_link_stats64 struct to locations in both the procfs and sysfs filesystems, exposing the information to a host of user space tools.

Let’s now take a look at two pseudo-filesystems that expose this information to user space.

procfs

/proc/net/dev

$ cat /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    lo: 10802074982 1034488    0    0    0     0          0         0 10802074982 1034488    0    0    0     0       0          0
enp0s31f6:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
wlp3s0: 14981762338 11585960    0    2    0     0          0         0 4703835446 4963146    0    0    0     0       0          0

Here are the columns and their meaning:

Column Description
bytes The total number of bytes of data transmitted or received by the interface.
packets The total number of packets of data transmitted or received by the interface.
errs The total number of transmit or receive errors detected by the device driver.
drop The total number of packets dropped by the device driver.
fifo The number of FIFO buffer errors.
frame The number of packet framing errors.
colls The number of collisions detected on the interface.
compressed The number of compressed packets transmitted or received by the device driver. (This appears to be unused in the 2.2.15 kernel.)
carrier The number of carrier losses detected by the device driver.
multicast The number of multicast frames transmitted or received by the device driver.

Now, we’re only interested in a particular interface and some of its columns, so, let’s be more precise:

$ cat /proc/net/dev | ag wlp3s0 | awk '{ print $2,$3,$10,$11 }'
14981738887 11585797 4703810950 4962952

sysfs

Each device directory in sysfs contains a statistics directory (e.g. /sys/class/net/lo/statistics/) with files corresponding to members of struct rtnl_link_stats64.

These directories are symlinked to directories within the /sys/devices/ directory.

$ readlink -f /sys/class/net/wlp3s0
/sys/devices/pci0000:00/0000:00:1c.6/0000:03:00.0/net/wlp3s0

For example, on my machine, /sys/class/net/ lists the following:

$ ls -l /sys/class/net
total 0
lrwxrwxrwx 1 root root 0 Jun 18 00:15 enp0s31f6 -> ../../devices/pci0000:00/0000:00:1f.6/net/enp0s31f6
lrwxrwxrwx 1 root root 0 Jun 18 00:15 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 Jun 18 00:15 wlp3s0 -> ../../devices/pci0000:00/0000:00:1c.6/0000:03:00.0/net/wlp3s0

This simple interface is convenient especially in constrained/embedded environments without access to tools. However, it’s inefficient when reading multiple stats as it internally performs a full dump of struct rtnl_link_stats64 and reports only the stat corresponding to the accessed file.

$ cat /sys/class/net/wlp3s0/statistics/{rx_packets,rx_bytes,tx_packets,tx_bytes}
11583631
14981141094
4960797
4703518718
$ ls /sys/class/net/wlp3s0/statistics
collisions  rx_compressed  rx_errors        rx_length_errors  rx_over_errors     tx_bytes           tx_dropped      tx_heartbeat_errors
multicast   rx_crc_errors  rx_fifo_errors   rx_missed_errors  rx_packets         tx_carrier_errors  tx_errors       tx_packets
rx_bytes    rx_dropped     rx_frame_errors  rx_nohandler      tx_aborted_errors  tx_compressed      tx_fifo_errors  tx_window_errors

User Space Tools

Most of these tools use one or both of the files that were just previously mentioned.

ip

The ip utility is a tool that does a lot of things (show and manipulate routing, network devices, interfaces and tunnels). It delegates to subcommands to perform its different tasks, and we’re be looking at the ip-link command.

$ sudo ip -s -s link show dev wlp3s0
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DORMANT group default qlen 1000
    link/ether a0:a4:c5:5f:f3:de brd ff:ff:ff:ff:ff:ff
    RX: bytes  packets  errors  dropped missed  mcast
    4298949547 3007618  0       0       0       0
    RX errors: length   crc     frame   fifo    overrun
               0        0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    102049599  348618   0       0       0       0
    TX errors: aborted  fifo   window heartbeat transns
               0        0       0       0       10

Output in json:

$ sudo ip -s -s -json link show dev wlp3s0
[{"ifindex":3,"ifname":"wlp3s0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"noqueue","operstate":"UP","linkmode":"DORMANT","group":"default","txqlen":1000,"link_type":"ether","address":"a0:a4:c5:5f:f3:de","broadcast":"ff:ff:ff:ff:ff:ff","stats64":{"rx":{"bytes":4298955775,"packets":3007679,"errors":0,"dropped":0,"over_errors":0,"multicast":0,"length_errors":0,"crc_errors":0,"frame_errors":0,"fifo_errors":0,"missed_errors":0},"tx":{"bytes":102057305,"packets":348689,"errors":0,"dropped":0,"carrier_errors":0,"collisions":0,"aborted_errors":0,"fifo_errors":0,"window_errors":0,"heartbeat_errors":0,"carrier_changes":10}}}]

Let’s only target the objects we’re interested in:

$ sudo ip -s -s -json link show dev wlp3s0 | jq '.[0].stats64'
{
  "rx": {
    "bytes": 4464190536,
    "packets": 3158482,
    "errors": 0,
    "dropped": 2,
    "over_errors": 0,
    "multicast": 0,
    "length_errors": 0,
    "crc_errors": 0,
    "frame_errors": 0,
    "fifo_errors": 0,
    "missed_errors": 0
  },
  "tx": {
    "bytes": 111182166,
    "packets": 400708,
    "errors": 0,
    "dropped": 0,
    "carrier_errors": 0,
    "collisions": 0,
    "aborted_errors": 0,
    "fifo_errors": 0,
    "window_errors": 0,
    "heartbeat_errors": 0,
    "carrier_changes": 22
  }
}

Specifying [-s] twice will display all the members of the struct rtnl_link_stats64 struct.

Option Description
-j, -json Output results in JavaScript Object Notation (JSON).
-s, -stats, -statistics Output more information. If the option appears twice or more, the amount of information increases. As a rule, the information is statistics or some time values.

ethtool

Low-level statistics can be accessed by the ethtool tool, the main utility for controlling network drivers and hardware.

$ sudo ethtool -S wlp3s0
NIC statistics:
     rx_packets: 16397
     rx_bytes: 19242044
     rx_duplicates: 2
     rx_fragments: 16218
     rx_dropped: 31
     tx_packets: 9054
     tx_bytes: 1871204
     tx_filtered: 0
     tx_retry_failed: 0
     tx_retries: 970
     sta_state: 4
     txrate: 520000000
     rxrate: 351000000
     signal: 180
     channel: 0
     noise: 18446744073709551615
     ch_time: 18446744073709551615
     ch_time_busy: 18446744073709551615
     ch_time_ext_busy: 18446744073709551615
     ch_time_rx: 18446744073709551615
     ch_time_tx: 18446744073709551615

Here is some other cool stuff that ethtool can do.

Print the current parameters of the network port:

$ sudo ethtool enp0s31f6
Settings for enp0s31f6:
        Supported ports: [ TP ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Supported pause frame use: No
        Supports auto-negotiation: Yes
        Supported FEC modes: Not reported
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Advertised pause frame use: No
        Advertised auto-negotiation: Yes
        Advertised FEC modes: Not reported
        Speed: Unknown!
        Duplex: Unknown! (255)
        Auto-negotiation: on
        Port: Twisted Pair
        PHYAD: 2
        Transceiver: internal
        MDI-X: Unknown (auto)
        Supports Wake-on: pumbg
        Wake-on: g
        Current message level: 0x00000007 (7)
                               drv probe link
        Link detected: no

Print the driver info of the wireless device:

$ sudo ethtool -i wlp3s0
driver: iwlwifi
version: 5.10.0-23-amd64
firmware-version: 36.ad812ee0.0 8265-36.ucode
expansion-rom-version:
bus-info: 0000:03:00.0
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no

Just for fun, let the link light of the Ethernet device flash for 30 seconds:

$ sudo ethtool -p enp0s31f6 30

Weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee

Option Description
-i, --driver Queries the specified network device for associated driver information.
--json Output results in JavaScript Object Notation (JSON). Only a subset of options support this. Those which do not will continue to output plain text in the presence of this option.
-p, --identify Initiates adapter-specific action intended to enable an operator to easily identify the adapter by sight. Typically this involves blinking one or more LEDs on the specific network port.
-S, --statistics Initiates adapter-specific action intended to enable an operator to easily identify the adapter by sight. Typically this involves blinking one or more LEDs on the specific network port.

netstat

The netstat utility will print network connections, routing tables, interface statistics, masquerade connections, and multicast memberships. Of course, we’re interested in the interface statistics here.

Print all network statistics:

$ sudo netstat wlp3s0

Print statistics for tcp protocol:

$ sudo netstat -st wlp3s0

Print statistics for udp protocol:

$ sudo netstat -su wlp3s0

sar

View both real-time and historical data with the sar utility. Of course, it allows you to see performance statistics for your network devices.

Interestingly, the sar man page states that /proc must be mounted for sar to work. This is a sure indication that sar gets its data from procfs and most likely /proc/net/dev.

$ sar -n DEV 1 3 --iface=wlp3s0
Linux 5.10.0-23-amd64 (kilgore-trout)   06/17/2023      _x86_64_        (8 CPU)

05:42:31 PM   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil IFACE
05:42:32 PM      1.00      0.00     92.0B      0.0B      0.00      0.00      0.00      0.0% wlp3s0

05:42:32 PM   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil IFACE
05:42:33 PM      6.00      8.00    685.0B    849.0B      0.00      0.00      0.00      0.0% wlp3s0

05:42:33 PM   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil IFACE
05:42:34 PM      0.00      0.00      0.0B      0.0B      0.00      0.00      0.00      0.0% wlp3s0

Average:      rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil IFACE
Average:         2.33      2.67    259.0B    283.0B      0.00      0.00      0.00      0.0% wlp3s0

ifstat

The ifstat command prints network interface statistics. The interface keeps records of the previous data displayed in history files, and, by default, it only displays the difference between the last and current calls.

It stores history files in /tmp/.ifstat.u$UID.

Show statistics for only the wireless device with total bandwidth:

$ sudo ifstat -i wlp3s0 -T
      wlp3s0              Total
 KB/s in  KB/s out   KB/s in  KB/s out
    0.06      0.09      0.06      0.09
    0.58      0.83      0.58      0.83
    0.48      1.79      0.48      1.79
  268.73      3.76    268.73      3.76
    0.43      0.54      0.43      0.54
 1919.43     10.74   1919.43     10.74

Add a timestamp:

$ sudo ifstat -t 3
  Time        enp0s31f6             wlp3s0
  HH:MM:SS   KB/s in  KB/s out   KB/s in  KB/s out
  21:18:28      0.00      0.00      0.00      0.00
  21:18:31      0.00      0.00      0.19      0.28
  21:18:34      0.00      0.00      1.80      0.54
  21:18:37      0.00      0.00    285.97     30.18
  21:18:40      0.00      0.00    200.09     11.04
  ...

Display loopback devices and hide interfaces that are up but not used:

$ sudo ifstat -lz
        lo                wlp3s0
 KB/s in  KB/s out   KB/s in  KB/s out
    0.00      0.00      0.00      0.00
    0.00      0.00      0.58      0.83
    ...
Option Description
-i Specifies the list of interfaces to monitor, separated by commas (if an interface name has a comma, it can be escaped with ‘'). Multiple instances of the options are added together.
-l Enables monitoring of loopback interfaces for which statistics are available. By default, ifstat monitors all non-loopback interfaces that are up.
-t Adds a timestamp at the beginning of each line.
-T Reports total bandwidth for all monitored interfaces.
-z Hides interface which counters are null, eg interfaces that are up but not used.

ifconfig

Of course, you can always use the older ifconfg, but it is now deprecated in favor of ip: Its information, according to its man page, comes from the following two files:

  • /proc/net/dev
  • /proc/net/if_inet6
$ sudo ifconfig wlp3s0
wlp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.96  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::f167:8747:fb33:88bc  prefixlen 64  scopeid 0x20<link>
        ether a0:a4:c5:5f:f3:de  txqueuelen 1000  (Ethernet)
        RX packets 11585859  bytes 14981744806 (13.9 GiB)
        RX errors 0  dropped 2  overruns 0  frame 0
        TX packets 4963028  bytes 4703819297 (4.3 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Hardware

If you’re unsure about the specs of your particular Ethernet network adapter, an easy way to get that information is by using the lspci utility.

$ sudo lspci -vs 03:00.0
03:00.0 Network controller: Intel Corporation Wireless 8265 / 8275 (rev 78)
        Subsystem: Intel Corporation Dual Band Wireless-AC 8265
        Flags: bus master, fast devsel, latency 0, IRQ 164, IOMMU group 12
        Memory at e9100000 (64-bit, non-prefetchable) [size=8K]
        Capabilities: [c8] Power Management version 3
        Capabilities: [d0] MSI: Enable+ Count=1/1 Maskable- 64bit+
        Capabilities: [40] Express Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting
        Capabilities: [140] Device Serial Number a0-a4-c5-ff-ff-5f-f3-de
        Capabilities: [14c] Latency Tolerance Reporting
        Capabilities: [154] L1 PM Substates
        Kernel driver in use: iwlwifi
        Kernel modules: iwlwifi

To see the capabilities, add another verbose flag (-v):

$ sudo lspci -vvs 03:00.0
$ sudo lspci -vmmnns 03:00.0
Slot:   03:00.0
Class:  Network controller [0280]
Vendor: Intel Corporation [8086]
Device: Wireless 8265 / 8275 [24fd]
SVendor:        Intel Corporation [8086]
SDevice:        Dual Band Wireless-AC 8265 [0010]
Rev:    78
IOMMUGroup:     12
Option Description
-mm Dump PCI device data in a machine readable form for easy parsing by scripts.
-nn Show PCI vendor and device codes as both numbers and names.
-v Be verbose and display detailed information about all devices.
-vv Be very verbose and display more details. This level includes everything deemed useful.

References