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 2022/11/11 06:37:05 UTC

[incubator-nuttx] 04/07: net: select NAT external port by tcp_selectport for TCP

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/incubator-nuttx.git

commit f49810251266565c995ff173b6bd0189680b8ded
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Fri Oct 28 16:08:36 2022 +0800

    net: select NAT external port by tcp_selectport for TCP
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/nat/ipv4_nat.c       |   2 +-
 net/nat/ipv4_nat_entry.c |  97 +++++++++++++++++++--
 net/nat/nat.h            |   5 +-
 net/tcp/tcp.h            |  21 +++++
 net/tcp/tcp_conn.c       | 222 +++++++++++++++++++++++------------------------
 5 files changed, 225 insertions(+), 122 deletions(-)

diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index 12a4831585..9e57644b61 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -131,7 +131,7 @@ static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
   FAR struct tcp_hdr_s *tcp =
       (FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
   FAR struct ipv4_nat_entry *entry = ipv4_nat_outbound_entry_find(
-      IP_PROTO_TCP, net_ip4addr_conv32(ipv4->srcipaddr), tcp->srcport);
+      dev, IP_PROTO_TCP, net_ip4addr_conv32(ipv4->srcipaddr), tcp->srcport);
   if (!entry)
     {
       /* Outbound entry creation failed, should have corresponding entry. */
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index a5e4681111..b7f22d846e 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -31,6 +31,7 @@
 #include <nuttx/queue.h>
 
 #include "nat/nat.h"
+#include "tcp/tcp.h"
 
 #if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
 
@@ -44,6 +45,46 @@ static dq_queue_t g_entries;
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: ipv4_nat_select_port_without_stack
+ *
+ * Description:
+ *   Select an available port number for TCP/UDP protocol, or id for ICMP.
+ *   Used when corresponding stack is disabled.
+ *
+ * Input Parameters:
+ *   protocol   - The L4 protocol of the packet.
+ *   ip         - The IP bind with the port (in network byte order).
+ *   portno     - The local port (in network byte order), as reference.
+ *
+ * Returned Value:
+ *   port number on success; 0 on failure
+ *
+ ****************************************************************************/
+
+#if (defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_NO_STACK)) || \
+    (defined(CONFIG_NET_UDP) && defined(CONFIG_NET_UDP_NO_STACK)) || \
+    (defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_SOCKET))
+
+static uint16_t ipv4_nat_select_port_without_stack(
+    uint8_t protocol, in_addr_t ip, uint16_t portno)
+{
+  uint16_t hport = NTOHS(portno);
+  while (ipv4_nat_port_inuse(protocol, ip, portno))
+    {
+      if (++hport >= 32000) /* TODO: Why we limit to 32000 in net stack? */
+        {
+          hport = 4096;
+        }
+
+      portno = HTONS(hport);
+    }
+
+  return portno;
+}
+
+#endif
+
 /****************************************************************************
  * Name: ipv4_nat_select_port
  *
@@ -51,6 +92,7 @@ static dq_queue_t g_entries;
  *   Select an available port number for TCP/UDP protocol, or id for ICMP.
  *
  * Input Parameters:
+ *   dev        - The device on which the packet will be sent.
  *   protocol   - The L4 protocol of the packet.
  *   local_port - The local port of the packet, as reference.
  *
@@ -59,13 +101,51 @@ static dq_queue_t g_entries;
  *
  ****************************************************************************/
 
-static uint16_t ipv4_nat_select_port(uint8_t protocol, uint16_t local_port)
+static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev,
+                                     uint8_t protocol,
+                                     uint16_t local_port)
 {
-  /* TODO: Implement this, need to consider local ports and nat ports.
-   * TODO: Shall we let the chosen port same as local_port if possible?
-   */
+  switch (protocol)
+    {
+#ifdef CONFIG_NET_TCP
+      case IP_PROTO_TCP:
+        {
+#ifndef CONFIG_NET_TCP_NO_STACK
+          /* Try to select local_port first. */
+
+          int ret = tcp_selectport(PF_INET,
+                        (FAR const union ip_addr_u *)&dev->d_draddr,
+                        local_port);
+
+          /* If failed, try select another unused port. */
+
+          if (ret < 0)
+            {
+              ret = tcp_selectport(PF_INET,
+                        (FAR const union ip_addr_u *)&dev->d_draddr, 0);
+            }
+
+          return ret > 0 ? ret : 0;
+#else
+          return ipv4_nat_select_port_without_stack(IP_PROTO_TCP,
+                                                    dev->d_draddr,
+                                                    local_port);
+#endif
+        }
+#endif
+
+#ifdef CONFIG_NET_UDP
+#     warning Missing logic
+#endif
 
-# warning Missing logic
+#ifdef CONFIG_NET_ICMP
+#     warning Missing logic
+#endif
+    }
+
+  /* TODO: Currently select original port for unsupported protocol, maybe
+   * return zero to indicate failure.
+   */
 
   return local_port;
 }
@@ -237,6 +317,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
  *   entry does not exist.
  *
  * Input Parameters:
+ *   dev        - The device on which the packet will be sent.
  *   protocol   - The L4 protocol of the packet.
  *   local_ip   - The local ip of the packet.
  *   local_port - The local port of the packet.
@@ -247,8 +328,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
  ****************************************************************************/
 
 FAR struct ipv4_nat_entry *
-ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
-                             uint16_t local_port)
+ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
+                             in_addr_t local_ip, uint16_t local_port)
 {
   FAR sq_entry_t *p;
   FAR sq_entry_t *tmp;
@@ -283,7 +364,7 @@ ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
         "proto=%d, local=%x:%d, try creating one.\n",
         protocol, local_ip, local_port);
 
-  uint16_t external_port = ipv4_nat_select_port(protocol, local_port);
+  uint16_t external_port = ipv4_nat_select_port(dev, protocol, local_port);
   if (!external_port)
     {
       nwarn("WARNING: Failed to find an available port!\n");
diff --git a/net/nat/nat.h b/net/nat/nat.h
index a68d0af4db..70e2887a2b 100644
--- a/net/nat/nat.h
+++ b/net/nat/nat.h
@@ -204,6 +204,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
  *   entry does not exist.
  *
  * Input Parameters:
+ *   dev        - The device on which the packet will be sent.
  *   protocol   - The L4 protocol of the packet.
  *   local_ip   - The local ip of the packet.
  *   local_port - The local port of the packet.
@@ -214,8 +215,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
  ****************************************************************************/
 
 FAR struct ipv4_nat_entry *
-ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
-                             uint16_t local_port);
+ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
+                             in_addr_t local_ip, uint16_t local_port);
 
 #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
 #endif /* __NET_NAT_NAT_H */
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index 891c8fa856..a7af8d3cec 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -552,6 +552,27 @@ int tcp_remote_ipv6_device(FAR struct tcp_conn_s *conn);
 FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
                                         FAR struct tcp_hdr_s *tcp);
 
