mirror of
https://github.com/apache/nuttx.git
synced 2026-05-20 04:16:35 +08:00
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 <wengzhe@xiaomi.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+31
-3
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+14
-1
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user