$linuxjunkies
>

Use tcpdump Effectively

Master tcpdump with BPF filters, ring buffer tuning, rotating pcap captures, and a systemd service wrapper — without dropping packets or wasting disk space.

IntermediateUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • Root or CAP_NET_RAW capability on the target host
  • tcpdump installed (version 4.99+ recommended)
  • A writable destination directory with sufficient disk space for pcap files
  • Basic familiarity with TCP/IP addressing and port numbers

tcpdump is the swiss-army knife of packet capture. It runs anywhere, produces pcap files every other tool can read, and with a decent BPF filter it won't bury you in noise. This guide covers writing effective Berkeley Packet Filter expressions, capturing to rotating pcap files, tuning the ring buffer to avoid dropped packets, and the mistakes that cost people hours of debugging.

Core Syntax Review

tcpdump's basic form is tcpdump [options] [filter expression]. The filter expression is compiled into a BPF program and pushed into the kernel, so only matching packets ever reach userspace. That distinction matters enormously for performance.

# Quick sanity check: list available interfaces
tcpdump -D
# Capture on a specific interface, don't resolve hostnames or ports (-nn)
tcpdump -i eth0 -nn

Always use -nn in production. DNS reverse lookups for every packet add latency and can distort timing data in your capture.

Writing BPF Filters

BPF filters are compiled at capture time and run inside the kernel. A poorly written or absent filter means every packet crosses the kernel/userspace boundary — on a busy 10 Gbps link that causes immediate drops.

Primitives and Combinators

BPF filters compose three types of primitives: host, port, and proto, joined with and, or, and not. Parentheses must be escaped or quoted.

# Traffic to or from a single host
tcpdump -i eth0 -nn host 192.168.1.50
# Inbound only (dst) on port 443
tcpdump -i eth0 -nn dst port 443
# HTTP or HTTPS from a subnet, excluding a management host
tcpdump -i eth0 -nn \
  'net 10.0.0.0/8 and (port 80 or port 443) and not host 10.0.0.1'
# Capture only TCP SYN packets (flags: SYN set, ACK clear)
# tcp[13] is the flags byte; 0x02 = SYN, 0x12 = SYN+ACK
tcpdump -i eth0 -nn 'tcp[13] == 0x02'
# ICMP only
tcpdump -i eth0 -nn icmp

Proto and Layer Tricks

# Match DNS queries (UDP port 53) larger than 100 bytes — useful to catch
# TCP-fallback DNS or unusually large responses
tcpdump -i eth0 -nn 'udp port 53 and greater 100'
# ARP traffic only (useful for detecting ARP storms)
tcpdump -i eth0 -nn arp
# IPv6 neighbour discovery
tcpdump -i eth0 -nn 'icmp6 and (ip6[40] == 135 or ip6[40] == 136)'

Capturing to File and Rotating pcaps

Capturing straight to a terminal is fine for quick checks. For anything you need to analyse later — or hand to Wireshark — write to a file. For long-running captures, rotate files so a crash or full disk doesn't lose everything.

Basic File Capture

# -w writes a pcap, -s 0 captures full packet (default in modern tcpdump)
tcpdump -i eth0 -nn -w /tmp/capture.pcap 'port 443'
# Read it back later (no live interface needed)
tcpdump -r /tmp/capture.pcap -nn 'host 10.0.0.5'

pcap Rotation with -G and -C

Two flags handle rotation. -G <seconds> rotates by time; -C <size-MB> rotates by file size. Use both together for predictable file sizes. The -W <count> flag limits total file count — oldest files are overwritten, giving you a rolling window.

# Rotate every 5 minutes, keep 12 files (~1 hour of history)
# %Y%m%d-%H%M%S in the filename makes each file uniquely timestamped
tcpdump -i eth0 -nn \
  -w /var/captures/eth0-%Y%m%d-%H%M%S.pcap \
  -G 300 \
  -W 12
# Rotate by size (100 MB per file), keep 10 files
tcpdump -i eth0 -nn \
  -w /var/captures/eth0.pcap \
  -C 100 \
  -W 10

When using -G with -W, tcpdump numbers files instead of using the strftime pattern if the count rolls over. Test your naming scheme before relying on it in production.

Ring Buffer and Dropped Packets

Dropped packets are the silent killer of packet captures. tcpdump prints a summary at exit, but by then the evidence is gone. The kernel-side ring buffer (-B flag, in KB) is your first line of defence.

# Default buffer is typically 2048 KB; raise it for high-rate interfaces
tcpdump -i eth0 -nn -B 65536 -w /var/captures/high-rate.pcap

