macOS Network Metrics Using sysctl()
Published on
Last Updated on
TL;DR
- Getting accurate network metrics on macOS is possible using
sysctl()
withNET_RT_IFLIST2
. - Traffic metrics are exposed in units of 1KiB to prevent fingerprinting (only for 3rd party programs).
- As of macOS Ventura 13.2.1, there’s a kernel bug which truncates traffic values at the 4GiB mark.
- Using getifaddrs() only exposes 32bit fields, so it’s not a viable API on modern systems.
Updates
Network Metrics on macOS
As part of my work on the Buck2 build system, I needed a way to observe the network throughput of the system. After some research, the conclusion was to use sysctl()
with NET_RT_IFLIST2
: this provided access to 64bit metrics1 which do not suffer from overflowing that affects the 32bit fields of the older APIs.
I wrote up a short sample program to quickly test the API. While the metrics for packets sent/received exactly matched the ones from Activity Monitor, the traffic metrics did not. I noticed two interesting behaviours:
- The traffic metrics were always increasing in multiples of 1KiB.
- The traffic metrics did not match the ones from Activity Monitor.
I decided to file a TSI with Apple before digging any further. Thankfully, Quinn resolved the mystery of the inaccurate numbers.
1KiB Units
If you looked at the traffic metrics, they would only ever increase in multiples of 1KiB. The reason for the behaviour is that the kernel applies batching to prevent malicious code from fingerprinting the system. This restriction applies only to 3rd party programs (i.e., not codesigned by Apple).
For example, if you were to copy the netstat
binary and re-sign it with an adhoc code signature, you will observe the batching while the Apple-signed binary works without any issues.
Inaccurate Numbers
Testing revealed that sometimes the API returned inaccurate traffic metrics. Upon further investigation, it became clear that the API truncates and wraps around the traffic metrics at the 4GiB mark. Again, this only affects 3rd party programs.
The behaviour is confirmed to be a bug in the kernel as of macOS Ventura 13.2.1 and it’s tracked as rdar://106029568.
Activity Monitor
Activity Monitor and nettop(1)
use the private NetworkStatistics.framework
to get network metrics. In the *OS Internals, Volume I book, there’s a bonus chapter which covers the details of how the private API works.
Netbottom is a clone of nettop(1)
that shows how to use the private APIs.
Alternative APIs
Unfortunately, there’s no alternative public API that can return 64bit metrics on macOS. Using getifaddrs() would only expose a struct if_data
which contains 32bit fields that overflow quickly on modern systems: it does not expose struct if_data64
.
Rust Crates
If you’re using Rust, the following crates use the NET_RT_IFLIST2
API to get metrics on macOS:
Update (31 Mar, 2023)
Many thanks to Mojo_66 who reached out to note that we can use an additional sysctl()
call to get 64bit network metrics which do not suffer from trunctation. Accordingly, I’ve updated the sample code.
Furthermore, using IFMIB_IFDATA
does not result in 1KiB batching of the reported metrics. My assumption is that this is a bug and would be fixed in a future version of macOS to prevent fingerprinting.
Thanks
Many thanks to Quinn “The Eskimo!” for investigating and digging into the macOS kernel. Many thanks to Mojo_66 for pointing out a 64bit network metrics API which does not suffer from truncation.
-
struct if_data64
fromif_var.h
. ↩︎