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, direction src/dst, joined by and/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 -w writes and Wireshark / tshark read 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 -nn to 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.

FlagDoesFlagDoes
-iInterface to capture on-wWrite raw packets to a PCAP
-DList interfaces-rRead packets from a PCAP
-nnNo DNS or port-name lookups-APrint payload as ASCII
-cStop after N packets-XPayload as hex + ASCII
-v / -vvMore protocol detail-eShow link-layer (MAC) headers
-i anyCapture on all interfaces-sSnap length (bytes per packet)
-lLine-buffer (for piping to grep)-G/-C/-WRotate by time / size / count
-ttttHuman-readable timestamps-ZDrop 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:

FlagsMeaningReading
[S]SYNA connection attempt
[S.]SYN-ACKThe service answered, it is up
[.]ACKHandshake completing / data acknowledged
[P.]PSH-ACKActual data in flight
[F.]FIN-ACKGraceful close
[R] / [R.]RSTActively 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#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall; open a fresh shell
Operation not permittedCapture needs privilegesPrefix with sudo
IFACE: No such device existsWrong interface nameRun sudo tcpdump -D, use a listed name
No packets at allWrong interface or too-tight filterTry -i any, loosen the filter, confirm scope
See only your own trafficSwitched network, no mirrorUse a SPAN/mirror port or capture on the host itself
Capture file grows huge fastNo filter / no rotationAdd a BPF filter or -G/-C/-W
Truncated payloadSnap length too smallAdd -s0 to force full packets
Garbled hostnames, slow linesName resolution is onAdd -nn
tcpdump: syntax errorBad BPF expressionQuote the filter; check host/port/and

Gotchas#

  • port 80 matches both TCP and UDP. Qualify it as tcp port 80, or a stray UDP packet on the same port muddies an otherwise clean capture.
  • -A on HTTPS shows garbage, not secrets. Encrypted payload stays encrypted; you capture the handshake, SNI, and metadata, and decrypt elsewhere with keys.
  • packets dropped by kernel at 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 80 can miss fragmented packets, because only the first fragment carries the port number; add or (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.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop