This is an automated email from the ASF dual-hosted git repository.

acassis 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 2ce31c442f net/tcp:Added tcp zero window probe timer support
2ce31c442f is described below

commit 2ce31c442f2fa1309235026ead3d8fdbafb72de7
Author: wangyingdong <[email protected]>
AuthorDate: Tue Jun 20 19:43:37 2023 +0800

    net/tcp:Added tcp zero window probe timer support
    
    https://www.rfc-editor.org/rfc/rfc1122#page-92
    
    Signed-off-by: wangyingdong <[email protected]>
---
 net/tcp/tcp.h               |  27 +++++++-
 net/tcp/tcp_input.c         |  36 +++++++++++
 net/tcp/tcp_send_buffered.c |   4 ++
 net/tcp/tcp_timer.c         | 147 +++++++++++++++++++++++++++++++++++++-------
 4 files changed, 192 insertions(+), 22 deletions(-)

diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index 2907f63bd3..7398084f06 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -126,6 +126,9 @@
 
 #define TCP_FAST_RETRANSMISSION_THRESH 3
 
+#define TCP_RTO_MAX 240 /* 120s,The unit is half a second */
+#define TCP_RTO_MIN 1   /* 0.5s */
+
 /****************************************************************************
  * Public Type Definitions
  ****************************************************************************/
@@ -355,6 +358,7 @@ struct tcp_conn_s
 #if defined(CONFIG_NET_SENDFILE) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
   bool       sendfile;    /* True if sendfile operation is in progress */
 #endif
+  bool       zero_probe;   /* TCP zero window probe timer */
 
   /* connevents is a list of callbacks for each socket the uses this
    * connection (there can be more that one in the event that the the socket
@@ -760,7 +764,7 @@ void tcp_stop_monitor(FAR struct tcp_conn_s *conn, uint16_t 
flags);
  * Input Parameters:
  *   conn  - The TCP connection of interest
  *   cb    - devif callback structure
- *   flags - Set of connection events events
+ *   flags - Set of connection events
  *
  * Returned Value:
  *   None
@@ -2304,6 +2308,27 @@ void tcp_cc_recv_ack(FAR struct tcp_conn_s *conn, FAR 
struct tcp_hdr_s *tcp);
 }
 #endif
 
+/****************************************************************************
+ * Name: tcp_set_zero_probe
+ *
+ * Description:
+ *   Update the TCP probe timer for the provided TCP connection,
+ *   The timeout is accurate
+ *
+ * Input Parameters:
+ *   conn    - The TCP "connection" to poll for TX data
+ *   flags   - Set of connection events
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   conn is not NULL.
+ *
+ ****************************************************************************/
+
+void tcp_set_zero_probe(FAR struct tcp_conn_s *conn, uint16_t flags);
+
 #endif /* !CONFIG_NET_TCP_NO_STACK */
 #endif /* CONFIG_NET_TCP */
 #endif /* __NET_TCP_TCP_H */
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index dd34cd369c..dcc16d9b07 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -633,6 +633,40 @@ static void tcp_parse_option(FAR struct net_driver_s *dev,
     }
 }
 
+/****************************************************************************
+ * Name: tcp_clear_zero_probe
+ *
+ * Description:
+ *   clear the TCP zero window probe
+ *
+ * Input Parameters:
+ *   conn   - The TCP connection of interest
+ *   tcp    - Header of TCP structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static void tcp_clear_zero_probe(FAR struct tcp_conn_s *conn,
+                                 FAR struct tcp_hdr_s *tcp)
+{
+  /* If the receive window is not 0,
+   * the zero window probe timer needs to be cleared
+   */
+
+  if ((tcp->wnd[0] || tcp->wnd[1]) && conn->zero_probe &&
+      (tcp->flags & TCP_ACK) != 0)
+    {
+      conn->zero_probe = false;
+      conn->nrtx = 0;
+      conn->timer = 0;
+    }
+}
+
 /****************************************************************************
  * Name: tcp_input
  *
@@ -1155,6 +1189,8 @@ found:
       tcp_update_retrantimer(conn, conn->rto);
     }
 
+  tcp_clear_zero_probe(conn, tcp);
+
   /* Update the connection's window size */
 
   if ((tcp->flags & TCP_ACK) != 0 &&
diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c
index f911831a28..4b6f4ba53b 100644
--- a/net/tcp/tcp_send_buffered.c
+++ b/net/tcp/tcp_send_buffered.c
@@ -1119,6 +1119,10 @@ static uint16_t psock_send_eventhandler(FAR struct 
net_driver_s *dev,
           flags &= ~TCP_POLL;
         }
     }
+  else
+    {
+      tcp_set_zero_probe(conn, flags);
+    }
 
   /* Continue waiting */
 
diff --git a/net/tcp/tcp_timer.c b/net/tcp/tcp_timer.c
index 591c884e12..c0b774c043 100644
--- a/net/tcp/tcp_timer.c
+++ b/net/tcp/tcp_timer.c
@@ -155,6 +155,50 @@ static void tcp_timer_expiry(FAR void *arg)
   net_unlock();
 }
 
