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

[incubator-nuttx] 03/07: net/nat: Add TCP entry expiration logic

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 8239ddeef41578b121d7cce341714d01f3ca0f6b
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Tue Nov 1 18:13:50 2022 +0800

    net/nat: Add TCP entry expiration logic
    
    Add TCP entry expiration logic for NAT, with entries changed from sq to dq for fast removal.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 include/nuttx/queue.h    |   4 ++
 net/nat/Kconfig          |  10 +++++
 net/nat/ipv4_nat.c       |   4 +-
 net/nat/ipv4_nat_entry.c | 105 ++++++++++++++++++++++++++++++++++++++++++++---
 net/nat/nat.h            |   9 ++--
 5 files changed, 122 insertions(+), 10 deletions(-)

diff --git a/include/nuttx/queue.h b/include/nuttx/queue.h
index 4bf3cbcb37..80b2a04acd 100644
--- a/include/nuttx/queue.h
+++ b/include/nuttx/queue.h
@@ -158,6 +158,10 @@
 #define sq_for_every(q, p) \
   for ((p) = (q)->head; (p) != NULL; (p) = (p)->flink)
 
+#define sq_for_every_safe(q, p, tmp) \
+  for((p) = (q)->head, (tmp) = (p) ? (p)->flink : NULL; \
+      (p) != NULL; (p) = (tmp), (tmp) = (p) ? (p)->flink : NULL)
+
 #define sq_rem(p, q) \
   do \
     { \
diff --git a/net/nat/Kconfig b/net/nat/Kconfig
index f50d237695..670a2f2d28 100644
--- a/net/nat/Kconfig
+++ b/net/nat/Kconfig
@@ -9,3 +9,13 @@ config NET_NAT
 	depends on NET_IPFORWARD
 	---help---
 		Enable or disable Network Address Translation (NAT) function.
+
+config NET_NAT_TCP_EXPIRE_SEC
+	int "TCP NAT entry expiration seconds"
+	default 86400
+	depends on NET_NAT
+	---help---
+		The expiration time for idle TCP entry in NAT.
+
+		Note: The default value 86400 is suggested by RFC2663, Section 2.6,
+		Page 5.
diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index 8b0811e52e..12a4831585 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -79,7 +79,7 @@ static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4)
   FAR struct tcp_hdr_s *tcp =
       (FAR struct tcp_hdr_s *)((FAR uint8_t *)ipv4 + iphdrlen);
   FAR struct ipv4_nat_entry *entry =
