f3b34c84c2
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>
229 lines
7.1 KiB
ReStructuredText
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
|