+/****************************************************************************
+ * Name: tcp_xmit_probe
+ *
+ * Description:
+ *   TCP retransmission probe packet
+ *
+ * Input Parameters:
+ *   dev    - The device driver structure to use in the send operation
+ *   conn   - The TCP "connection" to poll for TX data
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   dev is not NULL.
+ *   conn is not NULL.
+ *
+ ****************************************************************************/
+
+static void tcp_xmit_probe(FAR struct net_driver_s *dev,
+                           FAR struct tcp_conn_s *conn)
+{
+  /* And send the probe.
+   * The packet we send must have these properties:
+   *
+   *   - TCP_ACK flag (only) is set.
+   *   - Sequence number is the sequence number of
+   *     previously ACKed data, i.e., the expected
+   *     sequence number minus one.
+   *
+   * tcp_send() will send the TCP sequence number as
+   * conn->sndseq.  Rather than creating a new
+   * interface, we spoof tcp_end() here:
+   */
+
+  uint16_t hdrlen = tcpip_hdrsize(conn);
+  uint32_t saveseq = tcp_getsequence(conn->sndseq);
+  tcp_setsequence(conn->sndseq, saveseq - 1);
+
+  tcp_send(dev, conn, TCP_ACK, hdrlen);
+
+  tcp_setsequence(conn->sndseq, saveseq);
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -292,6 +336,40 @@ void tcp_stop_timer(FAR struct tcp_conn_s *conn)
   work_cancel(LPWORK, &conn->work);
 }
 
+/****************************************************************************
+ * Name: tcp_set_zero_probe
+ *
+ * Description:
+ *   Update the TCP probe timer for the provided TCP connection,
+ *   The timeout is accurate
+ *
+ * Input Parameters:
+ *   conn   - The TCP "connection" to poll for TX data
+ *   flags  - Set of connection events
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   conn is not NULL.
+ *
+ ****************************************************************************/
+
+void tcp_set_zero_probe(FAR struct tcp_conn_s *conn, uint16_t flags)
+{
+  if ((conn->tcpstateflags & TCP_ESTABLISHED) &&
+      ((flags & TCP_NEWDATA) == 0) && conn->tx_unacked <= 0 &&
+      (flags & (TCP_POLL | TCP_REXMIT | TCP_ACKDATA)) &&
+#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
+      !(sq_empty(&conn->write_q)) &&
+#endif
+      !conn->timeout && !conn->zero_probe)
+    {
+      tcp_update_retrantimer(conn, TCP_RTO_MIN);
+      conn->zero_probe = true;
+    }
+}
+
 /****************************************************************************
  * Name: tcp_timer
  *
@@ -616,8 +694,6 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
 
           if (conn->keepalive)
             {
-              uint32_t saveseq;
-
               /* Yes... has the idle period elapsed with no data or ACK
                * received from the remote peer?
                */
@@ -643,25 +719,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
                     }
                   else
                     {
-                      /* And send the probe.
-                       * The packet we send must have these properties:
-                       *
-                       *   - TCP_ACK flag (only) is set.
-                       *   - Sequence number is the sequence number of
-                       *     previously ACKed data, i.e., the expected
-                       *     sequence number minus one.
-                       *
-                       * tcp_send() will send the TCP sequence number as
-                       * conn->sndseq.  Rather than creating a new
-                       * interface, we spoof tcp_end() here:
-                       */
-
-                      saveseq = tcp_getsequence(conn->sndseq);
-                      tcp_setsequence(conn->sndseq, saveseq - 1);
-
-                      tcp_send(dev, conn, TCP_ACK, hdrlen);
-
-                      tcp_setsequence(conn->sndseq, saveseq);
+                      tcp_xmit_probe(dev, conn);
 
 #ifdef CONFIG_NET_TCP_WRITE_BUFFERS
                       /* Increment the un-ACKed sequence number */
@@ -679,6 +737,53 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
             }
 #endif
 
+          /* Is this an established connected with
+           * Zero window probe enabled?
+           */
+
+          if (conn->zero_probe)
+            {
+              if (conn->timer > hsec)
+                {
+                  /* Will not yet decrement to zero */
+
+                  conn->timer -= hsec;
+                }
+              else
+                {
+                  /* Yes.. Has the retry count expired? */
+
+                  if (conn->nrtx >= TCP_MAXRTX)
+                    {
+                      /* Yes... stop the network monitor, closing the
+                       * connection and all sockets associated with the
+                       * connection.
+                       */
+
+                      conn->zero_probe = false;
+                      tcp_stop_monitor(conn, TCP_ABORT);
+                    }
+                  else
+                    {
+                      tcp_xmit_probe(dev, conn);
+
+#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
+                      /* Increment the un-ACKed sequence number */
+
+                      conn->sndseq_max++;
+#endif
+
+                      /* Update for the next probe */
+
+                      conn->nrtx++;
+                      conn->timer = MIN((TCP_RTO_MIN << conn->nrtx),
+                                        TCP_RTO_MAX);
+                    }
+
+                  goto done;
+                }
+            }
+
 #ifdef CONFIG_NET_TCP_DELAYED_ACK
           /* Handle delayed acknowledgments.  Is there a segment with a
            * delayed acknowledgment?

Reply via email to