Why does tcpdump print `ack 1` and `seq 1:518` after a handshake, and when do you need `-S` instead?
answer
- not what is on the wire
- tcpdump remembers the first numbers
- first:last, last not included
- two captures, two starting points
basics
~20 stcpdump subtracts the initial sequence numbers it saw, so the first data byte in each direction is 1 and seq 1:518 covers bytes 1 to 517. -S prints the raw numbers, needed when comparing captures or matching other tools.
solid answer
~50 sBy default tcpdump prints **relative** sequence numbers. The SYN and SYN-ACK still show raw initial numbers: the SYN has no ACK bit, and the SYN-ACK is where tcpdump records both starting values for that address-and-port pair. From then on it subtracts them, so the client's ACK reads `ack 1` and the first request reads `seq 1:518`, in `first:last` notation where `last` is not included — 517 bytes, matching `length 517`. If the capture began mid-connection, the base is simply the first ACK-bearing segment tcpdump saw, so the numbers no longer count from the start of the stream. Use `-S` (`--absolute-tcp-sequence-numbers`) whenever numbers must match something outside this one capture: a second capture of the same connection started at another moment, a capture taken on the far side of a device that rewrites sequence numbers, or another tool's log of raw values.
code
bash · 2 linestcpdump -n -S -r client-side.pcap 'host 198.51.100.20 and tcp port 443'
tcpdump -n -S -r server-side.pcap 'host 192.0.2.10 and tcp port 443'go deeper
Recall that tcpdump shows relative numbers after the handshake and that seq first:last excludes last, so the payload size equals the length field.
Explain how tcpdump picks its base from the first ACK-bearing segment, why SYN lines stay raw, and what changes when the capture starts mid-connection.
Know when relative numbers break a comparison — two capture points, a sequence-rewriting device, another tool's raw log — and reach for -S before matching segments.
When a team builds runbooks for multi-point captures, standardise on absolute numbers for any cross-capture evidence so findings survive review.
## What the numbers on a tcpdump TCP line are Every TCP segment carries a 32-bit **sequence number** (the position of its first byte in the sender's byte stream) and, when the ACK bit is set, an **acknowledgment number** (the next byte the sender expects from the other side). On the wire these start at large random values chosen at connection time. tcpdump's default output does not show them that way: ``` IP 192.0.2.10.51514 > 198.51.100.20.443: Flags [S], seq 1584207323, win 64240, options [...], length 0 IP 198.51.100.20.443 > 192.0.2.10.51514: Flags [S.], seq 2836418390, ack 1584207324, win 65160, options [...], length 0 IP 192.0.2.10.51514 > 198.51.100.20.443: Flags [.], ack 1, win 502, options [...], length 0 IP 192.0.2.10.51514 > 198.51.100.20.443: Flags [P.], seq 1:518, ack 1, win 502, options [...], length 517 IP 198.51.100.20.443 > 192.0.2.10.51514: Flags [.], ack 518, win 506, options [...], length 0 ``` The first two lines show raw values; from the third on, the numbers are small. That is **relative sequence numbering**, and it is on unless you pass `-S`. ## How tcpdump computes them The tcpdump manual page and its TCP printer describe the same mechanism: 1. tcpdump keeps a small table keyed by the two addresses and two ports of a conversation. 2. It only relativises segments with the **ACK bit** set. A SYN has no ACK bit, so its line always shows the raw initial number. 3. The first ACK-bearing segment it sees for a conversation — normally the SYN-ACK — is printed raw, and tcpdump records both starting values from it. A later SYN-ACK on the same address and port pair resets the record. 4. Every later segment has those values subtracted, so the first data byte in each direction becomes **1**. ## Reading the result - **`ack 1`** on the third handshake line means "the next byte I expect from you is your byte 1" — no data has been received yet. - **`seq 1:518`** uses `first:last` notation where `last` is not included: bytes 1 through 517, which is why the line ends `length 517`. - **`ack 518`** from the server acknowledges all 517 bytes. - A pure acknowledgment prints no `seq` at all; tcpdump shows it only when there is payload, a SYN, FIN or RST, or with `-vv`. - **SACK blocks** in the options list are relativised the same way, so they line up with the `seq` ranges. ## Where relative numbers mislead | Situation | What goes wrong | Use | |---|---|---| | Capture started mid-connection | the base is the first ACK-bearing segment seen, not the connection's start | read offsets, or `-S` | | Two captures of the same connection | each capture has its own base, so the same segment prints different numbers | `-S` in both | | A device in the path rewrites sequence numbers | the two sides never agree on raw values; relative views hide the offset | `-S` on each side, then compute the difference | | Another tool logs raw numbers | relative values cannot be matched against it | `-S` | The mid-connection case catches people most often: `seq 1` there is not the first byte of the stream, only a small offset from where tcpdump started looking. ## A quick consistency check Relative numbers make a capture easy to audit by eye. In the fragment above, the client sends `seq 1:518` and the server answers `ack 518`: the acknowledgment is exactly one past the last byte sent, so nothing is missing at this capture point. Later lines keep the same rhythm — each `[P.]` from one side advances its `seq` range by its `length`, and the other side's `ack` catches up. When a receiver reports a hole, the gap shows up in the options list too, as something like `sack 1 {1449:2897}`, already in the same relative terms as the `seq` ranges. With `-vv`, tcpdump also prints `seq` on pure acknowledgments, which helps when a fragment has no data lines in one direction. ## What `-S` changes and what it does not `-S` (long form `--absolute-tcp-sequence-numbers`) turns the subtraction off, so every `seq`, `ack` and SACK value is printed as it appears in the header. It changes nothing on the SYN and SYN-ACK lines, which are raw anyway. It does not change the window, the options or the length. Arithmetic on these numbers — why an acknowledgment covers what it covers, and how SACK blocks describe gaps — belongs to TCP's sequencing rules. On this side of the line the job is simpler: know which numbers tcpdump has shifted, from which starting point, and switch to absolute numbers whenever the comparison crosses the boundary of one capture.
- A capture started in the middle of a long download; where do tcpdump's relative numbers count from?From the first segment with the ACK bit that tcpdump saw for that address-and-port pair. That line prints raw values and later lines are offsets from it, so a small `seq` there is not the start of the stream. If you need positions you can compare with anything else, read the file again with `-S`.
- Does -S change anything on the SYN or SYN-ACK lines?No. The SYN carries no ACK bit, so tcpdump never relativises it, and the SYN-ACK is the line where tcpdump records the starting values, so it prints them raw. `-S` changes the lines after the handshake, where relative numbering would otherwise apply.
Relative numbers are a trip meter tcpdump zeroes when it first meets the connection, and -S shows the odometer. Two drivers who reset their trip meters at different moments cannot compare readings; the odometer reads the same for both.
saying these in an interview costs you the question
- seq 1:518 means the segment carried 518 bytes.
- Relative numbers always count from the connection's first byte, even mid-capture.
- Relative sequence numbers are what the hosts actually put on the wire.
- You need -S to see the initial sequence number on the SYN line.
- Two captures of one connection always print the same relative numbers.