NuttX TCP State Machine Notes
This document describes how the current NuttX TCP stack implements TCP
state transitions. It is based on the in-tree implementation (primarily
in net/tcp) and focuses on what the code does today rather than a
generic RFC 793 description.
Scope
TCP connection state is tracked per
struct tcp_conn_s.State transitions happen mainly in:
net/tcp/tcp_input.c(incoming segments and most transitions)net/tcp/tcp_timer.c(timeouts and retransmissions)net/tcp/tcp_conn.c(connect/listen-side allocation and initial state)net/tcp/tcp_close.c(active close initiation)
State Representation
NuttX stores TCP state in tcp_conn_s::tcpstateflags.
Bits 0-3 are the state (
TCP_STATE_MASK).Bit 4 is a flag (
TCP_STOPPED) used by the socket layer to stop data flow.
The state values are defined in include/nuttx/net/tcp.h:
TCP_CLOSEDTCP_ALLOCATED(NuttX-internal: allocated but not yet connected)TCP_SYN_RCVDTCP_SYN_SENTTCP_ESTABLISHEDTCP_FIN_WAIT_1TCP_FIN_WAIT_2TCP_CLOSE_WAITTCP_CLOSINGTCP_TIME_WAITTCP_LAST_ACKTCP_STOPPED
Supported vs Unsupported (RFC State View)
NuttX largely follows the classic TCP state machine, the table below maps the traditional RFC 793 state names to what exists in NuttX today.
|
|---|
|
|
|
|
|
|
|
|
|
|
|
Note on TCP_ALLOCATED
TCP_ALLOCATED is NuttX-specific and has no direct RFC state name.
It is the pre-connect/pre-accept state for a newly created socket connection.
High-level Transition Summary
This section summarizes the most common state paths.
Active open (connect)
Typical client-side flow:
TCP_ALLOCATED
-> TCP_SYN_SENT (tcp_connect() prepares SYN)
-> TCP_ESTABLISHED (tcp_input receives SYN|ACK and replies ACK)
Passive open (listen/accept)
Listening sockets are registered in the listener table (not a LISTEN state). When a SYN arrives:
listener in tcp_listenports[]
-> new conn: TCP_SYN_RCVD (tcp_allocaccept() in tcp_conn.c)
-> TCP_ESTABLISHED (tcp_input receives final ACK)
-> accept() wakes up (tcp_accept_connection())
Graceful close (active close)
When the application initiates a close (or shutdown(SHUT_WR)), the stack
sends FIN and transitions:
TCP_ESTABLISHED
-> TCP_FIN_WAIT_1
-> TCP_FIN_WAIT_2 (ACK of our FIN)
-> TCP_TIME_WAIT (FIN from peer)
-> TCP_CLOSED (timer expiry)
Simultaneous close
If FIN is received while we are in TCP_FIN_WAIT_1 and our FIN has not been
fully ACKed, NuttX can enter TCP_CLOSING:
TCP_FIN_WAIT_1
-> TCP_CLOSING
-> TCP_TIME_WAIT (ACK of our FIN)
Passive close (peer closes first)
When FIN is received in ESTABLISHED, the application is notified
via callbacks. the stack sends ACK and goes to TCP_CLOSE_WAIT:
TCP_ESTABLISHED
-> TCP_CLOSE_WAIT (FIN received)
-> TCP_CLOSED (ACK of our FIN)
Detailed State Handling
TCP_SYN_SENT
Entered by
tcp_connect()(net/tcp/tcp_conn.c).On receiving
SYN|ACKwith a valid ACK:Parses options (e.g., MSS).
Sets
TCP_ESTABLISHED.Updates
rcvseqand window tracking.Notifies the socket layer using
TCP_CONNECTED.
On unexpected control segments or failure:
The connection is aborted (
TCP_ABORTcallback) and a RST may be sent.
TCP_SYN_RCVD
- Entered for a newly accepted connection when a SYN matches a listener.
Allocation and initialization occur in
tcp_allocaccept()(net/tcp/tcp_conn.c).
A SYN-ACK is sent. The retransmission is handled by
tcp_timer.c.On receiving the final ACK (
TCP_ACKDATA):Transition to
TCP_ESTABLISHED.tcp_accept_connection()is called to hand the connection to thelistening socket/accept logic.
TCP_ESTABLISHED
Normal data transfer occurs here.
Incoming data and ACK processing is handled in
net/tcp/tcp_input.c.If a FIN is received:
The application is notified (
TCP_CLOSEflag is included in callback).NuttX transitions to
TCP_CLOSE_WAITand sendsACK.
TCP_CLOSE_WAIT
Only entered when a FIN is received in ESTABLISHED.
The application is notified (
TCP_CLOSEflag in callback).NuttX can send data until the application initiates close.
- On application close request:
NuttX sends FIN and transitions to
TCP_LAST_ACK.
TCP_FIN_WAIT_1
- Entered when the application requests a graceful close.
This is initiated in
net/tcp/tcp_appsend.cwhen the callback result containsTCP_CLOSE.
On receiving FIN:
- If the FIN also ACKs our FIN and
tx_unacked == 0: transition to TCP_TIME_WAIT.
- If the FIN also ACKs our FIN and
Otherwise: transition to
TCP_CLOSING.In both cases, ACK the peer FIN.
On receiving an ACK that completes ACK of our FIN (and no FIN from peer):
Transition to
TCP_FIN_WAIT_2.
Data received in FIN_WAIT_1:
Can continue receiving data until close or peer FIN.
TCP_FIN_WAIT_2
Waiting for the peer FIN after our FIN was ACKed.
On receiving FIN:
Transition to
TCP_TIME_WAIT.ACK the FIN and notify close.
Data received in FIN_WAIT_2:
Can continue receiving data until close or peer FIN.
TCP_CLOSING
Simultaneous close case.
When the ACK for our FIN is received (
TCP_ACKDATA):Transition to
TCP_TIME_WAIT.
TCP_LAST_ACK
- Entered after FIN is received in ESTABLISHED and the application chooses
to close, causing the stack to send FIN.
On receiving ACK for our FIN (
TCP_ACKDATA):Transition to
TCP_CLOSED.Notify close via callback.
TCP_TIME_WAIT
NuttX responds to segments by sending an ACK.
Cleanup is timer-driven (see
tcp_timer.c):TCP_TIME_WAITare handled as “wait for timeout” states.When the per-connection timer expires, the state becomes
TCP_CLOSED.
Timers, Retransmissions, and Failure Handling
The TCP timer handler in net/tcp/tcp_timer.c drives:
Retransmission for connections with
tx_unacked > 0.State-specific retransmit behavior:
TCP_SYN_RCVD: retransmit SYN-ACK.TCP_SYN_SENT: retransmit SYN.TCP_ESTABLISHED: request retransmit via callback (TCP_REXMIT).TCP_FIN_WAIT_1,TCP_CLOSING,TCP_LAST_ACK: retransmit FIN|ACK.
Timeout cleanup:
TCP_SYN_RCVD: if SYN-ACK retransmits exceed limit, the half-openconnection is closed and freed.
TCP_SYN_SENTand established cases: if retransmits exceed limit, theconnection is closed, the socket is notified (
TCP_TIMEDOUT), and a RST may be sent.
Deviations and Notable Simplifications
LISTEN is not an explicit TCP state; it is represented by listener table entries.
RST processing is intentionally simple (accept RST and close).
Where to Look in the Code
State definitions:
include/nuttx/net/tcp.hIncoming-segment state logic:
net/tcp/tcp_input.cRetransmission/timeout logic:
net/tcp/tcp_timer.cConnect path / SYN_SENT setup:
net/tcp/tcp_conn.cAccept path / SYN_RCVD allocation:
net/tcp/tcp_conn.cActive close initiation:
net/tcp/tcp_close.candnet/tcp/tcp_shutdown.cListener table (LISTEN semantics):
net/tcp/tcp_listen.c