TL;DR
Establishment Protocol | RTT Setup Cost | Reliable Transmission | Stale Packet Protection | Basic Spoofing Protection |
---|---|---|---|---|
3-way Handshake | 1 | ✅ | ✅ | ✅ |
No handshake, no sequence numbers (UDP) | 0 | ❌ | ❌ | ❌ |
Sequence numbers at 0 | 0 | ✅ | ❌ | ❌ |
Data with Syn Packet | 0 | ✅ | ✅ | ❌ |
Data w/ Syn Packet & Pre-shared Secret (TFO) | 1 on first connection 0 on subsequent connections | ✅ | ✅ | ✅ |
Motivation
The 3-way handshake. It’s inevitable in any TCP discussion that you mention the TCP connection establishment three-way handshake. After writing about QUIC and how it aims to have 0-RTT connection establishment cost, I started wondering why TCP needed that 1-RTT 3-way handshake in the first place.
First, the handshake
It’s commonly reffered to in the shorthand as SYN, SYNACK, ACK. It might make more sense to call it SYN, ACKSYN, ACK. The client SYNchronizes its sequence number to the server. The server ACKnowledges the client’s sequence number, then SYNchronizes its own sequence number. The client then ACKnowledges the server’s sequence number. It’s called a 3-way handshake because there are 3 messages.
This exchange costs an entire back-and-forth or Round Trip Time (RTT) between the endpoints before any data is sent 1.
Why is the sequence number important?
Sequence numbers provide 2 features:
- Detecting when a message was lost (I got messages 1, 2 & 4, but not 3)
- Detecting when messages are duplicated (I got 3 copies of message 1, I can toss the extras).
The receiver can tell the sender when it is missing a message and ask for it to be sent again. Detecting and discarding duplicates means the sender can retransmit without fear of messing up the receiver.
Why not just start at 0?
It seems like we could save the 1-RTT establishment cost by just starting our sequence numbers at 0. No need to exchange numbers at the beginning. However, this leaves us open to two bad scenarios:
- Slow packets from closed connections can interfere with new connections2
- Guessing a sequence number makes it easy to spoof traffic
For #1, remember that duplicate messages are sent. It’s possible a connection closes, and a new connection opens on the same port, but a message from the old connection is in flight. When it arrives, if the sequence number is valid for the new connection, the receiver has no way of telling it’s an old message.
For #2, imagine Server C trying to impersonate Server B. Server C sends a SYN packet with Server B’s IP address listed to Server A. Server A sends a SYNACK to Server B. Server C sends an ACK and guesses the correct sequence number and follows up with a request for to perform some action, the server would assume it was Server B all along3. A random number makes guessing the correct sequence number. Sending the final ACK back with the correct sequence number provides evidence that the original SYN sender was actually at the IP it claimed to be.
Why not send data with the first SYN packet?
Another way to skip the 1-RTT cost would be to just send data with the initial SYN packet. Then the server could respond back with data in its SYN/ACK and we wouldn’t have any RTT cost for set up. However, this suffers the same spoofing problem as before. A malicious sender could send a false IP with a SYN packet full of data, and the server would assume the IP was real and take action based on the packet.
One way that’s been proposed to remedy the spoofing problem is to store a shared secret on the first time a connection is made, and then send that with the first SYN packet on the next time. This is called TCP Fast Open (TFO), and you can read more about it in the links below.
Recap
Establishment Protocol | RTT Setup Cost | Reliable Transmission | Stale Packet Protection | Basic Spoofing Protection |
---|---|---|---|---|
3-way Handshake | 1 | ✅ | ✅ | ✅ |
No handshake, no sequence numbers (UDP) | 0 | ❌ | ❌ | ❌ |
Sequence numbers at 0 | 0 | ✅ | ❌ | ❌ |
Data with Syn Packet | 0 | ✅ | ✅ | ❌ |
Data w/ Syn Packet & Pre-shared Secret (TFO) | 1 on first connection 0 on subsequent connections | ✅ | ✅ | ✅ |
Questions
Here are some questions I still have:
- What’s the worst an attacker could do with spoofing the correct sequence number?
- How does TLS change the connection establishment?
- Does QUIC use sequence numbers?
Resources
Resources:
- RFC 793: Transmission Control Protocol
- Stack Exchange: Why not a 2-way handshake?
- “TCP Fast Open: expediting web services” by Michael Kerrisk (TLPI author!)
- In depth packet capture of a TCP connection
- Quora: Why are sequence numbers random
- QUIC / TOU encrypting headers discussion
- Middle box problems with TFO deployment
-
Data can be sent with the last ACK packet, so only the first roundtrip is expended. ↩︎
-
Look at Figure 9 called “Recovery from Old Duplicate SYN” in RFC 793 for a diagram of this. ↩︎
-
It’s not immediately clear to me how an attacker could benefit from spoofing this first packet since eventually Server B would send an RST telling Server A to reset the connection. The attacker could trigger a bunch of traffic to Server B by requesting a large file. If the server was only validating identity based on IP address, it could be bad, but I don’t think that’s very common. ↩︎