FreeBSD.software
Home/Guides/How to Capture and Analyze Network Traffic on FreeBSD with tcpdump
tutorial·2026-04-09·13 min read

How to Capture and Analyze Network Traffic on FreeBSD with tcpdump

Master tcpdump on FreeBSD: capture filters, display filters, saving to pcap, analyzing HTTP/DNS/TLS traffic, BPF expressions, and integration with Wireshark.

How to Capture and Analyze Network Traffic on FreeBSD with tcpdump

tcpdump is the standard command-line packet analyzer on FreeBSD. It ships in the base system, requires no installation, and provides direct access to every packet crossing any network interface. For sysadmins troubleshooting connectivity issues, debugging firewall rules, or auditing traffic patterns, tcpdump is the first tool to reach for.

This guide covers everything from basic captures to advanced BPF filter expressions, pcap file management, protocol-specific analysis, and integration with Wireshark for graphical inspection.

Why tcpdump on FreeBSD

FreeBSD's network stack originated from the same BSD codebase that gave birth to tcpdump and the Berkeley Packet Filter (BPF). The integration between tcpdump and FreeBSD's kernel is native and tight. There is no wrapper layer, no compatibility shim, and no performance penalty from translation.

Key advantages of using tcpdump on FreeBSD:

  • Base system tool. No package installation required. Available immediately on every FreeBSD installation, including minimal jails and embedded systems.
  • BPF native. FreeBSD's BPF implementation is the reference implementation. Filter expressions compile directly to BPF bytecode that runs in the kernel, keeping userspace overhead minimal even on high-traffic interfaces.
  • Zero dependencies. Unlike tshark or ngrep, tcpdump has no library dependencies beyond what the base system provides. This makes it reliable in recovery scenarios where pkg is unavailable.
  • PF integration. When troubleshooting PF firewall rules, tcpdump can capture on the pflog0 interface to see exactly which rules are matching traffic.

Prerequisites

  • FreeBSD 13.0 or later (tcpdump is in the base system on all supported versions)
  • Root access (packet capture requires BPF device access)
  • Basic understanding of TCP/IP networking

Basic Packet Capture

The simplest tcpdump invocation captures all traffic on the default interface:

sh
tcpdump -i em0

Replace em0 with your actual interface name. To find your interfaces:

sh
ifconfig -l

This produces a list like lo0 em0 wlan0. Use the one carrying your production traffic.

Limiting Output Verbosity

By default, tcpdump prints a summary of each packet. Increase verbosity with -v, -vv, or -vvv:

sh
# Standard verbosity -- IP TTL, identification, total length, options tcpdump -i em0 -v # Maximum verbosity -- full protocol decode tcpdump -i em0 -vvv

Capturing a Fixed Number of Packets

Use -c to stop after a specific count:

sh
tcpdump -i em0 -c 100

This captures exactly 100 packets and exits. Useful for quick sampling without generating massive output.

Showing Packet Contents in Hex and ASCII

To inspect actual packet payloads:

sh
# Hex and ASCII dump tcpdump -i em0 -X # Hex only tcpdump -i em0 -x # Full frame including link-layer header tcpdump -i em0 -XX

Disabling Name Resolution

DNS lookups slow down tcpdump output and can generate additional traffic that appears in the capture. Always disable resolution when analyzing traffic:

sh
tcpdump -i em0 -nn

The -nn flag disables both hostname and port name resolution. This is the single most important flag for production use.

BPF Filter Expressions

BPF (Berkeley Packet Filter) expressions are the core of targeted packet capture. They compile to kernel bytecode and filter packets before they reach userspace, drastically reducing overhead on busy servers.

Filter by Host

sh
# All traffic to or from a specific IP tcpdump -i em0 -nn host 192.168.1.100 # Only traffic originating from a host tcpdump -i em0 -nn src host 192.168.1.100 # Only traffic destined for a host tcpdump -i em0 -nn dst host 192.168.1.100

Filter by Port

sh
# All traffic on port 443 (HTTPS) tcpdump -i em0 -nn port 443 # Source port only tcpdump -i em0 -nn src port 22 # Port range tcpdump -i em0 -nn portrange 8000-8100

Filter by Protocol

