§3.5.6TCP Connection Management

Transport layer Kurose & Ross pp. 249–254 · ~19 min read

  • syn segment
  • synack segment
  • fin segment
  • reset segment
  • half-open connection
  • syn flood attack
  • syn cookie
  • time_wait state

Where you are

  • Application layer
  • Transport layer you are here
  • Network layer
  • Link layer
  • Physical layer

Three segments to open a connection and four to close it. The fact that the server commits memory on the second of those three is the basis of one of the oldest attacks on the Internet.

Words you will meet

  • SYN (synchronize) segment — the first segment of a connection, with the SYN bit set.
  • SYNACK — the server’s reply: SYN set, plus an acknowledgement.
  • FIN segment — “I have no more data to send”; closes one direction.
  • Reset segment — RST set: “there is no socket here”.
  • Half-open connection — one the server has allocated for, whose client never finished.
  • SYN cookie — answering a SYN with a computed sequence number instead of remembering anything.

Why this matters

This looks like plumbing, and it is not.

Connection establishment adds significantly to perceived delay — section 2.2.2 counted a round trip per connection, and it is a real part of why a web page feels slow. And many of the most common network attacks exploit vulnerabilities in connection management, including the SYN flood, which this section covers in full.

Opening and closing

Figures 3.39 and 3.40 — opening, and closing

The first two segments carry no application data. The third may.

message 3 of 3
Client hostinitiatesTimeServer hostlisteningTimeserver allocates buffers and variables — before the handshake finishesclient allocatesSYN=1, seq=client_isnSYN=1, seq=server_isn, ack=client_isn+1SYN=0, seq=client_isn+1, ack=server_isn+1

Click any arrow to see what that message says and why it is sent.

Three segments to open, four to close. Click any arrow for what its header actually carries.

Read this diagram as text
  1. Client host sends SYN=1, seq=client_isn to Server host. The SYN bit is set and the sequence number field carries a randomly chosen initial value. No application data. Randomising client_isn properly matters for security — there has been considerable interest in getting it right [CERT 2001-09, RFC 4987].
  2. Server host sends SYN=1, seq=server_isn, ack=client_isn+1 to Client host. The connection-granted segment, called a SYNACK. Three pieces of information: the SYN bit set, an acknowledgement of client_isn + 1, and the server’s own randomly chosen initial sequence number. In the book’s words: "I received your SYN to start a connection with your initial sequence number. I agree. My own initial sequence number is server_isn." AND — the detail that matters — the server has already allocated its buffers and variables by now.
  3. Client host sends SYN=0, seq=client_isn+1, ack=server_isn+1 to Server host. The client allocates its own buffers and variables, then acknowledges the server’s initial sequence number. The SYN bit is now zero, and stays zero for the rest of the connection. This third segment MAY carry application data — which is how a client can get its first request out without waiting another round trip.

Lifelines, left to right: Client host (host), Server host (server).

The three steps, and what each one commits

Step 1. The client sends a segment with the SYN bit set to 1 and no application data, carrying a randomly chosen initial sequence number , client_isn.

Step 2. The server extracts the SYN and allocates the TCP (Transmission Control Protocol) buffers and variables to the connection. It replies with a SYNACK : SYN bit set to 1, acknowledgement field set to client_isn + 1, and its own randomly chosen server_isn in the sequence number field. Still no application data.

Step 3. The client allocates its own buffers and variables, and sends a third segment with server_isn + 1 in the acknowledgement field and the SYN bit back to zero. This one may carry application data.

Read step 2 again. The server commits memory when it has heard exactly one segment from a host that has proved nothing at all. Hold that thought.

Closing takes four segments, and either side may start. The client issues a close, its TCP sends a FIN , the server acknowledges, the server sends its own FIN, and the client acknowledges that. Then all the resources at both hosts are deallocated.

Everyday picture

A rock climber and a belayer use a three-way handshake that is identical to TCP’s, and for the same reason. “On belay?” — “Belay on.” — “Climbing.” Neither moves until both have confirmed that the other has confirmed.

Where the picture breaks: the climber and belayer can see each other, and either can simply repeat themselves until they get an answer. TCP’s two ends cannot, which is why the third message exists at all — and why the book leaves “why is a two-way handshake not enough?” as a homework problem.

The real thing

The same application over TCP — eight packets instead of two
No.TimeSourceDestinationProtocolLengthInfo
10.000000192.168.1.24198.51.100.7TCP5451874 → 12000 [SYN] Seq=0 Win=64240 Len=0
20.090400198.51.100.7192.168.1.24TCP5412000 → 51874 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0
30.180800192.168.1.24198.51.100.7TCP5451874 → 12000 [ACK] Seq=1 Ack=1 Win=64240 Len=0
40.181100192.168.1.24198.51.100.7TCP7251874 → 12000 [PSH, ACK] Seq=1 Ack=1 Len=18
50.271800198.51.100.7192.168.1.24TCP7212000 → 51874 [PSH, ACK] Seq=1 Ack=19 Len=18
60.271900198.51.100.7192.168.1.24TCP5412000 → 51874 [FIN, ACK] Seq=19 Ack=19 Len=0
70.362300192.168.1.24198.51.100.7TCP5451874 → 12000 [FIN, ACK] Seq=19 Ack=20 Len=0
80.452700198.51.100.7192.168.1.24TCP5412000 → 51874 [ACK] Seq=20 Ack=20 Len=0