+/****************************************************************************
+ * Name: tcp_selectport
+ *
+ * Description:
+ *   If the port number is zero; select an unused port for the connection.
+ *   If the port number is non-zero, verify that no other connection has
+ *   been created with this port number.
+ *
+ * Returned Value:
+ *   Selected or verified port number in network order on success, a negated
+ *   errno on failure.
+ *
+ * Assumptions:
+ *   Interrupts are disabled
+ *
+ ****************************************************************************/
+
+int tcp_selectport(uint8_t domain,
+                   FAR const union ip_addr_u *ipaddr,
+                   uint16_t portno);
+
 /****************************************************************************
  * Name: tcp_bind
  *
diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c
index a4719cec59..ab42955afe 100644
--- a/net/tcp/tcp_conn.c
+++ b/net/tcp/tcp_conn.c
@@ -165,117 +165,6 @@ static FAR struct tcp_conn_s *
   return NULL;
 }
 
-/****************************************************************************
- * Name: tcp_selectport
- *
- * Description:
- *   If the port number is zero; select an unused port for the connection.
- *   If the port number is non-zero, verify that no other connection has
- *   been created with this port number.
- *
- * Input Parameters:
- *   portno -- the selected port number in network order. Zero means no port
- *     selected.
- *
- * Returned Value:
- *   Selected or verified port number in network order on success, a negated
- *   errno on failure:
- *
- *   EADDRINUSE
- *     The given address is already in use.
- *   EADDRNOTAVAIL
- *     Cannot assign requested address (unlikely)
- *
- * Assumptions:
- *   Interrupts are disabled
- *
- ****************************************************************************/
-
-static int tcp_selectport(uint8_t domain,
-                          FAR const union ip_addr_u *ipaddr,
-                          uint16_t portno)
-{
-  static uint16_t g_last_tcp_port;
-  ssize_t ret;
-
-  /* Generate port base dynamically */
-
-  if (g_last_tcp_port == 0)
-    {
-      ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), 0);
-      if (ret < 0)
-        {
-          ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), GRND_RANDOM);
-        }
-
-      if (ret != sizeof(uint16_t))
-        {
-          g_last_tcp_port = clock_systime_ticks() % 32000;
-        }
-      else
-        {
-          g_last_tcp_port = g_last_tcp_port % 32000;
-        }
-
-      if (g_last_tcp_port < 4096)
-        {
-          g_last_tcp_port += 4096;
-        }
-    }
-
-  if (portno == 0)
-    {
-      /* No local port assigned. Loop until we find a valid listen port
-       * number that is not being used by any other connection. NOTE the
-       * following loop is assumed to terminate but could not if all
-       * 32000-4096+1 ports are in used (unlikely).
-       */
-
-      do
-        {
-          /* Guess that the next available port number will be the one after
-           * the last port number assigned. Make sure that the port number
-           * is within range.
-           */
-
-          if (++g_last_tcp_port >= 32000)
-            {
-              g_last_tcp_port = 4096;
-            }
-
-          portno = HTONS(g_last_tcp_port);
-        }
-      while (tcp_listener(domain, ipaddr, portno)
-#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
-             || (domain == PF_INET &&
-                 ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno))
-#endif
-      );
-    }
-  else
-    {
-      /* A port number has been supplied.  Verify that no other TCP/IP
-       * connection is using this local port.
-       */
-
-      if (tcp_listener(domain, ipaddr, portno)
-#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
-          || (domain == PF_INET &&
-              ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno))
-#endif
-      )
-        {
-          /* It is in use... return EADDRINUSE */
-
-          return -EADDRINUSE;
-        }
-    }
-
-  /* Return the selected or verified port number (host byte order) */
-
-  return portno;
-}
-
 /****************************************************************************
  * Name: tcp_ipv4_active
  *
@@ -586,6 +475,117 @@ FAR struct tcp_conn_s *tcp_alloc_conn(void)
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: tcp_selectport
+ *
+ * Description:
+ *   If the port number is zero; select an unused port for the connection.
+ *   If the port number is non-zero, verify that no other connection has
+ *   been created with this port number.
+ *
+ * Input Parameters:
+ *   portno -- the selected port number in network order. Zero means no port
+ *     selected.
+ *
+ * Returned Value:
+ *   Selected or verified port number in network order on success, a negated
+ *   errno on failure:
+ *
+ *   EADDRINUSE
+ *     The given address is already in use.
+ *   EADDRNOTAVAIL
+ *     Cannot assign requested address (unlikely)
+ *
+ * Assumptions:
+ *   Interrupts are disabled
+ *
+ ****************************************************************************/
+
+int tcp_selectport(uint8_t domain,
+                   FAR const union ip_addr_u *ipaddr,
+                   uint16_t portno)
+{
+  static uint16_t g_last_tcp_port;
+  ssize_t ret;
+
+  /* Generate port base dynamically */
+
+  if (g_last_tcp_port == 0)
+    {
+      ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), 0);
+      if (ret < 0)
+        {
+          ret = getrandom(&g_last_tcp_port, sizeof(uint16_t), GRND_RANDOM);
+        }
+
+      if (ret != sizeof(uint16_t))
+        {
+          g_last_tcp_port = clock_systime_ticks() % 32000;
+        }
+      else
+        {
+          g_last_tcp_port = g_last_tcp_port % 32000;
+        }
+
+      if (g_last_tcp_port < 4096)
+        {
+          g_last_tcp_port += 4096;
+        }
+    }
+
+  if (portno == 0)
+    {
+      /* No local port assigned. Loop until we find a valid listen port
+       * number that is not being used by any other connection. NOTE the
+       * following loop is assumed to terminate but could not if all
+       * 32000-4096+1 ports are in used (unlikely).
+       */
+
+      do
+        {
+          /* Guess that the next available port number will be the one after
+           * the last port number assigned. Make sure that the port number
+           * is within range.
+           */
+
+          if (++g_last_tcp_port >= 32000)
+            {
+              g_last_tcp_port = 4096;
+            }
+
+          portno = HTONS(g_last_tcp_port);
+        }
+      while (tcp_listener(domain, ipaddr, portno)
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+             || (domain == PF_INET &&
+                 ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno))
+#endif
+      );
+    }
+  else
+    {
+      /* A port number has been supplied.  Verify that no other TCP/IP
+       * connection is using this local port.
+       */
+
+      if (tcp_listener(domain, ipaddr, portno)
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+          || (domain == PF_INET &&
+              ipv4_nat_port_inuse(IP_PROTO_TCP, ipaddr->ipv4, portno))
+#endif
+      )
+        {
+          /* It is in use... return EADDRINUSE */
+
+          return -EADDRINUSE;
+        }
+    }
+
+  /* Return the selected or verified port number (host byte order) */
+
+  return portno;
+}
+
 /****************************************************************************
  * Name: tcp_initialize
  *