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 toexternal 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
tolocal 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
-
int nat_enable(FAR struct net_driver_s *dev);
Enable NAT function on a network device, on which the outbound packets will be masqueraded.
- Returns:
Zero is returned if NAT function is successfully enabled on the device; A negated errno value is returned if failed.
-
int nat_disable(FAR struct net_driver_s *dev);
Disable NAT function on a network device.
- Returns:
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:
Configure NuttX with >=2 TAP devices (host route mode) and NAT enabled:
CONFIG_NET_IPFORWARD=y CONFIG_NET_NAT=y # CONFIG_SIM_NET_BRIDGE is not set CONFIG_SIM_NETDEV_NUMBER=2
Call
nat_enable
on one dev on startup, or manually enable NAT withiptables
command (either may work).
/* arch/sim/src/sim/up_netdriver.c */ int netdriver_init(void) { ... nat_enable(&g_sim_dev[0]); ... }iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Set IP Address for NuttX on startup
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
Configure IP & namespace & route on host side (maybe need to be root, then try
sudo -i
)
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
Do anything in the LAN namespace will go through NAT
# 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# 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# LAN side sudo ip netns exec LAN ping 8.8.8.8 sudo ip netns exec LAN ping 2001:4860:4860::8888# 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# Host side tcpdump -nn -i tap0 # LAN side sudo ip netns exec LAN tcpdump -nn -i tap1