What tcpdump Does#
tcpdump captures network packets and displays them in real time or saves them to a file for later analysis. It’s the standard command-line packet analyzer on Linux — installed on most systems by default, works over SSH, and requires no GUI.
Use it to answer:
- Is my application actually sending requests?
- What’s in the traffic between two hosts?
- Why is this connection failing or timing out?
- Is DNS resolving correctly?
- Are packets reaching this machine at all?
If you’ve read through the OSI model and network troubleshooting posts on this site, tcpdump is where theory becomes visible. You can watch TCP handshakes happen, see DNS queries and responses, and inspect the exact bytes on the wire.
Installation#
Most systems have it already. If not:
# Debian/Ubuntu
sudo apt install tcpdump
# RHEL/Fedora
sudo dnf install tcpdumpVerify:
tcpdump --versionBasic Capture#
tcpdump requires root (or CAP_NET_RAW capability) because it puts the network interface into promiscuous mode.
# Capture all traffic on the default interface
sudo tcpdump
# Capture on a specific interface
sudo tcpdump -i eth0
# List available interfaces
sudo tcpdump -DWithout filters, this captures everything — which is overwhelming on a busy system. You’ll almost always want to narrow it down.
Reading the Output#
A typical tcpdump line:
19:42:03.182547 IP 192.168.1.50.43210 > 93.184.216.34.443: Flags [S], seq 1847302956, win 64240, length 0| Field | Meaning |
|---|---|
19:42:03.182547 | Timestamp (hour:min:sec.microseconds) |
IP | Protocol (IPv4) |
192.168.1.50.43210 | Source IP and port |
> | Direction |
93.184.216.34.443 | Destination IP and port |
Flags [S] | TCP flags (SYN in this case) |
seq 1847302956 | Sequence number |
win 64240 | TCP window size |
length 0 | Payload length |
TCP Flags#
| Flag | Name | Meaning |
|---|---|---|
S | SYN | Connection initiation |
S. | SYN-ACK | Connection accepted |
. | ACK | Acknowledgment |
P. | PSH-ACK | Data push |
F. | FIN-ACK | Connection closing |
R | RST | Connection reset (refused/aborted) |
A healthy TCP connection follows: S → S. → . (three-way handshake), then P. for data, then F. to close.
Essential Options#
| Option | Purpose |
|---|---|
-i eth0 | Capture on specific interface |
-c 50 | Stop after 50 packets |
-n | Don’t resolve hostnames (faster, clearer) |
-nn | Don’t resolve hostnames or port names |
-v | Verbose (show TTL, IP ID, total length) |
-vv | More verbose (full protocol decode) |
-X | Show packet contents in hex and ASCII |
-A | Show packet contents in ASCII only |
-w file.pcap | Write raw packets to file |
-r file.pcap | Read packets from file |
-s 0 | Capture full packets (no truncation) |
-q | Quiet — less protocol info, shorter lines |
The combination you’ll use most often:
sudo tcpdump -i eth0 -nn -c 100Numeric output, limited count, specific interface.
Capture Filters#
Filters go at the end of the command and use Berkeley Packet Filter (BPF) syntax. They reduce what gets captured — important on busy systems where you’d otherwise drown in irrelevant traffic.
Filter by host#
# All traffic to/from a specific IP
sudo tcpdump -nn host 192.168.1.100
# Only traffic FROM a specific IP
sudo tcpdump -nn src host 192.168.1.100
# Only traffic TO a specific IP
sudo tcpdump -nn dst host 192.168.1.100Filter by port#
# All traffic on port 443 (HTTPS)
sudo tcpdump -nn port 443
# Traffic on port 80 OR 443
sudo tcpdump -nn port 80 or port 443
# Only destination port 22 (SSH to this machine)
sudo tcpdump -nn dst port 22Filter by protocol#
# TCP only
sudo tcpdump -nn tcp
# UDP only
sudo tcpdump -nn udp
# ICMP only (ping traffic)
sudo tcpdump -nn icmpFilter by network#
# All traffic to/from a subnet
sudo tcpdump -nn net 192.168.1.0/24
# Traffic between two specific hosts
sudo tcpdump -nn host 192.168.1.50 and host 192.168.1.100Combining filters#
Use and, or, and not with parentheses:
# HTTPS traffic to a specific server
sudo tcpdump -nn dst host 93.184.216.34 and port 443
# All traffic except SSH (so your own session doesn't flood the output)
sudo tcpdump -nn not port 22
# DNS queries from a specific host
sudo tcpdump -nn src host 192.168.1.50 and dst port 53
# HTTP or HTTPS but not from localhost
sudo tcpdump -nn '(port 80 or port 443) and not host 127.0.0.1'Quote complex filters to prevent shell interpretation of parentheses.
Saving and Reading Captures#
Write to a file#
# Capture to file (pcap format)
sudo tcpdump -i eth0 -nn -s 0 -w capture.pcap
# Capture with a packet limit
sudo tcpdump -i eth0 -nn -s 0 -c 1000 -w capture.pcapThe -s 0 ensures full packet capture (no truncation). Modern tcpdump versions (4.9+) default to a 262144-byte snap length which covers most packets, but -s 0 guarantees nothing is cut short — especially important for jumbo frames or protocols with large payloads. The pcap file can be opened in Wireshark later if you need a GUI.
Read from a file#
# Read and display
tcpdump -nn -r capture.pcap
# Read with filters (apply after capture)
tcpdump -nn -r capture.pcap port 80
# Read with full packet content
tcpdump -nn -X -r capture.pcapReading from a file doesn’t require root.
Rotate capture files#
For long-running captures:
# Rotate every 100MB, keep 10 files
sudo tcpdump -i eth0 -nn -s 0 -w capture.pcap -C 100 -W 10This creates capture.pcap0, capture.pcap1, etc., rotating when each hits 100MB.
Practical Scenarios#
Watch a TCP handshake#
sudo tcpdump -nn -c 10 host 93.184.216.34 and port 443Then in another terminal:
curl -so /dev/null https://example.comNote: you can use a hostname in the filter (host example.com) and tcpdump will resolve it to an IP before capturing. But using the IP directly is clearer and avoids a DNS dependency at capture time.
You’ll see:
# SYN (client → server)
IP 192.168.1.50.43210 > 93.184.216.34.443: Flags [S], seq 1847302956
# SYN-ACK (server → client)
IP 93.184.216.34.443 > 192.168.1.50.43210: Flags [S.], seq 2984571023, ack 1847302957
# ACK (client → server) — handshake complete
IP 192.168.1.50.43210 > 93.184.216.34.443: Flags [.], ack 2984571024Debug DNS resolution#
sudo tcpdump -nn -i eth0 port 53Then:
dig linuxlearninglab.comYou’ll see the query and response:
IP 192.168.1.50.41234 > 192.168.1.1.53: 12345+ A? linuxlearninglab.com. (38)
IP 192.168.1.1.53 > 192.168.1.50.41234: 12345 1/0/0 A 76.223.105.230 (54)The A? means “query for an A record.” The response shows the resolved IP.
Find who’s connecting to a service#
# Watch incoming SYN packets to your web server (new connections only)
sudo tcpdump -nn dst port 80 and 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'This shows only pure SYN packets — the first packet of every new connection attempt, excluding SYN-ACKs. Each line is a new client connecting.
Detect connection failures#
# Watch for RST (reset) packets — refused connections
sudo tcpdump -nn 'tcp[tcpflags] & tcp-rst != 0'A flood of RSTs means something is trying to connect to a port that isn’t open, or a firewall is rejecting traffic.
Inspect HTTP request content#
# Show ASCII content of HTTP traffic (unencrypted only)
sudo tcpdump -nn -A port 80 | grep -E '(GET|POST|HTTP|Host:)'This won’t work for HTTPS (port 443) since the payload is encrypted — you’d only see the TLS handshake.
Monitor traffic volume between hosts#
# Quick count of packets between two hosts
sudo tcpdump -nn -c 1000 host 192.168.1.50 and host 10.0.0.5 -qThe -q (quiet) flag gives shorter output focused on the flow rather than protocol details.
Advanced Filters#
Filter by TCP flags#
# Only SYN packets (new connections)
sudo tcpdump -nn 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# Only RST packets (connection resets)
sudo tcpdump -nn 'tcp[tcpflags] & tcp-rst != 0'
# Only FIN packets (connection teardowns)
sudo tcpdump -nn 'tcp[tcpflags] & tcp-fin != 0'Filter by packet size#
# Only packets larger than 500 bytes (actual data, not just headers)
sudo tcpdump -nn greater 500
# Only small packets (likely ACKs or keepalives)
sudo tcpdump -nn less 100Filter by VLAN#
sudo tcpdump -nn vlan 100Quick Reference#
| Task | Command |
|---|---|
| Capture all on eth0 | sudo tcpdump -i eth0 -nn |
| Filter by host | sudo tcpdump -nn host 10.0.0.5 |
| Filter by port | sudo tcpdump -nn port 443 |
| Exclude SSH | sudo tcpdump -nn not port 22 |
| DNS traffic | sudo tcpdump -nn port 53 |
| Save to file | sudo tcpdump -nn -s 0 -w out.pcap |
| Read from file | tcpdump -nn -r out.pcap |
| Show packet content | sudo tcpdump -nn -X port 80 |
| New connections only | sudo tcpdump -nn 'tcp[tcpflags] & tcp-syn != 0' |
| Limit capture count | sudo tcpdump -nn -c 100 |
| Verbose output | sudo tcpdump -nn -vv host 10.0.0.5 |
Best Practices#
- Always use
-nn— name resolution is slow and makes output harder to parse. You can resolve IPs later if needed. - Always use
-cor time-limit captures — unbounded captures can fill disks fast on busy systems, especially with-w. Use-c 1000for a packet count limit, or-G 60to rotate files every 60 seconds. - Exclude your own SSH session — add
not port 22when capturing remotely, otherwise your SSH traffic clutters the output and makes it harder to find what you’re looking for. - Use
-s 0when writing to files — truncated packets are useless for later analysis. The default snap length may cut off important payload data. - Capture first, filter later — when debugging an unknown issue, save a broad capture to a pcap file, then apply filters with
-r. You can’t re-capture traffic you missed. - Be aware of encryption — tcpdump shows encrypted payload as gibberish. For HTTPS, you’ll see the TLS handshake (which reveals the SNI hostname) but not the request/response content.
- Use on production carefully — tcpdump adds CPU overhead and can capture sensitive data. Run with tight filters, limited counts, and delete capture files when done.
- Combine with other tools — use
ssto find what ports are active, tcpdump to inspect the traffic on those ports. Usedigto test DNS, tcpdump to see why it’s failing.


