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


The following commit(s) were added to refs/heads/master by this push:
     new a1a09f271f net/nat: Support Symmetric NAT
a1a09f271f is described below

commit a1a09f271fe8ea0b34b3f8ae1576edbd8d314b80
Author: Zhe Weng <[email protected]>
AuthorDate: Thu Mar 14 15:22:13 2024 +0800

    net/nat: Support Symmetric NAT
    
    The symmetric NAT limits one external port to be used with only one peer 
ip:port.
    
    Note:
    1. To avoid using too much #ifdef, we're always passing peer_ip and 
peer_port as arguments, but won't use them under full cone NAT, let the 
compiler optimize them.
    2. We need to find port binding without peer ip:port, so don't add peer 
ip:port into hash key.
    3. Symmetric NAT needs to *select another external port if a port is used 
by any other NAT entry*, this behavior is exactly same as Full Cone NAT, so we 
don't need to change anything related to `ipv4_nat_port_inuse`.
    
    Signed-off-by: Zhe Weng <[email protected]>
---
 Documentation/components/net/nat.rst | 15 +++++++++++++--
 net/nat/Kconfig                      | 19 +++++++++++++++++++
 net/nat/ipv4_nat.c                   | 34 +++++++++++++++++++++++++++++++---
 net/nat/ipv4_nat_entry.c             | 36 +++++++++++++++++++++++++++++++-----
 net/nat/nat.h                        | 15 ++++++++++++++-
 5 files changed, 108 insertions(+), 11 deletions(-)

diff --git a/Documentation/components/net/nat.rst 
b/Documentation/components/net/nat.rst
index 01215356b0..773bb5bf1a 100644
--- a/Documentation/components/net/nat.rst
+++ b/Documentation/components/net/nat.rst
@@ -2,7 +2,7 @@
 Network Address Translation (NAT)
 =================================
 
-NuttX supports full cone NAT logic, which currently supports
+NuttX supports full cone or symmetric NAT logic, which currently supports
 
 - TCP
 
@@ -46,6 +46,12 @@ Configuration Options
 ``CONFIG_NET_NAT``
   Enable or disable Network Address Translation (NAT) function.
   Depends on ``CONFIG_NET_IPFORWARD``.
+``CONFIG_NET_NAT_FULL_CONE``
+  Enable Full Cone NAT logic. Full Cone NAT is easier to traverse than
+  Symmetric NAT, and uses less resources than Symmetric NAT.
+``CONFIG_NET_NAT_SYMMETRIC``
+  Enable Symmetric NAT logic. Symmetric NAT will be safer than Full Cone NAT,
+  be more difficult to traverse, and has more entries which may lead to 
heavier load.
 ``CONFIG_NET_NAT_HASH_BITS``
   The bits of the hashtable of NAT entries, hashtable has (1 << bits) buckets.
 ``CONFIG_NET_NAT_TCP_EXPIRE_SEC``
@@ -101,7 +107,8 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by 
following steps:
       # CONFIG_SIM_NET_BRIDGE is not set
       CONFIG_SIM_NETDEV_NUMBER=2
 
-2. Call ``ipv4_nat_enable`` on one dev on startup
+2. Call ``ipv4_nat_enable`` on one dev on startup, or manually enable NAT
+   with ``iptables`` command (either may work).
 
   ..  code-block:: c
 
@@ -113,6 +120,10 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by 
following steps:
         ...
       }
 
+  ..  code-block:: shell
+
+      iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+
 3. Set IP Address for NuttX on startup
 
   ..  code-block:: shell
diff --git a/net/nat/Kconfig b/net/nat/Kconfig
index 0adc09c537..0ce9e71b1d 100644
--- a/net/nat/Kconfig
+++ b/net/nat/Kconfig
@@ -15,6 +15,25 @@ config NET_NAT
                and NAT may need a continuous buffer of at least 68 Bytes
                (IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B).
 
+choice
+       prompt "NAT Type"
+       default NET_NAT_FULL_CONE
+       depends on NET_NAT
+
+config NET_NAT_FULL_CONE
+       bool "Full Cone NAT"
+       ---help---
+               Full Cone NAT is easier to traverse than Symmetric NAT, and uses
+               less resources than Symmetric NAT.
+
+config NET_NAT_SYMMETRIC
+       bool "Symmetric NAT"
+       ---help---
+               Symmetric NAT will be safer than Full Cone NAT, be more 
difficult
+               to traverse, and has more entries which may lead to heavier 
load.
+
+endchoice
+
 config NET_NAT_HASH_BITS
        int "The bits of NAT entry hashtable"
        default 5
diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index b575a273c3..a7d28c72fe 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -58,6 +58,14 @@
 #define MANIP_PORT(l4hdr,manip_type) \
   ((manip_type) == NAT_MANIP_SRC ? &(l4hdr)->srcport : &(l4hdr)->destport)
 
+/* Getting peer IP & Port (just other than MANIP) */
+
+#define PEER_IPADDR(iphdr,manip_type) \
+  ((manip_type) != NAT_MANIP_SRC ? (iphdr)->srcipaddr : (iphdr)->destipaddr)
+
+#define PEER_PORT(l4hdr,manip_type) \
+  ((manip_type) != NAT_MANIP_SRC ? &(l4hdr)->srcport : &(l4hdr)->destport)
+
 /* Getting L4 header from IPv4 header. */
 
 #define L4_HDR(ipv4) \
@@ -204,10 +212,14 @@ ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4,
   FAR struct tcp_hdr_s      *tcp           = L4_HDR(ipv4);
   FAR uint16_t              *external_ip   = MANIP_IPADDR(ipv4, manip_type);
   FAR uint16_t              *external_port = MANIP_PORT(tcp, manip_type);
+  FAR uint16_t              *peer_ip       = PEER_IPADDR(ipv4, manip_type);
+  FAR uint16_t              *peer_port     = PEER_PORT(tcp, manip_type);
   FAR struct ipv4_nat_entry *entry         =
                  ipv4_nat_inbound_entry_find(IP_PROTO_TCP,
                                              net_ip4addr_conv32(external_ip),
-                                             *external_port, true);
+                                             *external_port,
+                                             net_ip4addr_conv32(peer_ip),
+                                             *peer_port, true);
   if (!entry)
     {
       return NULL;
@@ -253,11 +265,15 @@ ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *ipv4,
   FAR struct udp_hdr_s      *udp           = L4_HDR(ipv4);
   FAR uint16_t              *external_ip   = MANIP_IPADDR(ipv4, manip_type);
   FAR uint16_t              *external_port = MANIP_PORT(udp, manip_type);
+  FAR uint16_t              *peer_ip       = PEER_IPADDR(ipv4, manip_type);
+  FAR uint16_t              *peer_port     = PEER_PORT(udp, manip_type);
   FAR uint16_t              *udpchksum;
   FAR struct ipv4_nat_entry *entry         =
                  ipv4_nat_inbound_entry_find(IP_PROTO_UDP,
                                              net_ip4addr_conv32(external_ip),
-                                             *external_port, true);
+                                             *external_port,
+                                             net_ip4addr_conv32(peer_ip),
+                                             *peer_port, true);
 
   if (!entry)
     {
@@ -302,6 +318,7 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4,
 {
   FAR struct icmp_hdr_s     *icmp = L4_HDR(ipv4);
   FAR uint16_t              *external_ip;
+  FAR uint16_t              *peer_ip;
   FAR struct ipv4_nat_entry *entry;
 
   switch (icmp->type)
@@ -309,8 +326,11 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4,
       case ICMP_ECHO_REQUEST:
       case ICMP_ECHO_REPLY:
         external_ip = MANIP_IPADDR(ipv4, manip_type);
+        peer_ip     = PEER_IPADDR(ipv4, manip_type);
         entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP,
                                             net_ip4addr_conv32(external_ip),
+                                            icmp->id,
+                                            net_ip4addr_conv32(peer_ip),
                                             icmp->id, true);
         if (!entry)
           {
@@ -424,12 +444,15 @@ ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
   FAR struct tcp_hdr_s      *tcp        = L4_HDR(ipv4);
   FAR uint16_t              *local_ip   = MANIP_IPADDR(ipv4, manip_type);
   FAR uint16_t              *local_port = MANIP_PORT(tcp, manip_type);
+  FAR uint16_t              *peer_ip    = PEER_IPADDR(ipv4, manip_type);
+  FAR uint16_t              *peer_port  = PEER_PORT(tcp, manip_type);
   FAR struct ipv4_nat_entry *entry;
 
   /* Only create entry when it's the outermost packet (manip type is SRC). */
 
   entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_TCP,
               net_ip4addr_conv32(local_ip), *local_port,
+              net_ip4addr_conv32(peer_ip), *peer_port,
               (manip_type == NAT_MANIP_SRC));
   if (!entry)
     {
@@ -477,6 +500,8 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
   FAR struct udp_hdr_s      *udp        = L4_HDR(ipv4);
   FAR uint16_t              *local_ip   = MANIP_IPADDR(ipv4, manip_type);
   FAR uint16_t              *local_port = MANIP_PORT(udp, manip_type);
+  FAR uint16_t              *peer_ip    = PEER_IPADDR(ipv4, manip_type);
+  FAR uint16_t              *peer_port  = PEER_PORT(udp, manip_type);
   FAR uint16_t              *udpchksum;
   FAR struct ipv4_nat_entry *entry;
 
@@ -484,6 +509,7 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
 
   entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_UDP,
               net_ip4addr_conv32(local_ip), *local_port,
+              net_ip4addr_conv32(peer_ip), *peer_port,
               (manip_type == NAT_MANIP_SRC));
   if (!entry)
     {
@@ -529,6 +555,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
 {
   FAR struct icmp_hdr_s     *icmp     = L4_HDR(ipv4);
   FAR uint16_t              *local_ip = MANIP_IPADDR(ipv4, manip_type);
+  FAR uint16_t              *peer_ip  = PEER_IPADDR(ipv4, manip_type);
   FAR struct ipv4_nat_entry *entry;
 
   switch (icmp->type)
@@ -542,6 +569,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
 
         entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_ICMP,
                     net_ip4addr_conv32(local_ip), icmp->id,
+                    net_ip4addr_conv32(peer_ip), icmp->id,
                     (manip_type == NAT_MANIP_SRC));
         if (!entry)
           {
@@ -894,7 +922,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, ip, port, false);
+      ipv4_nat_inbound_entry_find(protocol, ip, port, INADDR_ANY, 0, false);
 
   return entry != NULL;
 }
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index e32b3c73c9..29b6e1cde3 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -295,6 +295,8 @@ static void ipv4_nat_entry_refresh(FAR struct 
ipv4_nat_entry *entry)
  *   external_port - The external port of the packet.
  *   local_ip      - The local ip of the packet.
  *   local_port    - The local port of the packet.
+ *   peer_ip       - The peer ip of the packet.
+ *   peer_port     - The peer port of the packet.
  *
  * Returned Value:
  *   Pointer to entry on success; null on failure
@@ -304,7 +306,8 @@ static void ipv4_nat_entry_refresh(FAR struct 
ipv4_nat_entry *entry)
 static FAR struct ipv4_nat_entry *
 ipv4_nat_entry_create(uint8_t protocol,
                       in_addr_t external_ip, uint16_t external_port,
-                      in_addr_t local_ip, uint16_t local_port)
+                      in_addr_t local_ip, uint16_t local_port,
+                      in_addr_t peer_ip, uint16_t peer_port)
 {
   FAR struct ipv4_nat_entry *entry =
       kmm_malloc(sizeof(struct ipv4_nat_entry));
@@ -319,6 +322,10 @@ ipv4_nat_entry_create(uint8_t protocol,
   entry->external_port = external_port;
   entry->local_ip      = local_ip;
   entry->local_port    = local_port;
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+  entry->peer_ip       = peer_ip;
+  entry->peer_port     = peer_port;
+#endif
 
   ipv4_nat_entry_refresh(entry);
 
@@ -456,6 +463,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev)
  *   protocol      - The L4 protocol of the packet.
  *   external_ip   - The external ip of the packet, supports INADDR_ANY.
  *   external_port - The external port of the packet.
+ *   peer_ip       - The peer ip of the packet.
+ *   peer_port     - The peer port of the packet.
  *   refresh       - Whether to refresh the selected entry.
  *
  * Returned Value:
@@ -465,11 +474,15 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev)
 
 FAR struct ipv4_nat_entry *
 ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
