NAT examples

The fw4 application has extensive support for NAT filterning. NAT is a powerful feature and is credited with extending the life of the IPv4 protocol.

As with other firewall section, this section will not delve into NAT background and theory. Some useful links for this are:

OpenWrt supports DNAT, SNAT, MASQUERADING.

See Netfilter Management for analyzing the netfilter rules and investigating conntrack sessions.

This section contains typical uses of the fw4 NAT features

The goal of this rule is to redirect all WAN-side SSH access on port 2222 to a the SSH (22) port of a single LAN-side station.

config redirect
       option target          DNAT
       option src             wan
       option dest            lan
       option proto           tcp
       option src_dport       2222
       option dest_ip         192.168.10.20
       option dest_port       22
       option enabled         1

To test from a WAN-side station (STA1), SSH on port 2222 to a non-existent IPv4 address on the LAN-side network:

ssh -p 2222 192.168.10.13 hostname; cat /proc/version

When the rule is enabled STA2 will reply with its hostname and kernel version. When the rule is disabled, the connection is refused.

The passionate reader will ask “So what netfilter rules does this create?”

iptables -t nat -A zone_wan_prerouting -p tcp -m tcp --dport 2222 -m comment --comment "!fw3: @redirect[0]" -j DNAT --to-destination 192.168.10.20:22
...
iptables -t nat -A zone_lan_prerouting -p tcp -s 192.168.10.0/255.255.255.0 -d 192.168.3.185/255.255.255.255 -m tcp --dport 2222 -m comment --comment "!fw3: @redirect[0] (reflection)" -j DNAT --to-destination 192.168.10.20:22

The first rule matches packets coming in the WAN-side if on TCP port 2222 and jumps to the DNAT filter to translate the destination to 192.168.10.20:22. The second rule matches packets coming in from the LAN-side to the WAN-side if on TCP port 2222. The DNAT target uses the same --to-destination parameters as the first rule to find the “reflection” in the conntrack table.

The next thought of the passionate reader is “So what is IN the conntrack table?”

ipv4     2 tcp      6 117 TIME_WAIT src=192.168.3.171 dst=192.168.10.13 sport=51390 dport=2222 packets=21 bytes=4837 src=192.168.10.20 dst=192.168.3.171 sport=22 dport=51390 packets=23 bytes=4063 [ASSURED] mark=0 use=2

This record shows the WAN-side src=STA1 and dst=192.168.10.13:2222 and the reverse direction LAN-side src=STA2:22 src=STA1.

This redirect rule will cause the router to translate the WAN-side source of 1.2.3.4 to the LAN-side STA2 and route the ICMP echo to it. The rule is reflexive in that STA2 will be translated by to 1.2.3.4 on the WAN-side.

config redirect
        option src      wan
        option src_dip  1.2.3.4
        option proto    icmp
        option dest     lan
        option dest_ip  192.168.10.20
        option target   DNAT
	option name     DNAT-ICMP-WAN-LAN
	option enabled  1

All redirection requires some form of NAT and connection tracking. For public servers behind the firewall the DNAT target is used to translate the public IP address on the WAN-side to the private address of the server in the LAN-side.

:!: Due to the high visibility of a public server, it may warrant putting it/them in a fw4 DMZ.

config redirect
        option target DNAT
        option src wan
        option src_dport 25
        option proto tcp
        option family ipv4
        option dest lan
        option dest_ip 192.168.10.20
        option dest_port 2525
        option name DNAT-MAIL-SERVER
        option enabled 1

In this example, STA2 is running an email server (e.g. postfix) listening on port 2525 for incoming email.

This redirect rule states: any incoming traffic from the wan on port 25, redirect to STA1 port 2525.

To verify what is going on dump /proc/net/nf_conntrack to observe the dynamic connnection for incoming traffic. There can be quite a few conntrack records in it so we will search on just the ones using port 2525:

