Packet capture and live triage with tcpdump. Capture-time BPF filters (different syntax from Wireshark display filters), file-rotation patterns for unattended captures, integration with tshark for offline analysis. Verified against tcpdump 4.99.6, libpcap 1.10.6.
Side: blue
Interfaces and basic capture
tcpdump -D # list available interfaces
sudo tcpdump -i eth0 # capture on one interface
sudo tcpdump -i any # capture on all interfaces (Linux-specific)
sudo tcpdump -n -i eth0 # disable name resolution (recommended for forensics)
sudo tcpdump -nn -i eth0 # also skip port-name resolution
sudo tcpdump -c 100 -i eth0 # stop after N packets
sudo tcpdump -vvv -i eth0 # max verbosity-n is mandatory in DFIR captures: any DNS resolution emits queries from the analysis host that contaminate the capture if you are also looking at DNS traffic.
BPF capture filters
BPF (Berkeley Packet Filter) is the capture-time language. Different from Wireshark display filter syntax. Faster because filtering happens in the kernel before packets reach userspace.
sudo tcpdump -n -i eth0 'host 192.168.1.50' # to/from host
sudo tcpdump -n -i eth0 'src host 192.168.1.50' # source only
sudo tcpdump -n -i eth0 'dst host 192.168.1.50' # destination only
sudo tcpdump -n -i eth0 'net 10.10.0.0/16' # subnet
sudo tcpdump -n -i eth0 'port 22' # one port
sudo tcpdump -n -i eth0 'portrange 1024-65535' # port range
sudo tcpdump -n -i eth0 'tcp port 443' # protocol + port
sudo tcpdump -n -i eth0 'udp and port 53' # UDP DNS only
sudo tcpdump -n -i eth0 'icmp' # all ICMP
sudo tcpdump -n -i eth0 'host 10.0.0.5 and not port 22' # exclude SSH from a host
sudo tcpdump -n -i eth0 'host 10.0.0.5 and (port 80 or port 443)' # web traffic to/from hostDirection (Linux pseudo-interface any)
sudo tcpdump -i eth0 inbound # inbound only
sudo tcpdump -i eth0 outbound # outbound onlyByte-offset matching
When there is no named field for what you want, BPF lets you reach into the packet by byte offset. Common patterns:
sudo tcpdump -n 'tcp[13] & 0x02 != 0' # TCP packets with SYN flag set
sudo tcpdump -n 'tcp[13] & 0x12 == 0x12' # SYN-ACK (SYN + ACK both set)
sudo tcpdump -n 'tcp[13] & 0x04 != 0' # RST packets
sudo tcpdump -n 'tcp[((tcp[12] & 0xf0) >> 2):4] = 0x47455420' # HTTP GET (ASCII "GET ")tcp[13] is the TCP flags byte. Bit values: 0x01 FIN, 0x02 SYN, 0x04 RST, 0x08 PSH, 0x10 ACK, 0x20 URG.
Read and write
sudo tcpdump -i eth0 -w capture.pcap # write capture to file
sudo tcpdump -i eth0 -w capture.pcap -s 0 # full packet bytes (default truncates to 262144 in 4.99+)
tcpdump -r capture.pcap # read saved capture
tcpdump -r capture.pcap -n 'host 10.0.0.5' # filter on read
tcpdump -r capture.pcap -nn -tttt -A 'port 80' # ASCII payload of HTTP traffic-s 0 is critical for forensic captures — older tcpdump versions truncate at 96 bytes by default, dropping payload. Always specify explicitly.
Display and save simultaneously
sudo tcpdump -n -i eth0 -l 'host 10.0.0.5' | tee live.txt # line-buffered, watchable + recorded-l forces line buffering so the pipe receives output immediately.
File rotation (unattended captures)
For captures that need to run for hours/days, rotate by size or time to keep individual files manageable.
sudo tcpdump -n -i eth0 -w 'capture-%Y%m%d-%H%M.pcap' -G 3600 # rotate every 3600 sec, strftime in name
sudo tcpdump -n -i eth0 -w 'capture-%H%M.pcap' -G 600 -C 100 # rotate at 600 sec OR 100 MB
sudo tcpdump -n -i eth0 -w 'capture.pcap' -C 100 -W 24 # 100 MB per file, max 24 files (rolling)| Flag | Meaning |
|---|---|
-G <sec> | rotate when N seconds elapsed |
-C <MB> | rotate when file reaches N MB |
-W <count> | maximum number of files (rolling buffer) |
-Z <user> | drop privileges to user after opening capture file |
For long-running captures, drop privileges with -Z:
sudo tcpdump -n -i eth0 -w 'cap-%Y%m%d.pcap' -G 86400 -Z tcpdumpOutput format
Default line decode
12:34:56.789012 IP 10.0.0.5.54321 > 10.0.0.10.443: Flags [S], seq 1234567890, win 65535, length 0
[timestamp] [protocol] [src ip].[src port] > [dst ip].[dst port]: [flags], [seq], [ack], [win], [options], [length]
TCP flag short codes:
| Symbol | Flag |
|---|---|
[S] | SYN |
[S.] | SYN-ACK |
[.] | ACK |
[P.] | PSH-ACK |
[F] | FIN |
[R] | RST |
[FP.] | FIN-PSH-ACK |
Time formats
tcpdump -tttt -r capture.pcap # absolute date+time, microsecond precision
tcpdump -ttt -r capture.pcap # delta from previous packet
tcpdump -tt -r capture.pcap # epoch seconds with microsecondsFor forensic reports, default to -tttt and convert to UTC explicitly when quoting times.
Packet content
tcpdump -r capture.pcap -A # ASCII payload
tcpdump -r capture.pcap -X # hex + ASCII (most useful for analysis)
tcpdump -r capture.pcap -x # hex only
tcpdump -r capture.pcap -e # link-layer headers (MAC addresses)
tcpdump -r capture.pcap -v # extra detail (TTL, IP id)
tcpdump -r capture.pcap -vv # more
tcpdump -r capture.pcap -vvv # even more (full DNS payloads, etc.)Common forensic patterns
# every host that talked to a target IP
sudo tcpdump -n -i eth0 -w session.pcap 'host 10.0.0.50'
# DNS-only capture for a host
sudo tcpdump -n -i eth0 -w dns.pcap 'host 10.0.0.5 and udp port 53'
# extract HTTP requests with hosts
tcpdump -r capture.pcap -A 'tcp port 80' | grep -E '^(GET|POST|Host:)'
# every SYN packet (scan or beacon detection)
tcpdump -nn -r capture.pcap 'tcp[13] & 0x02 != 0 and tcp[13] & 0x10 == 0'
# every TCP RST (port closed signal, possible scan response)
tcpdump -nn -r capture.pcap 'tcp[13] & 0x04 != 0'
# unique source IPs in a capture
tcpdump -nn -r capture.pcap | awk '{print $3}' | cut -d. -f1-4 | sort -u
# top destination ports
tcpdump -nn -r capture.pcap 'tcp' | awk '{print $5}' | rev | cut -d. -f1 | rev | sort | uniq -c | sort -rn | headFor deeper packet inspection, pipe captures through tshark which has named display filters and statistics.
Hashing the capture
Capture files become evidence. Hash immediately after capture and record the hash with the case file.
sha256sum capture.pcap > capture.pcap.sha256
md5sum capture.pcap > capture.pcap.md5If the capture is rotating, hash each rotated file as it closes — a script wrapper around tcpdump’s -G / -C rotation can sha256sum the previous file before the next one opens.
Pitfalls
- DNS resolution contaminates the capture: always use
-n(no host) and-nn(no host + no port). Without it, tcpdump’s own DNS lookups during decode appear in the very capture you are reading. - Default snap length truncates payload on older tcpdump (≤ 4.99). Use
-s 0(or-s <large>) to capture full packets. Modern versions default to a generous limit but verify with-s 0to be sure. - BPF syntax differs from Wireshark display filters: BPF has no named fields like
http.host. Use byte-offset matches for protocol-internal conditions, or capture broadly and filter offline with tshark /-Y. - Permission: capture requires
CAP_NET_RAW(root or capability-set). Drop to a lower-privilege user with-Zfor long-running captures. - Buffer drops:
tcpdumpreports<N> packets dropped by kernelat exit. High-traffic captures can lose data. Increase-B <kib>(capture buffer) or use a kernel-bypass capture stack (PF_RING, DPDK) for sustained high rates. - Direction filter
inbound/outboundis Linux-specific and only on theanypseudo-interface. On a real interface, filter by IP direction (src host Xvsdst host X). - Time zone: tcpdump prints in the analyst host’s local TZ. Convert to UTC for reports.
links:
Field Manual | Data Acquisition | Network Forensics | tshark