2 TCP
Watersrc-stack edited this page 2026-10-14 04:00:21 +02:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

TCP

The Transmission Control Protocol turns IP's lost, duplicated and reordered packets into a reliable, ordered byte stream between two programs. It numbers every byte, has the receiver acknowledge what it got, and resends what's missing. A connection is identified by four values: source address, source port, destination address and destination port.

The header

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |       |C|E|U|A|P|R|S|F|                               |
| Offset| Rsrvd |W|C|R|C|S|S|Y|I|            Window             |
|       |       |R|E|G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options (0 to 40 bytes)                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Offset Size Field Meaning
0 2 Source Port The sender's port: random (ephemeral) for a client.
2 2 Destination Port The service: 80 HTTP, 443 HTTPS, 22 SSH...
4 4 Sequence Number Number of the first data byte in this segment. Starts at a random value.
8 4 Acknowledgment Number Next byte expected from the other side (valid when ACK is set).
12 4 bits Data Offset Header length in 32-bit words: 5 (20 bytes) to 15 (60 bytes).
13 8 bits Flags SYN opens, FIN closes, RST aborts, ACK acknowledges, PSH "deliver now", URG, plus ECE / CWR for congestion.
14 2 Window How many bytes the sender of this segment can still receive (scaled by the window scale option).
16 2 Checksum Covers a pseudo-header (addresses, protocol, length), the header and the data.
18 2 Urgent Pointer Unused in practice.
20 0–40 Options MSS, window scale, SACK, timestamps...

Example: a connection, from opening to closing

curl fetching a small file from 192.0.2.2:8080, as l shows it:

#25    [veth0]     4.968173  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP     40 B  [SYN]
#26    [veth0]     4.968214  192.0.2.2:8080        -> 192.0.2.1:59682       ttl 64  TCP     40 B  [SYN,ACK]
#27    [veth0]     4.968242  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP     32 B  [ACK]
#28    [veth0]     4.968326  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP    119 B  [PSH,ACK]  [GET]
#29    [veth0]     4.968337  192.0.2.2:8080        -> 192.0.2.1:59682       ttl 64  TCP     32 B  [ACK]
#30    [veth0]     4.979895  192.0.2.2:8080        -> 192.0.2.1:59682       ttl 64  TCP    218 B  [PSH,ACK]  [HTTP 200]
#31    [veth0]     4.979909  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP     32 B  [ACK]
#32    [veth0]     4.980106  192.0.2.2:8080        -> 192.0.2.1:59682       ttl 64  TCP     61 B  [PSH,ACK]
#33    [veth0]     4.980111  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP     32 B  [ACK]
#34    [veth0]     4.980203  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP     32 B  [FIN,ACK]
#35    [veth0]     4.980260  192.0.2.2:8080        -> 192.0.2.1:59682       ttl 64  TCP     32 B  [FIN,ACK]
#36    [veth0]     4.980266  192.0.2.1:59682       -> 192.0.2.2:8080        ttl 64  TCP     32 B  [ACK]
sequenceDiagram
  participant C as client 192.0.2.1:59682
  participant S as server 192.0.2.2:8080
  C->>S: SYN seq=x
  S->>C: SYN,ACK seq=y ack=x+1
  C->>S: ACK ack=y+1
  Note over C,S: connection open (3-way handshake)
  C->>S: PSH,ACK "GET /hello.txt" (87 bytes)
  S->>C: ACK
  S->>C: PSH,ACK HTTP headers (186 bytes)
  C->>S: ACK
  S->>C: PSH,ACK file (29 bytes)
  C->>S: ACK
  C->>S: FIN,ACK
  S->>C: FIN,ACK
  C->>S: ACK
  Note over C,S: closed

The sizes in l include the TCP header: 32 bytes for most segments (20 + 12 bytes of timestamp options), 40 for the SYNs. So packet #28 carries 119 − 32 = 87 bytes of HTTP request, and the server sends its 186 bytes of headers (#30) and the 29-byte file (#32) in two segments.

The SYN, byte by byte

0020                    e9 22 1f 90 ac 19 e3 e2 00 00 00 00 a0 02
0030  fa f0 84 32 00 00 02 04 05 b4 04 02 08 0a f9 56
0040  4d a7 00 00 00 00 01 03 03 0a
Bytes Field Value
e9 22 Source Port 59682
1f 90 Destination Port 8080
ac 19 e3 e2 Sequence 2887377890, random
00 00 00 00 Acknowledgment 0: nothing to acknowledge yet
a0 Data Offset a = 10 words = 40 bytes
02 Flags 0000 0010: SYN
fa f0 Window 64240
84 32 Checksum see below
00 00 Urgent 0
02 04 05 b4 Option: MSS max segment size 1460 (1500 − 40 bytes of IP and TCP headers)
04 02 Option: SACK permitted
08 0a f9 56 4d a7 00 00 00 00 Option: timestamps the sender's clock, and 0 echoed
01 Option: NOP padding
03 03 0a Option: window scale windows are shifted left by 10 (×1024)

The SYN-ACK answers with its own random sequence number (4145906934) and acknowledges 2887377891, the client's sequence + 1: a SYN counts as one byte.

NetOtter's i for the SYN:

  Protocol     TCP  (6)
  TTL          64
  TCP header   40 bytes
    Ports      59682 -> 8080
    Flags      SYN
    Seq        2887377890
    Ack        0
    Window     64240
    Checksum   0x8432
    Urgent     0

Why the checksum looks wrong

0x8432 is not a valid checksum for this segment: the correct one is 0xe95a. Modern network cards compute TCP and UDP checksums in hardware (checksum offload). The system only fills in the pseudo-header's partial sum and leaves the rest to the card. A capture made on the sending machine sees the packet before the card finishes it.

Here, 0x8432 is exactly the sum of the pseudo-header: source, destination, protocol 6 and TCP length 40. The server's SYN-ACK shows 0x8432 too: both ends are on the same machine here, linked by a virtual cable that never computes checksums, so every segment of this capture carries the partial sum. Packets received from a real network card carry real checksums.

In NetOtter

  • Parsing: parse_tcph() (Parsers) reads the fixed fields and honours the Data Offset (so TCP header 40 bytes counts the options), but doesn't decode the options.
  • l: ports and flags ([SYN,ACK]).
  • Analysis view: well-known ports get names (:HTTPS). A TLS ClientHello in the first data segment gives the server's name; see Hostname learning.
  • Filters: proto tcp, port, portsrc, portdest.

Read more

  • RFC 9293: TCP (2022, replaces RFC 793)
  • RFC 7323: window scale and timestamps
  • RFC 2018: selective acknowledgements (SACK)