TCP Three-Way Handshake
Parameter Negotiation: Shaping Connection Behavior
Read aloud is unavailable in this browser.
The Hidden Handshake: Capability Discovery
The three-way handshake does more than establish sequence numbers—it's a capability discovery mechanism that shapes how the connection behaves for its entire lifetime. Every TCP option, every parameter, every performance characteristic is locked in during these three segments.
Modern TCP connections use sophisticated features like window scaling for high-bandwidth paths, selective acknowledgments for efficient loss recovery, and timestamps for accurate RTT measurement. But none of these work unless both endpoints agree during the handshake. Miss the negotiation window, and you're stuck with 1985-era TCP capabilities.
By the end of this page, you will understand how Maximum Segment Size is determined and why it matters, window scaling negotiation for high-bandwidth networks, SACK enablement for efficient loss recovery, timestamp option for RTT and PAWS, ECN negotiation for congestion signaling, and how parameter mismatches affect performance.
Why Parameter Negotiation Matters
TCP was designed in 1981 when networks were slow (kilobits), lossy, and simple. Over decades, TCP has evolved through backward-compatible extensions—optional features that enhance performance when both endpoints support them.
The Negotiation Challenge:
How do two endpoints discover each other's capabilities without a pre-arranged agreement? The solution: option fields in the TCP header. Each SYN and SYN-ACK carries optional capability advertisements. If both sides include an option, the feature is enabled. If either omits it, the feature is unavailable.
Why This Design?
-
Backward Compatibility — A new client with advanced options can still connect to an old server. The old server ignores unknown options and responds without them.
-
Path Transparency — Middleboxes (firewalls, NATs) may alter or strip options. Negotiation happens end-to-end with whatever survives.
-
Flexibility — Different connections can negotiate different capabilities based on endpoint support and path characteristics.
| Capability | If Negotiated | If Missing | Performance Impact |
|---|---|---|---|
| Window Scaling | Windows up to 1 GB | Max 64 KB window | Catastrophic on high-BW×delay paths |
| SACK | Selective retransmission | Go-back-N on loss | Major on lossy networks |
| Timestamps | Accurate RTT, PAWS | Coarse RTT estimation | Moderate to significant |
| Proper MSS | No fragmentation | IP fragmentation possible | Moderate (fragmentation overhead) |
| ECN | Router congestion signals | Only loss-based signals | Minimal to moderate |
There is no mechanism to renegotiate capabilities mid-connection. Whatever is agreed (or not) during the handshake applies for the connection's entire lifetime. If SACK wasn't enabled in the SYN/SYN-ACK exchange, you cannot add SACK later—even if both endpoints support it.
Maximum Segment Size (MSS) Negotiation
The Maximum Segment Size (MSS) option is arguably the most important parameter negotiated during the handshake. It specifies the largest TCP segment (data portion) that an endpoint is willing to receive.
Why MSS Matters:
TCP segments are encapsulated in IP datagrams. If a segment is too large, the IP datagram will be fragmented—broken into smaller pieces that are reassembled at the destination. Fragmentation is extremely undesirable:
- Overhead — Each fragment has its own IP header (20-40 bytes of waste)
- Reliability — If any fragment is lost, the entire segment is lost
- Performance — Reassembly consumes CPU cycles and memory
- Middlebox Issues — Many firewalls/NATs handle fragments poorly
MSS Calculation:
MSS = MTU - IP_Header - TCP_Header
For standard Ethernet with IPv4:
MSS = 1500 - 20 - 20 = 1460 bytes
For Ethernet with IPv6:
MSS = 1500 - 40 - 20 = 1440 bytes
| Network Type | MTU | IPv4 MSS | IPv6 MSS |
|---|---|---|---|
| Ethernet | 1500 | 1460 | 1440 |
| Jumbo Frames | 9000 | 8960 | 8940 |
| PPPoE | 1492 | 1452 | 1432 |
| VPN Overhead | ~1400 | ~1360 | ~1340 |
MSS Negotiation Mechanics:
- Client includes MSS option in SYN — Declares max segment size it can receive
- Server includes MSS option in SYN-ACK — Declares its max segment size
- Each side uses peer's MSS — When sending data, use the MSS the receiver advertised
Key Insight: MSS is per-direction. The client might send MSS=1460 while server sends MSS=1400. The client then sends segments ≤1400, and server sends segments ≤1460.
Path MTU Discovery (PMTUD):
MSS from the handshake assumes local interface MTU, but the path between endpoints may have lower MTU (e.g., VPN tunnels). Path MTU Discovery:
- Send large segments with Don't Fragment (DF) bit set
- If path has smaller MTU, router returns ICMP "Fragmentation Needed"
- TCP reduces segment size based on ICMP feedback
PMTUD adjusts MSS after the handshake, but initial MSS from negotiation is the starting point.
If MSS option is missing from SYN or SYN-ACK, TCP assumes a conservative default of 536 bytes (576-byte minimum MTU minus 40 bytes headers). This severely limits performance. All modern implementations include MSS in every SYN—there's no reason to omit it.
Window Scaling Negotiation
The original TCP header provides only 16 bits for window size, limiting the maximum receive window to 65,535 bytes. For modern high-bandwidth, high-latency networks, this is catastrophically insufficient.
The Bandwidth-Delay Product Problem:
Maximum Throughput = Window Size / RTT
With 64KB window and 100ms RTT:
Max Throughput = 65,535 bytes / 0.1s = 655,350 bytes/s ≈ 5.2 Mbps
A 100ms RTT path with 64KB window is capped at 5.2 Mbps—unusable for modern networks. A 1 Gbps link with 100ms RTT needs:
Required Window = 1 Gbps × 0.1s = 100 Mbit = 12.5 MB
Window scaling (RFC 7323) extends the window to up to 1 GB.
How Window Scaling Works:
The Window Scale option specifies a shift count (0-14). The actual window is:
Actual_Window = Window_Field × 2^(Scale_Factor)
Examples:
| Scale Factor | Multiplier | Max Window | Use Case |
|---|---|---|---|
| 0 | ×1 | 64 KB | Compatibility fallback |
| 4 | ×16 | 1 MB | LAN environments |
| 7 | ×128 | 8 MB | Standard internet |
| 10 | ×1024 | 64 MB | High-BDP paths |
| 14 | ×16384 | 1 GB | Maximum possible |
Window Scale Negotiation Rules:
-
Both sides must participate — If client includes Window Scale but server doesn't respond with Window Scale, the feature is disabled for the entire connection
-
Each side chooses its own scale — Client might use scale=7, server might use scale=5. They don't need to match.
-
Scale applies to advertised windows — When client advertises window with scale=7, server multiplies the window field by 128 to get actual receive window
-
Scale is fixed for connection lifetime — Cannot change after handshake
Negotiation Example:
Client SYN: Window=65535, Window_Scale=7
Server SYN-ACK: Window=32768, Window_Scale=5
Client's actual receive window: 65535 × 128 = 8,388,480 bytes
Server's actual receive window: 32768 × 32 = 1,048,576 bytes
Some broken middleboxes (old firewalls, NATs) don't understand Window Scale and strip it from SYN-ACK. The client thinks scaling is enabled, but the server's unscaled window is misinterpreted as huge. This causes data to pile up beyond the real buffer, eventually timing out. Diagnosing requires packet capture on both sides.
Selective Acknowledgments (SACK) Negotiation
Standard TCP acknowledgments are cumulative—they acknowledge all bytes up to a sequence number but can't specify gaps. When packet 3 of 5 is lost, the receiver can only say "I have bytes up to packet 2." The sender doesn't know if packets 4 and 5 arrived.
The Problem with Cumulative ACKs:
Sent: [1][2][3][4][5]
Received: [1][2][ ][4][5] (Packet 3 lost)
ACK: "I have up to sequence 2001"
Sender knows packet 3 is missing, but:
- Did 4 and 5 arrive?
- Should I retransmit only 3, or 3, 4, and 5?
Without SACK, TCP often retransmits more than necessary (Go-Back-N behavior) or uses complex algorithms (NewReno, etc.) to probe what was received.
Selective Acknowledgments (SACK):
SACK (RFC 2018) lets receivers explicitly report which non-contiguous blocks were received:
ACK: "Cumulative ACK for 2001; SACK blocks: [4001-5000], [6001-7000]"
Now the sender knows:
- Bytes 0-2000 received (cumulative)
- Bytes 2001-3999 missing (gap before first SACK block)
- Bytes 4001-5000 received (first SACK block)
- Bytes 5001-5999 missing (gap between SACK blocks)
- Bytes 6001-7000 received (second SACK block)
Retransmit only the missing ranges: major efficiency gain.
SACK Negotiation:
- •Included in SYN and SYN-ACK
- •Only 2 bytes (Kind + Length)
- •Indicates willingness to use SACK
- •Must appear in BOTH to enable
- •Has no value field—binary yes/no
- •Used during data transfer (not handshake)
- •Variable length (10-40 bytes typical)
- •Contains left-edge, right-edge pairs
- •Up to 4 blocks typically fit
- •Reports received out-of-order data
SACK Negotiation Success:
If both SYN and SYN-ACK contain SACK-Permitted:
- SACK is enabled for the connection
- Receiver can include SACK blocks in ACKs when appropriate
- Sender can use SACK information for precise retransmission
If SACK Not Negotiated:
- Receiver can still receive out-of-order data and buffer it
- But receiver cannot inform sender of what was received
- Sender falls back to cumulative-ACK-based recovery (NewReno, etc.)
SACK support is nearly universal today (Linux, Windows, macOS, iOS, Android all support it). However, some legacy or embedded systems may not include SACK-Permitted. When diagnosing poor performance after packet loss, check whether SACK was negotiated—its absence dramatically worsens recovery.
Timestamps Option Negotiation
The TCP Timestamps option (RFC 7323) provides two critical capabilities: accurate RTT measurement and Protection Against Wrapped Sequences (PAWS).
Timestamps Option Structure:
The option contains two 32-bit values:
+-------+-------+-------------------+-------------------+
| Kind | Len | TSval (4 bytes) | TSecr (4 bytes) |
| 8 | 10 | Sender's time | Echo of peer's |
+-------+-------+-------------------+-------------------+
- TSval (Timestamp Value): Sender's current timestamp
- TSecr (Timestamp Echo Reply): Most recent TSval received from peer
RTT Measurement:
Timestamps enable per-acknowledgment RTT calculation:
- Sender includes TSval = T1 in outgoing segment
- Receiver echoes TSval in TSecr of ACK
- Sender receives ACK with TSecr = T1
- Sender calculates RTT = CurrentTime - TSecr = CurrentTime - T1
Without timestamps, RTT can only be measured when retransmission timer is used—coarse and infrequent. Timestamps enable fine-grained, continuous RTT tracking.
Protection Against Wrapped Sequences (PAWS):
On very fast connections (10+ Gbps), the 32-bit sequence space can wrap in seconds:
10 Gbps = 1.25 GB/s → 4 GB (sequence space) wraps in ~3.4 seconds
An old duplicate packet with sequence X might arrive after the sequence has wrapped past X again. Without PAWS, TCP might accept this old data as new.
PAWS uses timestamps to reject such duplicates:
- Segments with timestamps older than recent ones are discarded
- Prevents sequence space wrap-around issues
Timestamps Negotiation:
- Client includes Timestamps in SYN: TSval = client_time, TSecr = 0
- Server includes Timestamps in SYN-ACK: TSval = server_time, TSecr = client_time
- Client includes in ACK: TSval = client_time2, TSecr = server_time
If both sides include Timestamps in their handshake segments, timestamps are enabled. If either omits them, timestamps are disabled for the connection.
Timestamps in SYN Example:
1234567891011121314
SYN: TCP Option - Timestamps TSval: 123456789 TSecr: 0 ← No previous timestamp to echo SYN-ACK: TCP Option - Timestamps TSval: 987654321 ← Server's timestamp TSecr: 123456789 ← Echoes client's SYN timestamp ACK: TCP Option - Timestamps TSval: 123456802 ← Client's new timestamp TSecr: 987654321 ← Echoes server's SYN-ACK timestampEach segment with timestamps carries 10 extra bytes (12 with padding). For small segments, this is non-negligible overhead. However, the benefits (RTT measurement, PAWS) justify the cost for most connections. High-frequency trading systems sometimes disable timestamps to minimize latency.
ECN (Explicit Congestion Notification) Negotiation
Traditional TCP detects congestion only through packet loss—by the time packets are dropped, the network is already severely congested. Explicit Congestion Notification (ECN) allows routers to signal congestion before dropping packets.
How ECN Works:
- Router marking: When queues build up, ECN-capable routers set the CE (Congestion Experienced) bit in IP header instead of dropping
- Receiver notification: Receiver sees CE bit, sets ECE (ECN-Echo) flag in TCP header
- Sender response: Sender sees ECE, reduces congestion window, sets CWR (Congestion Window Reduced)
Result: Congestion control without packet loss—lower latency, less retransmission.
ECN Negotiation During Handshake:
ECN is negotiated using two TCP flags: ECE (ECN-Echo) and CWR (Congestion Window Reduced).
SYN (Client):
Flags: SYN, ECE, CWR
Setting both ECE and CWR in SYN indicates "I support ECN."
SYN-ACK (Server):
Flags: SYN, ACK, ECE (if ECN supported)
or
Flags: SYN, ACK (if ECN not supported)
Server responds with ECE only (not CWR) to indicate ECN support.
| Client SYN Flags | Server SYN-ACK Flags | ECN Status |
|---|---|---|
| SYN, ECE, CWR | SYN, ACK, ECE | ECN Enabled ✓ |
| SYN, ECE, CWR | SYN, ACK | ECN Disabled (server doesn't support) |
| SYN | SYN, ACK, ECE | ECN Disabled (client didn't request) |
| SYN | SYN, ACK | ECN Disabled (neither supports) |
ECN Benefits:
- Lower latency — Congestion signaled before queues overflow; no retransmission wait
- Better utilization — Networks can run closer to capacity without packet loss
- Fairness with loss-based flows — ECN flows respond to congestion earlier
ECN Challenges:
- Middlebox issues — Some old firewalls drop packets with ECN flags set
- Limited deployment — Many internet paths still don't support ECN marking
- Game-ability — Malicious endpoints could ignore ECN and get more bandwidth
ECN was historically disabled due to middlebox issues, but support has improved dramatically. Apple, Google, and Microsoft now enable ECN by default on many platforms. Modern data centers use ECN extensively for DCTCP (Data Center TCP). Check ECN negotiation status when debugging latency-sensitive applications.
Option Ordering, Size Limits, and Compatibility
TCP options must fit within the header's size constraints and follow encoding rules. Understanding these mechanics helps diagnose option-related issues.
TCP Header Size Limit:
The Data Offset field is 4 bits, specifying header length in 32-bit words:
- Minimum header: 5 × 4 = 20 bytes (no options)
- Maximum header: 15 × 4 = 60 bytes
- Maximum options space: 60 - 20 = 40 bytes
Common Option Sizes:
| Option | Size (bytes) | Cumulative in Typical SYN |
|---|---|---|
| MSS | 4 | 4 |
| SACK Permitted | 2 | 6 |
| Timestamps | 10 | 16 |
| NOP (padding) | 1 | 17 |
| Window Scale | 3 | 20 |
| NOP + NOP (alignment) | 2 | 22 |
| End of Option List | 1 | Varies |
Option Encoding Details:
Options follow a Type-Length-Value (TLV) format:
+------+--------+-----------------+
| Kind | Length | Data |
+------+--------+-----------------+
1B 1B (Length-2) B
Exceptions:
- Kind 0 (End of Options): Single byte, no length or data
- Kind 1 (NOP): Single byte, no length or data, used for padding
Alignment Requirements:
Options don't require specific alignment, but implementations often pad to 32-bit boundaries for efficiency. NOP (0x01) is inserted as needed:
Actual option bytes: [MSS:4][WSc:3][SACK:2][TS:10] = 19 bytes
With padding: [MSS:4][NOP:1][WSc:3][SACK:2][NOP:1][NOP:1][TS:10] = 22 bytes
Option Processing Rules:
- Unknown options should be ignored — Old hosts encountering new options skip them
- Malformed options should cause RST — Invalid length or truncated options indicate errors
- Order doesn't matter — Options can appear in any sequence
- Duplicates are implementation-defined — Usually last occurrence wins
SYN Cookie Option Limitations:
When SYN cookies are active, the server doesn't store client options. The cookie can only encode:
- Limited MSS (3-4 bits, mapping to common values)
- No Window Scale, SACK, or Timestamps state
If the connection completes with cookies, these options may not be properly enabled. Modern implementations activate cookies only under attack, minimizing impact.
Some middleboxes strip or modify options they don't understand. Timestamps might be removed for 'security,' Window Scale might be stripped by old NATs. If performance is worse than expected, capture packets on both sides of potential middleboxes to see if options survive the path.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.