Packet 1 Packet 1 of 8. clientSocket.connect() produced this, and the client program has not yet sent a single byte of the sentence. The UDP version was already finished sending by now.

Protocol tree — click a field

The actual bytes

0000 aa bb cc 00 00 01 aa bb cc 00 00 11 08 00 45 00 ..............E.
0010 00 28 1a 01 40 00 40 06 34 d4 c0 a8 01 18 c6 33 .(..@.@.4......3
0020 64 07 ca a2 2e e0 3f 8a 12 c0 00 00 00 00 50 02 d.....?.......P.
0030 fa f0 7d 29 00 00 ..})..

TCPClient.py and TCPServer.py exchanging one sentence. Six of these eight packets carry no application data at all: three open the connection and three more close it.

A SYN consumes a sequence number, though it carries no data

Look at the first three packets. The client’s SYN has Seq=0; the server’s SYNACK has Ack=1; the client’s ACK has Seq=1.

The SYN carried no bytes, so why did the numbering advance by one?

Because the SYN bit itself occupies a place in the sequence space. That is what lets the handshake be acknowledged and retransmitted with exactly the same machinery as data. FIN works the same way, which is why the teardown numbers in packets 6, 7 and 8 also advance by one per FIN.

(The capture shows relative sequence numbers, as packet analysers do by convention. The real values are the random client_isn and server_isn.)

The two state machines

Figure 3.41 — the states a client TCP visits
client applicationinitiates a TCP connectionsend SYNreceive SYN & ACKsend ACKclient applicationinitiates close connectionsend FINreceive ACKsend nothingreceive FINsend ACKwait 30 secondsΛCLOSEDSYN_SENTESTAB-LISHEDFIN_WAIT_1FIN_WAIT_2TIME_WAIT
Fire an event

Outlined buttons are the events this state can actually handle. Try the others too — what cannot happen is as much of the protocol as what can.

Drive it round. Six states, and the connection spends essentially all of its life in one of them.

Figure 3.42 — the states a server TCP visits
server applicationcreates a listen socketΛreceive SYNsend SYN & ACKreceive ACKsend nothingreceive FINsend ACKthe application closessend FINreceive ACKsend nothingCLOSEDLISTENSYN_RCVDESTAB-LISHEDCLOSE_WAITLAST_ACK
Fire an event

Outlined buttons are the events this state can actually handle. Try the others too — what cannot happen is as much of the protocol as what can.

The mirror of the client, with one asymmetry worth noticing: no TIME_WAIT. The side that closes first is the side that waits.

In plain words

Twelve states between the two machines, and a healthy connection spends essentially its entire life in ESTABLISHED. Everything else is the few milliseconds at each end.

Notice the asymmetry: the client machine has a TIME_WAIT state and the server machine does not. That is not about clients and servers — it is about who closes first. The side that sends the first FIN is the side that must wait afterwards.

Why wait at all?

TIME_WAIT lets the client resend the final acknowledgement in case it was lost. If it were lost and the client had already vanished, the server would retransmit its FIN forever and never close.

The duration is implementation-dependent; 30 seconds, 1 minute and 2 minutes are all typical. Only when it ends is the connection formally closed and all resources released, including the port number. That is why a busy server which has just restarted can find its own port apparently still in use.

Neither diagram shows the pathological cases: both sides closing at once, or both opening at once. Stevens covers them [Stevens 1994].

When there is no socket at all

Both machines above assume the server is listening on the port the client aimed at. What if it is not?

The host sends a reset segment — a TCP segment with the RST flag set to 1. In effect: “I don’t have a socket for that segment. Please do not resend it.”

And the UDP (User Datagram Protocol) equivalent is not a UDP thing at all

A UDP segment may also arrive for a port with no open socket. The host cannot reply in kind, because the UDP header has only four fields and none of them is a flag. So the network layer reports it instead, with an ICMP (Internet Control Message Protocol) destination-unreachable datagram. Chapter 5 covers ICMP.

Same condition, two layers, because one protocol had a spare bit and the other did not.

What nmap learns from one SYN to port 6789
What it meansnmap reports

Cells marked ⓘ have an explanation — click to read it. Sortable columns have a ↕ in the heading.

Three outcomes, and the second is more useful to an attacker than it looks.

Section 3.2 introduced nmap and promised to come back once you knew how connection management worked. This is it: nmap sends a SYN to the port it wants to test and reads which of the three replies comes back. Repeat that across every port and it can map open TCP ports, open UDP ports, firewalls and their configurations, and often the versions of applications and operating systems. Almost all of it comes from manipulating connection-management segments.

