On macOS, Wireshark uses libpcap to capture network packets, and libpcap uses BPF:
Network interface (en0)
│
┌──────────────────┐
│ macOS kernel │
│ (BPF receives a │
│ copy of packets) │
└─────────┬────────┘
│
┌──────────────┴──────────────┐
│ │
normal /dev/bpf0
delivery /dev/bpf1
(Safari, Mail /dev/bpf*
other apps) │
libpcap
│
Wireshark
BPF
BPF (Berkeley Packet Filter) is the packet-capture facility built into the BSD layer of the macOS kernel.
It has two main parts:
-
- code that intercepts frames at the link layer
- this allows packets to get duplicated toward any attached BPF session as they pass through the network stack
-
- a bytecode virtual machine that evaluates a filter program against each packet to decide whether to keep it or throw it away
- the filter program is supplied by the capture tool
- filtering happens in-kernel before anything gets copied to userspace, rather than copying everything and filtering afterward
BPF provides pseudo-devices such as /dev/bpf0, /dev/bpf1, etc. through which user-space programs access BPF packet capture and filtering.
On macOS, there is an upper bound on the number of BPF pseudo-devices (sysctl debug.bpf_maxdevices).
BPF is the mechanism. /dev/bpf* is the file-like door into it.
When a program calls open() on an available /dev/bpf* pseudo-device, it obtains a BPF descriptor. It then attaches that descriptor to a specific network interface (en0, en1, etc.) and configures its capture parameters and filter.
Once attached, the BPF descriptor receives copies of raw frames tapped from that interface and buffers them in the kernel until the program calls read() on its file descriptor to drain them.
This is what makes low-level packet sniffing possible, since normal sockets don't expose that level of detail.
libpcap and packet-filter expressions
Wireshark uses libpcap to access the BPF capture mechanism and to compile capture-filter expressions into BPF filter programs.
You can see libpcap packet-filter expressions in man pcap-filter.
For example: tcp port 443.
libpcap compiles that expression into BPF bytecode.
MacPorts wireshark-chmodbpf
On macOS, /dev/bpf* files are owned by root. Normal users can't capture packets without sudo.
wireshark-chmodbpf is a permission helper for Wireshark on macOS:
- it creates a group called
access_bpfand instructs users to add their account to that group - it generates a
launchdstartup item:- at
/Library/LaunchDaemons/org.macports.wireshark.ChmodBPF.plist - set to run automatically as root every time the Mac boots
- at
- that daemon runs a workaround script (
/opt/local/sbin/wireshark-chmodbpf) that:- pre-creates up to 256 BPF pseudo-devices (or fewer if
debug.bpf_maxdevicesis lower): this is because macOS can create BPF devices on demand, so a device created later would otherwise not receive the desired permissions - allows members of the
access_bpfgroup to access the BPF pseudo-devices
- pre-creates up to 256 BPF pseudo-devices (or fewer if
It has to run at every single boot because /dev on macOS is rebuilt from scratch each startup.