tcpdump has a weird little filter language (BPF syntax) that I never remember under pressure. This page is my cheatsheet.

Basic syntax

tcpdump -i <interface> -n <filter>

-n  don't resolve addresses/ports
-i  interface (eth0, any, lo)
-v  verbose (-vv, -vvv more)
-w  write to file for later wireshark
-r  read from file
-c N stop after N packets
-s 0 capture full packet (not truncated)

Host and network filters

host 192.0.2.1              # to or from
src host 192.0.2.1          # from only
dst host 192.0.2.1          # to only
net 192.0.2.0/24            # subnet
src net 192.0.2.0/24        # subnet as source

Port filters

port 443                    # source or dest port 443
src port 443                # source only
dst port 443                # dest only
portrange 50000-60000       # range

Protocol filters

tcp                         # TCP only
udp                         # UDP only
icmp                        # ICMP only
arp                         # ARP
tcp port 443                # combine
'tcp[tcpflags] & tcp-syn != 0'  # TCP with SYN flag

TCP flag combinations

# SYN only (connection attempts)
'tcp[tcpflags] == tcp-syn'

# SYN-ACK
'tcp[tcpflags] == tcp-syn|tcp-ack'

# RST (connection resets)
'tcp[tcpflags] & tcp-rst != 0'

# FIN (connection closes)
'tcp[tcpflags] & tcp-fin != 0'

Combining filters

host 192.0.2.1 and tcp port 443
'host 192.0.2.1 and (port 80 or port 443)'
'not arp and not port 22'

Boolean operators: and, or, not (or &&, ||, !).

Payload filters (advanced)

Capture HTTP GET requests:

'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'

0x47455420 is ASCII “GET " (with trailing space). The [(tcp[12:1] & 0xf0) >> 2]:4 offset accesses the first 4 bytes of the TCP payload.

Similar for POST:

'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'

Interface tricks

# All interfaces (Linux)
tcpdump -i any

# Specific Docker container veth
tcpdump -i veth_abc123

# Wireguard interface
tcpdump -i wg0

Useful one-liners

Count packets by source IP:

tcpdump -i eth0 -n -c 1000 2>/dev/null | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head

DNS queries (port 53):

tcpdump -i any -n port 53 and not port 5353

SSL/TLS handshakes:

tcpdump -i eth0 -n 'tcp port 443 and (tcp[((tcp[12:1] & 0xf0) >> 2)] = 0x16)'

HTTP requests (including Host header):

tcpdump -i eth0 -A -s 0 'tcp port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420 or tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354)'

Saving for wireshark analysis

tcpdump -i eth0 -w capture.pcap -s 0 'host problem-server.example.com'

Then open in Wireshark GUI locally. Wireshark has infinitely better filters and display — tcpdump is for capture, Wireshark for analysis.

When tcpdump isn’t enough

  • ebpf tools (bpftrace, bcc) for kernel-level events tcpdump can’t see
  • ss for socket-level details (-i for TCP info like cwnd, rtt)
  • conntrack -L for NAT state
  • tshark for scriptable wireshark-style analysis