Skip to main content

Packet Analysis with tcpdump: Seeing What's on the Wire

·1651 words·8 mins
Linux Learning Lab
Author
Linux Learning Lab
Writing about code, tools, and workflows.

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 tcpdump

Verify:

tcpdump --version

Basic 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 -D

Without 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
FieldMeaning
19:42:03.182547Timestamp (hour:min:sec.microseconds)
IPProtocol (IPv4)
192.168.1.50.43210Source IP and port
>Direction
93.184.216.34.443Destination IP and port
Flags [S]TCP flags (SYN in this case)
seq 1847302956Sequence number
win 64240TCP window size
length 0Payload length

TCP Flags
#

FlagNameMeaning
SSYNConnection initiation
S.SYN-ACKConnection accepted
.ACKAcknowledgment
P.PSH-ACKData push
F.FIN-ACKConnection closing
RRSTConnection reset (refused/aborted)

A healthy TCP connection follows: SS.. (three-way handshake), then P. for data, then F. to close.

Essential Options
#

OptionPurpose
-i eth0Capture on specific interface
-c 50Stop after 50 packets
-nDon’t resolve hostnames (faster, clearer)
-nnDon’t resolve hostnames or port names
-vVerbose (show TTL, IP ID, total length)
-vvMore verbose (full protocol decode)
-XShow packet contents in hex and ASCII
-AShow packet contents in ASCII only
-w file.pcapWrite raw packets to file
-r file.pcapRead packets from file
-s 0Capture full packets (no truncation)
-qQuiet — less protocol info, shorter lines

The combination you’ll use most often:

sudo tcpdump -i eth0 -nn -c 100

Numeric 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.100

Filter 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 22

Filter by protocol
#

# TCP only
sudo tcpdump -nn tcp

# UDP only
sudo tcpdump -nn udp

# ICMP only (ping traffic)
sudo tcpdump -nn icmp

Filter 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.100

Combining 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.pcap

The -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.pcap

Reading 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 10

This 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 443

Then in another terminal:

curl -so /dev/null https://example.com

Note: 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 2984571024

Debug DNS resolution
#

sudo tcpdump -nn -i eth0 port 53

Then:

dig linuxlearninglab.com

You’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 -q

The -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 100

Filter by VLAN
#

sudo tcpdump -nn vlan 100

Quick Reference
#

TaskCommand
Capture all on eth0sudo tcpdump -i eth0 -nn
Filter by hostsudo tcpdump -nn host 10.0.0.5
Filter by portsudo tcpdump -nn port 443
Exclude SSHsudo tcpdump -nn not port 22
DNS trafficsudo tcpdump -nn port 53
Save to filesudo tcpdump -nn -s 0 -w out.pcap
Read from filetcpdump -nn -r out.pcap
Show packet contentsudo tcpdump -nn -X port 80
New connections onlysudo tcpdump -nn 'tcp[tcpflags] & tcp-syn != 0'
Limit capture countsudo tcpdump -nn -c 100
Verbose outputsudo 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 -c or time-limit captures — unbounded captures can fill disks fast on busy systems, especially with -w. Use -c 1000 for a packet count limit, or -G 60 to rotate files every 60 seconds.
  • Exclude your own SSH session — add not port 22 when capturing remotely, otherwise your SSH traffic clutters the output and makes it harder to find what you’re looking for.
  • Use -s 0 when 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 ss to find what ports are active, tcpdump to inspect the traffic on those ports. Use dig to test DNS, tcpdump to see why it’s failing.