The SYN flood

The SYN flood, and the cookie that defeats it
1 — The ordinary handshake, from the server’s sidestep 1 of 6
client, or attackerserverSYNbuffers + variablesallocated now — on the strength of one segment

A SYN arrives. The server allocates buffers and variables, sends a SYNACK, and waits for the ACK that completes the handshake.

Six steps. The turn comes at step 4, and it is a very small change with a very large consequence.

Read all steps as text
  1. 1 — The ordinary handshake, from the server’s sideA SYN arrives. The server allocates buffers and variables, sends a SYNACK, and waits for the ACK that completes the handshake.
  2. 2 — If the ACK never comesThe connection sits half open. Eventually — often after a minute or more — the server gives up and reclaims the resources. For one stalled client that is a harmless waste.
  3. 3 — The attackAn attacker sends a large number of SYNs and never completes the third step. The server’s connection resources are exhausted by half-open connections that will never be used, and legitimate clients are refused. This was among the first documented denial-of-service attacks [CERT SYN 1996].
  4. 4 — The fix: do not remember anythingWhen a SYN arrives, the server does NOT create a half-open connection. Instead it computes an initial sequence number that is a hash of the source and destination IP addresses and port numbers, plus a secret known only to the server. That value is the cookie. It goes in the SYNACK — and the server then forgets it completely.
  5. 5 — A legitimate client answersThe client returns an ACK whose acknowledgement field is the SYNACK’s initial sequence number plus one — that is, the cookie plus one. It has no idea it is carrying a cookie; it is simply following the protocol.
  6. 6 — And the server checks it without rememberingThe four values needed to recompute the hash — both addresses and both ports — are all in the arriving ACK, and the secret is the server’s own. If the recomputed hash plus one matches the acknowledgement field, this ACK belongs to a SYN the server really did answer. Only then does it create the connection and the socket.

The whole attack is one line of the handshake

The server allocates at step 2. If the client never sends the ACK, that allocation sits in a half-open connection for a minute or more before being reclaimed.

An attacker sends a great many SYNs and completes none of them. The server’s connection resources are consumed by half-open connections that will never carry a byte, and legitimate clients are denied service. The SYN flood was among the first documented denial-of-service attacks [CERT SYN 1996].

Note what is not required: no compromised software, no unusual traffic volume per packet, nothing malformed. Every SYN is perfectly valid. The attack is entirely in what is left undone.

SYN cookies — and why they work

SYN cookies [RFC 4987] are now deployed in most major operating systems, and the idea is elegant.

When a SYN arrives, the server does not know whether it is legitimate. So rather than deciding, it declines to commit. It computes an initial sequence number that is a hash of the source and destination IP (Internet Protocol) addresses and port numbers, plus a secret known only to the server. That value is the cookie. It goes in the SYNACK, and the server then remembers nothing at all.

A legitimate client returns an ACK whose acknowledgement field is, by the ordinary rule of the handshake, the SYNACK’s sequence number plus one. Every value needed to recompute the hash is present in that arriving segment — both addresses, both ports — and the secret is the server’s own. If the recomputed hash plus one matches, the ACK corresponds to a SYN the server really did answer, and only then is a fully open connection and socket created.

And if the client never answers, the original SYN has done no harm, because nothing was ever allocated.

A slip in the book’s description

The sidebar says the server compares its recomputed value against “the acknowledgment (cookie) value in the client’s SYNACK”.

Clients do not send SYNACKs — the server does. The value being checked is in the client’s ACK, the third segment of the handshake. Everything else in the description is right, and the surrounding sentences make the intent clear.

Check yourself

Check yourself

0 of 6 answered
  1. 1.At which step of the three-way handshake does the server allocate buffers and variables?

    This is the detail the whole SYN flood attack turns on.

  2. 2.predictIn the packet capture, segment 2 carries Seq=0 and Ack=1 while the client's SYN carried Seq=0. Why does the acknowledgement say 1?

  3. 3.Why does the client wait in TIME_WAIT rather than closing immediately?

  4. 4.predictnmap sends a SYN to port 6789 and receives an RST. What has it learned?

  5. 5.How does a SYN cookie let a server verify a returning ACK without having remembered anything?

  6. 6.A host receives a TCP SYN for port 80, but nothing is listening there. What does it send back? And what if it had been a UDP segment?

What to remember

  • Three segments to open — SYN, SYNACK, ACK — and four to close: FIN, ACK, FIN, ACK, with either side able to start. A SYN consumes one sequence number even though it carries no bytes, and so does a FIN.
  • The server allocates at step 2, before the handshake completes. That single fact is the SYN flood, and SYN cookies answer it by hashing the state into the sequence number instead of storing it.
  • The side that closes first enters TIME_WAIT, for 30 s, 1 min or 2 min, so it can resend the final acknowledgement. Its port is held until then.