From BPF to Wireshark: libpcap and wireshark-chmodbpf on macOS

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:

  1. a tap mechanism:

    • 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
  2. a filter engine:

    • 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_bpf and instructs users to add their account to that group
  • it generates a launchd startup item:
    • at /Library/LaunchDaemons/org.macports.wireshark.ChmodBPF.plist
    • set to run automatically as root every time the Mac boots
  • that daemon runs a workaround script (/opt/local/sbin/wireshark-chmodbpf) that:
    1. pre-creates up to 256 BPF pseudo-devices (or fewer if debug.bpf_maxdevices is lower): this is because macOS can create BPF devices on demand, so a device created later would otherwise not receive the desired permissions
    2. allows members of the access_bpf group to access the BPF pseudo-devices

It has to run at every single boot because /dev on macOS is rebuilt from scratch each startup.

Avant Notes on configuring MikroTik switches

A Kemar Joint