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

[incubator-nuttx] 05/07: net/nat: Add ICMP ECHO (REQUEST & REPLY) 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 8d401db5b9676be70fa9719f4486dcfbc7978a4d
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Mon Oct 31 11:33:06 2022 +0800

    net/nat: Add ICMP ECHO (REQUEST & REPLY) support
    
    Support ICMP ECHO REQUEST & REPLY. Id of ICMP is processed like port of TCP in NAT. However, our ICMP stack doesn't have a method to manage id allocation like tcp_selectport(), the id is set by apps (like icmp_ping.c) without conflict avoidance, so not adding such conflict avoidance logic to ICMP stack when implementing NAT.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/nat/Kconfig          |  10 ++++
 net/nat/ipv4_nat.c       | 126 ++++++++++++++++++++++++++++++++++++++++++++++-
 net/nat/ipv4_nat_entry.c |  43 ++++++++++++++--
 3 files changed, 173 insertions(+), 6 deletions(-)

diff --git a/net/nat/Kconfig b/net/nat/Kconfig
index 670a2f2d28..baa961f775 100644
--- a/net/nat/Kconfig
+++ b/net/nat/Kconfig
@@ -19,3 +19,13 @@ config NET_NAT_TCP_EXPIRE_SEC
 
 		Note: The default value 86400 is suggested by RFC2663, Section 2.6,
 		Page 5.
+
+config NET_NAT_ICMP_EXPIRE_SEC
+	int "ICMP NAT entry expiration seconds"
+	default 60
+	depends on NET_NAT
+	---help---
+		The expiration time for idle ICMP entry in NAT.
+
+		Note: The default value 60 is suggested by RFC5508, Section 3.2,
+		Page 8.
diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index 9e57644b61..b24d60e73a 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -29,6 +29,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <nuttx/net/icmp.h>
 #include <nuttx/net/tcp.h>
 
 #include "nat/nat.h"
@@ -102,6 +103,64 @@ static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4)
 }
 #endif
 
+/****************************************************************************
+ * Name: ipv4_nat_inbound_icmp
+ *
+ * Description:
+ *   Check if a received ICMP 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 g_dev and is targeting at the address assigned to
+ *   g_dev.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMP
+static int ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4)
+{
+  uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
+  FAR struct icmp_hdr_s *icmp =
+      (FAR struct icmp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
+  FAR struct ipv4_nat_entry *entry;
+
+  switch (icmp->type)
+    {
+      /* TODO: Support other ICMP types. */
+
+      case ICMP_ECHO_REQUEST:
+      case ICMP_ECHO_REPLY:
+        entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP, icmp->id, true);
+        if (!entry)
+          {
+            /* Inbound without entry is OK, skip NAT. */
+
+            return OK;
+          }
+
+        /* Modify id and checksum. */
+
+        chksum_adjust(icmp->icmpchksum, icmp->id, entry->local_port);
+        icmp->id = entry->local_port;
+
+        /* Modify address and checksum. */
+
+        chksum_adjust(ipv4->ipchksum, ipv4->destipaddr, entry->local_ip);
+        net_ipv4addr_hdrcopy(ipv4->destipaddr, &entry->local_ip);
+    }
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Name: ipv4_nat_outbound_tcp
  *
@@ -154,6 +213,67 @@ static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
 }
 #endif
 
+/****************************************************************************
+ * Name: ipv4_nat_outbound_icmp
+ *
+ * Description:
+ *   Check if we want to perform NAT with this outbound ICMP 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_ICMP
+static int ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
+                                  FAR struct ipv4_hdr_s *ipv4)
+{
+  uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
+  FAR struct icmp_hdr_s *icmp =
+      (FAR struct icmp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
+  FAR struct ipv4_nat_entry *entry;
+
+  switch (icmp->type)
+    {
+      /* TODO: Support other ICMP types. */
+
+      case ICMP_ECHO_REQUEST:
+      case ICMP_ECHO_REPLY:
+        entry = ipv4_nat_outbound_entry_find(
+            dev, IP_PROTO_ICMP, net_ip4addr_conv32(ipv4->srcipaddr),
+            icmp->id);
+        if (!entry)
+          {
+            /* Outbound entry creation failed. */
+
+            return -ENOMEM;
+          }
+
+        /* Modify id and checksum. */
+
+        chksum_adjust(icmp->icmpchksum, icmp->id, entry->external_port);
+        icmp->id = entry->external_port;
+
+        /* Modify address and checksum. */
+
+        chksum_adjust(ipv4->ipchksum, ipv4->srcipaddr, dev->d_ipaddr);
+        net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr);
+    }
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -263,7 +383,8 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
 #endif
 
 #ifdef CONFIG_NET_ICMP
-#         warning Missing logic
+          case IP_PROTO_ICMP:
+            return ipv4_nat_inbound_icmp(ipv4);
 #endif
         }
     }
@@ -313,7 +434,8 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
 #endif
 
 #ifdef CONFIG_NET_ICMP
-#         warning Missing logic
+          case IP_PROTO_ICMP:
+            return ipv4_nat_outbound_icmp(dev, ipv4);
 #endif
         }
     }
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index b7f22d846e..7869136e95 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -30,11 +30,21 @@
 #include <nuttx/kmalloc.h>
 #include <nuttx/queue.h>
 
+#include "icmp/icmp.h"
 #include "nat/nat.h"
 #include "tcp/tcp.h"
 
 #if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* TODO: Why we limit to 32000 in net stack? */
+
+#define NAT_PORT_REASSIGN_MAX 32000
+#define NAT_PORT_REASSIGN_MIN 4096
+
 /****************************************************************************
  * Private Data
  ****************************************************************************/
@@ -72,9 +82,9 @@ static uint16_t ipv4_nat_select_port_without_stack(
   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? */
+      if (++hport >= NAT_PORT_REASSIGN_MAX)
         {
-          hport = 4096;
+          hport = NAT_PORT_REASSIGN_MIN;
         }
 
       portno = HTONS(hport);
@@ -139,7 +149,29 @@ static uint16_t ipv4_nat_select_port(FAR struct net_driver_s *dev,
 #endif
 
 #ifdef CONFIG_NET_ICMP
-#     warning Missing logic
+      case IP_PROTO_ICMP:
+        {
+#ifdef CONFIG_NET_ICMP_SOCKET
+          uint16_t id = local_port;
+          uint16_t hid = NTOHS(id);
+          while (icmp_findconn(dev, id) ||
+                 ipv4_nat_port_inuse(IP_PROTO_ICMP, dev->d_draddr, id))
+            {
+              if (++hid >= NAT_PORT_REASSIGN_MAX)
+                {
+                  hid = NAT_PORT_REASSIGN_MIN;
+                }
+
+              id = HTONS(hid);
+            }
+
+          return id;
+#else
+          return ipv4_nat_select_port_without_stack(IP_PROTO_ICMP,
+                                                    dev->d_draddr,
+                                                    local_port);
+#endif
+        }
 #endif
     }
 
@@ -184,7 +216,10 @@ static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
 #endif
 
 #ifdef CONFIG_NET_ICMP
-#     warning Missing logic
+      case IP_PROTO_ICMP:
+        entry->expire_time = TICK2SEC(clock_systime_ticks()) +
+                             CONFIG_NET_NAT_ICMP_EXPIRE_SEC;
+        break;
 #endif
   }
 }