milen.me
milen.me

macOS Network Metrics Using sysctl()

Permalink | RSS

TL;DR

  • Getting accurate network metrics on macOS is possible using sysctl() with NET_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.

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 metrics 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:

Thanks

Many thanks to Quinn "The Eskimo!" for investigating and digging into the macOS kernel.

← Back to Writings

  1. struct if_data64 from if_var.h.

Any opinions and viewpoints expressed, explicitly or implicitly, are not endorsed by and do not represent any of my previous, current or future employers.