Chapter 14

tshark Fundamentals

tshark is Wireshark's command-line counterpart — the same protocol dissectors, the same display filter language, but fully scriptable and capable of running on headless servers. It's the right tool when you need to process large PCAPs programmatically, extract specific fields from thousands of packets, filter and write to a new PCAP from the command line, or pipe packet data into grep, awk, sort, and Python.

Scenario

You're on a headless Linux analysis server. You have 12 PCAP files from a ring buffer, each 500 MB. You need to extract all unique destination IPs from external connections, all DNS query names, all HTTP Host headers, and all TLS SNI values — and you need the output as CSV for enrichment in a Python script. Wireshark's GUI can't run here. tshark processes all 6 GB of capture in a pipeline and produces the four output files in under 5 minutes.

Core tshark Flags

  tshark Command Reference
  ═══════════════════════════════════════════════════════════════════

  Input/Output:
    -r          Read from pcap file
    -w          Write filtered output to new pcap file
    -i     Live capture on interface
    -F pcap           Force output format (pcap, pcapng)

  Filtering:
    -Y        Display filter (Wireshark filter language)
    -R        Read filter (applied before display filter; rarely needed)
    -f           Capture filter for live capture (BPF syntax)

  Output format:
    -T fields         Output specified fields tab-separated
    -T json           Output as JSON (full decoded packet)
    -T pdml           Output as PDML XML
    -T text           Default text output (like Wireshark packet summary)
    -T ek             Elasticsearch-compatible JSON
    -e         Add field to output (used with -T fields)
    -E separator=,    Change field separator (default: tab)
    -E header=y       Add CSV header row

  Other useful flags:
    -n                Disable name resolution (faster, IPs not resolved to hostnames)
    -q                Quiet mode (suppress per-packet output, used with -z)
    -z          Generate statistics (see Ch16)
    -V                Verbose packet decoding
    -x                Print hex dump of packet
    -c         Stop after N packets
    -l                Flush output after each packet (for piping to grep)

  Performance:
    Large file processing: tshark is single-threaded
    For multi-file: run multiple tshark instances in parallel with &

Field Extraction with -T fields -e

Bashtshark-field-extraction.sh
PCAP="merged.pcap"

# Extract all destination IPs and ports from TCP connections (tab-separated)
tshark -r $PCAP -n \
    -Y "tcp.flags.syn==1 and not tcp.flags.ack" \
    -T fields \
    -e ip.src \
    -e ip.dst \
    -e tcp.dstport \
    -E separator=, \
    -E header=y \
    > tcp_connections.csv

# Extract DNS queries (name and type)
tshark -r $PCAP -n \
    -Y "dns.flags.response==0" \
    -T fields \
    -e frame.time_epoch \
    -e ip.src \
    -e dns.qry.name \
    -e dns.qry.type \
    -E separator=, \
    -E header=y \
    > dns_queries.csv

# Extract HTTP Host headers and URIs
tshark -r $PCAP -n \
    -Y "http.request" \
    -T fields \
    -e frame.time_epoch \
    -e ip.src \
    -e ip.dst \
    -e http.host \
    -e http.request.method \
    -e http.request.uri \
    -e http.user_agent \
    -E separator=, \
    -E header=y \
    > http_requests.csv

# Extract TLS SNI and JA3 (if JA3 plugin is installed)
tshark -r $PCAP -n \
    -Y "tls.handshake.type==1" \
    -T fields \
    -e frame.time_epoch \
    -e ip.src \
    -e ip.dst \
    -e tls.handshake.extensions_server_name \
    -E separator=, \
    -E header=y \
    > tls_sni.csv

# Extract NTLM authentication usernames
tshark -r $PCAP -n \
    -Y "ntlmssp.auth.username" \
    -T fields \
    -e frame.time_epoch \
    -e ip.src \
    -e ip.dst \
    -e ntlmssp.auth.domain \
    -e ntlmssp.auth.username \
    -E separator=, \
    -E header=y \
    > ntlm_auth.csv

echo "Extraction complete:"
wc -l tcp_connections.csv dns_queries.csv http_requests.csv tls_sni.csv ntlm_auth.csv

Filtering and Writing New PCAPs

Bashtshark-filter-write.sh
PCAP="merged.pcap"

# Write a smaller PCAP containing only traffic to/from suspect IP
tshark -r $PCAP -n \
    -Y "ip.addr==185.220.101.47" \
    -w suspect_traffic.pcap

# Write only the attack time window
tshark -r $PCAP -n \
    -Y "frame.time >= \"2026-09-17 02:00:00\" and frame.time <= \"2026-09-17 06:00:00\"" \
    -w attack_window.pcap

# Write only DNS traffic from all input files in parallel
for f in ring/capture_*.pcap; do
    base=$(basename $f .pcap)
    tshark -r "$f" -n -Y "dns" -w "dns_only/${base}_dns.pcap" &
done
wait

# Chain: filter → merge results
tshark -r large.pcap -Y "http" -w http_only.pcap
capinfos http_only.pcap | grep "Number of packets"

# Write as JSON for Python processing
tshark -r $PCAP -n \
    -Y "http.request.method==POST" \
    -T json \
    > http_posts.json

# Read JSON in Python
python3 -c "
import json
with open('http_posts.json') as f:
    packets = json.load(f)
for pkt in packets:
    layers = pkt['_source']['layers']
    if 'http' in layers:
        host = layers['http'].get('http.host', [''])[0]
        uri = layers['http'].get('http.request.uri', [''])[0]
        print(f'{host}{uri}')
"
Mental model: tshark as a pipeline stage, not just a pcap reader

tshark's real power is as a pipeline component. tshark ... | sort | uniq -c | sort -rn | head -20 gives you the top 20 DNS query names in a 5-second command. tshark ... | awk '{print $NF}' | python3 enrichment.py feeds extracted IPs into a VirusTotal lookup script. for f in *.pcap; do tshark -r $f ... & done; wait processes all ring buffer files in parallel. Think of tshark as the extraction layer in a pipeline — it converts binary PCAP into text fields that every other Unix tool can operate on. Once the data is text, grep/awk/sort/uniq/jq handle the rest.

Q & A

Q: tshark is taking 45 minutes to process a 6 GB PCAP. How do I speed it up?

A few approaches: (1) Apply a capture-filter-equivalent pre-filter: use editcap to extract only the time window you care about, then run tshark on the smaller file. A 4-hour attack window from a 3-day capture might be 800 MB instead of 6 GB. (2) Add -n to disable name resolution: without -n, tshark does reverse DNS lookups for every IP. On a 6 GB file with millions of unique IPs, this adds hours. Always use -n unless you specifically need resolved names. (3) Use -Y only for the minimum filter: a complex display filter requires every packet to be fully decoded before the filter is evaluated. A BPF pre-filter (via editcap) reduces the packet count before tshark even starts decoding. (4) Parallelize across files: if your input is multiple ring buffer files, run one tshark per file in the background with & and combine the output. On a 4-core machine, processing 4 files simultaneously reduces wall time by roughly 4x. (5) Use -T fields instead of -T json: fields output is much faster to generate than full JSON decoding of every protocol layer.