FreeBSD.software
Home/Guides/tcpdump on FreeBSD: Packet Capture Review
review·2026-04-09·12 min read

tcpdump on FreeBSD: Packet Capture Review

Review of tcpdump on FreeBSD: BPF filters, capture modes, pcap file handling, advanced expressions, performance tuning, and comparison with Wireshark and tshark.

tcpdump on FreeBSD: Packet Capture Review

tcpdump is the standard command-line packet analyzer on FreeBSD. It ships in the base system -- no packages to install, no dependencies to manage. It reads packets directly from network interfaces via BPF (Berkeley Packet Filter), displays them in human-readable form, and writes them to pcap files for later analysis. This review covers tcpdump's capabilities on FreeBSD, BPF filter syntax, capture modes, pcap file handling, performance considerations, and how it compares to Wireshark and tshark.

What tcpdump Does

tcpdump captures network traffic and decodes protocol headers. It operates in two primary modes:

  1. Live capture: Read packets from a network interface in real time
  2. Offline analysis: Read and filter packets from a previously saved pcap file

tcpdump is not a protocol analyzer with a GUI. It does not reassemble TCP streams, decode application-layer protocols in depth, or provide statistical visualizations. What it does, it does with minimal overhead and maximum reliability. It is the tool you reach for when you need to see what is actually on the wire, right now.

On FreeBSD, tcpdump is part of the base system. It uses BPF natively, which is FreeBSD's kernel-level packet capture mechanism. This means zero external dependencies and tight integration with the operating system.

sh
# Verify tcpdump is available (base system) which tcpdump tcpdump --version

Basic Usage

The simplest invocation captures all traffic on the default interface:

sh
tcpdump -i em0

This dumps packet summaries to stdout. Press Ctrl+C to stop. The output shows timestamps, source and destination addresses, protocol information, and packet sizes.

Common Flags

| Flag | Purpose |

|------|---------|

| -i | Capture on specific interface |

| -n | Do not resolve hostnames |

| -nn | Do not resolve hostnames or port names |

| -v, -vv, -vvv | Increasing verbosity |

| -c | Capture N packets then stop |

| -w | Write raw packets to pcap file |

| -r | Read packets from pcap file |

| -s | Set capture length (0 = full packet) |

| -X | Show packet contents in hex and ASCII |

| -A | Show packet contents in ASCII only |

| -e | Show link-layer header |

| -q | Quiet output (less protocol info) |

| -t | Do not print timestamp |

| -tttt | Print human-readable timestamp |

Practical Examples

Capture DNS traffic on interface em0:

sh
tcpdump -i em0 -nn port 53

Capture the first 100 packets and save to file:

sh
tcpdump -i em0 -c 100 -w /tmp/capture.pcap

Read a pcap file and filter for HTTP:

sh
tcpdump -r /tmp/capture.pcap -nn port 80

Show full packet contents in hex and ASCII:

sh
tcpdump -i em0 -nn -X -s 0 port 443 -c 10

Capture on all interfaces:

sh
tcpdump -i any -nn -w /tmp/all-interfaces.pcap

BPF Filter Expressions

BPF filters are the core of tcpdump's power. They are compiled into bytecode and executed in the kernel, which means non-matching packets never leave kernel space. This is critical for performance on high-traffic links.

Filter Primitives

Filters combine primitives with logical operators (and, or, not, parentheses).

By host:

sh
tcpdump -i em0 host 192.168.1.100 tcpdump -i em0 src host 10.0.0.1 tcpdump -i em0 dst host 10.0.0.1

By network:

sh
tcpdump -i em0 net 192.168.1.0/24 tcpdump -i em0 src net 10.0.0.0/8

By port:

sh
tcpdump -i em0 port 22 tcpdump -i em0 dst port 80 tcpdump -i em0 portrange 8000-9000

By protocol:

sh
tcpdump -i em0 tcp tcpdump -i em0 udp tcpdump -i em0 icmp tcpdump -i em0 arp

Compound Filters

Combine primitives for precise captures:

sh
# HTTP traffic to or from a specific host tcpdump -i em0 host 192.168.1.100 and port 80 # SSH traffic excluding a specific source tcpdump -i em0 port 22 and not src host 10.0.0.5 # DNS or NTP traffic tcpdump -i em0 port 53 or port 123 # TCP SYN packets only (connection initiations) tcpdump -i em0 'tcp[tcpflags] & tcp-syn != 0' # TCP RST packets (connection resets) tcpdump -i em0 'tcp[tcpflags] & tcp-rst != 0'

Advanced Byte-Offset Filters

BPF allows filtering on arbitrary byte positions within a packet. This is powerful for matching specific protocol fields:

sh
# ICMP echo requests only (type 8) tcpdump -i em0 'icmp[icmptype] == icmp-echo' # TCP packets with SYN flag set and ACK not set (initial SYN only) tcpdump -i em0 'tcp[tcpflags] == tcp-syn' # IP packets with Don't Fragment bit set tcpdump -i em0 'ip[6] & 0x40 != 0' # HTTP GET requests (match "GET " at start of TCP payload) tcpdump -i em0 'tcp dst port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' # VLAN-tagged traffic (802.1Q) tcpdump -i em0 vlan tcpdump -i em0 'vlan and host 192.168.1.100'

These byte-offset filters compile to BPF bytecode and execute in the kernel, maintaining performance even with complex expressions.

Capture Modes and File Handling

Writing pcap Files

The -w flag writes raw packet data in pcap format. This is the standard interchange format understood by Wireshark, tshark, Suricata, Zeek, and virtually every network analysis tool.

sh
# Capture with full packet length to file tcpdump -i em0 -s 0 -w /tmp/full-capture.pcap # Capture with rotation: new file every 100MB, keep 10 files tcpdump -i em0 -s 0 -w /tmp/capture-%Y%m%d-%H%M%S.pcap -C 100 -W 10 # Capture with time-based rotation: new file every 3600 seconds tcpdump -i em0 -s 0 -w /tmp/capture.pcap -G 3600

File Rotation Options

| Flag | Behavior |

|------|----------|

| -C | Rotate when file reaches size |

| -W | Limit number of rotated files |

| -G | Rotate every N seconds |

Combining -C and -W creates a ring buffer. When the maximum file count is reached, the oldest file is overwritten. This is essential for continuous capture on systems with limited disk space.

sh
# Ring buffer: 10 files, 50MB each (500MB total maximum) tcpdump -i em0 -s 0 -w /tmp/ring.pcap -C 50 -W 10

Reading and Filtering pcap Files

Read a capture file and apply a filter after the fact:

sh
# Extract only SSH traffic from a capture tcpdump -r /tmp/capture.pcap -nn port 22 # Extract traffic between two hosts tcpdump -r /tmp/capture.pcap -nn host 10.0.0.1 and host 10.0.0.2 # Count packets matching a filter tcpdump -r /tmp/capture.pcap -nn port 80 | wc -l

BPF on FreeBSD

tcpdump on FreeBSD uses BPF (Berkeley Packet Filter) through the /dev/bpf device. BPF is a FreeBSD kernel subsystem that provides efficient, zero-copy packet capture.

How BPF Works

  1. tcpdump opens /dev/bpf and attaches it to an interface
  2. A BPF filter program (compiled from the expression) is loaded into the kernel
  3. The kernel applies the filter to every packet on the interface
  4. Only matching packets are copied to userspace
  5. tcpdump decodes and displays or writes the packets

This kernel-level filtering is why tcpdump performs well even on high-traffic interfaces. Non-matching packets never cross the kernel-userspace boundary.

sh
# Check BPF device nodes ls -la /dev/bpf* # View BPF statistics sysctl net.bpf.stats sysctl dev.bpf

