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

[incubator-nuttx] branch master updated (634bdeaa5c -> b1ad547deb)

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


    from 634bdeaa5c pthread_barrier:don't destory when barrier used
     new b595430578 net: add basic NAT workflow
     new 0a4e01d712 net: verify NAT port usage in tcp_selectport
     new 8239ddeef4 net/nat: Add TCP entry expiration logic
     new f498102512 net: select NAT external port by tcp_selectport for TCP
     new 8d401db5b9 net/nat: Add ICMP ECHO (REQUEST & REPLY) support
     new a3a669a5f6 net/nat: Add UDP support
     new b1ad547deb Documentation: add NAT description

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Documentation/reference/os/nat.rst | 169 +++++++++++
 include/net/if.h                   |   4 +
 include/nuttx/queue.h              |   4 +
 net/Kconfig                        |   1 +
 net/Makefile                       |   1 +
 net/devif/ipv4_input.c             |  11 +
 net/ipforward/ipv4_forward.c       |  13 +
 net/nat/Kconfig                    |  42 +++
 {drivers/i2s => net/nat}/Make.defs |  16 +-
 net/nat/ipv4_nat.c                 | 598 +++++++++++++++++++++++++++++++++++++
 net/nat/ipv4_nat_entry.c           | 432 +++++++++++++++++++++++++++
 net/nat/nat.h                      | 222 ++++++++++++++
 net/tcp/tcp.h                      |  21 ++
 net/tcp/tcp_conn.c                 | 213 ++++++-------
 net/udp/udp_conn.c                 |  17 +-
 15 files changed, 1655 insertions(+), 109 deletions(-)
 create mode 100644 Documentation/reference/os/nat.rst
 create mode 100644 net/nat/Kconfig
 copy {drivers/i2s => net/nat}/Make.defs (82%)
 create mode 100644 net/nat/ipv4_nat.c
 create mode 100644 net/nat/ipv4_nat_entry.c
 create mode 100644 net/nat/nat.h


[incubator-nuttx] 06/07: net/nat: Add UDP support

Posted by xi...@apache.org.
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 */
 


[incubator-nuttx] 02/07: net: verify NAT port usage in tcp_selectport

Posted by xi...@apache.org.
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 0a4e01d7123085e901b3f9067833f3094c885c76
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Wed Oct 26 14:31:04 2022 +0800

    net: verify NAT port usage in tcp_selectport
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/nat/ipv4_nat.c       | 30 ++++++++++++++++++++++++++++++
 net/nat/ipv4_nat_entry.c |  1 -
 net/nat/nat.h            | 19 +++++++++++++++++++
 net/tcp/tcp_conn.c       | 15 +++++++++++++--
 4 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index f1d44155fc..8b0811e52e 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -321,4 +321,34 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
   return OK;
 }
 
+/****************************************************************************
+ * Name: ipv4_nat_port_inuse
+ *
+ * Description:
+ *   Check whether a port is currently used by NAT.
+ *
+ * Input Parameters:
+ *   protocol      - The L4 protocol of the packet.
+ *   ip            - The IP bind with the port (in network byte order).
+ *   port          - The port number to check (in network byte order).
+ *
+ * Returned Value:
+ *   True if the port is already used by NAT, otherwise false.
+ *
+ ****************************************************************************/
+
+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);
+
+  /* Not checking ip is enough for single NAT device, may save external_ip in
+   * entry for multiple device support in future.
+   */
+
+  UNUSED(ip);
+
+  return entry != NULL;
+}
+
 #endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index e0f7e92513..4a57549355 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -62,7 +62,6 @@ static uint16_t ipv4_nat_select_port(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?
-   * TODO: Also need to modify local port number assignment.
    */
 
 # warning Missing logic
diff --git a/net/nat/nat.h b/net/nat/nat.h
index c5b13e5a3e..e5df4db633 100644
--- a/net/nat/nat.h
+++ b/net/nat/nat.h
@@ -27,6 +27,7 @@
 
 #include <nuttx/config.h>
 
+#include <stdbool.h>
 #include <stdint.h>
 
 #include <netinet/in.h>
@@ -156,6 +157,24 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
 int ipv4_nat_outbound(FAR struct net_driver_s *dev,
                       FAR struct ipv4_hdr_s *ipv4);
 
