This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new a1a09f271f net/nat: Support Symmetric NAT
a1a09f271f is described below
commit a1a09f271fe8ea0b34b3f8ae1576edbd8d314b80
Author: Zhe Weng <[email protected]>
AuthorDate: Thu Mar 14 15:22:13 2024 +0800
net/nat: Support Symmetric NAT
The symmetric NAT limits one external port to be used with only one peer
ip:port.
Note:
1. To avoid using too much #ifdef, we're always passing peer_ip and
peer_port as arguments, but won't use them under full cone NAT, let the
compiler optimize them.
2. We need to find port binding without peer ip:port, so don't add peer
ip:port into hash key.
3. Symmetric NAT needs to *select another external port if a port is used
by any other NAT entry*, this behavior is exactly same as Full Cone NAT, so we
don't need to change anything related to `ipv4_nat_port_inuse`.
Signed-off-by: Zhe Weng <[email protected]>
---
Documentation/components/net/nat.rst | 15 +++++++++++++--
net/nat/Kconfig | 19 +++++++++++++++++++
net/nat/ipv4_nat.c | 34 +++++++++++++++++++++++++++++++---
net/nat/ipv4_nat_entry.c | 36 +++++++++++++++++++++++++++++++-----
net/nat/nat.h | 15 ++++++++++++++-
5 files changed, 108 insertions(+), 11 deletions(-)
diff --git a/Documentation/components/net/nat.rst
b/Documentation/components/net/nat.rst
index 01215356b0..773bb5bf1a 100644
--- a/Documentation/components/net/nat.rst
+++ b/Documentation/components/net/nat.rst
@@ -2,7 +2,7 @@
Network Address Translation (NAT)
=================================
-NuttX supports full cone NAT logic, which currently supports
+NuttX supports full cone or symmetric NAT logic, which currently supports
- TCP
@@ -46,6 +46,12 @@ Configuration Options
``CONFIG_NET_NAT``
Enable or disable Network Address Translation (NAT) function.
Depends on ``CONFIG_NET_IPFORWARD``.
+``CONFIG_NET_NAT_FULL_CONE``
+ Enable Full Cone NAT logic. Full Cone NAT is easier to traverse than
+ Symmetric NAT, and uses less resources than Symmetric NAT.
+``CONFIG_NET_NAT_SYMMETRIC``
+ Enable Symmetric NAT logic. Symmetric NAT will be safer than Full Cone NAT,
+ be more difficult to traverse, and has more entries which may lead to
heavier load.
``CONFIG_NET_NAT_HASH_BITS``
The bits of the hashtable of NAT entries, hashtable has (1 << bits) buckets.
``CONFIG_NET_NAT_TCP_EXPIRE_SEC``
@@ -101,7 +107,8 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by
following steps:
# CONFIG_SIM_NET_BRIDGE is not set
CONFIG_SIM_NETDEV_NUMBER=2
-2. Call ``ipv4_nat_enable`` on one dev on startup
+2. Call ``ipv4_nat_enable`` on one dev on startup, or manually enable NAT
+ with ``iptables`` command (either may work).
.. code-block:: c
@@ -113,6 +120,10 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by
following steps:
...
}
+ .. code-block:: shell
+
+ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+
3. Set IP Address for NuttX on startup
.. code-block:: shell
diff --git a/net/nat/Kconfig b/net/nat/Kconfig
index 0adc09c537..0ce9e71b1d 100644
--- a/net/nat/Kconfig
+++ b/net/nat/Kconfig
@@ -15,6 +15,25 @@ config NET_NAT
and NAT may need a continuous buffer of at least 68 Bytes
(IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B).
+choice
+ prompt "NAT Type"
+ default NET_NAT_FULL_CONE
+ depends on NET_NAT
+
+config NET_NAT_FULL_CONE
+ bool "Full Cone NAT"
+ ---help---
+ Full Cone NAT is easier to traverse than Symmetric NAT, and uses
+ less resources than Symmetric NAT.
+
+config NET_NAT_SYMMETRIC
+ bool "Symmetric NAT"
+ ---help---
+ Symmetric NAT will be safer than Full Cone NAT, be more
difficult
+ to traverse, and has more entries which may lead to heavier
load.
+
+endchoice
+
config NET_NAT_HASH_BITS
int "The bits of NAT entry hashtable"
default 5
diff --git a/net/nat/ipv4_nat.c b/net/nat/ipv4_nat.c
index b575a273c3..a7d28c72fe 100644
--- a/net/nat/ipv4_nat.c
+++ b/net/nat/ipv4_nat.c
@@ -58,6 +58,14 @@
#define MANIP_PORT(l4hdr,manip_type) \
((manip_type) == NAT_MANIP_SRC ? &(l4hdr)->srcport : &(l4hdr)->destport)
+/* Getting peer IP & Port (just other than MANIP) */
+
+#define PEER_IPADDR(iphdr,manip_type) \
+ ((manip_type) != NAT_MANIP_SRC ? (iphdr)->srcipaddr : (iphdr)->destipaddr)
+
+#define PEER_PORT(l4hdr,manip_type) \
+ ((manip_type) != NAT_MANIP_SRC ? &(l4hdr)->srcport : &(l4hdr)->destport)
+
/* Getting L4 header from IPv4 header. */
#define L4_HDR(ipv4) \
@@ -204,10 +212,14 @@ ipv4_nat_inbound_tcp(FAR struct ipv4_hdr_s *ipv4,
FAR struct tcp_hdr_s *tcp = L4_HDR(ipv4);
FAR uint16_t *external_ip = MANIP_IPADDR(ipv4, manip_type);
FAR uint16_t *external_port = MANIP_PORT(tcp, manip_type);
+ FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type);
+ FAR uint16_t *peer_port = PEER_PORT(tcp, manip_type);
FAR struct ipv4_nat_entry *entry =
ipv4_nat_inbound_entry_find(IP_PROTO_TCP,
net_ip4addr_conv32(external_ip),
- *external_port, true);
+ *external_port,
+ net_ip4addr_conv32(peer_ip),
+ *peer_port, true);
if (!entry)
{
return NULL;
@@ -253,11 +265,15 @@ ipv4_nat_inbound_udp(FAR struct ipv4_hdr_s *ipv4,
FAR struct udp_hdr_s *udp = L4_HDR(ipv4);
FAR uint16_t *external_ip = MANIP_IPADDR(ipv4, manip_type);
FAR uint16_t *external_port = MANIP_PORT(udp, manip_type);
+ FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type);
+ FAR uint16_t *peer_port = PEER_PORT(udp, manip_type);
FAR uint16_t *udpchksum;
FAR struct ipv4_nat_entry *entry =
ipv4_nat_inbound_entry_find(IP_PROTO_UDP,
net_ip4addr_conv32(external_ip),
- *external_port, true);
+ *external_port,
+ net_ip4addr_conv32(peer_ip),
+ *peer_port, true);
if (!entry)
{
@@ -302,6 +318,7 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4,
{
FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4);
FAR uint16_t *external_ip;
+ FAR uint16_t *peer_ip;
FAR struct ipv4_nat_entry *entry;
switch (icmp->type)
@@ -309,8 +326,11 @@ ipv4_nat_inbound_icmp(FAR struct ipv4_hdr_s *ipv4,
case ICMP_ECHO_REQUEST:
case ICMP_ECHO_REPLY:
external_ip = MANIP_IPADDR(ipv4, manip_type);
+ peer_ip = PEER_IPADDR(ipv4, manip_type);
entry = ipv4_nat_inbound_entry_find(IP_PROTO_ICMP,
net_ip4addr_conv32(external_ip),
+ icmp->id,
+ net_ip4addr_conv32(peer_ip),
icmp->id, true);
if (!entry)
{
@@ -424,12 +444,15 @@ ipv4_nat_outbound_tcp(FAR struct net_driver_s *dev,
FAR struct tcp_hdr_s *tcp = L4_HDR(ipv4);
FAR uint16_t *local_ip = MANIP_IPADDR(ipv4, manip_type);
FAR uint16_t *local_port = MANIP_PORT(tcp, manip_type);
+ FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type);
+ FAR uint16_t *peer_port = PEER_PORT(tcp, manip_type);
FAR struct ipv4_nat_entry *entry;
/* Only create entry when it's the outermost packet (manip type is SRC). */
entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_TCP,
net_ip4addr_conv32(local_ip), *local_port,
+ net_ip4addr_conv32(peer_ip), *peer_port,
(manip_type == NAT_MANIP_SRC));
if (!entry)
{
@@ -477,6 +500,8 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
FAR struct udp_hdr_s *udp = L4_HDR(ipv4);
FAR uint16_t *local_ip = MANIP_IPADDR(ipv4, manip_type);
FAR uint16_t *local_port = MANIP_PORT(udp, manip_type);
+ FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type);
+ FAR uint16_t *peer_port = PEER_PORT(udp, manip_type);
FAR uint16_t *udpchksum;
FAR struct ipv4_nat_entry *entry;
@@ -484,6 +509,7 @@ ipv4_nat_outbound_udp(FAR struct net_driver_s *dev,
entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_UDP,
net_ip4addr_conv32(local_ip), *local_port,
+ net_ip4addr_conv32(peer_ip), *peer_port,
(manip_type == NAT_MANIP_SRC));
if (!entry)
{
@@ -529,6 +555,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
{
FAR struct icmp_hdr_s *icmp = L4_HDR(ipv4);
FAR uint16_t *local_ip = MANIP_IPADDR(ipv4, manip_type);
+ FAR uint16_t *peer_ip = PEER_IPADDR(ipv4, manip_type);
FAR struct ipv4_nat_entry *entry;
switch (icmp->type)
@@ -542,6 +569,7 @@ ipv4_nat_outbound_icmp(FAR struct net_driver_s *dev,
entry = ipv4_nat_outbound_entry_find(dev, IP_PROTO_ICMP,
net_ip4addr_conv32(local_ip), icmp->id,
+ net_ip4addr_conv32(peer_ip), icmp->id,
(manip_type == NAT_MANIP_SRC));
if (!entry)
{
@@ -894,7 +922,7 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
bool ipv4_nat_port_inuse(uint8_t protocol, in_addr_t ip, uint16_t port)
{
FAR struct ipv4_nat_entry *entry =
- ipv4_nat_inbound_entry_find(protocol, ip, port, false);
+ ipv4_nat_inbound_entry_find(protocol, ip, port, INADDR_ANY, 0, false);
return entry != NULL;
}
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index e32b3c73c9..29b6e1cde3 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -295,6 +295,8 @@ static void ipv4_nat_entry_refresh(FAR struct
ipv4_nat_entry *entry)
* external_port - The external port of the packet.
* local_ip - The local ip of the packet.
* local_port - The local port of the packet.
+ * peer_ip - The peer ip of the packet.
+ * peer_port - The peer port of the packet.
*
* Returned Value:
* Pointer to entry on success; null on failure
@@ -304,7 +306,8 @@ static void ipv4_nat_entry_refresh(FAR struct
ipv4_nat_entry *entry)
static FAR struct ipv4_nat_entry *
ipv4_nat_entry_create(uint8_t protocol,
in_addr_t external_ip, uint16_t external_port,
- in_addr_t local_ip, uint16_t local_port)
+ in_addr_t local_ip, uint16_t local_port,
+ in_addr_t peer_ip, uint16_t peer_port)
{
FAR struct ipv4_nat_entry *entry =
kmm_malloc(sizeof(struct ipv4_nat_entry));
@@ -319,6 +322,10 @@ ipv4_nat_entry_create(uint8_t protocol,
entry->external_port = external_port;
entry->local_ip = local_ip;
entry->local_port = local_port;
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+ entry->peer_ip = peer_ip;
+ entry->peer_port = peer_port;
+#endif
ipv4_nat_entry_refresh(entry);
@@ -456,6 +463,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev)
* protocol - The L4 protocol of the packet.
* external_ip - The external ip of the packet, supports INADDR_ANY.
* external_port - The external port of the packet.
+ * peer_ip - The peer ip of the packet.
+ * peer_port - The peer port of the packet.
* refresh - Whether to refresh the selected entry.
*
* Returned Value:
@@ -465,11 +474,15 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev)
FAR struct ipv4_nat_entry *
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
- uint16_t external_port, bool refresh)
+ uint16_t external_port, in_addr_t peer_ip,
+ uint16_t peer_port, bool refresh)
{
FAR hash_node_t *p;
FAR hash_node_t *tmp;
bool skip_ip = net_ipv4addr_cmp(external_ip, INADDR_ANY);
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+ bool skip_peer = net_ipv4addr_cmp(peer_ip, INADDR_ANY);
+#endif
int32_t current_time = TICK2SEC(clock_systime_ticks());
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
@@ -492,7 +505,12 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t
external_ip,
if (entry->protocol == protocol &&
(skip_ip || net_ipv4addr_cmp(entry->external_ip, external_ip)) &&
- entry->external_port == external_port)
+ entry->external_port == external_port
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+ && (skip_peer || (net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
+ entry->peer_port == peer_port))
+#endif
+ )
{
if (refresh)
{
@@ -525,6 +543,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t
external_ip,
* protocol - The L4 protocol of the packet.
* local_ip - The local ip of the packet.
* local_port - The local port of the packet.
+ * peer_ip - The peer ip of the packet.
+ * peer_port - The peer port of the packet.
* try_create - Try create the entry if no entry found.
*
* Returned Value:
@@ -535,6 +555,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t
external_ip,
FAR struct ipv4_nat_entry *
ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
in_addr_t local_ip, uint16_t local_port,
+ in_addr_t peer_ip, uint16_t peer_port,
bool try_create)
{
FAR hash_node_t *p;
@@ -562,7 +583,12 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev,
uint8_t protocol,
if (entry->protocol == protocol &&
net_ipv4addr_cmp(entry->external_ip, dev->d_ipaddr) &&
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
- entry->local_port == local_port)
+ entry->local_port == local_port
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+ && net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
+ entry->peer_port == peer_port
+#endif
+ )
{
ipv4_nat_entry_refresh(entry);
return entry;
@@ -588,7 +614,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev,
uint8_t protocol,
}
return ipv4_nat_entry_create(protocol, dev->d_ipaddr, external_port,
- local_ip, local_port);
+ local_ip, local_port, peer_ip, peer_port);
}
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
diff --git a/net/nat/nat.h b/net/nat/nat.h
index 4c70ceeb95..6273ecc131 100644
--- a/net/nat/nat.h
+++ b/net/nat/nat.h
@@ -55,13 +55,20 @@ struct ipv4_nat_entry
* |----------------|
*
* Full cone NAT only need to save local ip:port and external ip:port.
+ * Symmetric NAT need to save peer ip:port as well.
* For ICMP, save id in port field.
*/
in_addr_t local_ip; /* IP address of the local (private) host. */
in_addr_t external_ip; /* External IP address. */
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+ in_addr_t peer_ip; /* Peer IP address. */
+#endif
uint16_t local_port; /* Port of the local (private) host. */
uint16_t external_port; /* The external port of local (private) host. */
+#ifdef CONFIG_NET_NAT_SYMMETRIC
+ uint16_t peer_port; /* Peer port. */
+#endif
uint8_t protocol; /* L4 protocol (TCP, UDP etc). */
int32_t expire_time; /* The expiration time of this entry. */
@@ -203,6 +210,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
* protocol - The L4 protocol of the packet.
* external_ip - The external ip of the packet, supports INADDR_ANY.
* external_port - The external port of the packet.
+ * peer_ip - The peer ip of the packet.
+ * peer_port - The peer port of the packet.
* refresh - Whether to refresh the selected entry.
*
* Returned Value:
@@ -212,7 +221,8 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
FAR struct ipv4_nat_entry *
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
- uint16_t external_port, bool refresh);
+ uint16_t external_port, in_addr_t peer_ip,
+ uint16_t peer_port, bool refresh);
/****************************************************************************
* Name: ipv4_nat_outbound_entry_find
@@ -226,6 +236,8 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t
external_ip,
* protocol - The L4 protocol of the packet.
* local_ip - The local ip of the packet.
* local_port - The local port of the packet.
+ * peer_ip - The peer ip of the packet.
+ * peer_port - The peer port of the packet.
* try_create - Try create the entry if no entry found.
*
* Returned Value:
@@ -236,6 +248,7 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t
external_ip,
FAR struct ipv4_nat_entry *
ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
in_addr_t local_ip, uint16_t local_port,
+ in_addr_t peer_ip, uint16_t peer_port,
bool try_create);
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */