nuttx/Documentation/components/net/nat.rst
Zhe Weng f3b34c84c2 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>
2024-04-11 22:23:29 +08:00

229 lines
7.1 KiB
ReStructuredText

=================================
Network Address Translation (NAT)
=================================
NuttX supports full cone or symmetric NAT logic, which currently supports
- TCP
- UDP
- ICMP
- ECHO (REQUEST & REPLY)
- Error Messages (DEST_UNREACHABLE & TIME_EXCEEDED & PARAMETER_PROBLEM)
Workflow
========
::
Local Network (LAN) External Network (WAN)
|----------------|
<local IP, | | <external IP, <peer IP,
-----------| |-----------------------------
local port> | | external port> peer port>
|----------------|
- Outbound
- **LAN** -> **Forward** -> **NAT** (only if targeting at WAN) -> **WAN**
- All packets from **LAN** and targeting at **WAN** will be masqueraded
with ``local ip:port`` changed to ``external ip:port``.
- Inbound
- **WAN** -> **NAT** (only from WAN, change destination) -> **Forward** -> **LAN**
- Packets from **WAN** will try to be changed back from
``external ip:port`` to ``local ip:port`` and send to **LAN**.
Configuration Options
=====================
``CONFIG_NET_NAT``
Enable or disable Network Address Translation (NAT) function.
Depends on ``CONFIG_NET_IPFORWARD``.
``CONFIG_NET_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_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``
The bits of the hashtable of NAT entries, hashtable has (1 << bits) buckets.
``CONFIG_NET_NAT_TCP_EXPIRE_SEC``
The expiration time for idle TCP entry in NAT.
The default value 86400 is suggested by RFC2663, Section 2.6,
Page 5. But we may set it to shorter time like 240s for better
performance.
``CONFIG_NET_NAT_UDP_EXPIRE_SEC``
The expiration time for idle UDP entry in NAT.
``CONFIG_NET_NAT_ICMP_EXPIRE_SEC``
The expiration time for idle ICMP entry in NAT.
``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.
Expired entries will be automatically reclaimed when matching
inbound/outbound entries, so this config does not have significant
impact when NAT is normally used, but very useful when the hashtable
is big and there are only a few connections using NAT (which will
only trigger reclaiming on a few chains in hashtable).
Usage
=====
- :c:func:`nat_enable()`
- :c:func:`nat_disable()`
.. c:function:: int nat_enable(FAR struct net_driver_s *dev);
Enable NAT function on a network device, on which the outbound packets
will be masqueraded.
:return: Zero is returned if NAT function is successfully enabled on
the device; A negated errno value is returned if failed.
.. c:function:: int nat_disable(FAR struct net_driver_s *dev);
Disable NAT function on a network device.
:return: Zero is returned if NAT function is successfully disabled on
the device; A negated errno value is returned if failed.
Validation
==========
Validated on Ubuntu 22.04 x86_64 with NuttX SIM by following steps:
1. Configure NuttX with >=2 TAP devices (host route mode) and NAT enabled:
.. code-block:: Kconfig
CONFIG_NET_IPFORWARD=y
CONFIG_NET_NAT=y
# CONFIG_SIM_NET_BRIDGE is not set
CONFIG_SIM_NETDEV_NUMBER=2
2. Call ``nat_enable`` on one dev on startup, or manually enable NAT
with ``iptables`` command (either may work).
.. code-block:: c
/* arch/sim/src/sim/up_netdriver.c */
int netdriver_init(void)
{
...
nat_enable(&g_sim_dev[0]);
...
}
.. code-block:: shell
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
3. Set IP Address for NuttX on startup
.. code-block:: shell
ifconfig eth0 10.0.1.2
ifup eth0
ifconfig eth1 10.0.10.2
ifup eth1
# 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
IF_HOST="enp1s0"
IF_0="tap0"
IP_HOST_0="10.0.1.1"
IF_1="tap1"
IP_HOST_1="10.0.10.1"
IP_NUTTX_1="10.0.10.2"
# add net namespace LAN for $IF_1
ip netns add LAN
ip netns exec LAN sysctl -w net.ipv4.ip_forward=1
ip link set $IF_1 netns LAN
ip netns exec LAN ip link set $IF_1 up
ip netns exec LAN ip link set lo up
# add address and set default route
ip addr add $IP_HOST_0/24 dev $IF_0
ip netns exec LAN ip addr add $IP_HOST_1/24 dev $IF_1
ip netns exec LAN ip route add default dev $IF_1 via $IP_NUTTX_1
# nat to allow NuttX to access the internet
iptables -t nat -A POSTROUTING -o $IF_HOST -j MASQUERADE
iptables -A FORWARD -i $IF_HOST -o $IF_0 -j ACCEPT
iptables -A FORWARD -i $IF_0 -o $IF_HOST -j ACCEPT
sysctl -w net.ipv4.ip_forward=1
# 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
# Host side
iperf -B 10.0.1.1 -s -i 1
# LAN side
sudo ip netns exec LAN iperf -B 10.0.10.1 -c 10.0.1.1 -i 1
.. code-block:: shell
# Host side
python3 -m http.server -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
# Host side
tcpdump -nn -i tap0
# LAN side
sudo ip netns exec LAN tcpdump -nn -i tap1