...
ipv4     2 tcp      6 7436 ESTABLISHED src=192.168.3.171 dst=192.168.3.11 sport=41370 dport=25 packets=4 bytes=229 src=192.168.10.20 dst=192.168.3.171 sport=2525 dport=41370 packets=3 bytes=164 [ASSURED] mark=0 use=2
...

The connection is coming from STA1 port 25 to the DUT and is translated to STA2 on port 2525 with a response destination to STA1.

The relevant traffic matches the DNAT conntrack state which is allowed to traverse zones by OpenWrt firewall, so no extra permissive rules are required.

The goal of this rule is to translate the source IP address from a real station to a fictitious one on port 8080.

config redirect
        option target           SNAT
        option src              lan
        option dest             wan
	option proto            tcp
        option src_ip           192.168.10.20
        option src_dip          192.168.10.13
        option dest_port        8080
	option enabled          1

To test:

  1. use netcat to listen on the STA1, the WAN-side station: nc -l 8080
  2. use netcat to connect on the STA2, the LAN-side station: nc -v 192.168.3.171 8080

Type something on the LAN-side station and see it echoed on the WAN-side station. Check the connection on the WAN-side station using netstat -ntap and see the line:

tcp        0      0 192.168.3.171:8080      192.168.10.13:47970 ESTABLISHED 16746/nc

The WAN-side station shows the SNAT address connecting to it on port 8080!

When used alone, Source NAT is used to restrict a computer's access to the internet while allowing it to access a few services by forwarding what appears to be a few local services, e.g. NTP, to the internet. While DNAT hides the local network from the internet, SNAT hides the internet from the local network.

This is the most used and useful NAT function. It translates a local private network on the LAN-side to a single public address/port num on the WAN-side and then the reverse. It is the default firewall configuration for every IPv4 router. As a result it is a very simple fw4 configuration

The LAN-side uses a private network. The router translates the private addresses to the router address:port and the netfilter conntrack module manages the connection.

The masquerade is set on the WAN-side

config zone
	option name 'wan'
	list network 'wan'
	....
	option masq '1'

Simple, no?

The router will generally get its WAN ip address from the upstream DHCP server and be the DHCP server (and usually DNS server) for LAN stations. The network configuration file defines the private network and the dhcp configuration file defines how the OpenWrt router assigns LAN-side IPv4 addresses.

When MASQUERADE is enabled, all forwarded traffic between WAN and LAN is translated. Essentially, there is very little that can go wrong with the MASQUERADE firewall rules.

Dump /proc/net/nf_conntrack to inspect the current MASQUERADE connections. The following connection tracks SSH (22) access from STA1 to STA2.

ipv4     2 tcp      6 4615 ESTABLISHED src=192.168.3.171 dst=192.168.10.20 sport=60446 dport=22 packets=27 bytes=1812 src=192.168.10.20 dst=192.168.3.171 sport=22 dport=60446 packets=21 bytes=2544 [ASSURED] mark=0 use=2

:!: MASQUERADE supports two or more private LAN zones

:!: not tested

The following rule redirects all LAN-side HTTP traffic through an external proxy at 192.168.1.100 listening on port 3128. It assumes the lan address to be 192.168.1.1 - this is needed to masquerade redirected traffic towards the proxy.

config redirect
        option src              lan
        option proto            tcp
        option src_ip           !192.168.1.100
        option src_dport        80
        option dest_ip          192.168.1.100
        option dest_port        3128
        option target           DNAT
 
config redirect
        option dest             lan
        option proto            tcp
        option src_dip          192.168.1.1
        option dest_ip          192.168.1.100
        option dest_port        3128
        option target           SNAT

Enable masquerading aka NAT on the WAN zone.

uci set firewall.@zone[1].masq="1"
uci commit firewall
service firewall restart

Enable IPv6 masquerading aka NAT66 on the WAN zone.

uci set firewall.@zone[1].masq6="1"
uci commit firewall
service firewall restart

