tcp state machine specification
on this page
overview
the tcp finite state machine (fsm) defines all valid states and transitions for tcp connections. this specification provides the complete state machine as defined in rfc 9293, including edge cases, simultaneous operations, and error conditions.
state definitions
primary states
tl;dr
State | Description | TCB Exists | Valid Operations |
CLOSED | No connection exists, initial state | No | OPEN (active/passive) |
LISTEN | Waiting for connection request | Partial | SEND (error), CLOSE |
SYN-SENT | Active open, waiting for SYN-ACK | Yes | CLOSE, timeout |
SYN-RECEIVED | SYN received and sent, waiting for ACK | Yes | SEND, RECEIVE, CLOSE |
ESTABLISHED | Connection open, data transfer | Yes | SEND, RECEIVE, CLOSE |
FIN-WAIT-1 | FIN sent, waiting for ACK or FIN | Yes | RECEIVE, CLOSE |
FIN-WAIT-2 | FIN acknowledged, waiting for FIN | Yes | RECEIVE, CLOSE |
CLOSE-WAIT | FIN received, waiting for application | Yes | SEND, CLOSE |
CLOSING | Simultaneous close, waiting for ACK | Yes | timeout |
LAST-ACK | FIN sent after receiving FIN | Yes | timeout |
TIME-WAIT | Waiting for network to clear | Yes | timeout (2*MSL) |
tcb (transmission control block)
each tcp connection maintains a tcb containing:
struct tcb {
// Connection identification
uint32_t local_addr;
uint16_t local_port;
uint32_t remote_addr;
uint16_t remote_port;
// Send sequence variables
uint32_t snd_una; // oldest unacknowledged
uint32_t snd_nxt; // next send
uint32_t snd_wnd; // send window
uint32_t snd_up; // urgent pointer
uint32_t snd_wl1; // segment seq for last window update
uint32_t snd_wl2; // segment ack for last window update
uint32_t iss; // initial send sequence
// Receive sequence variables
uint32_t rcv_nxt; // next receive
uint32_t rcv_wnd; // receive window
uint32_t rcv_up; // urgent pointer
uint32_t irs; // initial receive sequence
// Connection state
enum tcp_state state;
// Timers
timer_t retransmit_timer;
timer_t persist_timer;
timer_t keepalive_timer;
timer_t timewait_timer;
// Options
uint16_t mss;
uint8_t wscale_snd;
uint8_t wscale_rcv;
bool sack_permitted;
bool timestamps;
};
tcp state machine diagram
TCP Finite State Machine
Rendering diagram...
Complete TCP state transitions showing normal connection flow, simultaneous operations, and error conditions. Each state represents a specific phase in the TCP connection lifecycle.
state transition table
complete transition matrix
Current State Event/Input Condition New State Action
------------- --------------- ---------------- ----------- ----------------
CLOSED passive OPEN - LISTEN create TCB
CLOSED active OPEN - SYN-SENT send SYN
CLOSED SEND - SYN-SENT send SYN
CLOSED rcv SYN - SYN-RECEIVED send SYN,ACK
CLOSED rcv other - CLOSED send RST
LISTEN rcv SYN - SYN-RECEIVED send SYN,ACK
LISTEN SEND - SYN-SENT send SYN
LISTEN CLOSE - CLOSED delete TCB
LISTEN rcv other - LISTEN ignore
SYN-SENT rcv SYN,ACK acceptable ACK ESTABLISHED send ACK
SYN-SENT rcv SYN,ACK !acceptable SYN-SENT send RST
SYN-SENT rcv SYN - SYN-RECEIVED send SYN,ACK
SYN-SENT timeout - CLOSED delete TCB
SYN-SENT CLOSE - CLOSED delete TCB
SYN-SENT rcv RST - CLOSED error to app
SYN-RECEIVED rcv ACK acceptable ESTABLISHED -
SYN-RECEIVED rcv ACK !acceptable SYN-RECEIVED send RST
SYN-RECEIVED rcv SYN - SYN-RECEIVED send SYN,ACK
SYN-RECEIVED rcv FIN - CLOSE-WAIT send ACK
SYN-RECEIVED CLOSE - FIN-WAIT-1 send FIN
SYN-RECEIVED rcv RST - LISTEN/CLOSED see below
ESTABLISHED rcv FIN - CLOSE-WAIT send ACK
ESTABLISHED CLOSE - FIN-WAIT-1 send FIN
ESTABLISHED rcv ACK - ESTABLISHED update window
ESTABLISHED rcv RST - CLOSED error to app
ESTABLISHED rcv data - ESTABLISHED process data
FIN-WAIT-1 rcv ACK acks FIN FIN-WAIT-2 -
FIN-WAIT-1 rcv FIN - CLOSING send ACK
FIN-WAIT-1 rcv FIN,ACK - TIME-WAIT send ACK
FIN-WAIT-1 rcv RST - CLOSED error to app
FIN-WAIT-2 rcv FIN - TIME-WAIT send ACK
FIN-WAIT-2 rcv RST - CLOSED error to app
FIN-WAIT-2 timeout - CLOSED delete TCB
CLOSE-WAIT CLOSE - LAST-ACK send FIN
CLOSE-WAIT rcv RST - CLOSED error to app
CLOSING rcv ACK acks FIN TIME-WAIT -
CLOSING rcv RST - CLOSED ignore
LAST-ACK rcv ACK acks FIN CLOSED delete TCB
LAST-ACK rcv RST - CLOSED delete TCB
TIME-WAIT timeout 2*MSL CLOSED delete TCB
TIME-WAIT rcv FIN - TIME-WAIT send ACK
TIME-WAIT rcv RST - CLOSED delete TCB
state transition events
user events (api calls)
tl;dr
Event | Description | Valid States |
OPEN (passive) | listen() system call | CLOSED |
OPEN (active) | connect() system call | CLOSED, LISTEN |
SEND | send() system call | ESTABLISHED, CLOSE-WAIT |
RECEIVE | recv() system call | ESTABLISHED, FIN-WAIT-1/2 |
CLOSE | close() system call | Any except CLOSED |
ABORT | SO_LINGER(0) close | Any except CLOSED |
STATUS | getsockopt() | Any |
network events (segment arrival)
tl;dr
Event | Flags | Description |
rcv SYN | SYN=1, ACK=0 | Connection request |
rcv SYN,ACK | SYN=1, ACK=1 | Connection accepted |
rcv ACK | SYN=0, ACK=1 | Acknowledgment |
rcv FIN | FIN=1 | Connection closing |
rcv FIN,ACK | FIN=1, ACK=1 | Close acknowledgment |
rcv RST | RST=1 | Connection reset |
rcv data | PSH=0/1 | Data segment |
timer events
tl;dr
Timer | Trigger Condition | Action |
Retransmission | No ACK within RTO | Retransmit segment |
TIME-WAIT | 2*MSL elapsed | Close connection |
FIN-WAIT-2 | Timeout waiting for FIN | Force close |
Persist | Zero window probe | Send 1-byte probe |
Keep-Alive | Idle connection | Send keep-alive probe |
User Timeout | Data unacked too long | Abort connection |
detailed state behaviors
closed state
CLOSED State Processing:
ON passive OPEN:
create TCB
state = LISTEN
return
ON active OPEN:
create TCB
select ISS
send SYN: <SEQ=ISS><CTL=SYN>
state = SYN-SENT
return
ON incoming segment:
if RST:
ignore
else:
send RST: <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
return
listen state
LISTEN State Processing:
ON incoming SYN:
if security/compartment mismatch:
send RST
return
create new TCB for connection
select ISS
send SYN,ACK: <SEQ=ISS><ACK=SEG.SEQ+1><CTL=SYN,ACK>
state = SYN-RECEIVED
return
ON incoming segment without SYN:
if RST:
ignore
else:
send RST
return
ON CLOSE:
delete TCB
state = CLOSED
return
established state
ESTABLISHED State Processing:
ON incoming segment:
check sequence number:
if outside window:
send ACK
return
if RST:
abort connection
state = CLOSED
signal error to application
return
if SYN:
send RST
abort connection
state = CLOSED
return
if ACK:
update send window
remove acknowledged data from retransmit queue
restart retransmission timer if data outstanding
process URG if present
process data:
queue for application
send ACK if needed (delayed ACK rules)
if FIN:
signal EOF to application
send ACK
state = CLOSE-WAIT
return
ON CLOSE:
send FIN
state = FIN-WAIT-1
return
simultaneous operations
simultaneous open
rare scenario where both ends initiate active open:
Host A State Segment Exchange Host B State
------------ ---------------- ------------
CLOSED CLOSED
| |
v OPEN v OPEN
SYN-SENT ---------> <SEQ=100><SYN> ---------> SYN-SENT
<--------- <SEQ=300><SYN> <---------
| (packets cross) |
v rcv SYN v rcv SYN
SYN-RECEIVED -----> <SEQ=100><ACK=301> -----> SYN-RECEIVED
<----- <SEQ=300><ACK=101> <-----
| |
v rcv ACK v rcv ACK
ESTABLISHED ESTABLISHED
simultaneous close
both ends initiate close at same time:
Host A State Segment Exchange Host B State
------------ ---------------- ------------
ESTABLISHED ESTABLISHED
| |
v CLOSE v CLOSE
FIN-WAIT-1 -------> <SEQ=100><FIN,ACK> -----> FIN-WAIT-1
<------- <SEQ=300><FIN,ACK> <------
| (packets cross) |
v rcv FIN v rcv FIN
CLOSING ----------> <SEQ=101><ACK=301> ------> CLOSING
<---------- <SEQ=301><ACK=101> <------
| |
v rcv ACK v rcv ACK
TIME-WAIT TIME-WAIT
| |
v timeout v timeout
CLOSED CLOSED
reset processing
when to send rst
Send RST Conditions:
1. No TCB exists (CLOSED state):
- Incoming segment except RST
- Format: <SEQ=0><ACK=SEG.SEQ+SEG.LEN><RST,ACK>
2. Half-synchronized (SYN-SENT/SYN-RECEIVED):
- Unacceptable ACK received
- Format: <SEQ=SEG.ACK><RST>
3. Synchronized states:
- Security level mismatch
- Unacceptable SYN received
- Format: <SEQ=SND.NXT><RST>
4. Aborting connection:
- User ABORT command
- Format: <SEQ=SND.NXT><RST>
when to accept rst
RST Acceptance Rules:
1. Check sequence:
- Must be in receive window
- If SEG.SEQ = RCV.NXT: exact match
- If in window: acceptable
2. State-specific handling:
SYN-SENT:
- If ACK matches: abort, return error
- Otherwise: ignore
SYN-RECEIVED:
- If passive open: return to LISTEN
- If active open: return to CLOSED
ESTABLISHED, FIN-WAIT-1/2, CLOSE-WAIT:
- Abort connection
- Signal error to application
- Return to CLOSED
CLOSING, LAST-ACK:
- Return to CLOSED
TIME-WAIT:
- Return to CLOSED
edge cases and error conditions
half-open connections
detection and recovery:
Scenario: Host A thinks connection open, Host B thinks closed
Host A Host B
------ ------
ESTABLISHED CLOSED
|
sends data ------------------>
(no TCB)
<-- RST ---------- sends RST
|
CLOSED (abort)
old duplicate syn
protection using initial sequence numbers:
ISN Selection Algorithm:
ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
Where:
- M = 4 microsecond timer (increases ISN over time)
- F = cryptographic hash function
- Prevents: sequence number prediction attacks
- Ensures: old duplicates have stale sequence numbers
connection hijacking prevention
sequence number validation:
Acceptable Segment Check:
RCV.NXT <= SEG.SEQ < RCV.NXT + RCV.WND
or
RCV.NXT <= SEG.SEQ + SEG.LEN - 1 < RCV.NXT + RCV.WND
If outside window:
- Send ACK: <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
- Drop segment
- Prevents blind injection attacks
implementation considerations
state storage optimization
Key Highlights
- closed - no storage required
- listen - minimal tcb (just port binding)
- syn-received - may use syn cookies to avoid state
- time-wait - can use compressed state
- established - full tcb required
performance optimizations
tl;dr
Optimization | State Impact | Benefit |
SYN cookies | No SYN-RECEIVED state | SYN flood protection |
TIME-WAIT assassination | Early TIME-WAIT exit | Port reuse |
Fast retransmit | ESTABLISHED behavior | Quicker loss recovery |
Header prediction | ESTABLISHED fast path | Reduced CPU usage |
debugging state machines
observing states
# linux - show states
ss -tan state established
ss -tan state time-wait
ss -tan state syn-sent
# count by state
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c
# watch state changes
tcpdump -i any -nn 'tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'
# kernel state counters
netstat -st | grep -i state
nstat -az | grep -i tcp.*state
common state issues
tl;dr
Symptom | Likely State | Investigation |
Connection hangs | SYN-SENT | Check routing, firewall |
Port exhaustion | TIME-WAIT | Check close patterns |
Memory leak | CLOSE-WAIT | Application not closing |
SYN flood | SYN-RECEIVED | Enable SYN cookies |
Half-open | ESTABLISHED | Enable keepalive |
references
- rfc 793 - original tcp specification (1981)
- rfc 9293 - tcp specification (2022, current)
- rfc 5961 - improving tcp’s robustness
- rfc 7323 - tcp extensions for high performance
key takeaways
- tcp state machine ensures reliable, ordered delivery
- eleven states manage connection lifecycle
- transitions triggered by user events, network events, and timers
- edge cases handled through sequence validation and timers
- implementation optimizations possible while maintaining correctness
- understanding states critical for debugging network issues
next steps
- study three-way handshake implementation details
- explore congestion control algorithms
- practice with connection lifecycle guide