-                            uint16_t external_port, bool refresh)
+                            uint16_t external_port, in_addr_t peer_ip,
+                            uint16_t peer_port, bool refresh)
 {
   FAR hash_node_t *p;
   FAR hash_node_t *tmp;
   bool skip_ip = net_ipv4addr_cmp(external_ip, INADDR_ANY);
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+  bool skip_peer = net_ipv4addr_cmp(peer_ip, INADDR_ANY);
+#endif
   int32_t current_time = TICK2SEC(clock_systime_ticks());
 
 #if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
@@ -492,7 +505,12 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t 
external_ip,
 
       if (entry->protocol == protocol &&
           (skip_ip || net_ipv4addr_cmp(entry->external_ip, external_ip)) &&
-          entry->external_port == external_port)
+          entry->external_port == external_port
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+          && (skip_peer || (net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
+                            entry->peer_port == peer_port))
+#endif
+          )
         {
           if (refresh)
             {
@@ -525,6 +543,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t 
external_ip,
  *   protocol   - The L4 protocol of the packet.
  *   local_ip   - The local ip of the packet.
  *   local_port - The local port of the packet.
+ *   peer_ip    - The peer ip of the packet.
+ *   peer_port  - The peer port of the packet.
  *   try_create - Try create the entry if no entry found.
  *
  * Returned Value:
@@ -535,6 +555,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t 
external_ip,
 FAR struct ipv4_nat_entry *
 ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
                              in_addr_t local_ip, uint16_t local_port,
+                             in_addr_t peer_ip, uint16_t peer_port,
                              bool try_create)
 {
   FAR hash_node_t *p;
@@ -562,7 +583,12 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, 
uint8_t protocol,
       if (entry->protocol == protocol &&
           net_ipv4addr_cmp(entry->external_ip, dev->d_ipaddr) &&
           net_ipv4addr_cmp(entry->local_ip, local_ip) &&
-          entry->local_port == local_port)
+          entry->local_port == local_port
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+          && net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
+          entry->peer_port == peer_port
+#endif
+          )
         {
           ipv4_nat_entry_refresh(entry);
           return entry;
@@ -588,7 +614,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, 
uint8_t protocol,
     }
 
   return ipv4_nat_entry_create(protocol, dev->d_ipaddr, external_port,
-                               local_ip, local_port);
+                               local_ip, local_port, peer_ip, peer_port);
 }
 
 #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