+/****************************************************************************
+ * Name: ipv4_nat_port_inuse
+ *
+ * Description:
+ *   Check whether a port is currently used by NAT.
+ *
+ * Input Parameters:
+ *   protocol      - The L4 protocol of the packet.
+ *   ip            - The IP bind with the port (in network byte order).
+ *   port          - The port number to check (in network byte order).
+ *
+ * Returned Value:
+ *   True if the port is already used by NAT, otherwise false.
+ *
+ ****************************************************************************/
+
+bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port);
+
 /****************************************************************************
  * Name: ipv4_nat_inbound_entry_find
  *
diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c
index 28c23998b9..a4719cec59 100644
--- a/net/tcp/tcp_conn.c
+++ b/net/tcp/tcp_conn.c
@@ -68,6 +68,7 @@
 #include "tcp/tcp.h"
 #include "arp/arp.h"
 #include "icmpv6/icmpv6.h"
+#include "nat/nat.h"
 
 /****************************************************************************
  * Private Data
@@ -244,7 +245,12 @@ static int tcp_selectport(uint8_t domain,
 
           portno = HTONS(g_last_tcp_port);
         }
-      while (tcp_listener(domain, ipaddr, portno));
+      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
     {
@@ -252,7 +258,12 @@ static int tcp_selectport(uint8_t domain,
        * connection is using this local port.
        */
 
-      if (tcp_listener(domain, ipaddr, portno))
+      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 */
 


[incubator-nuttx] 01/07: net: add basic NAT workflow

Posted by xi...@apache.org.
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 b595430578e252a5f7950c319cae32293f57d102
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Mon Oct 17 15:13:34 2022 +0800

    net: add basic NAT workflow
    
    Add basic functions for NAT (NAPT), remaining some logic unimplemented (UDP, ICMP, port assignment, etc). NAT for TCP can work now (unless port conflicts).
    Outbound: LAN ->  Forward  ->  NAT(only if targeting at WAN)  -> WAN
    Inbound:  WAN ->  NAT(only from WAN, change dest) -> Forward  -> LAN
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 include/net/if.h             |   4 +
 net/Kconfig                  |   1 +
 net/Makefile                 |   1 +
 net/devif/ipv4_input.c       |  11 ++
 net/ipforward/ipv4_forward.c |  13 ++
 net/nat/Kconfig              |  11 ++
 net/nat/Make.defs            |  34 +++++
 net/nat/ipv4_nat.c           | 324 +++++++++++++++++++++++++++++++++++++++++++
 net/nat/ipv4_nat_entry.c     | 203 +++++++++++++++++++++++++++
 net/nat/nat.h                | 199 ++++++++++++++++++++++++++
 10 files changed, 801 insertions(+)

diff --git a/include/net/if.h b/include/net/if.h
index aa6adcf3a6..299bfdc763 100644
--- a/include/net/if.h
+++ b/include/net/if.h
@@ -53,6 +53,7 @@
 #define IFF_LOOPBACK       (1 << 5)  /* Is a loopback net */
 #define IFF_POINTOPOINT    (1 << 6)  /* Is point-to-point link */
 #define IFF_NOARP          (1 << 7)  /* ARP is not required for this packet */
+#define IFF_NAT            (1 << 8)  /* NAT is enabled for this interface */
 #define IFF_MULTICAST      (1 << 12) /* Supports multicast. */
 #define IFF_BROADCAST      (1 << 13) /* Broadcast address valid. */
 
@@ -61,6 +62,7 @@
 #define IFF_SET_UP(f)          do { (f) |= IFF_UP; } while (0)
 #define IFF_SET_RUNNING(f)     do { (f) |= IFF_RUNNING; } while (0)
 #define IFF_SET_NOARP(f)       do { (f) |= IFF_NOARP; } while (0)
+#define IFF_SET_NAT(f)         do { (f) |= IFF_NAT; } while (0)
 #define IFF_SET_LOOPBACK(f)    do { (f) |= IFF_LOOPBACK; } while (0)
 #define IFF_SET_POINTOPOINT(f) do { (f) |= IFF_POINTOPOINT; } while (0)
 #define IFF_SET_MULTICAST(f)   do { (f) |= IFF_MULTICAST; } while (0)
@@ -69,6 +71,7 @@
 #define IFF_CLR_UP(f)          do { (f) &= ~IFF_UP; } while (0)
 #define IFF_CLR_RUNNING(f)     do { (f) &= ~IFF_RUNNING; } while (0)
 #define IFF_CLR_NOARP(f)       do { (f) &= ~IFF_NOARP; } while (0)
