42. Reading a Wireshark trace like a detective
42.1 First moves on any capture
- Statistics → Capture File Properties: duration, packet rate, drops.
- Statistics → Conversations (TCP tab): sort by bytes / by "Bits/s". Find the flow that matters. "Follow → TCP Stream" to see the app dialogue.
- Statistics → Protocol Hierarchy: what's actually on this wire.
- Set the time display to "Seconds Since Previous Displayed Packet" (View → Time Display Format) — gaps jump out.
- Enable
tcp.analysisexpert flags (on by default): look at Analyze → Expert Information.
42.2 The display filters that find problems
| Filter | Finds |
|---|---|
tcp.analysis.retransmission |
retransmitted segments |
tcp.analysis.fast_retransmission |
fast retransmits (3 dup-ACKs) |
tcp.analysis.spurious_retransmission |
retransmit of data already ACKed (RTO too tight / reordering) |
tcp.analysis.out_of_order |
reordering |
tcp.analysis.duplicate_ack |
dup-ACKs (tcp.analysis.duplicate_ack_num >= 3 = triggering) |
tcp.analysis.lost_segment |
a gap Wireshark inferred ("previous segment not captured") |
tcp.analysis.zero_window |
receiver said "stop, buffer full" |
tcp.analysis.window_full |
sender has filled the offered window |
tcp.analysis.ack_rtt > 0.2 |
ACKs that took >200 ms (delayed-ACK or path) |
tcp.flags.reset == 1 |
who reset, and when relative to the last data |
tcp.time_delta > 0.2 && tcp.flags.push == 1 |
app "think time" gaps |
tcp.analysis.bytes_in_flight |
plot it (I/O Graph) to see cwnd behavior |
tcp.stream == N |
isolate one connection |
42.3 Signatures → diagnosis
| What you see | Likely cause |
|---|---|
Steady retransmissions + duplicate_ack bursts, throughput sawtooths |
real packet loss on the path; check both-end captures & link counters |
spurious_retransmission + out_of_order, ~no true loss |
packet reordering (ECMP/LACP hashing), RTO too aggressive |
zero_window from the receiver, then window_update seconds later |
receiver app too slow — not a network problem |
Long tcp.time_delta gap before a PSH from the server, no missing packets |
server-side application latency (slow query, GC pause) |
| Client SYN, SYN, SYN at 1s/3s, no SYN-ACK | SYN or SYN-ACK lost; firewall DROP; accept-queue overflow |
| Handshake OK, first full-size (~1500) data packet retransmitted forever | PMTU black hole (ICMP filtered) — Ch. 8 |
RST immediately after SYN-ACK |
backlog overflow w/ abort, or app crashed on accept |
RST right after a normal data exchange, from a third pattern/TTL |
forged RST by a middlebox/IPS |
| ACKs every ~40 ms, tiny segments, request in 2 parts | Nagle × delayed-ACK — Ch. 33 |
win never grows past ~64 KB on a high-RTT path |
window scaling stripped/off — Ch. 30 |
Big bytes_in_flight, RTT climbing steadily with throughput flat |
bufferbloat at the bottleneck — Ch. 44 |
| Duplicate IP ID / duplicate SYN with different TTL | two hosts sharing an IP, or a retransmit vs a spoofed packet |
42.4 The I/O Graph (your most powerful view)
Statistics → I/O Graph. Plot on the same time axis:
tcp.analysis.bytes_in_flight(AVG) — proxy forcwnd.tcp.analysis.retransmission(COUNT) — loss events.tcp.analysis.ack_rtt(AVG/MAX) — RTT over time.- Throughput (bytes, "Bits/s").
Now you can see slow start ramp, the sawtooth of CUBIC on loss, an RTO (everything stops for ~200 ms–1 s then restarts from near zero), or RTT rising with in-flight bytes (bufferbloat).
42.5 tshark for scripted analysis
# Per-connection retransmit counts:
tshark -r cap.pcap -q -z conv,tcp
# List every retransmission with time and stream:
tshark -r cap.pcap -Y tcp.analysis.retransmission \
-T fields -e frame.time_relative -e tcp.stream -e ip.src -e ip.dst -e tcp.seq
# RTT distribution:
tshark -r cap.pcap -Y tcp.analysis.ack_rtt -T fields -e tcp.analysis.ack_rtt | \
sort -n | awk '{a[NR]=$1} END{print "p50",a[int(NR*.5)],"p95",a[int(NR*.95)],"max",a[NR]}'
# Time-to-first-byte per HTTP request:
tshark -r cap.pcap -Y http.response -T fields -e http.time -e http.request.uri
42.6 War story
A dashboard showed 2% "network errors" calling an internal API. The capture had
zero retransmissions or resets. Filtering http.response.code >= 500 and
"Follow TCP Stream" showed the server returned 503 with a JSON body in ~4 ms —
the API's own rate limiter. The "network" error was an application status code
the client's metrics mislabeled. Lesson: confirm at the packet layer whether
a failure is TCP (RST/timeout/retransmit) or application (a clean HTTP 4xx/5xx)
before opening a network ticket.
Try it yourself
tshark -r cap.pcap -q -z io,stat,1,'COUNT(tcp.analysis.retransmission)tcp.analysis.retransmission','AVG(tcp.analysis.bytes_in_flight)tcp.analysis.bytes_in_flight'
tshark -r cap.pcap -q -z expert
wireshark cap.pcap # then: Analyze > Expert Information; Statistics > I/O Graph