diff --git a/net/nat/nat.h b/net/nat/nat.h
index 4c70ceeb95..6273ecc131 100644
--- a/net/nat/nat.h
+++ b/net/nat/nat.h
@@ -55,13 +55,20 @@ struct ipv4_nat_entry
    *                |----------------|
    *
    * Full cone NAT only need to save local ip:port and external ip:port.
+   * Symmetric NAT need to save peer ip:port as well.
    * For ICMP, save id in port field.
    */
 
   in_addr_t  local_ip;       /* IP address of the local (private) host. */
   in_addr_t  external_ip;    /* External IP address. */
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+  in_addr_t  peer_ip;        /* Peer IP address. */
+#endif
   uint16_t   local_port;     /* Port of the local (private) host. */
   uint16_t   external_port;  /* The external port of local (private) host. */
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+  uint16_t   peer_port;      /* Peer port. */
+#endif
   uint8_t    protocol;       /* L4 protocol (TCP, UDP etc). */
 
   int32_t    expire_time;    /* The expiration time of this entry. */
@@ -203,6 +210,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
  *   protocol      - The L4 protocol of the packet.
  *   external_ip   - The external ip of the packet, supports INADDR_ANY.
  *   external_port - The external port of the packet.
+ *   peer_ip       - The peer ip of the packet.
+ *   peer_port     - The peer port of the packet.
  *   refresh       - Whether to refresh the selected entry.
  *
  * Returned Value:
@@ -212,7 +221,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
 
 FAR struct ipv4_nat_entry *
 ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
-                            uint16_t external_port, bool refresh);
+                            uint16_t external_port, in_addr_t peer_ip,
+                            uint16_t peer_port, bool refresh);
 
 /****************************************************************************
  * Name: ipv4_nat_outbound_entry_find
@@ -226,6 +236,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t 
external_ip,
  *   protocol   - The L4 protocol of the packet.
  *   local_ip   - The local ip of the packet.
  *   local_port - The local port of the packet.
+ *   peer_ip    - The peer ip of the packet.
+ *   peer_port  - The peer port of the packet.
  *   try_create - Try create the entry if no entry found.
  *
  * Returned Value:
@@ -236,6 +248,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t 
external_ip,
 FAR struct ipv4_nat_entry *
 ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
                              in_addr_t local_ip, uint16_t local_port,
+                             in_addr_t peer_ip, uint16_t peer_port,
                              bool try_create);
 
 #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */

Reply via email to