net/nat: Support IPv6 Masquerading (NAT66)
Notes: 1. This version of NAT66 is a stateful one like NAT44, corresponding to Linux's MASQUERADE target of ip6tables. We can support stateless NAT66 & NPTv6 later by slightly modify the address & port selection logic (maybe just match the rules and skip the entry find). 2. We're using same flag `IFF_NAT` for both NAT44 & NAT66 to make control easier. Which means, if we enable NAT, both NAT44 & NAT66 will be enabled. If we don't want one of them, we can just disable that one in Kconfig. 3. Maybe we can accelerate the checksum adjustment by pre-calculate a difference of checksum, and apply it to each packet, instead of calling `net_chksum_adjust` each time. Just a thought, maybe do it later. 4. IP fragment segments on NAT66 connections are not supported yet. Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
parent
676826cb7c
commit
f3b34c84c2
@ -46,10 +46,13 @@ Configuration Options
|
||||
``CONFIG_NET_NAT``
|
||||
Enable or disable Network Address Translation (NAT) function.
|
||||
Depends on ``CONFIG_NET_IPFORWARD``.
|
||||
``CONFIG_NET_NAT_FULL_CONE``
|
||||
``CONFIG_NET_NAT44`` & ``CONFIG_NET_NAT66``
|
||||
Enable or disable NAT on IPv4 / IPv6.
|
||||
Depends on ``CONFIG_NET_NAT``.
|
||||
``CONFIG_NET_NAT44_FULL_CONE`` & ``CONFIG_NET_NAT66_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``
|
||||
``CONFIG_NET_NAT44_SYMMETRIC`` & ``CONFIG_NET_NAT66_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``
|
||||
@ -63,6 +66,8 @@ Configuration Options
|
||||
The expiration time for idle UDP entry in NAT.
|
||||
``CONFIG_NET_NAT_ICMP_EXPIRE_SEC``
|
||||
The expiration time for idle ICMP entry in NAT.
|
||||
``CONFIG_NET_NAT_ICMPv6_EXPIRE_SEC``
|
||||
The expiration time for idle ICMPv6 entry in NAT.
|
||||
``CONFIG_NET_NAT_ENTRY_RECLAIM_SEC``
|
||||
The time to auto reclaim all expired NAT entries. A value of zero will
|
||||
disable auto reclaiming.
|
||||
@ -133,6 +138,10 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps:
|
||||
ifconfig eth1 10.0.10.2
|
||||
ifup eth1
|
||||
|
||||
# IPv6 if you need
|
||||
ifconfig eth0 inet6 add fc00:1::2/64 gw fc00:1::1
|
||||
ifconfig eth1 inet6 add fc00:10::2/64
|
||||
|
||||
4. Configure IP & namespace & route on host side (maybe need to be root, then try ``sudo -i``)
|
||||
|
||||
.. code-block:: bash
|
||||
@ -162,6 +171,22 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps:
|
||||
iptables -A FORWARD -i $IF_0 -o $IF_HOST -j ACCEPT
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
# IPv6 if you need
|
||||
IP6_HOST_0="fc00:1::1"
|
||||
IP6_HOST_1="fc00:10::1"
|
||||
IP6_NUTTX_1="fc00:10::2"
|
||||
|
||||
# add address and set default route
|
||||
ip -6 addr add $IP6_HOST_0/64 dev $IF_0
|
||||
ip netns exec LAN ip -6 addr add $IP6_HOST_1/64 dev $IF_1
|
||||
ip netns exec LAN ip -6 route add default dev $IF_1 via $IP6_NUTTX_1
|
||||
|
||||
# nat to allow NuttX to access the internet
|
||||
ip6tables -t nat -A POSTROUTING -o $IF_HOST -j MASQUERADE
|
||||
ip6tables -A FORWARD -i $IF_HOST -o $IF_0 -j ACCEPT
|
||||
ip6tables -A FORWARD -i $IF_0 -o $IF_HOST -j ACCEPT
|
||||
sysctl -w net.ipv6.conf.all.forwarding=1
|
||||
|
||||
5. Do anything in the LAN namespace will go through NAT
|
||||
|
||||
.. code-block:: shell
|
||||
@ -174,20 +199,26 @@ Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps:
|
||||
.. code-block:: shell
|
||||
|
||||
# Host side
|
||||
python3 -m http.server
|
||||
python3 -m http.server -b ::
|
||||
# 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
|
||||
for i in {1..20000}; do sudo ip netns exec LAN curl 'http://[fc00:1::1]:8000/' > /dev/null 2>1; done
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# LAN side
|
||||
sudo ip netns exec LAN ping 8.8.8.8
|
||||
sudo ip netns exec LAN ping 2001:4860:4860::8888
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# LAN side
|
||||
sudo ip netns exec LAN traceroute -n 8.8.8.8 # ICMP error msg of UDP
|
||||
sudo ip netns exec LAN traceroute -n -T 8.8.8.8 # ICMP error msg of TCP
|
||||
sudo ip netns exec LAN traceroute -n -I 8.8.8.8 # ICMP error msg of ICMP
|
||||
sudo ip netns exec LAN traceroute -n 2001:4860:4860::8888
|
||||
sudo ip netns exec LAN traceroute -n -T 2001:4860:4860::8888
|
||||
sudo ip netns exec LAN traceroute -n -I 2001:4860:4860::8888
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
|
@ -235,14 +235,10 @@ static int ipv4_in(FAR struct net_driver_s *dev)
|
||||
goto drop;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_NAT
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
/* 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;
|
||||
}
|
||||
ipv4_nat_inbound(dev, ipv4);
|
||||
#endif
|
||||
|
||||
/* Get the destination IP address in a friendlier form */
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "pkt/pkt.h"
|
||||
#include "icmpv6/icmpv6.h"
|
||||
|
||||
#include "nat/nat.h"
|
||||
#include "netdev/netdev.h"
|
||||
#include "ipforward/ipforward.h"
|
||||
#include "inet/inet.h"
|
||||
@ -296,6 +297,12 @@ static int ipv6_in(FAR struct net_driver_s *dev)
|
||||
nxthdr = exthdr->nxthdr;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
/* Try NAT inbound, rule matching will be performed in NAT module. */
|
||||
|
||||
ipv6_nat_inbound(dev, ipv6);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_BROADCAST
|
||||
/* Check for a multicast packet, which may be destined to us (even if
|
||||
* there is no IP address yet assigned to the device). We only expect
|
||||
|
@ -286,13 +286,13 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
|
||||
goto errout_with_fwd;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_NAT
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
/* Try NAT outbound, rule matching will be performed in NAT module. */
|
||||
|
||||
ret = ipv4_nat_outbound(fwd->f_dev, ipv4, NAT_MANIP_SRC);
|
||||
if (ret < 0)
|
||||
{
|
||||
nwarn("WARNING: Performing NAT outbound failed, dropping!\n");
|
||||
nwarn("WARNING: Performing NAT44 outbound failed, dropping!\n");
|
||||
goto errout_with_fwd;
|
||||
}
|
||||
#endif
|
||||
@ -532,13 +532,13 @@ drop:
|
||||
|
||||
#ifdef CONFIG_NET_ICMP
|
||||
reply:
|
||||
# ifdef CONFIG_NET_NAT
|
||||
# ifdef CONFIG_NET_NAT44
|
||||
/* Before we reply ICMP, call NAT outbound to try to translate destination
|
||||
* address & port back to original status.
|
||||
*/
|
||||
|
||||
ipv4_nat_outbound(dev, ipv4, NAT_MANIP_DST);
|
||||
# endif /* CONFIG_NET_NAT */
|
||||
# endif /* CONFIG_NET_NAT44 */
|
||||
|
||||
icmp_reply(dev, icmp_reply_type, icmp_reply_code);
|
||||
return OK;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <nuttx/net/netdev.h>
|
||||
#include <nuttx/net/netstats.h>
|
||||
|
||||
#include "nat/nat.h"
|
||||
#include "netdev/netdev.h"
|
||||
#include "sixlowpan/sixlowpan.h"
|
||||
#include "devif/devif.h"
|
||||
@ -423,6 +424,17 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev,
|
||||
goto errout_with_fwd;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
/* Try NAT outbound, rule matching will be performed in NAT module. */
|
||||
|
||||
ret = ipv6_nat_outbound(fwd->f_dev, ipv6, NAT_MANIP_SRC);
|
||||
if (ret < 0)
|
||||
{
|
||||
nwarn("WARNING: Performing NAT66 outbound failed, dropping!\n");
|
||||
goto errout_with_fwd;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Then set up to forward the packet according to the protocol. */
|
||||
|
||||
ret = ipfwd_forward(fwd);
|
||||
@ -560,6 +572,11 @@ int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6)
|
||||
{
|
||||
FAR struct net_driver_s *fwddev;
|
||||
int ret;
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
int icmpv6_reply_type;
|
||||
int icmpv6_reply_code;
|
||||
int icmpv6_reply_data;
|
||||
#endif /* CONFIG_NET_ICMP */
|
||||
|
||||
/* Search for a device that can forward this packet. */
|
||||
|
||||
@ -659,18 +676,22 @@ drop:
|
||||
switch (ret)
|
||||
{
|
||||
case -ENETUNREACH:
|
||||
icmpv6_reply(dev, ICMPv6_DEST_UNREACHABLE, ICMPv6_ADDR_UNREACH, 0);
|
||||
return OK;
|
||||
icmpv6_reply_type = ICMPv6_DEST_UNREACHABLE;
|
||||
icmpv6_reply_code = ICMPv6_ADDR_UNREACH;
|
||||
icmpv6_reply_data = 0;
|
||||
goto reply;
|
||||
|
||||
case -EFBIG:
|
||||
icmpv6_reply(dev, ICMPv6_PACKET_TOO_BIG, 0,
|
||||
NETDEV_PKTSIZE(fwddev) - NET_LL_HDRLEN(fwddev));
|
||||
return OK;
|
||||
icmpv6_reply_type = ICMPv6_PACKET_TOO_BIG;
|
||||
icmpv6_reply_code = 0;
|
||||
icmpv6_reply_data = NETDEV_PKTSIZE(fwddev) - NET_LL_HDRLEN(fwddev);
|
||||
goto reply;
|
||||
|
||||
case -EMULTIHOP:
|
||||
icmpv6_reply(dev, ICMPv6_PACKET_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
|
||||
0);
|
||||
return OK;
|
||||
icmpv6_reply_type = ICMPv6_PACKET_TIME_EXCEEDED;
|
||||
icmpv6_reply_code = ICMPV6_EXC_HOPLIMIT;
|
||||
icmpv6_reply_data = 0;
|
||||
goto reply;
|
||||
|
||||
default:
|
||||
break; /* We don't know how to reply, just go on (to drop). */
|
||||
@ -679,6 +700,20 @@ drop:
|
||||
|
||||
dev->d_len = 0;
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
reply:
|
||||
# ifdef CONFIG_NET_NAT66
|
||||
/* Before we reply ICMPv6, call NAT outbound to try to translate
|
||||
* destination address & port back to original status.
|
||||
*/
|
||||
|
||||
ipv6_nat_outbound(dev, ipv6, NAT_MANIP_DST);
|
||||
# endif /* CONFIG_NET_NAT66 */
|
||||
|
||||
icmpv6_reply(dev, icmpv6_reply_type, icmpv6_reply_code, icmpv6_reply_data);
|
||||
return OK;
|
||||
#endif /* CONFIG_NET_ICMP */
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -24,10 +24,14 @@ if(CONFIG_NET_NAT)
|
||||
|
||||
list(APPEND SRCS nat.c)
|
||||
|
||||
if(CONFIG_NET_IPv4)
|
||||
if(CONFIG_NET_NAT44)
|
||||
list(APPEND SRCS ipv4_nat.c ipv4_nat_entry.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_NET_NAT66)
|
||||
list(APPEND SRCS ipv6_nat.c ipv6_nat_entry.c)
|
||||
endif()
|
||||
|
||||
target_sources(net PRIVATE ${SRCS})
|
||||
|
||||
endif()
|
||||
|
@ -6,27 +6,58 @@
|
||||
config NET_NAT
|
||||
bool "Network Address Translation (NAT)"
|
||||
default n
|
||||
depends on NET_IPFORWARD && IOB_BUFSIZE >= 68
|
||||
depends on NET_IPFORWARD
|
||||
---help---
|
||||
Enable or disable Network Address Translation (NAT) function.
|
||||
|
||||
Note: When forwarding IPv4 packet and applying NAT, NAT may be
|
||||
applied directly on a single I/O buffer containing L3 packet header,
|
||||
and NAT may need a continuous buffer of at least 68 Bytes
|
||||
(IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B).
|
||||
(IPv4 20B + ICMP 8B + IPv4 20B + TCP 20B). And 108 Bytes for IPv6.
|
||||
|
||||
config NET_NAT44
|
||||
bool "IPv4-to-IPv4 NAT (NAT44)"
|
||||
default y
|
||||
depends on NET_IPv4 && NET_NAT
|
||||
depends on IOB_BUFSIZE >= 68
|
||||
|
||||
choice
|
||||
prompt "NAT Type"
|
||||
default NET_NAT_FULL_CONE
|
||||
depends on NET_NAT
|
||||
prompt "NAT44 Type"
|
||||
default NET_NAT44_FULL_CONE
|
||||
depends on NET_NAT44
|
||||
|
||||
config NET_NAT_FULL_CONE
|
||||
config NET_NAT44_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
|
||||
config NET_NAT44_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_NAT66
|
||||
bool "IPv6-to-IPv6 NAT (NAT66)"
|
||||
default y
|
||||
depends on NET_IPv6 && NET_NAT
|
||||
depends on IOB_BUFSIZE >= 108
|
||||
|
||||
choice
|
||||
prompt "NAT66 Type"
|
||||
default NET_NAT66_FULL_CONE
|
||||
depends on NET_NAT66
|
||||
|
||||
config NET_NAT66_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_NAT66_SYMMETRIC
|
||||
bool "Symmetric NAT"
|
||||
---help---
|
||||
Symmetric NAT will be safer than Full Cone NAT, be more difficult
|
||||
@ -73,6 +104,13 @@ config NET_NAT_ICMP_EXPIRE_SEC
|
||||
Note: The default value 60 is suggested by RFC5508, Section 3.2,
|
||||
Page 8.
|
||||
|
||||
config NET_NAT_ICMPv6_EXPIRE_SEC
|
||||
int "ICMPv6 NAT entry expiration seconds"
|
||||
default 60
|
||||
depends on NET_NAT
|
||||
---help---
|
||||
The expiration time for idle ICMPv6 entry in NAT.
|
||||
|
||||
config NET_NAT_ENTRY_RECLAIM_SEC
|
||||
int "The time to auto reclaim all expired entries"
|
||||
default 3600
|
||||
|
@ -24,10 +24,14 @@ ifeq ($(CONFIG_NET_NAT),y)
|
||||
|
||||
NET_CSRCS += nat.c
|
||||
|
||||
ifeq ($(CONFIG_NET_IPv4),y)
|
||||
ifeq ($(CONFIG_NET_NAT44),y)
|
||||
NET_CSRCS += ipv4_nat.c ipv4_nat_entry.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_NET_NAT66),y)
|
||||
NET_CSRCS += ipv6_nat.c ipv6_nat_entry.c
|
||||
endif
|
||||
|
||||
# Include NAT build support
|
||||
|
||||
DEPPATH += --dep-path nat
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "nat/nat.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
@ -732,15 +732,10 @@ ipv4_nat_outbound_internal(FAR struct net_driver_s *dev,
|
||||
* 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)
|
||||
void 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.
|
||||
@ -755,11 +750,9 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
|
||||
{
|
||||
/* Inbound without entry is OK (e.g. towards NuttX itself), skip. */
|
||||
|
||||
return OK;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -801,11 +794,11 @@ int ipv4_nat_outbound(FAR struct net_driver_s *dev,
|
||||
{
|
||||
/* Outbound entry creation failed, should have entry. */
|
||||
|
||||
return -ENOMEM;
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
|
||||
#endif /* CONFIG_NET_NAT44 */
|
||||
|
@ -34,14 +34,14 @@
|
||||
|
||||
#include "nat/nat.h"
|
||||
|
||||
#if defined(CONFIG_NET_NAT) && defined(CONFIG_NET_IPv4)
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static DECLARE_HASHTABLE(g_table_inbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
static DECLARE_HASHTABLE(g_table_outbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
static DECLARE_HASHTABLE(g_nat44_inbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
static DECLARE_HASHTABLE(g_nat44_outbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
@ -134,16 +134,16 @@ 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
|
||||
#ifdef CONFIG_NET_NAT44_SYMMETRIC
|
||||
entry->peer_ip = peer_ip;
|
||||
entry->peer_port = peer_port;
|
||||
#endif
|
||||
|
||||
ipv4_nat_entry_refresh(entry);
|
||||
|
||||
hashtable_add(g_table_inbound, &entry->hash_inbound,
|
||||
hashtable_add(g_nat44_inbound, &entry->hash_inbound,
|
||||
ipv4_nat_inbound_key(external_ip, external_port, protocol));
|
||||
hashtable_add(g_table_outbound, &entry->hash_outbound,
|
||||
hashtable_add(g_nat44_outbound, &entry->hash_outbound,
|
||||
ipv4_nat_outbound_key(local_ip, local_port, protocol));
|
||||
|
||||
return entry;
|
||||
@ -162,16 +162,16 @@ ipv4_nat_entry_create(uint8_t protocol,
|
||||
|
||||
static void ipv4_nat_entry_delete(FAR struct ipv4_nat_entry *entry)
|
||||
{
|
||||
ninfo("INFO: Removing NAT entry proto=%" PRIu8
|
||||
ninfo("INFO: Removing NAT44 entry proto=%" PRIu8
|
||||
", local=%" PRIx32 ":%" PRIu16 ", external=:%" PRIu16 "\n",
|
||||
entry->protocol, entry->local_ip, entry->local_port,
|
||||
entry->external_port);
|
||||
|
||||
hashtable_delete(g_table_inbound, &entry->hash_inbound,
|
||||
hashtable_delete(g_nat44_inbound, &entry->hash_inbound,
|
||||
ipv4_nat_inbound_key(entry->external_ip,
|
||||
entry->external_port,
|
||||
entry->protocol));
|
||||
hashtable_delete(g_table_outbound, &entry->hash_outbound,
|
||||
hashtable_delete(g_nat44_outbound, &entry->hash_outbound,
|
||||
ipv4_nat_outbound_key(entry->local_ip,
|
||||
entry->local_port,
|
||||
entry->protocol));
|
||||
@ -207,9 +207,9 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
ninfo("INFO: Reclaiming all expired NAT entries.\n");
|
||||
ninfo("INFO: Reclaiming all expired NAT44 entries.\n");
|
||||
|
||||
hashtable_for_every_safe(g_table_inbound, p, tmp, i)
|
||||
hashtable_for_every_safe(g_nat44_inbound, p, tmp, i)
|
||||
{
|
||||
FAR struct ipv4_nat_entry *entry =
|
||||
container_of(p, struct ipv4_nat_entry, hash_inbound);
|
||||
@ -221,10 +221,12 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
|
||||
}
|
||||
}
|
||||
|
||||
ninfo("INFO: %d expired NAT entries reclaimed.\n", count);
|
||||
ninfo("INFO: %d expired NAT44 entries reclaimed.\n", count);
|
||||
next_reclaim_time = current_time + CONFIG_NET_NAT_ENTRY_RECLAIM_SEC;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define ipv4_nat_reclaim_entry(t)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
@ -239,7 +241,7 @@ static void ipv4_nat_reclaim_entry(int32_t current_time)
|
||||
* any device.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device on which NAT entries will be cleared.
|
||||
* dev - The device on which NAT entries will be cleared.
|
||||
*
|
||||
* Assumptions:
|
||||
* NAT is initialized.
|
||||
@ -252,9 +254,9 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev)
|
||||
FAR hash_node_t *tmp;
|
||||
int i;
|
||||
|
||||
ninfo("INFO: Clearing all NAT entries for %s\n", dev->d_ifname);
|
||||
ninfo("INFO: Clearing all NAT44 entries for %s\n", dev->d_ifname);
|
||||
|
||||
hashtable_for_every_safe(g_table_inbound, p, tmp, i)
|
||||
hashtable_for_every_safe(g_nat44_inbound, p, tmp, i)
|
||||
{
|
||||
FAR struct ipv4_nat_entry *entry =
|
||||
container_of(p, struct ipv4_nat_entry, hash_inbound);
|
||||
@ -293,16 +295,14 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
|
||||
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
|
||||
#ifdef CONFIG_NET_NAT44_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
|
||||
ipv4_nat_reclaim_entry(current_time);
|
||||
#endif
|
||||
|
||||
hashtable_for_every_possible_safe(g_table_inbound, p, tmp,
|
||||
hashtable_for_every_possible_safe(g_nat44_inbound, p, tmp,
|
||||
ipv4_nat_inbound_key(external_ip, external_port, protocol))
|
||||
{
|
||||
FAR struct ipv4_nat_entry *entry =
|
||||
@ -319,7 +319,7 @@ 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
|
||||
#ifdef CONFIG_NET_NAT_SYMMETRIC
|
||||
#ifdef CONFIG_NET_NAT44_SYMMETRIC
|
||||
&& (skip_peer || (net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
|
||||
entry->peer_port == peer_port))
|
||||
#endif
|
||||
@ -376,11 +376,9 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||
uint16_t external_port;
|
||||
int32_t current_time = TICK2SEC(clock_systime_ticks());
|
||||
|
||||
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
|
||||
ipv4_nat_reclaim_entry(current_time);
|
||||
#endif
|
||||
|
||||
hashtable_for_every_possible_safe(g_table_outbound, p, tmp,
|
||||
hashtable_for_every_possible_safe(g_nat44_outbound, p, tmp,
|
||||
ipv4_nat_outbound_key(local_ip, local_port, protocol))
|
||||
{
|
||||
FAR struct ipv4_nat_entry *entry =
|
||||
@ -398,7 +396,7 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||
net_ipv4addr_cmp(entry->external_ip, dev->d_ipaddr) &&
|
||||
net_ipv4addr_cmp(entry->local_ip, local_ip) &&
|
||||
entry->local_port == local_port
|
||||
#ifdef CONFIG_NET_NAT_SYMMETRIC
|
||||
#ifdef CONFIG_NET_NAT44_SYMMETRIC
|
||||
&& net_ipv4addr_cmp(entry->peer_ip, peer_ip) &&
|
||||
entry->peer_port == peer_port
|
||||
#endif
|
||||
@ -432,4 +430,4 @@ ipv4_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||
local_ip, local_port, peer_ip, peer_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_NAT && CONFIG_NET_IPv4 */
|
||||
#endif /* CONFIG_NET_NAT44 */
|
||||
|
692
net/nat/ipv6_nat.c
Normal file
692
net/nat/ipv6_nat.c
Normal file
@ -0,0 +1,692 @@
|
||||
/****************************************************************************
|
||||
* net/nat/ipv6_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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <nuttx/net/icmpv6.h>
|
||||
#include <nuttx/net/tcp.h>
|
||||
#include <nuttx/net/udp.h>
|
||||
|
||||
#include "nat/nat.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_internal(FAR struct ipv6_hdr_s *ipv6,
|
||||
enum nat_manip_type_e manip_type);
|
||||
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_internal(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6,
|
||||
enum nat_manip_type_e manip_type);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_ip_adjust
|
||||
*
|
||||
* Description:
|
||||
* Adjust address and checksum for network packet.
|
||||
*
|
||||
* Input Parameters:
|
||||
* l4chksum - Points to the L4 checksum to adjust, NULL for not adjust.
|
||||
* old_ip - The IP to be set.
|
||||
* new_ip - The IP to set into header.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void ipv6_nat_ip_adjust(FAR uint16_t *l4chksum, FAR uint16_t *old_ip,
|
||||
net_ipv6addr_t new_ip)
|
||||
{
|
||||
/* TODO: Maybe we can accelerate the checksum adjustment by pre-calculate a
|
||||
* difference of checksum, and apply it to each packet, instead of calling
|
||||
* chksum_adjust each time.
|
||||
*/
|
||||
|
||||
if (l4chksum != NULL)
|
||||
{
|
||||
nat_chksum_adjust(l4chksum, old_ip, new_ip, sizeof(net_ipv6addr_t));
|
||||
}
|
||||
|
||||
net_ipv6addr_hdrcopy(old_ip, new_ip);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_port_adjust
|
||||
*
|
||||
* Description:
|
||||
* Adjust port and checksum for network packet.
|
||||
*
|
||||
* Input Parameters:
|
||||
* l4chksum - Points to the L4 checksum to adjust, NULL for not adjust.
|
||||
* old_port - The port to be set.
|
||||
* new_port - The port to set into header.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void ipv6_nat_port_adjust(FAR uint16_t *l4chksum,
|
||||
FAR uint16_t *old_port, uint16_t new_port)
|
||||
{
|
||||
if (l4chksum != NULL)
|
||||
{
|
||||
nat_chksum_adjust(l4chksum, old_port, &new_port, sizeof(new_port));
|
||||
}
|
||||
|
||||
*old_port = new_port;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_inbound_tcp
|
||||
*
|
||||
* Description:
|
||||
* Check if a received TCP packet belongs to a NAT entry. If so, translate
|
||||
* the external IP/Port to local IP/Port.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* tcp - Points to the TCP header to translate.
|
||||
* manip_type - Whether external IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet is received on NAT device and is targeting at the address
|
||||
* assigned to the device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_TCP
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_tcp(FAR struct ipv6_hdr_s *ipv6, FAR struct tcp_hdr_s *tcp,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
FAR uint16_t *external_ip = MANIP_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *external_port = MANIP_PORT(tcp, manip_type);
|
||||
FAR uint16_t *peer_ip = PEER_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *peer_port = PEER_PORT(tcp, manip_type);
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
ipv6_nat_inbound_entry_find(IP_PROTO_TCP,
|
||||
external_ip, *external_port,
|
||||
peer_ip, *peer_port, true);
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Note: Field tcpchksum is not guaranteed exists in TCP header inside
|
||||
* ICMPv6 Error MSG, but we manually guarantee that it is inside valid
|
||||
* address (IOB >= IP + ICMPv6 + IP + TCP), so we can update it safely.
|
||||
*/
|
||||
|
||||
ipv6_nat_port_adjust(&tcp->tcpchksum, external_port, entry->local_port);
|
||||
ipv6_nat_ip_adjust(&tcp->tcpchksum, external_ip, entry->local_ip);
|
||||
|
||||
return entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_inbound_udp
|
||||
*
|
||||
* Description:
|
||||
* Check if a received UDP packet belongs to a NAT entry. If so, translate
|
||||
* the external IP/Port to local IP/Port.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* udp - Points to the UDP header to translate.
|
||||
* manip_type - Whether external IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet is received on NAT device and is targeting at the address
|
||||
* assigned to the device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_UDP
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_udp(FAR struct ipv6_hdr_s *ipv6, FAR struct udp_hdr_s *udp,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
FAR uint16_t *external_ip = MANIP_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *external_port = MANIP_PORT(udp, manip_type);
|
||||
FAR uint16_t *peer_ip = PEER_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *peer_port = PEER_PORT(udp, manip_type);
|
||||
FAR uint16_t *udpchksum;
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
ipv6_nat_inbound_entry_find(IP_PROTO_UDP,
|
||||
external_ip, *external_port,
|
||||
peer_ip, *peer_port, true);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* UDP checksum has special case 0 (no checksum) */
|
||||
|
||||
udpchksum = udp->udpchksum != 0 ? &udp->udpchksum : NULL;
|
||||
|
||||
ipv6_nat_port_adjust(udpchksum, external_port, entry->local_port);
|
||||
ipv6_nat_ip_adjust(udpchksum, external_ip, entry->local_ip);
|
||||
|
||||
return entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_inbound_icmpv6
|
||||
*
|
||||
* Description:
|
||||
* Check if a received ICMPv6 packet belongs to a NAT entry. If so,
|
||||
* translate the external IP/ID to local IP/ID.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* icmpv6 - Points to the ICMPv6 header to translate.
|
||||
* manip_type - Whether external IP is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet is received on g_dev and is targeting at the address assigned to
|
||||
* g_dev.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_icmpv6(FAR struct ipv6_hdr_s *ipv6,
|
||||
FAR struct icmpv6_hdr_s *icmpv6,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
FAR uint16_t *external_ip = MANIP_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *peer_ip = PEER_IPADDR(ipv6, manip_type);
|
||||
FAR struct ipv6_nat_entry *entry;
|
||||
|
||||
switch (icmpv6->type)
|
||||
{
|
||||
case ICMPv6_ECHO_REQUEST:
|
||||
case ICMPv6_ECHO_REPLY:
|
||||
entry = ipv6_nat_inbound_entry_find(IP_PROTO_ICMP6,
|
||||
external_ip, icmpv6->data[0],
|
||||
peer_ip, icmpv6->data[0], true);
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ipv6_nat_port_adjust(&icmpv6->chksum,
|
||||
&icmpv6->data[0], entry->local_port);
|
||||
ipv6_nat_ip_adjust(&icmpv6->chksum, external_ip, entry->local_ip);
|
||||
return entry;
|
||||
|
||||
case ICMPv6_DEST_UNREACHABLE:
|
||||
case ICMPv6_PACKET_TOO_BIG:
|
||||
case ICMPv6_PACKET_TIME_EXCEEDED:
|
||||
case ICMPv6_PACKET_PARAM_PROBLEM:
|
||||
/* ICMPv6 Error MSG inside another ICMPv6 Error MSG is forbidden by
|
||||
* RFC4443, Section 2.4, Page 6, so we only process the outermost
|
||||
* ICMPv6 Error MSG (manip type is DST).
|
||||
*/
|
||||
|
||||
if (manip_type == NAT_MANIP_DST)
|
||||
{
|
||||
/* The payload in the ICMPv6 packet is the origin packet we sent.
|
||||
* We don't need to check or backup any inner L4 data, because
|
||||
* every ICMPv6 error message (type < 128) MUST include as much
|
||||
* of the IPv6 offending (invoking) packet as possible. And the
|
||||
* inner packet will be translated by the inbound process
|
||||
* without needed to modify any outer packet checksum.
|
||||
*/
|
||||
|
||||
FAR struct ipv6_hdr_s *inner =
|
||||
(FAR struct ipv6_hdr_s *)(icmpv6 + 1);
|
||||
|
||||
/* Find entry and translate inner. */
|
||||
|
||||
entry = ipv6_nat_inbound_internal(inner, NAT_MANIP_SRC);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adjust outer IP */
|
||||
|
||||
ipv6_nat_ip_adjust(&icmpv6->chksum, external_ip,
|
||||
entry->local_ip);
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_outbound_tcp
|
||||
*
|
||||
* Description:
|
||||
* Check if we want to perform NAT with this outbound TCP packet before
|
||||
* sending it. If so, translate the local IP/Port to external IP/Port.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device to sent the packet (to get external IP).
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* tcp - Points to the TCP header to translate.
|
||||
* manip_type - Whether local IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet will be sent on NAT device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_TCP
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_tcp(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6, FAR struct tcp_hdr_s *tcp,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
FAR uint16_t *local_ip = MANIP_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *local_port = MANIP_PORT(tcp, manip_type);
|
||||
FAR uint16_t *peer_ip = PEER_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *peer_port = PEER_PORT(tcp, manip_type);
|
||||
FAR struct ipv6_nat_entry *entry;
|
||||
|
||||
/* Only create entry when it's the outermost packet (manip type is SRC). */
|
||||
|
||||
entry = ipv6_nat_outbound_entry_find(dev, IP_PROTO_TCP,
|
||||
local_ip, *local_port, peer_ip, *peer_port,
|
||||
manip_type == NAT_MANIP_SRC);
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Note: Field tcpchksum is not guaranteed exists in TCP header inside
|
||||
* ICMPv6 Error MSG, but we manually guarantee that it is inside valid
|
||||
* address (IOB >= IP + ICMPv6 + IP + TCP), so we can update it safely.
|
||||
*/
|
||||
|
||||
ipv6_nat_port_adjust(&tcp->tcpchksum, local_port, entry->external_port);
|
||||
ipv6_nat_ip_adjust(&tcp->tcpchksum, local_ip, entry->external_ip);
|
||||
|
||||
return entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_outbound_udp
|
||||
*
|
||||
* Description:
|
||||
* Check if we want to perform NAT with this outbound UDP packet before
|
||||
* sending it. If so, translate the local IP/Port to external IP/Port.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device to sent the packet (to get external IP).
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* udp - Points to the UDP header to translate.
|
||||
* manip_type - Whether local IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet will be sent on NAT device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_UDP
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_udp(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6, FAR struct udp_hdr_s *udp,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
FAR uint16_t *local_ip = MANIP_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *local_port = MANIP_PORT(udp, manip_type);
|
||||
FAR uint16_t *peer_ip = PEER_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *peer_port = PEER_PORT(udp, manip_type);
|
||||
FAR uint16_t *udpchksum;
|
||||
FAR struct ipv6_nat_entry *entry;
|
||||
|
||||
/* Only create entry when it's the outermost packet (manip type is SRC). */
|
||||
|
||||
entry = ipv6_nat_outbound_entry_find(dev, IP_PROTO_UDP,
|
||||
local_ip, *local_port, peer_ip, *peer_port,
|
||||
manip_type == NAT_MANIP_SRC);
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* UDP checksum has special case 0 (no checksum) */
|
||||
|
||||
udpchksum = udp->udpchksum != 0 ? &udp->udpchksum : NULL;
|
||||
|
||||
ipv6_nat_port_adjust(udpchksum, local_port, entry->external_port);
|
||||
ipv6_nat_ip_adjust(udpchksum, local_ip, entry->external_ip);
|
||||
|
||||
return entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_outbound_icmpv6
|
||||
*
|
||||
* Description:
|
||||
* Check if we want to perform NAT with this outbound ICMPv6 packet before
|
||||
* sending it. If so, translate the local IP/ID to external IP/ID.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device to sent the packet (to get external IP).
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* icmpv6 - Points to the ICMPv6 header to translate.
|
||||
* manip_type - Whether local IP is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet will be sent on NAT device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_icmpv6(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6,
|
||||
FAR struct icmpv6_hdr_s *icmpv6,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
FAR uint16_t *local_ip = MANIP_IPADDR(ipv6, manip_type);
|
||||
FAR uint16_t *peer_ip = PEER_IPADDR(ipv6, manip_type);
|
||||
FAR struct ipv6_nat_entry *entry;
|
||||
|
||||
switch (icmpv6->type)
|
||||
{
|
||||
case ICMPv6_ECHO_REQUEST:
|
||||
case ICMPv6_ECHO_REPLY:
|
||||
|
||||
/* Note: Only create new entry when it's the outermost packet (that
|
||||
* is, manip type is SRC).
|
||||
*/
|
||||
|
||||
entry = ipv6_nat_outbound_entry_find(dev, IP_PROTO_ICMP6,
|
||||
local_ip, icmpv6->data[0], peer_ip, icmpv6->data[0],
|
||||
manip_type == NAT_MANIP_SRC);
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ipv6_nat_port_adjust(&icmpv6->chksum,
|
||||
&icmpv6->data[0], entry->external_port);
|
||||
ipv6_nat_ip_adjust(&icmpv6->chksum, local_ip, entry->external_ip);
|
||||
return entry;
|
||||
|
||||
case ICMPv6_DEST_UNREACHABLE:
|
||||
case ICMPv6_PACKET_TOO_BIG:
|
||||
case ICMPv6_PACKET_TIME_EXCEEDED:
|
||||
case ICMPv6_PACKET_PARAM_PROBLEM:
|
||||
/* ICMPv6 Error MSG inside another ICMPv6 Error MSG is forbidden by
|
||||
* RFC4443, Section 2.4, Page 6, so we only process the outermost
|
||||
* ICMPv6 Error MSG (manip type is DST).
|
||||
*/
|
||||
|
||||
if (manip_type == NAT_MANIP_SRC)
|
||||
{
|
||||
/* The payload in the ICMPv6 packet is the origin packet we got.
|
||||
* We don't need to check or backup any inner L4 data, because
|
||||
* every ICMPv6 error message (type < 128) MUST include as much
|
||||
* of the IPv6 offending (invoking) packet as possible. And the
|
||||
* inner packet will be translated by the inbound process
|
||||
* without needed to modify any outer packet checksum.
|
||||
*/
|
||||
|
||||
FAR struct ipv6_hdr_s *inner =
|
||||
(FAR struct ipv6_hdr_s *)(icmpv6 + 1);
|
||||
|
||||
/* Find entry and translate inner. */
|
||||
|
||||
entry = ipv6_nat_outbound_internal(dev, inner, NAT_MANIP_DST);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adjust outer IP */
|
||||
|
||||
ipv6_nat_ip_adjust(&icmpv6->chksum, local_ip,
|
||||
entry->external_ip);
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_inbound_internal
|
||||
*
|
||||
* Description:
|
||||
* Check if a received packet belongs to a NAT entry. If so, translate
|
||||
* the external IP/Port to local IP/Port.
|
||||
*
|
||||
* Input Parameters:
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* manip_type - Whether external IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet is received on NAT device and is targeting at the address
|
||||
* assigned to the device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_internal(FAR struct ipv6_hdr_s *ipv6,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
uint8_t proto;
|
||||
FAR void *l4hdr = net_ipv6_payload(ipv6, &proto);
|
||||
|
||||
switch (ipv6->proto)
|
||||
{
|
||||
#ifdef CONFIG_NET_TCP
|
||||
case IP_PROTO_TCP:
|
||||
return ipv6_nat_inbound_tcp(ipv6, l4hdr, manip_type);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_UDP
|
||||
case IP_PROTO_UDP:
|
||||
return ipv6_nat_inbound_udp(ipv6, l4hdr, manip_type);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
case IP_PROTO_ICMP6:
|
||||
return ipv6_nat_inbound_icmpv6(ipv6, l4hdr, manip_type);
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_outbound_internal
|
||||
*
|
||||
* Description:
|
||||
* Check if we want to perform NAT with this outbound packet before
|
||||
* sending it. If so, translate the local IP/Port to external IP/Port.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device to sent the packet (to get external IP).
|
||||
* ipv6 - Points to the IPv6 header to translate.
|
||||
* manip_type - Whether local IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding NAT entry of the packet.
|
||||
*
|
||||
* Assumptions:
|
||||
* Packet will be sent on NAT device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_internal(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
uint8_t proto;
|
||||
FAR void *l4hdr = net_ipv6_payload(ipv6, &proto);
|
||||
|
||||
switch (proto)
|
||||
{
|
||||
#ifdef CONFIG_NET_TCP
|
||||
case IP_PROTO_TCP:
|
||||
return ipv6_nat_outbound_tcp(dev, ipv6, l4hdr, manip_type);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_UDP
|
||||
case IP_PROTO_UDP:
|
||||
return ipv6_nat_outbound_udp(dev, ipv6, l4hdr, manip_type);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
case IP_PROTO_ICMP6:
|
||||
return ipv6_nat_outbound_icmpv6(dev, ipv6, l4hdr, manip_type);
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_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.
|
||||
* ipv6 - Points to the IPv6 header with dev->d_buf.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void ipv6_nat_inbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6)
|
||||
{
|
||||
/* We only process packets from NAT device and targeting at the address
|
||||
* assigned to the device.
|
||||
*/
|
||||
|
||||
if (IFF_IS_NAT(dev->d_flags) &&
|
||||
NETDEV_IS_MY_V6ADDR(dev, ipv6->destipaddr))
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
ipv6_nat_inbound_internal(ipv6, NAT_MANIP_DST);
|
||||
if (!entry)
|
||||
{
|
||||
/* Inbound without entry is OK (e.g. towards NuttX itself), skip. */
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_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.
|
||||
* ipv6 - Points to the IPv6 header to be filled into dev->d_buf later.
|
||||
* manip_type - Whether local IP/Port is in source or destination.
|
||||
*
|
||||
* 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 ipv6_nat_outbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6,
|
||||
enum nat_manip_type_e manip_type)
|
||||
{
|
||||
/* 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) &&
|
||||
!NETDEV_IS_MY_V6ADDR(dev, ipv6->srcipaddr) &&
|
||||
!NETDEV_IS_MY_V6ADDR(dev, ipv6->destipaddr))
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
ipv6_nat_outbound_internal(dev, ipv6, manip_type);
|
||||
if (manip_type == NAT_MANIP_SRC && !entry)
|
||||
{
|
||||
/* Outbound entry creation failed, should have entry. */
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_NAT66 */
|
437
net/nat/ipv6_nat_entry.c
Normal file
437
net/nat/ipv6_nat_entry.c
Normal file
@ -0,0 +1,437 @@
|
||||
/****************************************************************************
|
||||
* net/nat/ipv6_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 <stdint.h>
|
||||
|
||||
#include <nuttx/clock.h>
|
||||
#include <nuttx/hashtable.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/nuttx.h>
|
||||
|
||||
#include "inet/inet.h"
|
||||
#include "nat/nat.h"
|
||||
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static DECLARE_HASHTABLE(g_nat66_inbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
static DECLARE_HASHTABLE(g_nat66_outbound, CONFIG_NET_NAT_HASH_BITS);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_hash_key
|
||||
*
|
||||
* Description:
|
||||
* Create a hash key for NAT66.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint32_t ipv6_nat_hash_key(const net_ipv6addr_t ip,
|
||||
uint16_t port, uint8_t protocol)
|
||||
{
|
||||
uint32_t key = (((uint32_t)ip[0] << 16) | ip[1]) ^
|
||||
(((uint32_t)ip[2] << 16) | ip[3]) ^
|
||||
(((uint32_t)ip[4] << 16) | ip[5]) ^
|
||||
(((uint32_t)ip[6] << 16) | ip[7]);
|
||||
|
||||
return key ^ ((uint32_t)protocol << 16) ^ port;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_entry_refresh
|
||||
*
|
||||
* Description:
|
||||
* Refresh a NAT entry, update its expiration time.
|
||||
*
|
||||
* Input Parameters:
|
||||
* entry - The entry to refresh.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void ipv6_nat_entry_refresh(FAR struct ipv6_nat_entry *entry)
|
||||
{
|
||||
entry->expire_time = nat_expire_time(entry->protocol);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_entry_create
|
||||
*
|
||||
* Description:
|
||||
* Create a NAT entry and insert into entry list.
|
||||
*
|
||||
* Input Parameters:
|
||||
* protocol - The L4 protocol of the packet.
|
||||
* external_ip - The external ip 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.
|
||||
* 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
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_entry_create(uint8_t protocol, const net_ipv6addr_t external_ip,
|
||||
uint16_t external_port, const net_ipv6addr_t local_ip,
|
||||
uint16_t local_port, const net_ipv6addr_t peer_ip,
|
||||
uint16_t peer_port)
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
kmm_malloc(sizeof(struct ipv6_nat_entry));
|
||||
if (entry == NULL)
|
||||
{
|
||||
nwarn("WARNING: Failed to allocate IPv6 NAT entry\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->protocol = protocol;
|
||||
entry->external_port = external_port;
|
||||
entry->local_port = local_port;
|
||||
#ifdef CONFIG_NET_NAT66_SYMMETRIC
|
||||
entry->peer_port = peer_port;
|
||||
#endif
|
||||
net_ipv6addr_copy(entry->external_ip, external_ip);
|
||||
net_ipv6addr_copy(entry->local_ip, local_ip);
|
||||
#ifdef CONFIG_NET_NAT66_SYMMETRIC
|
||||
net_ipv6addr_copy(entry->peer_ip, peer_ip);
|
||||
#endif
|
||||
|
||||
ipv6_nat_entry_refresh(entry);
|
||||
|
||||
hashtable_add(g_nat66_inbound, &entry->hash_inbound,
|
||||
ipv6_nat_hash_key(external_ip, external_port, protocol));
|
||||
hashtable_add(g_nat66_outbound, &entry->hash_outbound,
|
||||
ipv6_nat_hash_key(local_ip, local_port, protocol));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_entry_delete
|
||||
*
|
||||
* Description:
|
||||
* Delete a NAT entry and remove from entry list.
|
||||
*
|
||||
* Input Parameters:
|
||||
* entry - The entry to remove.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void ipv6_nat_entry_delete(FAR struct ipv6_nat_entry *entry)
|
||||
{
|
||||
ninfo("INFO: Removing NAT66 entry proto=%" PRIu8
|
||||
", local=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%" PRIu16
|
||||
", external=:%" PRIu16 "\n",
|
||||
entry->protocol, entry->local_ip[0], entry->local_ip[1],
|
||||
entry->local_ip[2], entry->local_ip[3], entry->local_ip[4],
|
||||
entry->local_ip[5], entry->local_ip[6], entry->local_ip[7],
|
||||
entry->local_port, entry->external_port);
|
||||
|
||||
hashtable_delete(g_nat66_inbound, &entry->hash_inbound,
|
||||
ipv6_nat_hash_key(entry->external_ip,
|
||||
entry->external_port,
|
||||
entry->protocol));
|
||||
hashtable_delete(g_nat66_outbound, &entry->hash_outbound,
|
||||
ipv6_nat_hash_key(entry->local_ip,
|
||||
entry->local_port,
|
||||
entry->protocol));
|
||||
|
||||
kmm_free(entry);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_reclaim_entry
|
||||
*
|
||||
* Description:
|
||||
* Try reclaim all expired NAT entries.
|
||||
* Only works after every CONFIG_NET_NAT_ENTRY_RECLAIM_SEC (low frequency).
|
||||
*
|
||||
* Although expired entries will be automatically reclaimed when matching
|
||||
* inbound/outbound entries, there might be some situations that entries
|
||||
* will be kept in memory, e.g. big hashtable with only a few connections.
|
||||
*
|
||||
* Assumptions:
|
||||
* NAT is initialized.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if CONFIG_NET_NAT_ENTRY_RECLAIM_SEC > 0
|
||||
static void ipv6_nat_reclaim_entry(int32_t current_time)
|
||||
{
|
||||
static int32_t next_reclaim_time = CONFIG_NET_NAT_ENTRY_RECLAIM_SEC;
|
||||
|
||||
if (next_reclaim_time - current_time <= 0)
|
||||
{
|
||||
FAR hash_node_t *p;
|
||||
FAR hash_node_t *tmp;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
ninfo("INFO: Reclaiming all expired NAT66 entries.\n");
|
||||
|
||||
hashtable_for_every_safe(g_nat66_inbound, p, tmp, i)
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
container_of(p, struct ipv6_nat_entry, hash_inbound);
|
||||
|
||||
if (entry->expire_time - current_time <= 0)
|
||||
{
|
||||
ipv6_nat_entry_delete(entry);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
ninfo("INFO: %d expired NAT66 entries reclaimed.\n", count);
|
||||
next_reclaim_time = current_time + CONFIG_NET_NAT_ENTRY_RECLAIM_SEC;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define ipv6_nat_reclaim_entry(t)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_entry_clear
|
||||
*
|
||||
* Description:
|
||||
* Clear all entries related to dev. Called when NAT will be disabled on
|
||||
* any device.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device on which NAT entries will be cleared.
|
||||
*
|
||||
* Assumptions:
|
||||
* NAT is initialized.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void ipv6_nat_entry_clear(FAR struct net_driver_s *dev)
|
||||
{
|
||||
FAR hash_node_t *p;
|
||||
FAR hash_node_t *tmp;
|
||||
int i;
|
||||
|
||||
ninfo("INFO: Clearing all NAT66 entries for %s\n", dev->d_ifname);
|
||||
|
||||
hashtable_for_every_safe(g_nat66_inbound, p, tmp, i)
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
container_of(p, struct ipv6_nat_entry, hash_inbound);
|
||||
|
||||
if (NETDEV_IS_MY_V6ADDR(dev, entry->external_ip))
|
||||
{
|
||||
ipv6_nat_entry_delete(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_inbound_entry_find
|
||||
*
|
||||
* Description:
|
||||
* Find the inbound entry in NAT entry list.
|
||||
*
|
||||
* Input Parameters:
|
||||
* 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:
|
||||
* Pointer to entry on success; null on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_entry_find(uint8_t protocol,
|
||||
const net_ipv6addr_t external_ip,
|
||||
uint16_t external_port,
|
||||
const net_ipv6addr_t peer_ip,
|
||||
uint16_t peer_port, bool refresh)
|
||||
{
|
||||
FAR hash_node_t *p;
|
||||
FAR hash_node_t *tmp;
|
||||
bool skip_ip = net_ipv6addr_cmp(external_ip, g_ipv6_unspecaddr);
|
||||
#ifdef CONFIG_NET_NAT66_SYMMETRIC
|
||||
bool skip_peer = net_ipv6addr_cmp(peer_ip, g_ipv6_unspecaddr);
|
||||
#endif
|
||||
int32_t current_time = TICK2SEC(clock_systime_ticks());
|
||||
|
||||
ipv6_nat_reclaim_entry(current_time);
|
||||
|
||||
hashtable_for_every_possible_safe(g_nat66_inbound, p, tmp,
|
||||
ipv6_nat_hash_key(external_ip, external_port, protocol))
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
container_of(p, struct ipv6_nat_entry, hash_inbound);
|
||||
|
||||
/* Remove expired entries. */
|
||||
|
||||
if (entry->expire_time - current_time <= 0)
|
||||
{
|
||||
ipv6_nat_entry_delete(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry->protocol == protocol &&
|
||||
(skip_ip || net_ipv6addr_cmp(entry->external_ip, external_ip)) &&
|
||||
entry->external_port == external_port
|
||||
#ifdef CONFIG_NET_NAT66_SYMMETRIC
|
||||
&& (skip_peer || (net_ipv6addr_cmp(entry->peer_ip, peer_ip) &&
|
||||
entry->peer_port == peer_port))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (refresh)
|
||||
{
|
||||
ipv6_nat_entry_refresh(entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh) /* false = a test of whether entry exists, no need to warn */
|
||||
{
|
||||
nwarn("WARNING: Failed to find IPv6 inbound NAT entry for proto="
|
||||
"%" PRIu8 ",external=[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:"
|
||||
"%" PRIu16 "\n",
|
||||
protocol, external_ip[0], external_ip[1], external_ip[2],
|
||||
external_ip[3], external_ip[4], external_ip[5], external_ip[6],
|
||||
external_ip[7], external_port);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv6_nat_outbound_entry_find
|
||||
*
|
||||
* Description:
|
||||
* Find the outbound entry in NAT entry list. Create one if corresponding
|
||||
* 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.
|
||||
* 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:
|
||||
* Pointer to entry on success; null on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||
const net_ipv6addr_t local_ip,
|
||||
uint16_t local_port,
|
||||
const net_ipv6addr_t peer_ip,
|
||||
uint16_t peer_port, bool try_create)
|
||||
{
|
||||
FAR hash_node_t *p;
|
||||
FAR hash_node_t *tmp;
|
||||
FAR union ip_addr_u *external_ip;
|
||||
uint16_t external_port;
|
||||
int32_t current_time = TICK2SEC(clock_systime_ticks());
|
||||
|
||||
ipv6_nat_reclaim_entry(current_time);
|
||||
|
||||
hashtable_for_every_possible_safe(g_nat66_outbound, p, tmp,
|
||||
ipv6_nat_hash_key(local_ip, local_port, protocol))
|
||||
{
|
||||
FAR struct ipv6_nat_entry *entry =
|
||||
container_of(p, struct ipv6_nat_entry, hash_outbound);
|
||||
|
||||
/* Remove expired entries. */
|
||||
|
||||
if (entry->expire_time - current_time <= 0)
|
||||
{
|
||||
ipv6_nat_entry_delete(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry->protocol == protocol &&
|
||||
NETDEV_IS_MY_V6ADDR(dev, entry->external_ip) &&
|
||||
net_ipv6addr_cmp(entry->local_ip, local_ip) &&
|
||||
entry->local_port == local_port
|
||||
#ifdef CONFIG_NET_NAT66_SYMMETRIC
|
||||
&& net_ipv6addr_cmp(entry->peer_ip, peer_ip) &&
|
||||
entry->peer_port == peer_port
|
||||
#endif
|
||||
)
|
||||
{
|
||||
ipv6_nat_entry_refresh(entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (!try_create)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Failed to find the entry, create one. */
|
||||
|
||||
ninfo("INFO: Failed to find IPv6 outbound NAT entry for proto=%" PRIu8
|
||||
", local=[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%" PRIu16
|
||||
", try create one.\n",
|
||||
protocol, local_ip[0], local_ip[1], local_ip[2], local_ip[3],
|
||||
local_ip[4], local_ip[5], local_ip[6], local_ip[7], local_port);
|
||||
|
||||
external_ip = (FAR union ip_addr_u *)netdev_ipv6_srcaddr(dev, peer_ip);
|
||||
external_port = nat_port_select(dev, PF_INET6, protocol,
|
||||
external_ip, local_port);
|
||||
|
||||
if (!external_port)
|
||||
{
|
||||
nwarn("WARNING: Failed to find an available port!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ipv6_nat_entry_create(protocol, external_ip->ipv6, external_port,
|
||||
local_ip, local_port, peer_ip, peer_port);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_NAT66 */
|
@ -27,6 +27,8 @@
|
||||
#include <debug.h>
|
||||
|
||||
#include "icmp/icmp.h"
|
||||
#include "icmpv6/icmpv6.h"
|
||||
#include "inet/inet.h"
|
||||
#include "nat/nat.h"
|
||||
#include "tcp/tcp.h"
|
||||
#include "udp/udp.h"
|
||||
@ -57,7 +59,8 @@
|
||||
|
||||
#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))
|
||||
(defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_SOCKET)) || \
|
||||
(defined(CONFIG_NET_ICMPv6) && !defined(CONFIG_NET_ICMPv6_SOCKET))
|
||||
|
||||
static uint16_t nat_port_select_without_stack(
|
||||
uint8_t domain, uint8_t protocol, FAR const union ip_addr_u *ip,
|
||||
@ -145,9 +148,12 @@ int nat_disable(FAR struct net_driver_s *dev)
|
||||
|
||||
/* Clear entries related to dev. */
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
ipv4_nat_entry_clear(dev);
|
||||
#endif
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
ipv6_nat_entry_clear(dev);
|
||||
#endif
|
||||
|
||||
IFF_CLR_NAT(dev->d_flags);
|
||||
|
||||
@ -175,7 +181,7 @@ int nat_disable(FAR struct net_driver_s *dev)
|
||||
bool nat_port_inuse(uint8_t domain, uint8_t protocol,
|
||||
FAR const union ip_addr_u *ip, uint16_t port)
|
||||
{
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
if (domain == PF_INET)
|
||||
{
|
||||
return !!ipv4_nat_inbound_entry_find(protocol, ip->ipv4, port,
|
||||
@ -183,6 +189,14 @@ bool nat_port_inuse(uint8_t domain, uint8_t protocol,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
if (domain == PF_INET6)
|
||||
{
|
||||
return !!ipv6_nat_inbound_entry_find(protocol, ip->ipv6, port,
|
||||
g_ipv6_unspecaddr, 0, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -239,8 +253,25 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev,
|
||||
{
|
||||
#ifndef CONFIG_NET_UDP_NO_STACK
|
||||
union ip_binding_u u;
|
||||
u.ipv4.laddr = external_ip->ipv4;
|
||||
u.ipv4.raddr = INADDR_ANY;
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_IPv6
|
||||
if (domain == PF_INET)
|
||||
#endif
|
||||
{
|
||||
u.ipv4.laddr = external_ip->ipv4;
|
||||
u.ipv4.raddr = INADDR_ANY;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_NET_IPv6
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
else
|
||||
#endif
|
||||
{
|
||||
net_ipv6addr_copy(u.ipv6.laddr, external_ip->ipv6);
|
||||
net_ipv6addr_copy(u.ipv6.raddr, g_ipv6_unspecaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* TODO: Try keep origin port as possible. */
|
||||
|
||||
@ -278,6 +309,33 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev,
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
case IP_PROTO_ICMP6:
|
||||
{
|
||||
#ifdef CONFIG_NET_ICMPv6_SOCKET
|
||||
uint16_t id = local_port;
|
||||
uint16_t hid = NTOHS(id);
|
||||
while (icmpv6_active(id) ||
|
||||
nat_port_inuse(domain, IP_PROTO_ICMP6, external_ip, id))
|
||||
{
|
||||
++hid;
|
||||
if (hid >= CONFIG_NET_DEFAULT_MAX_PORT ||
|
||||
hid < CONFIG_NET_DEFAULT_MIN_PORT)
|
||||
{
|
||||
hid = CONFIG_NET_DEFAULT_MIN_PORT;
|
||||
}
|
||||
|
||||
id = HTONS(hid);
|
||||
}
|
||||
|
||||
return id;
|
||||
#else
|
||||
return nat_port_select_without_stack(domain, IP_PROTO_ICMP6,
|
||||
external_ip, local_port);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Select original port for unsupported protocol. */
|
||||
@ -332,6 +390,12 @@ uint32_t nat_expire_time(uint8_t protocol)
|
||||
CONFIG_NET_NAT_ICMP_EXPIRE_SEC;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_ICMPv6
|
||||
case IP_PROTO_ICMP6:
|
||||
return TICK2SEC(clock_systime_ticks()) +
|
||||
CONFIG_NET_NAT_ICMPv6_EXPIRE_SEC;
|
||||
#endif
|
||||
|
||||
default:
|
||||
nwarn("WARNING: Unsupported protocol %" PRIu8 "\n", protocol);
|
||||
return 0;
|
||||
|
@ -87,12 +87,12 @@ struct ipv4_nat_entry
|
||||
|
||||
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
|
||||
#ifdef CONFIG_NET_NAT44_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
|
||||
#ifdef CONFIG_NET_NAT44_SYMMETRIC
|
||||
uint16_t peer_port; /* Peer port. */
|
||||
#endif
|
||||
uint8_t protocol; /* L4 protocol (TCP, UDP etc). */
|
||||
@ -100,6 +100,26 @@ struct ipv4_nat_entry
|
||||
int32_t expire_time; /* The expiration time of this entry. */
|
||||
};
|
||||
|
||||
struct ipv6_nat_entry
|
||||
{
|
||||
hash_node_t hash_inbound;
|
||||
hash_node_t hash_outbound;
|
||||
|
||||
net_ipv6addr_t local_ip; /* IP address of the local host. */
|
||||
net_ipv6addr_t external_ip; /* External IP address. */
|
||||
#ifdef CONFIG_NET_NAT66_SYMMETRIC
|
||||
net_ipv6addr_t peer_ip; /* Peer IP address. */
|
||||
#endif
|
||||
uint16_t local_port; /* Port of the local host. */
|
||||
uint16_t external_port; /* The external port of local host. */
|
||||
#ifdef CONFIG_NET_NAT66_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. */
|
||||
};
|
||||
|
||||
/* NAT IP/Port manipulate type, to indicate whether to manipulate source or
|
||||
* destination IP/Port in a packet.
|
||||
*/
|
||||
@ -149,37 +169,36 @@ int nat_enable(FAR struct net_driver_s *dev);
|
||||
int nat_disable(FAR struct net_driver_s *dev);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_inbound
|
||||
* Name: ipv4/ipv6_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.
|
||||
* dev - The device on which the packet is received.
|
||||
* ipv4/ipv6 - Points to the IP header with dev->d_buf.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
int ipv4_nat_inbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv4_hdr_s *ipv4);
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
void ipv4_nat_inbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv4_hdr_s *ipv4);
|
||||
#endif
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
void ipv6_nat_inbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_outbound
|
||||
* Name: ipv4/ipv6_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.
|
||||
* dev - The device on which the packet will be sent.
|
||||
* ipv4/ipv6 - Points to the IP header to be filled into dev->d_buf later.
|
||||
* manip_type - Whether local IP/Port is in source or destination.
|
||||
*
|
||||
* Returned Value:
|
||||
@ -189,11 +208,16 @@ int ipv4_nat_inbound(FAR struct net_driver_s *dev,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
int ipv4_nat_outbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv4_hdr_s *ipv4,
|
||||
enum nat_manip_type_e manip_type);
|
||||
#endif
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
int ipv6_nat_outbound(FAR struct net_driver_s *dev,
|
||||
FAR struct ipv6_hdr_s *ipv6,
|
||||
enum nat_manip_type_e manip_type);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nat_port_inuse
|
||||
@ -255,26 +279,29 @@ uint16_t nat_port_select(FAR struct net_driver_s *dev,
|
||||
uint32_t nat_expire_time(uint8_t protocol);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_entry_clear
|
||||
* Name: ipv4/ipv6_nat_entry_clear
|
||||
*
|
||||
* Description:
|
||||
* Clear all entries related to dev. Called when NAT will be disabled on
|
||||
* any device.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The device on which NAT entries will be cleared.
|
||||
* dev - The device on which NAT entries will be cleared.
|
||||
*
|
||||
* Assumptions:
|
||||
* NAT is initialized.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
|
||||
#endif
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
void ipv6_nat_entry_clear(FAR struct net_driver_s *dev);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_inbound_entry_find
|
||||
* Name: ipv4/ipv6_nat_inbound_entry_find
|
||||
*
|
||||
* Description:
|
||||
* Find the inbound entry in NAT entry list.
|
||||
@ -292,15 +319,23 @@ void ipv4_nat_entry_clear(FAR struct net_driver_s *dev);
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
FAR struct ipv4_nat_entry *
|
||||
ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
|
||||
uint16_t external_port, in_addr_t peer_ip,
|
||||
uint16_t peer_port, bool refresh);
|
||||
#endif
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_inbound_entry_find(uint8_t protocol,
|
||||
const net_ipv6addr_t external_ip,
|
||||
uint16_t external_port,
|
||||
const net_ipv6addr_t peer_ip,
|
||||
uint16_t peer_port, bool refresh);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ipv4_nat_outbound_entry_find
|
||||
* Name: ipv4/ipv6_nat_outbound_entry_find
|
||||
*
|
||||
* Description:
|
||||
* Find the outbound entry in NAT entry list. Create one if corresponding
|
||||
@ -320,13 +355,21 @@ ipv4_nat_inbound_entry_find(uint8_t protocol, in_addr_t external_ip,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv4
|
||||
#ifdef CONFIG_NET_NAT44
|
||||
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
|
||||
#ifdef CONFIG_NET_NAT66
|
||||
FAR struct ipv6_nat_entry *
|
||||
ipv6_nat_outbound_entry_find(FAR struct net_driver_s *dev, uint8_t protocol,
|
||||
const net_ipv6addr_t local_ip,
|
||||
uint16_t local_port,
|
||||
const net_ipv6addr_t peer_ip,
|
||||
uint16_t peer_port, bool try_create);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_NET_NAT */
|
||||
#endif /* __NET_NAT_NAT_H */
|
||||
|
Loading…
Reference in New Issue
Block a user