Basics
tcpdump needs root. Every command here assumes , and Ctrl+C stops a capture.
| Command | What it does |
|---|---|
| Capture on one interface | |
| Capture on all interfaces at once | |
| List interfaces you can capture on | |
| Skip DNS lookups, show raw IPs and ports (faster, and usually what you want) | |
| Skip port name lookups too, so 443 stays 443 and not | |
| Stop after 100 packets | |
| Verbose output: TTL, ID, checksums ( for more) | |
| Quieter, shorter lines | |
| Put a full date and time on every line | |
| Version, plus the libpcap it was built with |
Reading the Output
A line is timestamp, source, destination, then the TCP state in brackets. This is one SYN opening a connection to port 443:
14:22:31.478934 IP 10.0.0.9.52344 > 10.0.0.5.443: Flags [S], seq 1829375192, win 64240, length 0| Field | What it means |
|---|---|
| Local time to the microsecond | |
| Source IP and port: the number after the last dot is the port | |
| Destination IP and port | |
| Which TCP flags are set on this packet | |
| Sequence number; after the first packet tcpdump shows relative numbers unless you pass | |
| Next byte this side expects from the other | |
| Receive window the sender is advertising | |
| The receiver's buffer is full and the sender has to wait | |
| Payload bytes, headers not counted |
The dot inside the brackets is the ACK bit, so means push plus ack.
| Flag | What it means |
|---|---|
| SYN, opening a connection | |
| SYN-ACK, the other end accepting | |
| Plain ACK, no data | |
| Data being pushed to the application | |
| FIN-ACK, a clean close | |
| or | RST, one side refusing or killing the connection |
Non-TCP lines have their own shapes:
| Line | What it means |
|---|---|
| 10.0.0.9 is asking which MAC owns that IP | |
| The answer, with the MAC address | |
| A ping leaving the host | |
| Nothing is listening on that UDP port | |
| A DNS query for an A record (dig asks the same thing on purpose) |
Host and Port Filters
Quote any filter that has more than one word so the shell leaves it alone.
| Command | What it does |
|---|---|
| Traffic to or from one IP | |
| Traffic coming from an IP | |
| Traffic going to an IP | |
| Traffic on a port, either direction | |
| Traffic from a source port | |
| Traffic to a destination port | |
| A range of ports | |
| Traffic to or from a whole subnet | |
| To one IP on one port | |
| Resolves the name once at startup, then filters on the IPs |
Combining and Excluding
, , and combine primitives; parentheses group them. Always quote the whole expression, or the shell will grab the special characters first.
| Command | What it does |
|---|---|
| One host's traffic on port 80 | |
| Only the conversation between two hosts | |
| Either of two ports | |
| Everything except SSH (essential when capturing over SSH) | |
| Exclude one noisy machine | |
| Exclude SSH and DNS together | |
| From a host, minus its DNS lookups | |
| Grouping with parentheses | |
| Two hosts, each with anyone |
Protocols
A bare protocol name is a filter on its own, and it combines with everything above.
| Command | What it does |
|---|---|
| TCP only | |
| UDP only | |
| Pings and other ICMP (watch a land) | |
| ARP requests and replies (who-has, is-at) | |
| DNS queries and answers | |
| All DNS, including large TCP responses | |
| HTTP over TCP | |
| DHCP offers and requests | |
| NTP, when a clock has drifted | |
| QUIC and HTTP/3, which never touch TCP | |
| GRE tunnel traffic | |
| The low-level chatter, useful when a host is unreachable |
Saving Captures
Write with , analyze later with or Wireshark. Terminal output stops while writing to a file; add to see a packet count tick up.
| Command | What it does |
|---|---|
| Write raw packets to a file | |
| Full packets, not truncated (do this for Wireshark) | |
| Capture 1000 packets, then stop | |
| Write each packet immediately instead of buffering | |
| Read a capture back | |
| Read it back without resolving anything | |
| Filter while reading (filters work on files too) | |
| Cut a big capture down to one host |
Stopping and Rotating
Nothing stops a capture on its own, and a busy interface fills a disk faster than you expect.
| Command | What it does |
|---|---|
| Stop after 500 packets | |
| Stop after 30 seconds | |
| Rotate at 100 MB, keep 5 files | |
| Start a new file every hour | |
| Hourly files, one day, then overwrite | |
| Gzip each file as it rotates | |
| Leave it running after you log out |
needs strftime placeholders (, ) in the filename, or every rotation overwrites the same file.
Wireshark Handoff
Capture on the server, analyze on your desktop. Wireshark opens and directly.
| Command | What it does |
|---|---|
| Capture full packets so nothing is half-parsed | |
| Copy the file down to your machine | |
| Trim a huge capture before opening it | |
| Stream a remote capture into Wireshark live | |
| Wireshark's decoders, in the terminal | |
| Split a capture into 100k-packet files |
Payloads and Hex
| Command | What it does |
|---|---|
| Print payloads as ASCII (readable HTTP) | |
| Hex and ASCII side by side | |
| Full payloads; without long packets are cut off | |
| Hex including the ethernet header | |
| Line-buffered, so sees output right away |
TLS traffic on 443 shows only gibberish with . That is the encryption working, not a tcpdump problem.
# Busiest source addresses in a saved capture:
sudo tcpdump -nr capture.pcap | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | headMAC and VLAN Filters
For layer 2 problems: DHCP fights, ARP mysteries, tagged frames, finding a device with no IP yet.
| Command | What it does |
|---|---|
| Traffic to or from one MAC | |
| Frames sent by a MAC | |
| Show MAC addresses on every line | |
| Broadcast frames only | |
| Tagged frames, with the VLAN id visible | |
| One VLAN only | |
| Port 80 inside a tagged frame | |
| Multicast frames (routing chatter, mDNS) |
shifts every offset that follows it, so it has to come first in the expression. matches nothing useful.
BPF Syntax
Filters compile to Berkeley Packet Filter programs, and the bracket syntax reads raw bytes: is a byte within that protocol's header. The practical use is matching TCP flags.
| Filter | What it matches |
|---|---|
| Any packet with SYN set (new connections) | |
| SYN only, no ACK (inbound connection attempts) | |
| Same, spelled with a mask | |
| Resets (something refusing connections) | |
| Connection teardowns | |
| SYN again, by raw offset (13 is the flags byte) | |
| Packets of at least 1000 bytes |
# Who is trying to open connections to this box, live:
sudo tcpdump -i any -n 'tcp[tcpflags] == tcp-syn'
# Check what a filter compiles to without capturing:
sudo tcpdump -d 'tcp[tcpflags] & tcp-syn != 0'Installing tcpdump
| Command | Where |
|---|---|
| Debian, Ubuntu | |
| Fedora, RHEL 8 and newer, Rocky, Alma | |
| RHEL 7, CentOS 7 | |
| Alpine, and most slim containers | |
| When is really a PATH problem |
lives in , which is not on a normal user's on Debian and Ubuntu. Running it under finds it; typing as yourself often does not.
Gotchas
- at the end means tcpdump could not keep up. Add , write to a file with instead of printing, and narrow the filter; raises the capture buffer.
- is usually the shell eating your quotes. Wrap any multi-word filter in single quotes, and check keywords (, not ). compiles a filter without capturing, so you can test one in a second.
- Capturing over SSH shows you your own SSH packets, which generate more packets, forever. Start every remote capture with .
- sees all interfaces but cannot use promiscuous mode, so it misses traffic not addressed to the host. Switch to the real interface once you know it.
- Old tcpdump versions truncated packets at 68 bytes by default. Modern ones capture full packets, but still makes the intent explicit for files headed to Wireshark.
- on outgoing packets is almost always checksum offload: the NIC fills the field in after tcpdump has already seen the packet. Nothing is wrong.
- tcpdump has no idea which process sent a packet. Map the port to a PID with ss or lsof, then go back to the capture.
- tcpdump shows packets arriving; it does not show what the firewall then drops. Compare with your iptables rules when packets arrive but the service never sees them.