Announce IPv6 default route for the ULA prefix.

uci set dhcp.lan.ra_default="1"
uci commit dhcp
service odhcpd restart

Disable IPv6 source filter on the upstream interface.

uci set network.wan6.sourcefilter="0"
uci commit network
service network restart

Enable masquerading selectively for a specific source subnet.

uci -q delete firewall.nat
uci set firewall.nat="nat"
uci set firewall.nat.family="ipv4"
uci set firewall.nat.proto="all"
uci set firewall.nat.src="wan"
uci set firewall.nat.src_ip="192.168.2.0/24"
uci set firewall.nat.target="MASQUERADE"
uci commit firewall
service firewall restart

Enable IPv6 masquerading selectively for a specific source subnet.

uci -q delete firewall.nat6
uci set firewall.nat6="nat"
uci set firewall.nat6.family="ipv6"
uci set firewall.nat6.proto="all"
uci set firewall.nat6.src="wan"
uci set firewall.nat6.src_ip="fd00:2::/64"
uci set firewall.nat6.target="MASQUERADE"
uci commit firewall
service firewall restart

Enable IPv4 to IPv4 network prefix translation.

cat << "EOF" > /etc/nftables.d/npt.sh
LAN_PFX="192.168.1.0/24"
WAN_PFX="192.168.2.0/24"
. /lib/functions/network.sh
network_flush_cache
network_find_wan WAN_IF
network_get_device WAN_DEV "${WAN_IF}"
nft add rule inet fw4 srcnat \
oifname "${WAN_DEV}" snat ip prefix to ip \
saddr map { "${LAN_PFX}" : "${WAN_PFX}" }
EOF
uci -q delete firewall.npt
uci set firewall.npt="include"
uci set firewall.npt.path="/etc/nftables.d/npt.sh"
uci commit firewall
service firewall restart

Enable IPv6 to IPv6 network prefix translation.

cat << "EOF" > /etc/nftables.d/npt6.sh
LAN_PFX="$(uci -q get network.globals.ula_prefix)"
. /lib/functions/network.sh
network_flush_cache
network_find_wan6 WAN_IF
network_get_device WAN_DEV "${WAN_IF}"
network_get_prefix6 WAN_PFX "${WAN_IF}"
nft add rule inet fw4 srcnat \
oifname "${WAN_DEV}" snat ip6 prefix to ip6 \
saddr map { "${LAN_PFX}" : "${WAN_PFX}" }
EOF
uci -q delete firewall.npt6
uci set firewall.npt6="include"
uci set firewall.npt6.path="/etc/nftables.d/npt6.sh"
uci commit firewall
service firewall restart

Enable IPv6 network prefix translation with multiple WAN interfaces (e.g. for mwan3).

cat << "EOF" > /etc/nftables.d/npt6.sh
LAN_IF="lan"
WAN_IF="wana6 wanb6"
. /lib/functions/network.sh
network_flush_cache
network_get_prefix_assignment6 LAN_PFX "${LAN_IF}"
for WAN_IF in ${WAN_IF}
do
network_get_device WAN_DEV "${WAN_IF}"
network_get_prefix6 WAN_PFX "${WAN_IF}"
nft add rule inet fw4 srcnat \
oif "${WAN_DEV}" snat ip6 prefix to ip6 \
saddr map { "${LAN_PFX}" : "${WAN_PFX}" }
done
EOF
uci -q delete firewall.npt6
uci set firewall.npt6="include"
uci set firewall.npt6.path="/etc/nftables.d/npt6.sh"
uci commit firewall
service firewall restart

Enable symmetric dynamic IPv6 to IPv6 network prefix translation.

cat << "EOF" > /etc/nftables.d/npt6.sh
LAN_IF="lan"
sleep 5
. /lib/functions/network.sh
network_flush_cache
network_get_device LAN_DEV "${LAN_IF}"
network_get_prefix_assignment6 LAN_PFX "${LAN_IF}"
network_find_wan6 WAN_IF
network_get_device WAN_DEV "${WAN_IF}"
network_get_prefix6 WAN_PFX "${WAN_IF}"
nft add rule inet fw4 srcnat \
oifname "${WAN_DEV}" snat ip6 prefix to ip6 \
saddr map { "${LAN_PFX}" : "${WAN_PFX}" }
nft add rule inet fw4 srcnat \
oifname "${LAN_DEV}" snat ip6 prefix to ip6 \
saddr map { "${WAN_PFX}" : "${LAN_PFX}" }
EOF
uci -q delete firewall.npt6
uci set firewall.npt6="include"
uci set firewall.npt6.path="/etc/nftables.d/npt6.sh"
uci commit firewall
service firewall restart

Enable IPv6 to IPv4 NAT aka NAT64 for IPv6-only networks with Jool. Use DNS64 to resolve domain names.

opkg update
opkg install jool-tools-netfilter
. /usr/share/libubox/jshn.sh
json_init
json_add_string "instance" "default"
json_add_string "framework" "netfilter"
json_add_object "global"
json_add_string "pool6" "64:ff9b::/96"
json_close_object
json_dump > /etc/jool/jool-nat64.conf.json
uci set jool.general.enabled="1"
uci set jool.nat64.enabled="1"
uci commit jool
service jool restart

Enable IPv6 to IPv4 NAT aka NAT64 for IPv6-only networks with Tayga. Use DNS64 to resolve domain names.

opkg update
opkg install tayga
uci del_list firewall.lan.network="nat64"
uci add_list firewall.lan.network="nat64"
uci commit firewall
service firewall restart
uci -q delete network.nat64
uci set network.nat64="interface"
uci set network.nat64.proto="tayga"
uci set network.nat64.prefix="64:ff9b::/96"
uci set network.nat64.ipv6_addr="fd00:ffff::1"
uci set network.nat64.dynamic_pool="192.168.255.0/24"
uci set network.nat64.ipv4_addr="192.168.255.1"
uci commit network
service network restart

Modify TTL for egress traffic.

cat << "EOF" > /etc/nftables.d/ttl.sh
WAN_TTL="65"
. /lib/functions/network.sh
network_flush_cache
network_find_wan WAN_IF
network_get_device WAN_DEV "${WAN_IF}"
nft add rule inet fw4 mangle_postrouting \
oifname "${WAN_DEV}" ip ttl set "${WAN_TTL}"
EOF
uci -q delete firewall.ttl
uci set firewall.ttl="include"
uci set firewall.ttl.path="/etc/nftables.d/ttl.sh"
uci commit firewall
service firewall restart

Modify IPv6 hop limit for egress traffic.

cat << "EOF" > /etc/nftables.d/hlim.sh
WAN_HLIM="65"
. /lib/functions/network.sh
network_flush_cache
network_find_wan6 WAN_IF
network_get_device WAN_DEV "${WAN_IF}"
nft add rule inet fw4 mangle_postrouting \
oifname "${WAN_DEV}" ip6 hoplimit set "${WAN_HLIM}"
EOF
uci -q delete firewall.hlim
uci set firewall.hlim="include"
uci set firewall.hlim.path="/etc/nftables.d/hlim.sh"
uci commit firewall
service firewall restart

Enable NAT passthrough for FTP using kmod-nf-nathelper.

opkg update
opkg install kmod-nf-nathelper
service firewall restart

Enable NAT passthrough for SIP, PPTP, GRE, etc. using kmod-nf-nathelper-extra.

opkg update
opkg install kmod-nf-nathelper-extra
service firewall restart

Enable NAT passthrough for RTSP using kmod-ipt-nathelper-rtsp.

opkg update
opkg install kmod-ipt-nathelper-rtsp
service firewall restart
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2023/12/10 11:42
  • by vgaetera