sh
# TCP only tcpdump -i em0 -nn tcp # UDP only tcpdump -i em0 -nn udp # ICMP only tcpdump -i em0 -nn icmp # ICMPv6 tcpdump -i em0 -nn icmp6

Filter by Network

sh
# All traffic to/from a subnet tcpdump -i em0 -nn net 10.0.0.0/24 # Traffic between two specific subnets tcpdump -i em0 -nn 'src net 10.0.0.0/24 and dst net 172.16.0.0/16'

Combining Filters with Boolean Logic

Use and, or, and not (or their symbolic equivalents &&, ||, !):

sh
# HTTP or HTTPS traffic from a specific host tcpdump -i em0 -nn 'host 192.168.1.50 and (port 80 or port 443)' # All traffic except SSH tcpdump -i em0 -nn 'not port 22' # DNS queries to a specific server, excluding a noisy host tcpdump -i em0 -nn 'dst port 53 and not src host 10.0.0.5'

Always quote complex filter expressions to prevent shell interpretation of parentheses and other special characters.

TCP Flag Filters

Filter by specific TCP flags to catch SYN floods, RST storms, or connection issues:

sh
# SYN packets only (new connection attempts) tcpdump -i em0 -nn 'tcp[tcpflags] & tcp-syn != 0' # SYN-ACK packets (connection responses) tcpdump -i em0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)' # RST packets (connection resets) tcpdump -i em0 -nn 'tcp[tcpflags] & tcp-rst != 0' # FIN packets (connection teardowns) tcpdump -i em0 -nn 'tcp[tcpflags] & tcp-fin != 0' # Packets with the PUSH flag (data delivery) tcpdump -i em0 -nn 'tcp[tcpflags] & tcp-push != 0'

Packet Size Filters

sh
# Packets larger than 1000 bytes tcpdump -i em0 -nn 'greater 1000' # Packets smaller than 100 bytes tcpdump -i em0 -nn 'less 100'

Saving and Reading Capture Files

For any serious analysis, write captures to pcap files rather than reading live output.

Writing to a Pcap File

sh
tcpdump -i em0 -nn -w /tmp/capture.pcap

When writing to a file, tcpdump does not decode packets to stdout. This is significantly faster and captures every packet even on high-speed interfaces.

Rotating Capture Files

For long-running captures, use file rotation to prevent filling the disk:

sh
# Rotate every 100 MB, keep 10 files tcpdump -i em0 -nn -w /tmp/capture.pcap -C 100 -W 10 # Rotate every 3600 seconds (1 hour) tcpdump -i em0 -nn -w /tmp/capture.pcap -G 3600 -W 24

With -G (time-based rotation), use strftime format strings in the filename:

sh
tcpdump -i em0 -nn -w '/tmp/capture_%Y%m%d_%H%M%S.pcap' -G 3600

Reading a Pcap File

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

You can apply any BPF filter when reading:

sh
tcpdump -nn -r /tmp/capture.pcap 'port 80 and host 10.0.0.1'

Snap Length

By default, tcpdump captures the full packet. To capture only headers (reducing file size for high-volume captures):

sh
# Capture first 96 bytes of each packet tcpdump -i em0 -nn -s 96 -w /tmp/headers_only.pcap

For full payload analysis, ensure the snap length covers the entire packet:

sh
tcpdump -i em0 -nn -s 0 -w /tmp/full_capture.pcap

-s 0 sets the snap length to the maximum (262144 bytes), capturing every byte.

Protocol-Specific Analysis

DNS Traffic

sh
# All DNS queries and responses tcpdump -i em0 -nn port 53 # DNS queries only (to nameserver) tcpdump -i em0 -nn dst port 53 # Verbose DNS decode tcpdump -i em0 -nn -vvv port 53

With -vvv, tcpdump decodes DNS query types, response codes, TTLs, and record data. This is invaluable for debugging resolution failures.

HTTP Traffic

sh
# HTTP requests and responses tcpdump -i em0 -nn -A port 80 # Capture HTTP request lines (GET, POST, etc.) tcpdump -i em0 -nn -A 'port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'

The second filter matches packets starting with "GET " at the TCP payload offset. The -A flag prints packet data in ASCII, making HTTP headers readable.

TLS/HTTPS Traffic

You cannot decrypt TLS traffic with tcpdump, but you can observe the handshake:

sh
# TLS Client Hello messages (connection initiation) tcpdump -i em0 -nn 'port 443 and tcp[((tcp[12:1] & 0xf0) >> 2)] = 0x16 and tcp[((tcp[12:1] & 0xf0) >> 2) + 5] = 0x01'

This captures TLS Client Hello packets, useful for identifying which clients are connecting and which TLS versions they offer.

ICMP Analysis

sh
# All ICMP -- ping, unreachable, TTL exceeded tcpdump -i em0 -nn icmp # Echo requests only tcpdump -i em0 -nn 'icmp[icmptype] = icmp-echo' # Destination unreachable tcpdump -i em0 -nn 'icmp[icmptype] = icmp-unreach'

Capturing PF Firewall Log Traffic

FreeBSD's PF firewall can log matched packets to the pflog0 interface. This is the most reliable way to debug firewall rules.

First, ensure pflog is enabled:

sh
# Load the pflog kernel module kldload pflog # Create the pflog interface if it does not exist ifconfig pflog0 create # Enable pflog at boot sysrc pflog_enable="YES"

In your PF rules, add the log keyword:

shell
# /etc/pf.conf block log all pass log quick on em0 proto tcp to port 22

Reload PF and capture logged traffic:

sh
pfctl -f /etc/pf.conf tcpdump -i pflog0 -nn

The pflog output includes the PF rule number, action (pass/block), direction, and interface. This makes it straightforward to correlate blocked packets with specific rules.

sh
# Show only blocked packets tcpdump -i pflog0 -nn 'action block' # Show only passed packets on a specific interface tcpdump -i pflog0 -nn 'action pass and on em0'

Integration with Wireshark

tcpdump writes standard pcap files that Wireshark reads natively. The workflow is:

  1. Capture on the FreeBSD server with tcpdump
  2. Transfer the pcap file to a workstation
  3. Open in Wireshark for graphical analysis
sh
# On FreeBSD: capture to file tcpdump -i em0 -nn -s 0 -w /tmp/analysis.pcap -c 10000 # Transfer to workstation scp /tmp/analysis.pcap user@workstation:/tmp/

For real-time remote capture, pipe tcpdump output directly to Wireshark over SSH:

sh
# On workstation (Linux/macOS) ssh root@freebsd-server 'tcpdump -i em0 -nn -s 0 -U -w -' | wireshark -k -i -

The -U flag tells tcpdump to write each packet immediately (packet-buffered) rather than waiting for the output buffer to fill. This ensures Wireshark receives packets in real time.

Performance Considerations

On high-traffic servers (1 Gbps+), unfiltered packet capture can drop packets. Mitigate this with:

Use kernel filters. Always apply BPF filters rather than capturing everything and filtering in post-processing. BPF runs in the kernel and discards non-matching packets before they are copied to userspace.

Write to fast storage. Capture files should go to a tmpfs, SSD, or RAM disk. Avoid writing to slow disks on busy servers:

sh
# Create a memory-backed filesystem for captures mdconfig -a -t malloc -s 512m -u 0 newfs /dev/md0 mount /dev/md0 /mnt/capture tcpdump -i em0 -nn -w /mnt/capture/traffic.pcap

Increase the BPF buffer size. On FreeBSD, the default BPF buffer can be too small for high-speed links:

sh
# Increase BPF buffer to 4 MB tcpdump -i em0 -nn -B 4096 -w /tmp/capture.pcap

The -B flag sets the BPF buffer size in KiB. Larger buffers reduce the chance of kernel drops.

Monitor for drops. When tcpdump exits, it reports captured, received by filter, and dropped by kernel counts:

shell
1847293 packets captured 1847301 packets received by filter 8 packets dropped by kernel

If the dropped count is significant, increase the BPF buffer or narrow your filter.

Common Troubleshooting Recipes

Finding Who Is Connecting to a Service

sh
# All new TCP connections to port 443 tcpdump -i em0 -nn 'dst port 443 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'

Detecting Port Scans

sh
# SYN packets to multiple ports from a single source tcpdump -i em0 -nn 'tcp[tcpflags] == tcp-syn' -c 1000 | sort -t. -k1,4 | uniq -c | sort -rn | head -20