The exit summary shows kernel and userspace drop counts:

# Example output (yours will differ)
# 1482 packets captured
# 1482 packets received by filter
# 0 packets dropped by kernel

If the kernel drop count is nonzero, increase -B, tighten your filter, or reduce the capture rate. If userspace drops appear, your disk or CPU is the bottleneck — write to a RAM-backed tmpfs or a faster storage device.

Writing to tmpfs for High-Rate Captures

# Mount a tmpfs (adjust size to available RAM)
mount -t tmpfs -o size=4G tmpfs /mnt/capture-ram
tcpdump -i eth0 -nn -B 131072 -w /mnt/capture-ram/fast.pcap 'port 443'

Move completed pcap files off the tmpfs before unmounting, or they are gone.

Running tcpdump as a systemd Service

For persistent background capture — common in incident-response setups — wrap tcpdump in a systemd unit rather than a screen session.

cat /etc/systemd/system/tcpdump-eth0.service
[Unit]
Description=tcpdump rolling capture on eth0
After=network.target

[Service]
ExecStart=/usr/sbin/tcpdump -i eth0 -nn -B 65536 \
  -w /var/captures/eth0-%%Y%%m%%d-%%H%%M%%S.pcap \
  -G 600 -W 24
Restart=on-failure
User=root

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now tcpdump-eth0.service
systemctl status tcpdump-eth0.service

Note: the %% escaping is required in systemd unit files where % has special meaning.

Verifying Your Capture

# Confirm packets are being written
watch -n2 'ls -lh /var/captures/'
# Quick packet count in an existing file
tcpdump -r /var/captures/eth0-20240601-120000.pcap --count -nn 2>/dev/null | tail -1
# capinfos (part of wireshark-common / wireshark-cli) gives richer stats
capinfos /var/captures/eth0-20240601-120000.pcap

Common Pitfalls

  • Capturing on loopback for local traffic. Use -i lo, not the physical interface, when both endpoints are on the same host.
  • Missing VLAN tags. Traffic on trunked interfaces includes 802.1Q headers. Add vlan <id> to your filter or capture on the sub-interface directly.
  • Snaplen too small. The old default was 68 bytes, which truncates payloads. Modern tcpdump defaults to 262144. Confirm with tcpdump --version or explicitly pass -s 0.
  • TSO/GSO offload inflating packet sizes. On some NICs, captured frames exceed the 1500-byte MTU because the kernel hasn't segmented them yet. Disable offloading on the capture interface with ethtool -K eth0 tso off gso off if you need accurate segment-level data.
  • Filter applied at the wrong layer. port 443 matches transport-layer ports. For encapsulated traffic (GRE, VXLAN), the inner headers aren't visible to a simple port filter — you need offset-based matching or capture the raw stream and filter in Wireshark.
  • Clock skew in multi-host captures. If correlating captures from two machines, verify NTP sync (timedatectl status) before you start. Millisecond offsets matter.

Installing tcpdump

# Debian / Ubuntu
apt install tcpdump
# Fedora / RHEL / Rocky
dnf install tcpdump
# Arch
pacman -S tcpdump
tested on:Ubuntu 24.04Debian 12Fedora 40Arch rolling

Frequently asked questions

Do I always need root to run tcpdump?
By default yes, because raw socket access requires CAP_NET_RAW. On Linux you can grant that specific capability to the tcpdump binary with setcap cap_net_raw+ep $(which tcpdump), which avoids full root, though this has security implications on shared systems.
What is the difference between -C and -G for rotation?
-C rotates based on file size in megabytes; -G rotates based on elapsed time in seconds. They can be used together, and the file is rotated whichever condition triggers first.
Why does my BPF filter seem to miss some packets?
Common causes are VLAN encapsulation (the inner header is offset), TSO/GSO offloading creating oversized frames, or a typo in the filter expression. Test your filter in a low-traffic environment first and use -v to confirm the compiled filter is what you expect.
How do I capture traffic on a bonded or bridged interface?
Capture on the bond master (e.g., bond0) or bridge interface (e.g., br0) directly. Traffic traversing member interfaces is also visible on the parent device in most configurations, though capture on individual member NICs can reveal lower-level link issues.
Can I read a tcpdump pcap file in Wireshark?
Yes. The libpcap format produced by tcpdump -w is natively supported by Wireshark. Open the file with File > Open or pass it on the command line: wireshark -r capture.pcap. Wireshark's display filters are separate from BPF and can be applied after the fact.

Related guides