mirror of
https://github.com/apache/nuttx.git
synced 2026-08-04 13:49:06 +00:00
shutdown should send TCP_FIN packet.
close should send TCP_RST packet when the data in readahead has not been
read and NEW_DATA has arrived.
send TCP_FIN packet when in other cases.
Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
324 lines
9.1 KiB
ReStructuredText
324 lines
9.1 KiB
ReStructuredText
=============================
|
|
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_CLOSED``
|
|
* ``TCP_ALLOCATED`` (NuttX-internal: allocated but not yet connected)
|
|
* ``TCP_SYN_RCVD``
|
|
* ``TCP_SYN_SENT``
|
|
* ``TCP_ESTABLISHED``
|
|
* ``TCP_FIN_WAIT_1``
|
|
* ``TCP_FIN_WAIT_2``
|
|
* ``TCP_CLOSE_WAIT``
|
|
* ``TCP_CLOSING``
|
|
* ``TCP_TIME_WAIT``
|
|
* ``TCP_LAST_ACK``
|
|
* ``TCP_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.
|
|
|
|
.. list-table:: RFC TCP states and their NuttX support
|
|
:header-rows: 1
|
|
:widths: auto
|
|
|
|
* - RFC state name
|
|
- NuttX representation
|
|
- Supported
|
|
- Notes
|
|
* - CLOSED
|
|
- ``TCP_CLOSED``
|
|
- Yes
|
|
- Connection is unused/available.
|
|
* - LISTEN
|
|
- No ``tcpstateflags`` state
|
|
- Partially
|
|
- Listening is implemented via the listener table in ``net/tcp/tcp_listen.c``(``tcp_listenports[]``) rather than a per-connection LISTEN state.
|
|
* - SYN-SENT
|
|
- ``TCP_SYN_SENT``
|
|
- Yes
|
|
- Set by ``tcp_connect()`` in ``net/tcp/tcp_conn.c``.
|
|
* - SYN-RECEIVED
|
|
- ``TCP_SYN_RCVD``
|
|
- Yes
|
|
- Set when accepting an incoming SYN (new connection allocated for a listener).
|
|
* - ESTABLISHED
|
|
- ``TCP_ESTABLISHED``
|
|
- Yes
|
|
- Data transfer state.
|
|
* - FIN-WAIT-1
|
|
- ``TCP_FIN_WAIT_1``
|
|
- Yes
|
|
- Entered on active close (local FIN sent).
|
|
* - FIN-WAIT-2
|
|
- ``TCP_FIN_WAIT_2``
|
|
- Yes
|
|
- Entered after ACK for local FIN (when peer hasn't closed yet).
|
|
* - CLOSE-WAIT
|
|
- Not implemented
|
|
- Yes
|
|
- The TCP input path explicitly notes CLOSE_WAIT is not implemented; NuttX forces the application to close when FIN is received and moves directly toward ``TCP_LAST_ACK``.
|
|
* - CLOSING
|
|
- ``TCP_CLOSING``
|
|
- Yes
|
|
- Used for simultaneous close handling.
|
|
* - LAST-ACK
|
|
- ``TCP_LAST_ACK``
|
|
- Yes
|
|
- Used after receiving FIN and sending FIN in response.
|
|
* - TIME-WAIT
|
|
- ``TCP_TIME_WAIT``
|
|
- Yes
|
|
- Used after the close handshake; timer-driven cleanup.
|
|
|
|
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|ACK`` with a valid ACK:
|
|
|
|
* Parses options (e.g., MSS).
|
|
* Sets ``TCP_ESTABLISHED``.
|
|
* Updates ``rcvseq`` and window tracking.
|
|
* Notifies the socket layer using ``TCP_CONNECTED``.
|
|
|
|
* On unexpected control segments or failure:
|
|
|
|
* The connection is aborted (``TCP_ABORT`` callback) 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 the
|
|
listening 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_CLOSE`` flag is included in callback).
|
|
* NuttX transitions to ``TCP_CLOSE_WAIT`` and sends ``ACK``.
|
|
|
|
TCP_CLOSE_WAIT
|
|
--------------
|
|
|
|
* Only entered when a FIN is received in ESTABLISHED.
|
|
* The application is notified (``TCP_CLOSE`` flag 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.c`` when the callback result
|
|
contains ``TCP_CLOSE``.
|
|
|
|
* On receiving FIN:
|
|
|
|
* If the FIN also ACKs our FIN and ``tx_unacked == 0``: transition to
|
|
``TCP_TIME_WAIT``.
|
|
* 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_WAIT`` are 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-open
|
|
connection is closed and freed.
|
|
* ``TCP_SYN_SENT`` and established cases: if retransmits exceed limit, the
|
|
connection 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.h``
|
|
* Incoming-segment state logic: ``net/tcp/tcp_input.c``
|
|
* Retransmission/timeout logic: ``net/tcp/tcp_timer.c``
|
|
* Connect path / SYN_SENT setup: ``net/tcp/tcp_conn.c``
|
|
* Accept path / SYN_RCVD allocation: ``net/tcp/tcp_conn.c``
|
|
* Active close initiation: ``net/tcp/tcp_close.c`` and ``net/tcp/tcp_shutdown.c``
|
|
* Listener table (LISTEN semantics): ``net/tcp/tcp_listen.c``
|