-      ipv4_nat_inbound_entry_find(IP_PROTO_TCP, tcp->destport);
+      ipv4_nat_inbound_entry_find(IP_PROTO_TCP, tcp->destport, true);
   if (!entry)
     {
       /* Inbound without entry is OK (e.g. towards NuttX itself), skip NAT. */
@@ -340,7 +340,7 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
 bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port)
 {
   FAR struct ipv4_nat_entry *entry =
-      ipv4_nat_inbound_entry_find(protocol, port);
+      ipv4_nat_inbound_entry_find(protocol, port, false);
 
   /* Not checking ip is enough for single NAT device, may save external_ip in
    * entry for multiple device support in future.
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index 4a57549355..a5e4681111 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -26,6 +26,7 @@
 
 #include <debug.h>
 
+#include <nuttx/clock.h>
 #include <nuttx/kmalloc.h>
 #include <nuttx/queue.h>
 
@@ -37,7 +38,7 @@
  * Private Data
  ****************************************************************************/
 
-static sq_queue_t g_entries;
+static dq_queue_t g_entries;
 
 /****************************************************************************
  * Private Functions
@@ -69,6 +70,45 @@ static uint16_t ipv4_nat_select_port(uint8_t protocol, uint16_t local_port)
   return local_port;
 }
 
+/****************************************************************************
+ * Name: ipv4_nat_entry_refresh
+ *
+ * Description:
+ *   Refresh a NAT entry, update its expiration time.
+ *
+ * Input Parameters:
+ *   entry      - The entry to refresh.
+ *
+ ****************************************************************************/
+
+static void ipv4_nat_entry_refresh(FAR struct ipv4_nat_entry *entry)
+{
+  switch (entry->protocol)
+    {
+#ifdef CONFIG_NET_TCP
+      case IP_PROTO_TCP:
+        /* NOTE: According to RFC2663, Section 2.6, Page 5, we can reduce the
+         * time to 4min if we have received FINs from both side of one
+         * connection, and keep 24h for other TCP connections. However, full
+         * cone NAT may have multiple connections on one entry, so this
+         * optimization may not work and we only use one expiration time.
+         */
+
+        entry->expire_time = TICK2SEC(clock_systime_ticks()) +
+                             CONFIG_NET_NAT_TCP_EXPIRE_SEC;
+        break;
+#endif
+
+#ifdef CONFIG_NET_UDP
+#     warning Missing logic
+#endif
+
+#ifdef CONFIG_NET_ICMP
+#     warning Missing logic
+#endif
+  }
+}
+
 /****************************************************************************
  * Name: ipv4_nat_entry_create
  *
@@ -103,10 +143,33 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
   entry->local_ip      = local_ip;
   entry->local_port    = local_port;
 
-  sq_addfirst((FAR sq_entry_t *)entry, &g_entries);
+  ipv4_nat_entry_refresh(entry);
+
+  dq_addfirst((FAR dq_entry_t *)entry, &g_entries);
   return entry;
 }
 
+/****************************************************************************
+ * Name: ipv4_nat_entry_delete
+ *
+ * Description:
+ *   Delete a NAT entry and remove from entry list.
+ *
+ * Input Parameters:
+ *   entry      - The entry to remove.
+ *
+ ****************************************************************************/
+
+void ipv4_nat_entry_delete(FAR struct ipv4_nat_entry *entry)
+{
+  ninfo("INFO: Removing NAT entry proto=%d, local=%x:%d, external=:%d\n",
+        entry->protocol, entry->local_ip, entry->local_port,
+        entry->external_port);
+
+  dq_rem((FAR dq_entry_t *)entry, &g_entries);
+  kmm_free(entry);
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -120,6 +183,7 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
  * Input Parameters:
  *   protocol      - The L4 protocol of the packet.
  *   external_port - The external port of the packet.
+ *   refresh       - Whether to refresh the selected entry.
  *
  * Returned Value:
  *   Pointer to entry on success; null on failure
@@ -127,17 +191,35 @@ ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
  ****************************************************************************/
 
 FAR struct ipv4_nat_entry *
-ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port)
+ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
+                            bool refresh)
 {
   FAR sq_entry_t *p;
-  sq_for_every(&g_entries, p)
+  FAR sq_entry_t *tmp;
+  uint32_t current_time = TICK2SEC(clock_systime_ticks());
+
+  sq_for_every_safe((FAR sq_queue_t *)&g_entries, p, tmp)
     {
       FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p;
+
+      /* Remove expired entries. */
+
+      if (entry->expire_time < current_time)
+        {
+          ipv4_nat_entry_delete(entry);
+          continue;
+        }
+
       if (entry->protocol == protocol &&
           entry->external_port == external_port)
         {
           /* TODO: Use hash table, or move recent node to head. */
 
+          if (refresh)
+            {
+              ipv4_nat_entry_refresh(entry);
+            }
+
           return entry;
         }
     }
@@ -169,15 +251,28 @@ ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
                              uint16_t local_port)
 {
   FAR sq_entry_t *p;
-  sq_for_every(&g_entries, p)
+  FAR sq_entry_t *tmp;
+  uint32_t current_time = TICK2SEC(clock_systime_ticks());
+
+  sq_for_every_safe((FAR sq_queue_t *)&g_entries, p, tmp)
     {
       FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p;
+
+      /* Remove expired entries. */
+
+      if (entry->expire_time < current_time)
+        {
+          ipv4_nat_entry_delete(entry);
+          continue;
+        }
+
       if (entry->protocol == protocol &&
           net_ipv4addr_cmp(entry->local_ip, local_ip) &&
           entry->local_port == local_port)
         {
           /* TODO: Use hash table, or move recent node to head. */
 
+          ipv4_nat_entry_refresh(entry);
           return entry;
         }
     }
diff --git a/net/nat/nat.h b/net/nat/nat.h
index e5df4db633..a68d0af4db 100644
--- a/net/nat/nat.h
+++ b/net/nat/nat.h
@@ -43,13 +43,14 @@
 
 struct ipv4_nat_entry
 {
-  /* Support for single-linked lists.
+  /* Support for doubly-linked lists.
    *
    * TODO: Implement a general hash table, and use it to optimize performance
    * here.
    */
 
   FAR struct ipv4_nat_entry *flink;
+  FAR struct ipv4_nat_entry *blink;
 
   /*  Local Network                             External Network
    *                |----------------|
@@ -67,7 +68,7 @@ struct ipv4_nat_entry
   uint16_t   external_port;  /* The external port of local (private) host. */
   uint8_t    protocol;       /* L4 protocol (TCP, UDP etc). */
 
-  /* TODO: Timeout check and remove outdated entry. */
+  uint32_t   expire_time;    /* The expiration time of this entry. */
 };
 
 /****************************************************************************
@@ -184,6 +185,7 @@ bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port);
  * Input Parameters:
  *   protocol      - The L4 protocol of the packet.
  *   external_port - The external port of the packet.
+ *   refresh       - Whether to refresh the selected entry.
  *
  * Returned Value:
  *   Pointer to entry on success; null on failure
@@ -191,7 +193,8 @@ bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port);
  ****************************************************************************/
 
 FAR struct ipv4_nat_entry *
-ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port);
+ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port,
+                            bool refresh);
 
 /****************************************************************************
  * Name: ipv4_nat_outbound_entry_find