+#define IFF_CLR_NAT(f)         do { (f) &= ~IFF_NAT; } while (0)
 #define IFF_CLR_LOOPBACK(f)    do { (f) &= ~IFF_LOOPBACK; } while (0)
 #define IFF_CLR_POINTOPOINT(f) do { (f) &= ~IFF_POINTOPOINT; } while (0)
 #define IFF_CLR_MULTICAST(f)   do { (f) &= ~IFF_MULTICAST; } while (0)
@@ -77,6 +80,7 @@
 #define IFF_IS_UP(f)          (((f) & IFF_UP) != 0)
 #define IFF_IS_RUNNING(f)     (((f) & IFF_RUNNING) != 0)
 #define IFF_IS_NOARP(f)       (((f) & IFF_NOARP) != 0)
+#define IFF_IS_NAT(f)         (((f) & IFF_NAT) != 0)
 #define IFF_IS_LOOPBACK(f)    (((f) & IFF_LOOPBACK) != 0)
 #define IFF_IS_POINTOPOINT(f) (((f) & IFF_POINTOPOINT) != 0)
 #define IFF_IS_MULTICAST(f)   (((f) & IFF_MULTICAST) != 0)
diff --git a/net/Kconfig b/net/Kconfig
index 8dcd20d18a..c38539fcbf 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -320,6 +320,7 @@ menuconfig NET_6LOWPAN
 
 source "net/sixlowpan/Kconfig"
 source "net/ipforward/Kconfig"
+source "net/nat/Kconfig"
 
 endmenu # Internet Protocol Selection
 
diff --git a/net/Makefile b/net/Makefile
index b9b596a80a..e9e80904db 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -47,6 +47,7 @@ include bluetooth/Make.defs
 include ieee802154/Make.defs
 include devif/Make.defs
 include ipforward/Make.defs
+include nat/Make.defs
 include route/Make.defs
 include procfs/Make.defs
 include usrsock/Make.defs
diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c
index 338d18f365..d492fff50d 100644
--- a/net/devif/ipv4_input.c
+++ b/net/devif/ipv4_input.c
@@ -103,6 +103,7 @@
 
 #include "ipforward/ipforward.h"
 #include "devif/devif.h"
+#include "nat/nat.h"
 
 /****************************************************************************
  * Private Data
@@ -206,6 +207,16 @@ int ipv4_input(FAR struct net_driver_s *dev)
       goto drop;
     }
 
+#ifdef CONFIG_NET_NAT
+  /* Try NAT inbound, rule matching will be performed in NAT module. */
+
+  if (ipv4_nat_inbound(dev, ipv4) < 0)
+    {
+      nwarn("WARNING: Performing NAT inbound failed!\n");
+      goto drop;
+    }
+#endif
+
   /* Get the destination IP address in a friendlier form */
 
   destipaddr = net_ip4addr_conv32(ipv4->destipaddr);
diff --git a/net/ipforward/ipv4_forward.c b/net/ipforward/ipv4_forward.c
index c0c549dc83..8da46a528c 100644
--- a/net/ipforward/ipv4_forward.c
+++ b/net/ipforward/ipv4_forward.c
@@ -38,6 +38,7 @@
 #include "utils/utils.h"
 #include "sixlowpan/sixlowpan.h"
 #include "ipforward/ipforward.h"
+#include "nat/nat.h"
 #include "devif/devif.h"
 
 #if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv4)
@@ -312,6 +313,18 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
       goto errout_with_iobchain;
     }
 
+#ifdef CONFIG_NET_NAT
+  /* Try NAT outbound, rule matching will be performed in NAT module. */
+
+  ret = ipv4_nat_outbound(fwd->f_dev,
+                          (FAR struct ipv4_hdr_s *)fwd->f_iob->io_data);
+  if (ret < 0)
+    {
+      nwarn("WARNING: Performing NAT outbound failed, dropping!\n");
+      goto errout_with_iobchain;
+    }
+#endif
+
   /* Then set up to forward the packet according to the protocol. */
 
   ret = ipfwd_forward(fwd);
