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 host
Direction (Linux pseudo-interface any)
sudo tcpdump -i eth0 inbound                              # inbound only
sudo tcpdump -i eth0 outbound                             # outbound only
Byte-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)
FlagMeaning
-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 tcpdump

Output 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:

SymbolFlag
[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 microseconds

For 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 | head

For 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.md5

If 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 0 to 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 -Z for long-running captures.
  • Buffer drops: tcpdump reports <N> packets dropped by kernel at 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/outbound is Linux-specific and only on the any pseudo-interface. On a real interface, filter by IP direction (src host X vs dst host X).
  • Time zone: tcpdump prints in the analyst host’s local TZ. Convert to UTC for reports.

Field Manual | Data Acquisition | Network Forensics | tshark