Measuring DNS Response Times

sh
# Capture DNS traffic with timestamps tcpdump -i em0 -nn -ttt port 53

The -ttt flag shows the delta between each packet and the previous one. Match query and response pairs to measure DNS latency.

Identifying Bandwidth-Hungry Hosts

sh
# Capture to file, then analyze with tcpdump and sort tcpdump -i em0 -nn -w /tmp/traffic.pcap -c 50000 tcpdump -nn -r /tmp/traffic.pcap | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head -20

Checking for MTU / Fragmentation Issues

sh
# Find fragmented IP packets tcpdump -i em0 -nn 'ip[6:2] & 0x3fff != 0'

This catches all fragments except the first (which has the offset zero flag). Frequent fragmentation usually indicates an MTU mismatch.

Automating Captures with cron

For periodic traffic sampling, schedule tcpdump via cron:

sh
# /etc/cron.d/tcpdump-sample # Capture 60 seconds of DNS traffic every hour 0 * * * * root /usr/sbin/tcpdump -i em0 -nn -w /var/log/captures/dns_$(date +\%Y\%m\%d_\%H\%M).pcap -G 60 -W 1 port 53

Ensure the capture directory exists and has appropriate permissions:

sh
mkdir -p /var/log/captures chmod 700 /var/log/captures

Add a cleanup job to prevent disk exhaustion:

sh
# Remove captures older than 7 days 0 0 * * * root find /var/log/captures -name '*.pcap' -mtime +7 -delete

Useful tcpdump Command Reference

| Flag | Purpose |

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

| -i em0 | Capture on interface em0 |

| -nn | Disable name resolution |

| -v / -vv / -vvv | Increase verbosity |

| -c N | Stop after N packets |

| -w file | Write to pcap file |

| -r file | Read from pcap file |

| -s N | Snap length (0 = max) |

| -A | Print packet data in ASCII |

| -X | Print packet data in hex and ASCII |

| -B N | BPF buffer size in KiB |

| -C N | Rotate file after N MB |

| -G N | Rotate file after N seconds |

| -W N | Max number of rotated files |

| -U | Packet-buffered output |

| -ttt | Delta timestamps |

| -e | Print link-layer header |

| -D | List available interfaces |

FAQ

Does tcpdump require root access on FreeBSD?

Yes. Packet capture requires access to BPF devices (/dev/bpf*), which are restricted to root by default. You can adjust permissions on BPF devices or use sudo, but in production, root access is the standard approach.

Can tcpdump capture traffic inside jails?

A jail with its own VNET (virtual network stack) can run tcpdump on its virtual interface. Standard (non-VNET) jails share the host network stack and cannot access BPF devices. To capture traffic for a non-VNET jail, run tcpdump on the host and filter by the jail's IP address.

How do I capture on all interfaces simultaneously?

Use the special any interface:

sh
tcpdump -i any -nn

Note that when capturing on any, the link-layer header type changes to Linux cooked capture (SLL) format, which some tools may handle differently.

What is the difference between tcpdump and tshark?

tcpdump is a base system tool with minimal overhead and BPF-native filtering. tshark (part of the Wireshark suite) provides deeper protocol dissection, more output formats, and display filters. Use tcpdump for capture and tshark for detailed protocol analysis. Install tshark with pkg install wireshark.

How do I capture traffic on a VLAN interface?

Capture directly on the VLAN interface:

sh
tcpdump -i em0.100 -nn

Or capture on the parent interface with a VLAN filter:

sh
tcpdump -i em0 -nn -e 'vlan 100'

The -e flag is necessary to see VLAN tags in the output.

Can tcpdump handle 10 Gbps traffic?

tcpdump can capture on 10 Gbps interfaces, but unfiltered capture at line rate will drop packets. Always use BPF filters to limit the capture to relevant traffic. For full line-rate capture on 10 Gbps+ links, consider netmap-based tools or commercial solutions.

How do I view real-time statistics without full packet output?

Use the -q (quiet) flag for minimal output, or pipe through standard Unix tools:

sh
# Count packets per second tcpdump -i em0 -nn -q -l | pv -l -i 1 > /dev/null

The -l flag makes tcpdump line-buffer its output, enabling real-time piping to other tools.

Get more FreeBSD guides

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