diff --git a/net/nat/Kconfig b/net/nat/Kconfig
new file mode 100644
index 0000000000..f50d237695
--- /dev/null
+++ b/net/nat/Kconfig
@@ -0,0 +1,11 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config NET_NAT
+	bool "Network Address Translation (NAT)"
+	default n
+	depends on NET_IPFORWARD
+	---help---
+		Enable or disable Network Address Translation (NAT) function.
diff --git a/net/nat/Make.defs b/net/nat/Make.defs
new file mode 100644
index 0000000000..43d185837e
--- /dev/null
+++ b/net/nat/Make.defs
@@ -0,0 +1,34 @@
+############################################################################
+# net/nat/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+# NAT source files
+
+ifeq ($(CONFIG_NET_NAT),y)
+
+ifeq ($(CONFIG_NET_IPv4),y)
+NET_CSRCS += ipv4_nat.c ipv4_nat_entry.c
+endif
+
+# Include NAT build support
+
+DEPPATH += --dep-path nat
+VPATH += :nat
+
+endif
diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
new file mode 100644
index 0000000000..f1d44155fc
--- /dev/null
+++ b/net/nat/ipv4_nat.c
@@ -0,0 +1,324 @@
+/****************************************************************************
+ * net/nat/ipv4_nat.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <nuttx/net/tcp.h>
+
+#include "nat/nat.h"
+#include "utils/utils.h"
+
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Adjust checksums in headers. */
+
+#define chksum_adjust(chksum,old_data,new_data) \
+  net_chksum_adjust((FAR uint16_t *)&(chksum), \
+                    (FAR uint16_t *)&(old_data), sizeof(old_data), \
+                    (FAR uint16_t *)&(new_data), sizeof(new_data))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv4_nat_inbound_tcp
+ *
+ * Description:
+ *   Check if a received TCP 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_TCP
+static int ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4)
+{
+  uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
+  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);
+  if (!entry)
+    {
+      /* Inbound without entry is OK (e.g. towards NuttX itself), skip NAT. */
+
+      return OK;
+    }
+
+  /* Modify port and checksum. */
+
+  chksum_adjust(tcp->tcpchksum, tcp->destport, entry->local_port);
+  tcp->destport = entry->local_port;
+
+  /* Modify address and checksum. */
+
+  chksum_adjust(tcp->tcpchksum, 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_outbound_tcp
+ *
+ * Description:
+ *   Check if we want to perform NAT with this outbound TCP 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_TCP
+static int ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
+                                 FAR struct ipv4_hdr_s *ipv4)
+{
+  uint16_t iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
+  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);
+  if (!entry)
+    {
+      /* Outbound entry creation failed, should have corresponding entry. */
+
+      return -ENOMEM;
+    }
+
+  /* Modify port and checksum. */
+
+  chksum_adjust(tcp->tcpchksum, tcp->srcport, entry->external_port);
+  tcp->srcport = entry->external_port;
+
+  /* Modify address and checksum. */
+
+  chksum_adjust(tcp->tcpchksum, 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
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv4_nat_enable
+ *
+ * Description:
+ *   Enable NAT function on a network device.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the outbound packets will be masqueraded.
+ *
+ * Returned Value:
+ *   Zero is returned if NAT function is successfully enabled on the device;
+ *   A negated errno value is returned if failed.
+ *
+ * Assumptions:
+ *   NAT will only be enabled on at most one device.
+ *
+ * Limitations:
+ *   External ports are not isolated between devices yet, so if NAT is
+ *   enabled on more than one device, an external port used on one device
+ *   will also be used by same local ip:port on another device.
+ *
+ * TODO:
+ *   Support multiple NAT devices with isolated external port mapping.
+ ****************************************************************************/
+
+int ipv4_nat_enable(FAR struct net_driver_s *dev)
+{
+  if (IFF_IS_NAT(dev->d_flags))
+    {
+      nwarn("WARNING: NAT was already enabled for %s!\n", dev->d_ifname);
+      return -EEXIST;
+    }
+
+  IFF_SET_NAT(dev->d_flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ipv4_nat_disable
+ *
+ * Description:
+ *   Disable NAT function on a network device.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the NAT function will be disabled.
+ *
+ * Returned Value:
+ *   Zero is returned if NAT function is successfully disabled on the device;
+ *   A negated errno value is returned if failed.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_disable(FAR struct net_driver_s *dev)
+{
+  if (!IFF_IS_NAT(dev->d_flags))
+    {
+      nwarn("WARNING: NAT was not enabled for %s!\n", dev->d_ifname);
+      return -ENODEV;
+    }
+
+  /* TODO: Clear entries related to dev. */
+
+  IFF_CLR_NAT(dev->d_flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ipv4_nat_inbound
+ *
+ * Description:
+ *   Check if a received packet belongs to a NAT entry. If so, translate it.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet is received.
+ *   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.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_inbound(FAR struct net_driver_s *dev,
+                     FAR struct ipv4_hdr_s *ipv4)
+{
+  /* We only process packets from NAT device and targeting at the address
+   * assigned to the device.
+   */
+
+  if (IFF_IS_NAT(dev->d_flags) &&
+      net_ipv4addr_hdrcmp(ipv4->destipaddr, &dev->d_ipaddr))
+    {
+      switch (ipv4->proto)
+        {
+#ifdef CONFIG_NET_TCP
+          case IP_PROTO_TCP:
+            return ipv4_nat_inbound_tcp(ipv4);
+#endif
+
+#ifdef CONFIG_NET_UDP
+#         warning Missing logic
+#endif
+
+#ifdef CONFIG_NET_ICMP
+#         warning Missing logic
+#endif
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ipv4_nat_outbound
+ *
+ * Description:
+ *   Check if we want to perform NAT with this outbound packet before sending
+ *   it. If so, translate it.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet will be sent.
+ *   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.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_outbound(FAR struct net_driver_s *dev,
+                      FAR struct ipv4_hdr_s *ipv4)
+{
+  /* We only process packets targeting at NAT device but not targeting at the
+   * address assigned to the device.
+   */
+
+  if (IFF_IS_NAT(dev->d_flags) &&
+      !net_ipv4addr_hdrcmp(ipv4->destipaddr, &dev->d_ipaddr))
+    {
+      /* TODO: Skip broadcast? */
+
+      switch (ipv4->proto)
+        {
+#ifdef CONFIG_NET_TCP
+          case IP_PROTO_TCP:
+            return ipv4_nat_outbound_tcp(dev, ipv4);
+#endif
+
+#ifdef CONFIG_NET_UDP
+#         warning Missing logic
+#endif
+
+#ifdef CONFIG_NET_ICMP
+#         warning Missing logic
+#endif
+        }
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
new file mode 100644
index 0000000000..e0f7e92513
--- /dev/null
+++ b/net/nat/ipv4_nat_entry.c
@@ -0,0 +1,203 @@
+/****************************************************************************
+ * net/nat/ipv4_nat_entry.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/queue.h>
+
+#include "nat/nat.h"
+
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static sq_queue_t g_entries;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv4_nat_select_port
+ *
+ * Description:
+ *   Select an available port number for TCP/UDP protocol, or id for ICMP.
+ *
+ * Input Parameters:
+ *   protocol   - The L4 protocol of the packet.
+ *   local_port - The local port of the packet, as reference.
+ *
+ * Returned Value:
+ *   port number on success; 0 on failure
+ *
+ ****************************************************************************/
+
+static uint16_t ipv4_nat_select_port(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?
+   * TODO: Also need to modify local port number assignment.
+   */
+
+# warning Missing logic
+
+  return local_port;
+}
+
+/****************************************************************************
+ * Name: ipv4_nat_entry_create
+ *
+ * Description:
+ *   Create a NAT entry and insert into entry list.
+ *
+ * Input Parameters:
+ *   protocol      - The L4 protocol of the packet.
+ *   external_port - The external port of the packet.
+ *   local_ip      - The local ip of the packet.
+ *   local_port    - The local port of the packet.
+ *
+ * Returned Value:
+ *   Pointer to entry on success; null on failure
+ *
+ ****************************************************************************/
+
+static FAR struct ipv4_nat_entry *
+ipv4_nat_entry_create(uint8_t protocol, uint16_t external_port,
+                      in_addr_t local_ip, uint16_t local_port)
+{
+  FAR struct ipv4_nat_entry *entry =
+      (FAR struct ipv4_nat_entry *)kmm_malloc(sizeof(struct ipv4_nat_entry));
+  if (entry == NULL)
+    {
+      nwarn("WARNING: Failed to allocate IPv4 NAT entry\n");
+      return NULL;
+    }
+
+  entry->protocol      = protocol;
+  entry->external_port = external_port;
+  entry->local_ip      = local_ip;
+  entry->local_port    = local_port;
+
+  sq_addfirst((FAR sq_entry_t *)entry, &g_entries);
+  return entry;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv4_nat_inbound_entry_find
+ *
+ * Description:
+ *   Find the inbound entry in NAT entry list.
+ *
+ * Input Parameters:
+ *   protocol      - The L4 protocol of the packet.
+ *   external_port - The external port of the packet.
+ *
+ * Returned Value:
+ *   Pointer to entry on success; null on failure
+ *
+ ****************************************************************************/
+
+FAR struct ipv4_nat_entry *
+ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port)
+{
+  FAR sq_entry_t *p;
+  sq_for_every(&g_entries, p)
+    {
+      FAR struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p;
+      if (entry->protocol == protocol &&
+          entry->external_port == external_port)
+        {
+          /* TODO: Use hash table, or move recent node to head. */
+
+          return entry;
+        }
+    }
+
+  nwarn("WARNING: Failed to find IPv4 inbound NAT entry for "
+        "proto=%d, external_port=%d\n", protocol, external_port);
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: ipv4_nat_outbound_entry_find
+ *
+ * Description:
+ *   Find the outbound entry in NAT entry list. Create one if corresponding
+ *   entry does not exist.
+ *
+ * Input Parameters:
+ *   protocol   - The L4 protocol of the packet.
+ *   local_ip   - The local ip of the packet.
+ *   local_port - The local port of the packet.
+ *
+ * Returned Value:
+ *   Pointer to entry on success; null on failure
+ *
+ ****************************************************************************/
+
+FAR struct ipv4_nat_entry *
+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 struct ipv4_nat_entry *entry = (FAR struct ipv4_nat_entry *)p;
+      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. */
+
+          return entry;
+        }
+    }
+
+  /* Failed to find the entry, create one. */
+
+  ninfo("INFO: Failed to find IPv4 outbound NAT entry for "
+        "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);
+  if (!external_port)
+    {
+      nwarn("WARNING: Failed to find an available port!\n");
+      return NULL;
+    }
+
+  return ipv4_nat_entry_create(protocol, external_port, local_ip,
+                               local_port);
+}
+
+#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
diff --git a/net/nat/nat.h b/net/nat/nat.h
new file mode 100644
index 0000000000..c5b13e5a3e
--- /dev/null
+++ b/net/nat/nat.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+ * net/nat/nat.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __NET_NAT_NAT_H
+#define __NET_NAT_NAT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include <netinet/in.h>
+
+#include <nuttx/net/ip.h>
+#include <nuttx/net/netdev.h>
+
+#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct ipv4_nat_entry
+{
+  /* Support for single-linked lists.
+   *
+   * TODO: Implement a general hash table, and use it to optimize performance
+   * here.
+   */
+
+  FAR struct ipv4_nat_entry *flink;
+
+  /*  Local Network                             External Network
+   *                |----------------|
+   *   <local IP,   |                | <external IP,             <peer IP,
+   *     -----------|                |-----------------------------
+   *    local port> |                |  external port>            peer port>
+   *                |----------------|
+   *
+   * Full cone NAT on single WAN only need to save local ip:port and external
+   * port. For ICMP, save id in port field.
+   */
+
+  in_addr_t  local_ip;       /* IP address of the local (private) host. */
+  uint16_t   local_port;     /* Port of the local (private) host. */
+  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. */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv4_nat_enable
+ *
+ * Description:
+ *   Enable NAT function on a network device.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the outbound packets will be masqueraded.
+ *
+ * Returned Value:
+ *   Zero is returned if NAT function is successfully enabled on the device;
+ *   A negated errno value is returned if failed.
+ *
+ * Assumptions:
+ *   NAT will only be enabled on at most one device.
+ *
+ * Limitations:
+ *   External ports are not isolated between devices yet, so if NAT is
+ *   enabled on more than one device, an external port used on one device
+ *   will also be used by same local ip:port on another device.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_enable(FAR struct net_driver_s *dev);
+
+/****************************************************************************
+ * Name: ipv4_nat_disable
+ *
+ * Description:
+ *   Disable NAT function on a network device.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the NAT function will be disabled.
+ *
+ * Returned Value:
+ *   Zero is returned if NAT function is successfully disabled on the device;
+ *   A negated errno value is returned if failed.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_disable(FAR struct net_driver_s *dev);
+
+/****************************************************************************
+ * Name: ipv4_nat_inbound
+ *
+ * Description:
+ *   Check if a received packet belongs to a NAT entry. If so, translate it.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet is received.
+ *   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.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_inbound(FAR struct net_driver_s *dev,
+                     FAR struct ipv4_hdr_s *ipv4);
+
+/****************************************************************************
+ * Name: ipv4_nat_outbound
+ *
+ * Description:
+ *   Check if we want to perform NAT with this outbound packet before sending
+ *   it. If so, translate it.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet will be sent.
+ *   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.
+ *
+ ****************************************************************************/
+
+int ipv4_nat_outbound(FAR struct net_driver_s *dev,
+                      FAR struct ipv4_hdr_s *ipv4);
+
+/****************************************************************************
+ * Name: ipv4_nat_inbound_entry_find
+ *
+ * Description:
+ *   Find the inbound entry in NAT entry list.
+ *
+ * Input Parameters:
+ *   protocol      - The L4 protocol of the packet.
+ *   external_port - The external port of the packet.
+ *
+ * Returned Value:
+ *   Pointer to entry on success; null on failure
+ *
+ ****************************************************************************/
+
+FAR struct ipv4_nat_entry *
+ipv4_nat_inbound_entry_find(uint8_t protocol, uint16_t external_port);
+
+/****************************************************************************
+ * Name: ipv4_nat_outbound_entry_find
+ *
+ * Description:
+ *   Find the outbound entry in NAT entry list. Create one if corresponding
+ *   entry does not exist.
+ *
+ * Input Parameters:
+ *   protocol   - The L4 protocol of the packet.
+ *   local_ip   - The local ip of the packet.
+ *   local_port - The local port of the packet.
+ *
+ * Returned Value:
+ *   Pointer to entry on success; null on failure
+ *
+ ****************************************************************************/
+
+FAR struct ipv4_nat_entry *
+ipv4_nat_outbound_entry_find(uint8_t protocol, in_addr_t local_ip,
+                             uint16_t local_port);
+
+#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
+#endif /* __NET_NAT_NAT_H */


[incubator-nuttx] 07/07: Documentation: add NAT description

Posted by xi...@apache.org.
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 b1ad547deb254d41f6aa49d9e3067edea0cfd626
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Thu Nov 10 14:35:44 2022 +0800

    Documentation: add NAT description
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 Documentation/reference/os/nat.rst | 169 +++++++++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+)

diff --git a/Documentation/reference/os/nat.rst b/Documentation/reference/os/nat.rst
new file mode 100644
index 0000000000..978eec60ce
--- /dev/null
+++ b/Documentation/reference/os/nat.rst
@@ -0,0 +1,169 @@
+=================================
+Network Address Translation (NAT)
+=================================
+
+NuttX supports full cone NAT logic, which currently supports
+
+- TCP
+- UDP
+- ICMP ECHO (REQUEST & REPLY)
+
+Workflow
+========
+
+::
+
+  Local Network (LAN)                          External Network (WAN)
+                    |----------------|
+       <local IP,   |                | <external IP,             <peer IP,
+         -----------|                |-----------------------------
+        local port> |                |  external port>            peer port>
+                    |----------------|
+
+- Outbound
+
+  - **LAN** -> **Forward** -> **NAT** (only if targeting at WAN) -> **WAN**
+
+  - All packets from **LAN** and targeting at **WAN** will be masqueraded
+    with ``local ip:port`` changed to ``external ip:port``.
+
+- Inbound
+
+  - **WAN** -> **NAT** (only from WAN, change destination) -> **Forward** -> **LAN**
+
+  - Packets from **WAN** will try to be changed back from
+    ``external ip:port`` to ``local ip:port`` and send to **LAN**.
+
+Configuration Options
+=====================
+
+``CONFIG_NET_NAT``
+  Enable or disable Network Address Translation (NAT) function.
+  Depends on ``CONFIG_NET_IPFORWARD``.
+``CONFIG_NET_NAT_TCP_EXPIRE_SEC``
+  The expiration time for idle TCP entry in NAT.
+  The default value 86400 is suggested by RFC2663, Section 2.6,
+  Page 5. But we may set it to shorter time like 240s for better
+  performance.
+``CONFIG_NET_NAT_UDP_EXPIRE_SEC``
+  The expiration time for idle UDP entry in NAT.
+``CONFIG_NET_NAT_ICMP_EXPIRE_SEC``
+  The expiration time for idle ICMP entry in NAT.
+
+Usage
+=====
+
+  - :c:func:`ipv4_nat_enable()`
+  - :c:func:`ipv4_nat_disable()`
+
+.. c:function:: int ipv4_nat_enable(FAR struct net_driver_s *dev);
+
+  Enable NAT function on a network device, on which the outbound packets
+  will be masqueraded.
+
+  Note that NAT is currently designed to be enabled on single device, it
+  may work when enabled on multiple devices, but external ports will not
+  be isolated between devices, so an external port used on one NAT device
+  will also be used by same local ip:port on another NAT device.
+
+  :return: Zero is returned if NAT function is successfully enabled on
+    the device; A negated errno value is returned if failed.
+
+.. c:function:: int ipv4_nat_disable(FAR struct net_driver_s *dev);
+
+  Disable NAT function on a network device.
+
+  :return: Zero is returned if NAT function is successfully disabled on
+    the device; A negated errno value is returned if failed.
+
+Validation
+==========
+
+Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps:
+
+1. Configure NuttX with >=2 TAP devices (host route mode) and NAT enabled:
+
+  ..  code-block:: Kconfig
+
+      CONFIG_NET_IPFORWARD=y
+      CONFIG_NET_NAT=y
+      # CONFIG_SIM_NET_BRIDGE is not set
+      CONFIG_SIM_NETDEV_NUMBER=2
+
+2. Call ``ipv4_nat_enable`` on one dev on startup
+
+  ..  code-block:: c
+
+      /* arch/sim/src/sim/up_netdriver.c */
+      int netdriver_init(void)
+      {
+        ...
+        ipv4_nat_enable(&g_sim_dev[0]);
+        ...
+      }
+
+3. Set IP Address for NuttX on startup
+
+  ..  code-block:: shell
+
+    ifconfig eth0 10.0.1.2
+    ifup eth0
+    ifconfig eth1 10.0.10.2
+    ifup eth1
+
+4. Configure IP & namespace & route on host side (maybe need to be root, then try ``sudo -i``)
+
+  ..  code-block:: bash
+
+    IF_HOST="enp1s0"
+    IF_0="tap0"
+    IP_HOST_0="10.0.1.1"
+    IF_1="tap1"
+    IP_HOST_1="10.0.10.1"
+    IP_NUTTX_1="10.0.10.2"
+
+    # add net namespace LAN for $IF_1
+    ip netns add LAN
+    ip netns exec LAN sysctl -w net.ipv4.ip_forward=1
+    ip link set $IF_1 netns LAN
+    ip netns exec LAN ip link set $IF_1 up
+    ip netns exec LAN ip link set lo up
+
+    # add address and set default route
+    ip addr add $IP_HOST_0/24 dev $IF_0
+    ip netns exec LAN ip addr add $IP_HOST_1/24 dev $IF_1
+    ip netns exec LAN ip route add default dev $IF_1 via $IP_NUTTX_1
+
+    # nat to allow NuttX to access the internet
+    iptables -t nat -A POSTROUTING -o $IF_HOST -j MASQUERADE
+    iptables -A FORWARD -i $IF_HOST -o $IF_0 -j ACCEPT
+    iptables -A FORWARD -i $IF_0 -o $IF_HOST -j ACCEPT
+    sysctl -w net.ipv4.ip_forward=1
+
+5. Do anything in the LAN namespace will go through NAT
+
+  ..  code-block:: shell
+
+    # Host side
+    iperf -B 10.0.1.1 -s -i 1
+    # LAN side
+    sudo ip netns exec LAN iperf -B 10.0.10.1 -c 10.0.1.1 -i 1
+
+  ..  code-block:: shell
+
+    # Host side
+    python3 -m http.server
+    # LAN side
+    for i in {1..20000}; do sudo ip netns exec LAN curl 'http://10.0.1.1:8000/' > /dev/null 2>1; done
+
+  ..  code-block:: shell
+
+    # LAN side
+    sudo ip netns exec LAN ping 8.8.8.8
+
+  ..  code-block:: shell
+
+    # Host side
+    tcpdump -nn -i tap0
+    # LAN side
+    sudo ip netns exec LAN tcpdump -nn -i tap1


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

Posted by xi...@apache.org.
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


[incubator-nuttx] 05/07: net/nat: Add ICMP ECHO (REQUEST & REPLY) support

Posted by xi...@apache.org.
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
   }
 }


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

Posted by xi...@apache.org.
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
  *