What does Wireshark's tcp.stream field number, and why can one client and server port pair end up with two different stream indexes?
answer
- a counter, not a port tuple
- numbered from 0 per file
- a fresh SYN on the same ports
- port numbers reused
- renumbered when the file changes
basics
~20 stcp.stream numbers each TCP conversation in a file from 0, in order of first appearance. A new SYN reusing the same addresses and ports, with a new initial sequence number or after a FIN or RST, gets a new index.
solid answer
~50 s`tcp.stream` is the **Stream index**, a counter Wireshark assigns while dissecting: the first TCP conversation in the file is 0, the next new one 1, and so on. It is not stored in the capture, so it is recomputed every time a file is opened, and a subset you save or a file you merge gets new numbers. A conversation is normally one address-and-port pair, but when a SYN arrives on a pair Wireshark already knows, with a different initial sequence number or after that conversation saw a FIN or RST, Wireshark starts a new conversation with a new index and marks the packet `[TCP Port numbers reused]`. That is why `tcp.stream eq N` isolates one connection where a port filter can lump several retries together. Other protocols keep their own counters: in Wireshark 4.6, Follow UDP Stream filters on `udp.stream` and Follow TLS Stream on `tls.stream`.
go deeper
Recall that tcp.stream is a conversation number starting at 0 and that Follow TCP Stream filters on it.
Explain how Wireshark decides a SYN starts a new conversation on a reused port pair, and why the index changes when the file changes.
Show that you hand over evidence with endpoints and timestamps rather than bare stream numbers, and that you separate retries on a recycled source port before comparing them.
Consider how an analysis team names connections in tickets and reports so findings survive re-cut, merged and rotated captures.
## What the index is `tcp.stream`, labelled **Stream index** in the packet details, is a number Wireshark gives every TCP **conversation** it finds in a capture. It is the handle the rest of the tool uses: **Follow TCP Stream** applies `tcp.stream eq N`, the Follow dialog's Stream selector steps through the same numbers, and `tshark -z follow,tcp,...,N` takes it as an argument. It identifies a connection far more precisely than an address or a port does, and it is cheap to filter on. Because it is a single integer, it is also a convenient key when you export fields with `tshark` and group packets per connection in a script or spreadsheet. ## How Wireshark assigns it 1. The first packet of an address-and-port pair Wireshark has not seen creates a conversation and takes the next free number, starting at **0**. 2. Later packets of the same pair join that conversation and carry the same index. 3. A SYN on a known pair whose initial sequence number differs from the one the conversation started with, or a SYN arriving after that conversation saw a **FIN** or **RST**, creates a **new** conversation with a new index; the packet is marked `[TCP Port numbers reused]`. 4. A SYN that repeats the original initial sequence number on a conversation with no FIN or RST is treated as a repeat of the same handshake and keeps the old index. 5. A SYN-ACK is handled the same way when the capture is missing the SYN of the new connection. ## Why one port pair can become two streams Clients that open many short connections, NAT gateways and proxies recycle source ports, so a capture covering hours can hold several connections with an identical pair of addresses and ports. A filter such as `tcp.port == 50412` lumps them into one apparent session, with a handshake in the middle that makes no sense. The stream index keeps them apart. In the corrupted-upload case this matters directly: a client that retries an upload from the same source port produces two attempts, two indexes, and two sets of bytes you can compare. ## Why the number is not portable - It is **computed during dissection**, not written into pcap or pcapng, and the counter restarts at 0 whenever a file is opened. - Saving only stream 42 with **Export Specified Packets** produces a file in which that connection is stream **0**. - Merging files, or opening one file of a ring buffer on its own, renumbers every connection. - A ticket that says "look at stream 7" is therefore only meaningful together with the exact file; quoting the endpoints and a timestamp as well survives any re-cut of the capture. ## Other stream counters Wireshark keeps a separate counter or identifier for each protocol it can follow, and in Wireshark 4.6 the Follow dialogs apply these filters: | Follow menu entry | Filter applied | What the numbers mean | |---|---|---| | TCP Stream | `tcp.stream eq N` | TCP conversation in this file | | UDP Stream | `udp.stream eq N` | UDP conversation in this file | | TLS Stream | `tls.stream eq N` | TLS session counter, numbered separately from TCP | | HTTP/2 Stream | `tcp.stream eq N and http2.streamid eq M` | TCP connection plus the HTTP/2 stream identifier | | QUIC Stream | `quic.connection.number eq N and quic.stream.stream_id eq M` | QUIC connection plus the QUIC stream ID | Because the TLS counter only advances for sessions that carry TLS, `tls.stream` 0 can sit on `tcp.stream` 3; never assume the two numbers match. For HTTP/2 and QUIC the dialog adds a **Substream** selector, since many streams share one connection; why those protocols multiplex is a protocol subject of its own. ## Working with the index - List conversations with their index: `tshark -r capture.pcapng -T fields -e tcp.stream -e ip.src -e tcp.srcport -e ip.dst -e tcp.dstport -Y 'tcp.flags.syn == 1 && tcp.flags.ack == 0'`. - Open **Statistics > Conversations**, select a TCP row and press **Follow Stream...** to jump straight to that connection. - Add `tcp.stream` as a packet-list column when a capture interleaves many connections; the eye picks up a change of number faster than a change of port. - Compare two attempts on a recycled port by opening Follow TCP Stream on one and moving the dialog's Stream selector to the other.
- How do you follow one HTTP/2 request rather than the whole TCP connection that carries it?Use **Analyze > Follow > HTTP/2 Stream**. Its Stream selector picks the TCP connection and its Substream selector picks the HTTP/2 stream identifier, so the filter becomes `tcp.stream eq N and http2.streamid eq M`. Over TLS the frames must be decrypted first. QUIC works the same way with the connection number and the QUIC stream ID.
- A colleague says the attack is in stream 7, but stream 7 in your copy is a DNS-over-TCP lookup. What went wrong?The index is not stored in the file; it is recomputed per file in order of first appearance. If either of you filtered, re-saved, merged or opened a different ring-buffer file, the numbering differs. Ask for the endpoints, ports and a timestamp, or for the exact file, and find the connection from those.
saying these in an interview costs you the question
- tcp.stream is a value stored in each packet of the pcapng file.
- Two connections with the same addresses and ports always share one tcp.stream index.
- Filtering on the client's source port isolates a connection just as well as tcp.stream does.
- Follow TLS Stream filters on the same number as the connection's tcp.stream.
- Wireshark numbers TCP streams from 1 in every capture file.