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:07 UTC

[incubator-nuttx] 06/07: net/nat: Add UDP 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/incubator-nuttx.git

commit a3a669a5f6bbd19b08d6a9e6183238a350040271
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Thu Nov 3 14:26:21 2022 +0800

    net/nat: Add UDP support
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/nat/Kconfig          |  11 +++++
 net/nat/ipv4_nat.c       | 126 ++++++++++++++++++++++++++++++++++++++++++++++-
 net/nat/ipv4_nat_entry.c |  23 ++++++++-
 net/udp/udp_conn.c       |  17 ++++++-
 4 files changed, 171 insertions(+), 6 deletions(-)

diff --git a/net/nat/Kconfig b/net/nat/Kconfig
index baa961f775..9ca48a0382 100644
--- a/net/nat/Kconfig
+++ b/net/nat/Kconfig
@@ -20,6 +20,17 @@ config NET_NAT_TCP_EXPIRE_SEC
 		Note: The default value 86400 is suggested by RFC2663, Section 2.6,
 		Page 5.
 
+config NET_NAT_UDP_EXPIRE_SEC
+	int "UDP NAT entry expiration seconds"
+	default 240
+	depends on NET_NAT
+	---help---
+		The expiration time for idle UDP entry in NAT.
+
+		Note: RFC2663 (Section 2.6, Page 5) suggests that non-TCP sessions
+		that have not been used for a couple of minutes can be assumed to be
+		terminated.
+
 config NET_NAT_ICMP_EXPIRE_SEC
 	int "ICMP NAT entry expiration seconds"
 	default 60
diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index b24d60e73a..5cbdd73d21 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -31,6 +31,7 @@
 
 #include <nuttx/net/icmp.h>
 #include <nuttx/net/tcp.h>
+#include <nuttx/net/udp.h>
 
 #include "nat/nat.h"
 #include "utils/utils.h"
@@ -103,6 +104,65 @@ static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4)
 }
 #endif
 
+/****************************************************************************
+ * Name: ipv4_nat_inbound_udp
+ *
+ * Description:
+ *   Check if a received UDP packet belongs to a NAT entry. If so, translate
+ *   it.
+ *
+ * Input Parameters:
+ *   ipv4  - Points to the IPv4 header with dev->d_buf.
+ *
+ * Returned Value:
+ *   Zero is returned if NAT is successfully applied, or is not enabled for
+ *   this packet;
+ *   A negated errno value is returned if error occured.
+ *
+ * Assumptions:
+ *   Packet is received on NAT device and is targeting at the address
+ *   assigned to the device.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_UDP
+static int ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *ipv4)
+{
+  uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
+  FAR struct udp_hdr_s *udp =
+      (FAR struct udp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
+  FAR struct ipv4_nat_entry *entry =
+      ipv4_nat_inbound_entry_find(IP_PROTO_UDP, udp->destport, true);
+  if (!entry)
+    {
+      /* Inbound without entry is OK (e.g. towards NuttX itself), skip NAT. */
+
+      return OK;
+    }
+
+  /* Modify port and checksum. */
+
+  if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */
+    {
+      chksum_adjust(udp->udpchksum, udp->destport, entry->local_port);
+    }
+
+  udp->destport = entry->local_port;
+
+  /* Modify address and checksum. */
+
+  if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */
+    {
+      chksum_adjust(udp->udpchksum, ipv4->destipaddr, entry->local_ip);
+    }
+
+  chksum_adjust(ipv4->ipchksum, ipv4->destipaddr, entry->local_ip);
+  net_ipv4addr_hdrcopy(ipv4->destipaddr, &entry->local_ip);
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Name: ipv4_nat_inbound_icmp
  *
@@ -213,6 +273,66 @@ static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
 }
 #endif
 
