This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 746d68916f9 net/tcp: add support for the CLOSE_WAIT state
746d68916f9 is described below
commit 746d68916f94dbcb775bb300ad100bea4b1b9a9a
Author: zhanghongyu <[email protected]>
AuthorDate: Wed Aug 6 21:40:47 2025 +0800
net/tcp: add support for the CLOSE_WAIT state
CLOSE-WAIT - represents waiting for a connection termination request
from the local user.
TCP A TCP B
1. ESTABLISHED ESTABLISHED
2. (Close)
FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> --> CLOSE-WAIT
3. FIN-WAIT-2 <-- <SEQ=300><ACK=101><CTL=ACK> <-- CLOSE-WAIT
4. (Close)
TIME-WAIT <-- <SEQ=300><ACK=101><CTL=FIN,ACK> <-- LAST-ACK
5. TIME-WAIT --> <SEQ=101><ACK=301><CTL=ACK> --> CLOSED
6. (2 MSL)
CLOSED
in the current state, we can continue to send data until the user
calls shutdown or close, then directly enter the TCP_LAST_ACK state
Signed-off-by: zhanghongyu <[email protected]>
---
Documentation/components/net/index.rst | 1 +
Documentation/components/net/tcp_state_machine.rst | 327 +++++++++++++++++++++
include/nuttx/net/tcp.h | 7 +-
net/tcp/tcp_appsend.c | 5 +-
net/tcp/tcp_close.c | 6 +-
net/tcp/tcp_devpoll.c | 3 +-
net/tcp/tcp_input.c | 66 +++--
net/tcp/tcp_send_buffered.c | 10 +-
net/tcp/tcp_shutdown.c | 3 +-
net/tcp/tcp_timer.c | 4 +-
10 files changed, 395 insertions(+), 37 deletions(-)
diff --git a/Documentation/components/net/index.rst
b/Documentation/components/net/index.rst
index 148880d8982..f1cd6010832 100644
--- a/Documentation/components/net/index.rst
+++ b/Documentation/components/net/index.rst
@@ -19,6 +19,7 @@ Network Support
wqueuedeadlocks.rst
tcp_network_perf.rst
delay_act_and_tcp_perf.rst
+ tcp_state_machine.rst
``net`` Directory Structure ::
diff --git a/Documentation/components/net/tcp_state_machine.rst
b/Documentation/components/net/tcp_state_machine.rst
new file mode 100644
index 00000000000..da4b6156136
--- /dev/null
+++ b/Documentation/components/net/tcp_state_machine.rst
@@ -0,0 +1,327 @@
+=============================
+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). However, it is
currently unable to continue receiving data in this state
+ * - FIN-WAIT-2
+ - ``TCP_FIN_WAIT_2``
+ - Yes
+ - Entered after ACK for local FIN (when peer hasn't closed
yet). However, it is currently unable to continue receiving data in this state
+ * - 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:
+
+ * Current behavior is to send a RST and force ``TCP_CLOSED``.
+ * The implementation notes this as a TODO to improve shutdown behavior.
+
+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:
+
+ * Current behavior is to send a RST and force ``TCP_CLOSED``.
+
+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.
+* FIN_WAIT_* data handling is currently strict: received payload data in
+ FIN_WAIT_1/2 results in sending RST and closing the connection.
+* 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``
diff --git a/include/nuttx/net/tcp.h b/include/nuttx/net/tcp.h
index 5d921d6deb2..5804b19f5a0 100644
--- a/include/nuttx/net/tcp.h
+++ b/include/nuttx/net/tcp.h
@@ -88,9 +88,10 @@
# define TCP_ESTABLISHED 0x04
# define TCP_FIN_WAIT_1 0x05
# define TCP_FIN_WAIT_2 0x06
-# define TCP_CLOSING 0x07
-# define TCP_TIME_WAIT 0x08
-# define TCP_LAST_ACK 0x09
+# define TCP_CLOSE_WAIT 0x07
+# define TCP_CLOSING 0x08
+# define TCP_TIME_WAIT 0x09
+# define TCP_LAST_ACK 0x0a
# define TCP_STOPPED 0x10 /* Bit 4: stopped */
/* Bit 5-7: Unused, but not available */
diff --git a/net/tcp/tcp_appsend.c b/net/tcp/tcp_appsend.c
index 97b1cf16d7a..bf1f2e6d6a8 100644
--- a/net/tcp/tcp_appsend.c
+++ b/net/tcp/tcp_appsend.c
@@ -194,13 +194,14 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct
tcp_conn_s *conn,
else if ((result & TCP_CLOSE) != 0)
{
- conn->tcpstateflags = TCP_FIN_WAIT_1;
+ conn->tcpstateflags = conn->tcpstateflags == TCP_CLOSE_WAIT ?
+ TCP_LAST_ACK : TCP_FIN_WAIT_1;
conn->tx_unacked = 1;
conn->nrtx = 0;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->sndseq_max = tcp_getsequence(conn->sndseq) + 1;
#endif
- ninfo("TCP state: TCP_FIN_WAIT_1\n");
+ ninfo("TCP state: %d\n", conn->tcpstateflags);
dev->d_sndlen = 0;
tcp_send(dev, conn, TCP_FIN | TCP_ACK, hdrlen);
diff --git a/net/tcp/tcp_close.c b/net/tcp/tcp_close.c
index d392d684b5d..d91e2b1227c 100644
--- a/net/tcp/tcp_close.c
+++ b/net/tcp/tcp_close.c
@@ -153,7 +153,8 @@ static uint16_t tcp_close_eventhandler(FAR struct
net_driver_s *dev,
* TCP_CLOSE is handled above.
*/
- DEBUGASSERT(conn->tcpstateflags == TCP_ESTABLISHED);
+ DEBUGASSERT(conn->tcpstateflags == TCP_ESTABLISHED ||
+ conn->tcpstateflags == TCP_CLOSE_WAIT);
/* Drop data received in this state and make sure that TCP_CLOSE
* is set in the response
@@ -236,7 +237,8 @@ static inline int tcp_close_disconnect(FAR struct socket
*psock)
*/
if ((conn->tcpstateflags == TCP_ESTABLISHED ||
- conn->tcpstateflags == TCP_LAST_ACK) &&
+ conn->tcpstateflags == TCP_LAST_ACK ||
+ conn->tcpstateflags == TCP_CLOSE_WAIT) &&
(conn->clscb = tcp_callback_alloc(conn)) != NULL)
{
/* Free rx buffers of the connection immediately */
diff --git a/net/tcp/tcp_devpoll.c b/net/tcp/tcp_devpoll.c
index 741b13ff72c..b3e37dd4b5c 100644
--- a/net/tcp/tcp_devpoll.c
+++ b/net/tcp/tcp_devpoll.c
@@ -109,7 +109,8 @@ void tcp_poll(FAR struct net_driver_s *dev, FAR struct
tcp_conn_s *conn)
/* Verify that the connection is established. */
- if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
+ if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED ||
+ (conn->tcpstateflags & TCP_STATE_MASK) == TCP_CLOSE_WAIT)
{
/* Set up for the callback. We can't know in advance if the
* application is going to send a IPv4 or an IPv6 packet, so this
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index 1bb3378d258..07780467135 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -982,8 +982,7 @@ found:
g_netstats.tcp.drop, seq, TCP_SEQ_ADD(seq, dev->d_len),
dev->d_len);
- dev->d_len = 0;
- return;
+ goto drop;
}
}
#endif
@@ -1046,7 +1045,8 @@ found:
* bytes
*/
- if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
+ if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED ||
+ (conn->tcpstateflags & TCP_STATE_MASK) == TCP_CLOSE_WAIT)
{
nwarn("WARNING: ackseq > unackseq\n");
nwarn("sndseq=%" PRIu32 " tx_unacked=%" PRIu32
@@ -1109,7 +1109,8 @@ found:
/* Check if no packet need to retransmission, clear timer. */
- if (conn->tx_unacked == 0 && conn->tcpstateflags == TCP_ESTABLISHED)
+ if (conn->tx_unacked == 0 && (conn->tcpstateflags == TCP_ESTABLISHED ||
+ conn->tcpstateflags == TCP_CLOSE_WAIT))
{
timeout = 0;
}
@@ -1397,8 +1398,6 @@ found:
* has been closed.
*/
- flags |= TCP_CLOSE;
-
if (dev->d_len > 0)
{
flags |= TCP_NEWDATA;
@@ -1406,23 +1405,10 @@ found:
result = tcp_callback(dev, conn, flags);
- if ((result & TCP_CLOSE) != 0)
- {
- conn->tcpstateflags = TCP_LAST_ACK;
- conn->tx_unacked = 1;
- conn->nrtx = 0;
- net_incr32(conn->rcvseq, 1); /* ack FIN */
-#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
- conn->sndseq_max = tcp_getsequence(conn->sndseq) + 1;
-#endif
- ninfo("TCP state: TCP_LAST_ACK\n");
- tcp_send(dev, conn, TCP_FIN | TCP_ACK, tcpiplen);
- }
- else
- {
- ninfo("TCP: Dropped a FIN\n");
- tcp_appsend(dev, conn, result);
- }
+ conn->tcpstateflags = TCP_CLOSE_WAIT;
+ net_incr32(conn->rcvseq, 1); /* ack FIN */
+ ninfo("TCP state: TCP_CLOSE_WAIT\n");
+ tcp_appsend(dev, conn, result | TCP_SNDACK);
return;
}
@@ -1681,6 +1667,40 @@ found:
ninfo("TCP state: TCP_TIME_WAIT\n");
}
+ goto drop;
+
+ case TCP_CLOSE_WAIT:
+#ifdef CONFIG_NET_TCP_KEEPALIVE
+ /* If the established socket receives an ACK or any kind of data
+ * from the remote peer (whether we accept it or not), then reset
+ * the keep alive timer.
+ */
+
+ if (conn->keepalive && (tcp->flags & TCP_ACK) != 0)
+ {
+ /* Reset the "alive" timer. */
+
+ tcp_update_keeptimer(conn, conn->keepidle);
+ conn->keepretries = 0;
+ }
+#endif
+
+ if ((flags & TCP_ACKDATA) != 0)
+ {
+ dev->d_sndlen = 0;
+
+ /* Provide the packet to the application */
+
+ result = tcp_callback(dev, conn, flags);
+
+ /* Send the response, ACKing the data or not, as appropriate */
+
+ tcp_appsend(dev, conn, result);
+ return;
+ }
+
+ goto drop;
+
default:
break;
}
diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c
index 1e47081aefb..bbf1fa6957f 100644
--- a/net/tcp/tcp_send_buffered.c
+++ b/net/tcp/tcp_send_buffered.c
@@ -197,8 +197,8 @@ static void retransmit_segment(FAR struct tcp_conn_s *conn,
* retransmitted, and un-ACKed, if expired is not zero, the
* connection will be closed.
*
- * field expired can only be updated at TCP_ESTABLISHED
- * state
+ * field expired can only be updated at TCP_ESTABLISHED and
+ * TCP_CLOSE_WAIT state.
*/
conn->expired++;
@@ -936,7 +936,8 @@ static uint16_t psock_send_eventhandler(FAR struct
net_driver_s *dev,
* retransmitted, and un-ACKed, if expired is not zero, the
* connection will be closed.
*
- * field expired can only be updated at TCP_ESTABLISHED state
+ * field expired can only be updated at TCP_ESTABLISHED and
+ * TCP_CLOSE_WAIT state.
*/
conn->expired++;
@@ -1012,7 +1013,8 @@ static uint16_t psock_send_eventhandler(FAR struct
net_driver_s *dev,
* will have to wait for the next polling cycle.
*/
- if ((conn->tcpstateflags & TCP_ESTABLISHED) &&
+ if ((conn->tcpstateflags & TCP_ESTABLISHED ||
+ conn->tcpstateflags & TCP_CLOSE_WAIT) &&
((flags & TCP_NEWDATA) == 0) &&
(flags & (TCP_POLL | TCP_REXMIT | TCP_ACKDATA)) &&
!(sq_empty(&conn->write_q)) &&
diff --git a/net/tcp/tcp_shutdown.c b/net/tcp/tcp_shutdown.c
index 2f18a9814f2..840a2160eef 100644
--- a/net/tcp/tcp_shutdown.c
+++ b/net/tcp/tcp_shutdown.c
@@ -112,7 +112,8 @@ static inline int tcp_send_fin(FAR struct socket *psock)
if ((conn->tcpstateflags == TCP_ESTABLISHED ||
conn->tcpstateflags == TCP_SYN_SENT ||
- conn->tcpstateflags == TCP_SYN_RCVD))
+ conn->tcpstateflags == TCP_SYN_RCVD ||
+ conn->tcpstateflags == TCP_CLOSE_WAIT))
{
if ((conn->shdcb = tcp_callback_alloc(conn)) == NULL)
{
diff --git a/net/tcp/tcp_timer.c b/net/tcp/tcp_timer.c
index d178e2f51bc..76715c0471c 100644
--- a/net/tcp/tcp_timer.c
+++ b/net/tcp/tcp_timer.c
@@ -623,6 +623,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct
tcp_conn_s *conn)
goto done;
case TCP_ESTABLISHED:
+ case TCP_CLOSE_WAIT:
/* In the ESTABLISHED state, we call upon the application
* to do the actual retransmit after which we jump into
@@ -673,7 +674,8 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct
tcp_conn_s *conn)
* connection has been established.
*/
- else if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
+ else if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED ||
+ (conn->tcpstateflags & TCP_STATE_MASK) == TCP_CLOSE_WAIT)
{
#ifdef CONFIG_NET_TCP_KEEPALIVE
/* Is this an established connected with KeepAlive enabled? */