tcpdump
tcpdump captures packets straight off an interface and prints one readable line per packet: who talked to whom, on which port, with which TCP flags. You reach for it when a higher-level tool is lying to you and you need proof the packets actually left (or arrived at) the box: a service that “should” be reachable, a handshake that never completes, a DNS lookup that goes nowhere.
Mental model: sudo tcpdump -i IFACE [filter]. The BPF filter is what keeps a capture readable instead of a firehose. Only capture on interfaces you own or are authorized to monitor, and treat every PCAP as sensitive evidence: captures routinely carry credentials and tokens in clear text.
New to tcpdump? Core concepts
- Interface (
-i IFACE): the NIC you tap (eth0,tun0,any); the wrong one reads as “no traffic” - BPF filter: the expression that scopes a capture, from primitives
host,port,net, directionsrc/dst, joined byand/or/not - Capture vs read: live off the wire (
-i, needs root) versus replaying a saved file (-r, no root) - PCAP: the raw packet file
-wwrites and Wireshark /tsharkread back - Promiscuous mode / switched LAN: on a switch you see only your own traffic plus broadcast/multicast unless a port is mirrored (SPAN)
- Snap length (
-s): bytes captured per packet; too small truncates the payload - TCP flags: the
[S]/[S.]/[.]/[R]on each line spell out the handshake (SYN, SYN-ACK, ACK, reset) - Name resolution: pass
-nnto keep IPs and ports numeric and the lines fast; omit it and tcpdump does slow reverse DNS on every packet
When to reach for tcpdump (and when not)
Reach for it to prove packets actually left or arrived, isolate one host, service, or DNS lookup on a busy segment, confirm a handshake completes (or catch a SYN that never gets a reply), read clear-text payload on a protocol you are cleared to inspect, or save a clean, scoped PCAP on a headless box with no GUI.
Reach for something else when you need stream reassembly, follow-the-conversation, or protocol-aware decoding: open the PCAP in wireshark / tshark instead (tcpdump captures, they dissect). Use ngrep when you would rather pattern-match payloads than write a BPF filter, and nmap / curl / nc to generate the traffic you are watching land rather than to capture it.
Install, update, verify#
sudo apt install -y tcpdump # Debian / Ubuntu / Kali
sudo dnf install -y tcpdump # Fedora / RHEL
sudo apt install --only-upgrade -y tcpdump # update
tcpdump --version # version + linked libpcap
command -v tcpdump # confirm it is on PATH
sudo tcpdump -D # list capturable interfaces
tcpdump ships by default on macOS and most BSDs. Live capture needs root, so expect sudo on every capture command even once it is on your PATH. Throughout, IFACE is the interface carrying the traffic (eth0, ens33, tun0, wlan0) and TARGET_IP is a host you are authorized to capture.
Flags you’ll actually use#
Skim these first; the cheat sheet below is these flags combined.
| Flag | Does | Flag | Does |
|---|---|---|---|
-i | Interface to capture on | -w | Write raw packets to a PCAP |
-D | List interfaces | -r | Read packets from a PCAP |
-nn | No DNS or port-name lookups | -A | Print payload as ASCII |
-c | Stop after N packets | -X | Payload as hex + ASCII |
-v / -vv | More protocol detail | -e | Show link-layer (MAC) headers |
-i any | Capture on all interfaces | -s | Snap length (bytes per packet) |
-l | Line-buffer (for piping to grep) | -G/-C/-W | Rotate by time / size / count |
-tttt | Human-readable timestamps | -Z | Drop privileges to a user after opening |
For rotation: -C + -W is a ring that overwrites the oldest file; -G + -W writes one file per interval and exits at the count.
Cheat sheet#
Every block starts simple and scopes down one primitive at a time, so you can stop at the line that isolates your traffic.
# Pick where to tap, take a small sample first
sudo tcpdump -D # list capturable interfaces
sudo tcpdump -nn -i IFACE -c 20 # 20-packet sample, no name lookups
sudo tcpdump -nn -i any # every interface at once
# Build up a filter: stop at the line that isolates your traffic
sudo tcpdump -nn -i IFACE # 1. everything (firehose)
sudo tcpdump -nn -i IFACE host TARGET_IP # 2. + one host, both ways
sudo tcpdump -nn -i IFACE host TARGET_IP and tcp port 80 # 3. + one service on it
sudo tcpdump -nn -i IFACE src host TARGET_IP and tcp port 80 # 4. + only packets FROM it
# Scope by address, protocol, direction, negation
sudo tcpdump -nn -i IFACE net 192.168.1.0/24 # a whole subnet
sudo tcpdump -nn -i IFACE udp port 53 # DNS only
sudo tcpdump -nn -i IFACE icmp # ping / unreachables only
sudo tcpdump -nn -i IFACE tcp portrange 8000-8100 # a range of ports
sudo tcpdump -nn -i IFACE 'not port 22' # everything but your SSH
sudo tcpdump -nn -i IFACE 'tcp[tcpflags] & tcp-syn != 0' # new connection attempts
# See payload
sudo tcpdump -nnA -i IFACE tcp port 80 # clear-text ASCII payload
sudo tcpdump -nnX -i IFACE host TARGET_IP # hex + ASCII bytes
sudo tcpdump -nnA -l -i IFACE tcp port 80 | grep -i 'GET\|POST\|host:' # grep it live
# Save, read, re-filter
sudo tcpdump -nn -i IFACE -w cap.pcap host TARGET_IP # write a PCAP (nothing prints)
tcpdump -nn -r cap.pcap # read it back (no root)
tcpdump -nn -r cap.pcap 'tcp port 443' # re-filter on read, non-destructive
# Long run without filling the disk
sudo tcpdump -nn -i IFACE -C 100 -W 10 -w cap.pcap # rolling ~1GB ring
sudo tcpdump -nn -i IFACE -G 3600 -W 24 -w 'run-%Y%m%d-%H%M%S.pcap' # 1 file/hr, exits after 24
Command breakdowns#
Each block below explains one situation, the exact command, and what to expect back.
List the interfaces you can capture on#
sudo tcpdump -D
Numbered names like 1.eth0, 2.lo, 3.any. Pick one for -i before anything else, since the wrong interface is the number-one reason for “no traffic”.
Watch one host in both directions#
sudo tcpdump -nn -i IFACE host TARGET_IP
Every packet to or from TARGET_IP. The single biggest noise cut on a busy segment, and usually the first filter you reach for.
Capture a single service (host and port)#
sudo tcpdump -nn -i IFACE host TARGET_IP and tcp port 80
Just the web conversation with that host. Combining host and tcp port keeps you on one service instead of the host’s entire chatter.
Capture only DNS lookups#
sudo tcpdump -nn -i IFACE udp port 53
Queries and answers. Queries with no matching answer point at a resolver or routing problem, not the application above it.
Capture a whole subnet#
sudo tcpdump -nn -i IFACE net 192.168.1.0/24
Everything sourced from or destined to that range. Useful for spotting who is talking on a lab segment before you narrow to one host.
Capture only ICMP (ping and unreachables)#
sudo tcpdump -nn -i IFACE icmp
Echo request/reply plus host unreachable and port unreachable messages, which is how you tell “no route” apart from “port closed” when a connection fails.
Filter by direction (source or destination)#
sudo tcpdump -nn -i IFACE src host TARGET_IP
Only packets from the target (dst host for the reverse). Cuts the capture roughly in half when you only care about the replies coming back.
Exclude your own SSH from the capture#
sudo tcpdump -nn -i IFACE 'not port 22'
When you are SSHed into the capturing box, an unfiltered capture shows the packets carrying your own session. not port 22 drops them so you see the traffic you came for.
Catch new connection attempts (SYN)#
sudo tcpdump -nn -i IFACE 'tcp[tcpflags] & tcp-syn != 0'
Matches packets with the SYN bit set, both SYN and SYN-ACK, so you see who is trying to connect and who is answering. Quote the expression so the shell does not touch the brackets.
Read clear-text payload as ASCII#
sudo tcpdump -nnA -i IFACE host TARGET_IP and tcp port 80
-A appends the printable payload after each header line: HTTP verbs, Host headers, plaintext form fields. Only on protocols you are cleared to read.
Inspect the raw bytes as hex#
sudo tcpdump -nnX -i IFACE host TARGET_IP
Hex on the left, ASCII on the right. For binary protocols where -A alone is unreadable, or when you need exact byte offsets.
Grep a live capture in real time#
sudo tcpdump -nnA -l -i IFACE tcp port 80 | grep -i 'host:\|GET\|POST'
-l line-buffers the output so grep sees packets as they arrive instead of in stalled batches. The fastest way to pull one field out of a live stream.
Save a capture for Wireshark#
sudo tcpdump -nn -i IFACE -w captures/web.pcap host TARGET_IP and tcp port 80
Nothing prints because it is writing raw packets; press Ctrl+C to stop. The file opens directly in Wireshark or tshark for full dissection.
Read a PCAP back and re-filter it#
tcpdump -nn -r captures/web.pcap 'tcp port 443'
Reading from a file needs no root and no live access. Filtering on read is non-destructive, so the original PCAP is untouched and you can re-run with a different filter as often as you like.
Watch ARP at layer 2#
sudo tcpdump -nn -e -i IFACE arp
who-has / is-at lines with MAC addresses (-e shows the link layer). The tool for “two hosts on the same LAN cannot see each other”.
Rotate long captures so you don’t fill the disk#
sudo tcpdump -nn -i IFACE -C 100 -W 10 -w captures/cap.pcap # true ring, overwrites
sudo tcpdump -nn -i IFACE -G 3600 -W 24 -w 'captures/run-%Y%m%d-%H%M%S.pcap' # 1 file/hr, then exits
Size-based -C 100 -W 10 is a true ring: ten roughly 100MB files (about 1GB total) with the oldest overwritten, so it runs unattended forever. Time-based -G 3600 -W 24 writes one file per hour and exits after 24, a bounded run rather than a ring.
Add human-readable timestamps#
sudo tcpdump -nn -tttt -i IFACE host TARGET_IP
Prefixes each line with a full YYYY-MM-DD hh:mm:ss, worth it the moment the capture goes into report notes instead of the default seconds-since-capture.
Reading the output#
A line reads: time IP src.port > dst.port: Flags [X], seq ..., ack ..., win .... The flags are the story:
| Flags | Meaning | Reading |
|---|---|---|
[S] | SYN | A connection attempt |
[S.] | SYN-ACK | The service answered, it is up |
[.] | ACK | Handshake completing / data acknowledged |
[P.] | PSH-ACK | Actual data in flight |
[F.] | FIN-ACK | Graceful close |
[R] / [R.] | RST | Actively refused, a closed port or a reset by a middlebox |
SYN -> SYN-ACK -> ACK is a healthy handshake: the service is up and answering. A SYN with no reply means packets are leaving but nothing answers: filtering, a down service, or a routing problem. Empty output usually means the wrong interface, a too-tight filter, or you are not in the traffic path on a switched network.
Troubleshooting#
| Problem | Cause | Fix |
|---|---|---|
command not found | Not installed / stale PATH | Reinstall; open a fresh shell |
Operation not permitted | Capture needs privileges | Prefix with sudo |
IFACE: No such device exists | Wrong interface name | Run sudo tcpdump -D, use a listed name |
| No packets at all | Wrong interface or too-tight filter | Try -i any, loosen the filter, confirm scope |
| See only your own traffic | Switched network, no mirror | Use a SPAN/mirror port or capture on the host itself |
| Capture file grows huge fast | No filter / no rotation | Add a BPF filter or -G/-C/-W |
| Truncated payload | Snap length too small | Add -s0 to force full packets |
| Garbled hostnames, slow lines | Name resolution is on | Add -nn |
tcpdump: syntax error | Bad BPF expression | Quote the filter; check host/port/and |
Gotchas#
port 80matches both TCP and UDP. Qualify it astcp port 80, or a stray UDP packet on the same port muddies an otherwise clean capture.-Aon HTTPS shows garbage, not secrets. Encrypted payload stays encrypted; you capture the handshake, SNI, and metadata, and decrypt elsewhere with keys.packets dropped by kernelat exit means you missed traffic. The filter is too broad or the disk too slow; tighten the BPF or write to faster storage before you trust the capture.- A capture filter is not a display filter. tcpdump filters once in the kernel with no Wireshark-style live re-filter, so to change scope you re-run the capture or re-read the PCAP with a new filter after
-r. tcp port 80can miss fragmented packets, because only the first fragment carries the port number; addor (ip[6:2] & 0x1fff != 0)when fragmentation is in play.- A PCAP can hold credentials and tokens in clear text. Treat it as evidence and scrub it before sharing.
Pairs with#
wireshark and tshark open the PCAP you saved with -w for stream reassembly and protocol-aware decoding, since tcpdump captures and they dissect. nmap, curl, and nc generate reproducible traffic while you watch it land. Reach for ngrep when you would rather pattern-match payloads than write a BPF filter, and move the capture into wireshark the moment you need to follow a full conversation rather than read one packet at a time.