macOS Network Metrics Using sysctl()
Last Updated on
- Getting accurate network metrics on macOS is possible using
- 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.
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
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.
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.
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 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.
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
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.
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.
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.
← Back to Writings