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

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

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 */