You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/04/21 17:42:55 UTC

[nuttx] 02/02: tcp: add TCP_MAXSEG support

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

commit 93c3b8f19e0095493543105ce38148ce66588457
Author: zhanghongyu <zh...@xiaomi.com>
AuthorDate: Wed Mar 8 23:43:38 2023 +0800

    tcp: add TCP_MAXSEG support
    
    Signed-off-by: zhanghongyu <zh...@xiaomi.com>
---
 include/nuttx/net/tcp.h  |  6 ++++++
 net/socket/Kconfig       |  2 +-
 net/tcp/tcp.h            |  4 ++++
 net/tcp/tcp_conn.c       | 28 ++++++++++++++++++++++------
 net/tcp/tcp_getsockopt.c | 29 +++++++++++++++++++++--------
 net/tcp/tcp_input.c      |  7 +++++++
 net/tcp/tcp_send.c       | 11 ++++++++++-
 net/tcp/tcp_setsockopt.c | 38 ++++++++++++++++++++++++++++++--------
 8 files changed, 101 insertions(+), 24 deletions(-)

diff --git a/include/nuttx/net/tcp.h b/include/nuttx/net/tcp.h
index 83e4fa1b05..9a76af558a 100644
--- a/include/nuttx/net/tcp.h
+++ b/include/nuttx/net/tcp.h
@@ -137,6 +137,12 @@
 #define TCP_DEFAULT_IPv4_MSS  536
 #define TCP_DEFAULT_IPv6_MSS  1220
 
+/* Minimal accepted MSS. It is (60+60+8) - (20+20).
+ * (MAX_IP_HDR + MAX_TCP_HDR + MIN_IP_FRAG) - (MIN_IP_HDR + MIN_TCP_HDR)
+ */
+
+#define TCP_MIN_MSS           88
+
 /* However, we do need to make allowance for certain links such as SLIP that
  * have unusually small MTUs.
  */
diff --git a/net/socket/Kconfig b/net/socket/Kconfig
index 6877dc3d5f..ada47c6de8 100644
--- a/net/socket/Kconfig
+++ b/net/socket/Kconfig
@@ -43,7 +43,7 @@ config NET_SOCKOPTS
 		Enable or disable support for socket options
 
 config NET_TCPPROTO_OPTIONS
-	bool
+	bool "TCP proto socket options"
 	default n
 	---help---
 		Enable or disable support for TCP protocol level socket options.
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index aae70be268..c133882542 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -229,6 +229,10 @@ struct tcp_conn_s
   uint16_t rport;         /* The remoteTCP port, in network byte order */
   uint16_t mss;           /* Current maximum segment size for the
                            * connection */
+#ifdef CONFIG_NET_TCPPROTO_OPTIONS
+  uint16_t user_mss;      /* Configured maximum segment size for the
+                           * connection */
+#endif
   uint32_t rcv_adv;       /* The right edge of the recv window advertized */
 #ifdef CONFIG_NET_TCP_WINDOW_SCALE
   uint32_t snd_wnd;       /* Sequence and acknowledgement numbers of last
diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c
index 329196e5ad..b5658e019d 100644
--- a/net/tcp/tcp_conn.c
+++ b/net/tcp/tcp_conn.c
@@ -754,6 +754,28 @@ FAR struct tcp_conn_s *tcp_alloc(uint8_t domain)
 
       nxsem_init(&conn->snd_sem, 0, 0);
 #endif
+
+      /* Set the default value of mss to max, this field will changed when
+       * receive SYN.
+       */
+
+#ifdef CONFIG_NET_IPv4
+#ifdef CONFIG_NET_IPv6
+      if (domain == PF_INET)
+#endif
+        {
+          conn->mss = MIN_IPv4_TCP_INITIAL_MSS;
+        }
+#endif /* CONFIG_NET_IPv4 */
+
+#ifdef CONFIG_NET_IPv6
+#ifdef CONFIG_NET_IPv4
+      else
+#endif
+        {
+          conn->mss = MIN_IPv6_TCP_INITIAL_MSS;
+        }
+#endif /* CONFIG_NET_IPv6 */
     }
 
   return conn;
@@ -1293,9 +1315,6 @@ int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
       FAR const struct sockaddr_in *inaddr =
         (FAR const struct sockaddr_in *)addr;
 
-      /* Save MSS and the port from the sockaddr (already in network order) */
-
-      conn->mss    = MIN_IPv4_TCP_INITIAL_MSS;
       conn->rport  = inaddr->sin_port;
 
       /* The sockaddr address is 32-bits in network order.
@@ -1327,9 +1346,6 @@ int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
       FAR const struct sockaddr_in6 *inaddr =
         (FAR const struct sockaddr_in6 *)addr;
 
-      /* Save MSS and the port from the sockaddr (already in network order) */
-
-      conn->mss     = MIN_IPv6_TCP_INITIAL_MSS;
       conn->rport   = inaddr->sin6_port;
 
       /* The sockaddr address is 128-bits in network order.
diff --git a/net/tcp/tcp_getsockopt.c b/net/tcp/tcp_getsockopt.c
index 7d815f6a5f..c8d3d7142f 100644
--- a/net/tcp/tcp_getsockopt.c
+++ b/net/tcp/tcp_getsockopt.c
@@ -82,11 +82,6 @@
 int tcp_getsockopt(FAR struct socket *psock, int option,
                    FAR void *value, FAR socklen_t *value_len)
 {
-#ifdef CONFIG_NET_TCP_KEEPALIVE
-  /* Keep alive options are the only TCP protocol socket option currently
-   * supported.
-   */
-
   FAR struct tcp_conn_s *conn;
   int ret;
 