+/****************************************************************************
+ * Name: ipv4_nat_outbound_udp
+ *
+ * Description:
+ *   Check if we want to perform NAT with this outbound UDP packet before
+ *   sending it. If so, translate it.
+ *
+ * Input Parameters:
+ *   dev   - The device to sent the packet.
+ *   ipv4  - Points to the IPv4 header to be filled into dev->d_buf later.
+ *
+ * Returned Value:
+ *   Zero is returned if NAT is successfully applied, or is not enabled for
+ *   this packet;
+ *   A negated errno value is returned if error occured.
+ *
+ * Assumptions:
+ *   Packet will be sent on NAT device.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_UDP
+static int ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
+                                 FAR struct ipv4_hdr_s *ipv4)
+{
+  uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
+  FAR struct udp_hdr_s *udp =
+      (FAR struct udp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
+  FAR struct ipv4_nat_entry *entry = ipv4_nat_outbound_entry_find(
+      dev, IP_PROTO_UDP, net_ip4addr_conv32(ipv4->srcipaddr), udp->srcport);
+  if (!entry)
+    {
+      /* Outbound entry creation failed, should have corresponding entry. */
+
+      return -ENOMEM;
+    }
+
+  /* Modify port and checksum. */
+
+  if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */
+    {
+      chksum_adjust(udp->udpchksum, udp->srcport, entry->external_port);
+    }
+
+  udp->srcport = entry->external_port;
+
+  /* Modify address and checksum. */
+
+  if (udp->udpchksum != 0) /* UDP checksum has special case 0 (no checksum) */
+    {
+      chksum_adjust(udp->udpchksum, ipv4->srcipaddr, dev->d_ipaddr);
+    }
+
+  chksum_adjust(ipv4->ipchksum, ipv4->srcipaddr, dev->d_ipaddr);
+  net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr);
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Name: ipv4_nat_outbound_icmp
  *
@@ -379,7 +499,8 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
 #endif
 
 #ifdef CONFIG_NET_UDP
-#         warning Missing logic
+          case IP_PROTO_UDP:
+            return ipv4_nat_inbound_udp(ipv4);
 #endif
 
 #ifdef CONFIG_NET_ICMP
@@ -430,7 +551,8 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
 #endif
 
 #ifdef CONFIG_NET_UDP
-#         warning Missing logic
+          case IP_PROTO_UDP:
+            return ipv4_nat_outbound_udp(dev, ipv4);
 #endif
 
 #ifdef CONFIG_NET_ICMP
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index 7869136e95..cbfc369fb7 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -33,6 +33,7 @@
 #include "icmp/icmp.h"
 #include "nat/nat.h"
 #include "tcp/tcp.h"
+#include "udp/udp.h"
 
 #if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
 
@@ -145,7 +146,22 @@ static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev,
 #endif
 
 #ifdef CONFIG_NET_UDP
-#     warning Missing logic
+      case IP_PROTO_UDP:
+        {
+#ifndef CONFIG_NET_UDP_NO_STACK
+          union ip_binding_u u;
+          u.ipv4.laddr = dev->d_draddr;
+          u.ipv4.raddr = INADDR_ANY;
+
+          /* TODO: Try keep origin port as possible. */
+
+          return HTONS(udp_select_port(PF_INET, &u));
+#else
+          return ipv4_nat_select_port_without_stack(IP_PROTO_UDP,
+                                                    dev->d_draddr,
+                                                    local_port);
+#endif
+        }
 #endif
 
 #ifdef CONFIG_NET_ICMP
@@ -212,7 +228,10 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
 #endif
 
 #ifdef CONFIG_NET_UDP
-#     warning Missing logic
+      case IP_PROTO_UDP:
+        entry->expire_time = TICK2SEC(clock_systime_ticks()) +
+                             CONFIG_NET_NAT_UDP_EXPIRE_SEC;
+        break;
 #endif
 
 #ifdef CONFIG_NET_ICMP
diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c
index 24f4085aaf..51c5bc86ee 100644
--- a/net/udp/udp_conn.c
+++ b/net/udp/udp_conn.c
@@ -64,6 +64,7 @@
 #include <nuttx/net/udp.h>
 
 #include "devif/devif.h"
+#include "nat/nat.h"
 #include "netdev/netdev.h"
 #include "inet/inet.h"
 #include "udp/udp.h"
@@ -533,7 +534,13 @@ uint16_t udp_select_port(uint8_t domain, FAR union ip_binding_u *u)
           g_last_udp_port = 4096;
         }
     }
-  while (udp_find_conn(domain, u, HTONS(g_last_udp_port)) != NULL);
+  while (udp_find_conn(domain, u, HTONS(g_last_udp_port)) != NULL
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+         || (domain == PF_INET &&
+             ipv4_nat_port_inuse(IP_PROTO_UDP, u->ipv4.laddr,
+                                 HTONS(g_last_udp_port)))
+#endif
+  );
 
   /* Initialize and return the connection structure, bind it to the
    * port number
@@ -816,7 +823,13 @@ int udp_bind(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr)
        * and port ?
        */
 
-      if (udp_find_conn(conn->domain, &conn->u, portno) == NULL)
+      if (udp_find_conn(conn->domain, &conn->u, portno) == NULL
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+          && !(conn->domain == PF_INET &&
+               ipv4_nat_port_inuse(IP_PROTO_UDP, conn->u.ipv4.laddr,
+                                   portno))
+#endif
+      )
         {
           /* No.. then bind the socket to the port */