BPF Buffer Tuning

The BPF buffer size affects capture reliability under high traffic. The default may be too small for sustained gigabit capture.

sh
# Check current BPF buffer size sysctl net.bpf.bufsize # Increase BPF buffer for high-traffic capture sysctl net.bpf.bufsize=2097152 sysctl net.bpf.maxbufsize=4194304 # Make persistent in /etc/sysctl.conf echo 'net.bpf.bufsize=2097152' >> /etc/sysctl.conf echo 'net.bpf.maxbufsize=4194304' >> /etc/sysctl.conf

Promiscuous Mode

By default, tcpdump puts the interface into promiscuous mode, capturing all traffic visible on the wire segment (not just traffic destined for the local machine). Use -p to disable this:

sh
# Capture only traffic to/from this host (no promiscuous mode) tcpdump -i em0 -p -nn # Verify promiscuous mode ifconfig em0 | grep PROMISC

Performance Considerations

Capture on High-Traffic Interfaces

On busy interfaces (1 Gbps+), tcpdump can drop packets if it cannot process them fast enough. The drop counter is shown when you stop the capture:

shell
X packets captured Y packets received by filter Z packets dropped by kernel

To minimize drops:

sh
# Use a large snap length only if needed (reduces copy overhead) tcpdump -i em0 -s 96 -w /tmp/headers-only.pcap # Increase buffer size tcpdump -i em0 -B 4096 -w /tmp/capture.pcap # Use a restrictive filter to reduce captured volume tcpdump -i em0 -s 0 -w /tmp/web.pcap 'port 80 or port 443'

Snap Length

The snap length (-s) controls how many bytes of each packet are captured. The default on modern tcpdump is 262144 bytes (effectively the full packet). For high-volume captures where you only need headers:

sh
# Capture only first 96 bytes (enough for most headers) tcpdump -i em0 -s 96 -w /tmp/headers.pcap # Capture full packets tcpdump -i em0 -s 0 -w /tmp/full.pcap

Reducing snap length significantly reduces disk I/O and improves capture reliability on busy links.

Using tcpdump with Other Tools

Pipe to Wireshark

On a remote FreeBSD server, capture packets and stream them to a local Wireshark instance:

sh
# On your local workstation (Wireshark must be installed) ssh root@freebsd-server "tcpdump -i em0 -s 0 -U -w -" | wireshark -k -i -

The -U flag enables packet-buffered output, so packets appear in Wireshark in real time rather than waiting for the write buffer to fill.

Convert Between Formats

tcpdump reads and writes pcap. For pcap-ng or other formats, use the editcap utility from the Wireshark package:

sh
pkg install wireshark-lite # Convert pcap to pcap-ng editcap -F pcapng /tmp/capture.pcap /tmp/capture.pcapng

Extract Specific Packets

sh
# Split a large capture into 100MB files tcpdump -r /tmp/large-capture.pcap -w /tmp/split.pcap -C 100 # Extract a time range (requires editcap) editcap -A "2026-04-09 10:00:00" -B "2026-04-09 10:05:00" \ /tmp/capture.pcap /tmp/five-minutes.pcap

tcpdump vs Wireshark vs tshark

| Feature | tcpdump | tshark | Wireshark |

|---------|---------|--------|-----------|

| Interface | CLI | CLI | GUI |

| Install | Base system | pkg install wireshark | pkg install wireshark |

| Protocol Decoders | ~200 | ~3,000 | ~3,000 |

| Stream Reassembly | No | Yes | Yes |

| Display Filters | No (BPF only) | Yes | Yes |

| Live Capture | Yes | Yes | Yes |

| pcap Read/Write | Yes | Yes (plus pcap-ng) | Yes (plus pcap-ng) |

| Performance | Excellent | Good | Moderate |

| Remote Friendly | Yes (SSH) | Yes (SSH) | No (X11/Wayland) |

