Install
tcpdump is in the base install on most distros; if not:
sudo apt install tcpdump
sudo dnf install tcpdump
sudo pacman -S tcpdump
sudo apk add tcpdump
It needs root (or CAP_NET_RAW + CAP_NET_ADMIN capabilities) to capture. On systems with strict policy, setcap on the binary lets a regular user run it:
sudo setcap cap_net_raw,cap_net_admin=eip $(which tcpdump)
The first capture
# List interfaces
sudo tcpdump -D
# Capture on a specific interface; -n disables DNS resolution (much faster output)
sudo tcpdump -ni eth0
# More verbose; -v / -vv / -vvv add increasing detail
sudo tcpdump -nvv -i eth0
# Limit to 100 packets then exit
sudo tcpdump -nc 100 -i eth0
The default output is one line per packet. From left to right: timestamp, protocol, src → dst, flags, options, length.
BPF filter syntax
Filters are positional expressions combining primitives with and / or / not:
# By host
sudo tcpdump -n host 10.0.5.20
sudo tcpdump -n src host 10.0.5.20
sudo tcpdump -n dst host 10.0.5.20
# By port
sudo tcpdump -n port 443
sudo tcpdump -n tcp port 80 or tcp port 443
sudo tcpdump -n port not 22 # exclude SSH noise
# By network
sudo tcpdump -n net 192.168.1.0/24
sudo tcpdump -n src net 10.0.0.0/8
# By protocol
sudo tcpdump icmp
sudo tcpdump arp
sudo tcpdump udp
# Combinations
sudo tcpdump -n 'host 10.0.5.20 and (tcp port 5432 or tcp port 5433)'
sudo tcpdump -n 'not (port 22 or port 5353 or arp)'
Single-quote complex expressions to keep the shell out of the parsing.
The output flags worth remembering
-n Don't resolve hostnames / ports (fast, prints raw IPs)
-nn Also don't resolve port numbers to service names
-i <intf> Interface (or 'any' for all)
-c <N> Exit after N packets
-s 0 Capture full packets (default snaplen used to be 96; modern tcpdump default is 262144, full)
-w file.pcap Write raw packets to a file (later open in Wireshark or read with -r)
-r file.pcap Read from a file instead of live capture
-v / -vv More verbose decoding
-X Hex + ASCII dump of packet contents
-A ASCII-only dump (useful for HTTP / plain protocols)
-e Include the link-layer header (MAC addresses)
-tttt Human-readable timestamps
-G <sec> -W N Rotate the output file every <sec> seconds, keep N files
Save for later, analyze with Wireshark
# Capture matching traffic to a file (binary pcap format)
sudo tcpdump -nni eth0 -w /tmp/web.pcap 'host 10.0.5.20 and tcp port 443'
# Capture only TLS ClientHello + ServerHello (small, useful for cert debugging)
sudo tcpdump -nni eth0 -w /tmp/tls-handshake.pcap \
'(tcp port 443) and (tcp[((tcp[12] & 0xf0) >> 2)] = 22)'
# Stop with Ctrl-C
# Then scp /tmp/web.pcap to your laptop and open in Wireshark
The big bit-arithmetic expression above is the classic "match TLS handshake bytes" filter — it inspects byte 0 of the TCP payload (after the variable IP+TCP headers) and matches if it equals 22, which is the TLS record-type byte for Handshake.
Useful one-liners
# See HTTP requests in plain text (only un-encrypted)
sudo tcpdump -nA -s 0 -i eth0 'tcp port 80 and (((ip[2:2]-((ip[0]&0xf)<<2))-((tcp[12]&0xf0)>>2))!=0)'
# Top talkers right now (capture briefly, sort)
sudo timeout 10 tcpdump -nl -i eth0 -c 5000 2>/dev/null \
| awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head
# Watch for SYN floods (high SYN-only rate from one source)
sudo tcpdump -ni eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn' | head -50
# Detect MTU issues: ICMP "Fragmentation Needed"
sudo tcpdump -ni eth0 'icmp and icmp[0] = 3 and icmp[1] = 4'
# Suspect ARP activity (changes to known mappings)
sudo tcpdump -ni eth0 arp -v
# DHCP traffic
sudo tcpdump -ni eth0 -s 0 port 67 or port 68
Capture rotation for long-running diagnostics
sudo tcpdump -nni eth0 \
-w /tmp/diag-%Y%m%d-%H%M.pcap \
-G 300 -W 12 \
-z gzip \
'host 10.0.5.20'
# -G 300: rotate every 5 minutes
# -W 12: keep 12 files (1 hour)
# -z gzip: compress rotated files
# %Y%m%d-%H%M: filename uses strftime fields
tcpdump and containers
Containers have their own network namespaces. To capture from inside a container:
# For Docker / Podman containers
sudo nsenter -t $(docker inspect -f '{{.State.Pid}}' <container>) \
-n tcpdump -ni eth0
# Or run a sidecar
docker run --rm -it --net container:<target> nicolaka/netshoot tcpdump -ni eth0
For Kubernetes pods, kubectl debug (or the netshoot debug image) gives the same access.
The big mental model
Two things to internalize:
- tcpdump shows you what's on the wire. Not what the application thinks it sent — what actually crossed the NIC. If tcpdump doesn't see it, the application's send call didn't make it out; if tcpdump sees it but the other side doesn't reply, the network is doing something. This split makes most network bugs solvable.
- Filter early, look later. A loose filter floods the screen and slows the process; an over-tight filter misses the packet you needed. Start moderately specific, then narrow as you confirm what you're looking at.
-wto a file gives you the freedom to re-filter offline.
When to use tcpdump vs Wireshark
- tcpdump on the server, capture to a pcap, scp to a laptop, open in Wireshark for analysis — the most common workflow.
- tshark (Wireshark's CLI) is a richer tcpdump — same captures, but with display filters and the full protocol dissector. See the tshark tutorial.
- tcpdump is preinstalled or one-package-install on every Unix; tshark sometimes isn't. For the smallest-tooling debugging, tcpdump is the universal pick.