@@ -118,6 +113,7 @@ int tcp_getsockopt(FAR struct socket *psock, int option,
        * all of the clones that may use the underlying connection.
        */
 
+#ifdef CONFIG_NET_TCP_KEEPALIVE
       case SO_KEEPALIVE:  /* Verifies TCP connections active by enabling the
                            * periodic transmission of probes */
         if (*value_len < sizeof(int))
@@ -221,6 +217,26 @@ int tcp_getsockopt(FAR struct socket *psock, int option,
             ret              = OK;
           }
         break;
+#endif /* CONFIG_NET_TCP_KEEPALIVE */
+
+      case TCP_MAXSEG:   /* The maximum segment size */
+        if (*value_len < sizeof(int))
+          {
+            /* REVISIT: POSIX says that we should truncate the value if it
+             * is larger than value_len.   That just doesn't make sense
+             * to me in this case.
+             */
+
+            ret          = -EINVAL;
+          }
+        else
+          {
+            FAR int *mss = (FAR int *)value;
+            *mss         = conn->mss;
+            *value_len   = sizeof(int);
+            ret          = OK;
+          }
+        break;
 
       default:
         nerr("ERROR: Unrecognized TCP option: %d\n", option);
@@ -229,9 +245,6 @@ int tcp_getsockopt(FAR struct socket *psock, int option,
     }
 
   return ret;
-#else
-  return -ENOPROTOOPT;
-#endif /* CONFIG_NET_TCP_KEEPALIVE */
 }
 
 #endif /* CONFIG_NET_TCPPROTO_OPTIONS */
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index ce9fc4f018..06d21a07c3 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -586,6 +586,13 @@ static void tcp_parse_option(FAR struct net_driver_s *dev,
 
           tmp16 = ((uint16_t)IPDATA(tcpiplen + 2 + i) << 8) |
                    (uint16_t)IPDATA(tcpiplen + 3 + i);
+#ifdef CONFIG_NET_TCPPROTO_OPTIONS
+          if (conn->user_mss > 0 && conn->user_mss < tcp_mss)
+            {
+              tcp_mss = conn->user_mss;
+            }
+#endif
+
           conn->mss = tmp16 > tcp_mss ? tcp_mss : tmp16;
         }
 #ifdef CONFIG_NET_TCP_WINDOW_SCALE
diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c
index 416e0e5f21..edd6422c13 100644
--- a/net/tcp/tcp_send.c
+++ b/net/tcp/tcp_send.c
@@ -585,7 +585,16 @@ void tcp_synack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
 
   /* Set the packet length for the TCP Maximum Segment Size */
 
-  tcp_mss = tcp_rx_mss(dev);
+#ifdef CONFIG_NET_TCPPROTO_OPTIONS
+  if (conn->user_mss != 0 && conn->user_mss < tcp_rx_mss(dev))
+    {
+      tcp_mss = conn->user_mss;
+    }
+  else
+#endif
+    {
+      tcp_mss = tcp_rx_mss(dev);
+    }
 
   /* Save the ACK bits */
 
diff --git a/net/tcp/tcp_setsockopt.c b/net/tcp/tcp_setsockopt.c
index d447ea84b1..1123d71e92 100644
--- a/net/tcp/tcp_setsockopt.c
+++ b/net/tcp/tcp_setsockopt.c
@@ -72,11 +72,6 @@
 int tcp_setsockopt(FAR struct socket *psock, int option,
                    FAR const void *value, socklen_t value_len)
 {
-#ifdef CONFIG_NET_TCP_KEEPALIVE
-  /* Keep alive options are the only TCP protocol socket option currently
-   * supported.
-   */
-
   FAR struct tcp_conn_s *conn;
   int ret = OK;
 
@@ -107,6 +102,7 @@ int tcp_setsockopt(FAR struct socket *psock, int option,
        * all of the clones that may use the underlying connection.
        */
 
+#ifdef CONFIG_NET_TCP_KEEPALIVE
       case SO_KEEPALIVE: /* Verifies TCP connections active by enabling the
                           * periodic transmission of probes */
         if (value_len != sizeof(int))
@@ -233,6 +229,35 @@ int tcp_setsockopt(FAR struct socket *psock, int option,
               }
           }
         break;
+#endif /* CONFIG_NET_TCP_KEEPALIVE */
+
+      case TCP_MAXSEG: /* The maximum segment size */
+        if (value_len != sizeof(int))
+          {
+            ret = -EFAULT;
+          }
+        else
+          {
+            int mss = *(FAR int *)value;
+
+            if (conn->tcpstateflags != TCP_ALLOCATED)
+              {
+                /* Set TCP_MAXSEG in the wrong state, direct return success */
+
+                return OK;
+              }
+
+            if (mss < TCP_MIN_MSS || mss > UINT16_MAX)
+              {
+                nerr("ERROR: TCP_MAXSEG value out of range: %d\n", mss);
+                return -EINVAL;
+              }
+            else
+              {
+                conn->user_mss = mss;
+              }
+          }
+        break;
 
       default:
         nerr("ERROR: Unrecognized TCP option: %d\n", option);
@@ -241,9 +266,6 @@ int tcp_setsockopt(FAR struct socket *psock, int option,
     }
 
   return ret;
-#else
-  return -ENOPROTOOPT;
-#endif /* CONFIG_NET_TCP_KEEPALIVE */
 }
 
 #endif /* CONFIG_NET_TCPPROTO_OPTIONS */