| Scripting | Limited | Lua support | Lua support |

Use tcpdump when:

  • You need a quick capture on a remote server
  • No packages are installed and you cannot install any
  • You want minimal overhead on a busy production system
  • You need a ring buffer for continuous capture

Use tshark when:

  • You need deep protocol decoding from the command line
  • You need display filters (Wireshark filter syntax)
  • You need to export specific fields in CSV or JSON format
  • You need stream reassembly

Use Wireshark when:

  • You need visual protocol analysis
  • You are dissecting a complex conversation (TCP stream follow, etc.)
  • You need protocol-specific statistics
  • You need expert analysis features

In practice, many engineers use tcpdump on the server for capture and Wireshark locally for analysis:

sh
# Capture on server tcpdump -i em0 -s 0 -w /tmp/debug.pcap 'host 10.0.0.50 and port 443' -c 10000 # Transfer to workstation scp root@server:/tmp/debug.pcap . # Open in Wireshark wireshark debug.pcap

Security Considerations

Running tcpdump requires root privileges or membership in a group with BPF access. On FreeBSD, the /dev/bpf devices are owned by root:wheel with mode 0600 by default.

sh
# Check BPF device permissions ls -la /dev/bpf0 # Allow a group to use BPF (devfs rule) # Add to /etc/devfs.rules: # [localrules=10] # add path 'bpf*' mode 0640 group network

Be aware that tcpdump in promiscuous mode captures all traffic on the segment, including passwords and sensitive data transmitted in cleartext. Use it responsibly and delete capture files when no longer needed.

sh
# Securely delete a capture file rm -P /tmp/capture.pcap

Frequently Asked Questions

Does tcpdump come installed on FreeBSD by default?

Yes. tcpdump is part of the FreeBSD base system. No packages are required.

Can tcpdump capture encrypted (TLS/SSL) traffic?

tcpdump captures the encrypted packets, but it cannot decrypt them. You see the TLS handshake (ClientHello, ServerHello, Certificate) in cleartext, but application data is encrypted. To decrypt, you need the session keys and a tool like Wireshark with SSLKEYLOGFILE support.

How do I capture traffic on a VLAN interface?

Capture directly on the VLAN interface:

sh
tcpdump -i em0.100 -nn -w /tmp/vlan100.pcap

Or capture on the parent interface and filter by VLAN tag:

sh
tcpdump -i em0 -nn vlan 100

Why am I seeing packet drops?

Drops occur when the kernel buffer fills faster than tcpdump reads it. Increase the buffer with -B, reduce captured bytes with -s, or use a more restrictive filter.

Can I use tcpdump to capture loopback traffic?

Yes. Use the loopback interface:

sh
tcpdump -i lo0 -nn

How do I capture only packet headers without payload?

Set the snap length to capture only headers:

sh
tcpdump -i em0 -s 64 -nn -w /tmp/headers.pcap

Can tcpdump filter by MAC address?

Yes. Use the ether qualifier:

sh
tcpdump -i em0 ether src 00:11:22:33:44:55 tcpdump -i em0 ether dst ff:ff:ff:ff:ff:ff

How do I capture on all interfaces simultaneously?

Use the any pseudo-interface:

sh
tcpdump -i any -nn -w /tmp/all.pcap

Note that link-layer headers are not available on the any interface (they are replaced with a Linux cooked capture header or similar).

What is the maximum file size for a pcap file?

pcap files can grow to the filesystem limit, but individual pcap files larger than 2 GB may cause issues with older tools. Use file rotation (-C and -W) for long captures.

How do I filter by TCP flags?

Use the tcp flags syntax:

sh
# SYN packets tcpdump -i em0 'tcp[tcpflags] & tcp-syn != 0' # FIN packets tcpdump -i em0 'tcp[tcpflags] & tcp-fin != 0' # SYN-ACK packets tcpdump -i em0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.