Port forwarding not working, possibly double NAT issues

I have OpenWrt 18.06.2 installed on a LinkSys wrt1900ac.

I have several zones that are separated from each other with vlans. Two of the zones are meant for normal home users (lan and guest). And I have a separate zone (srv) meant for servers that host some internet hobby projects that I want to keep separate from my home network for various reasons.

Looks like this:

I want to expose some ports from the srv zone to the internet, so I wrote some basic port forwarding rules. For example:

config redirect
option name 'code'
option target 'DNAT'
option proto 'tcp udp'
option src_dip '52.128.23.153'
option src_dport '2262'
option dest_ip '192.168.4.62'
option dest_port '22'
option dest 'srv'
option src 'wan'

This works fine when connecting from outside my router, to my public IP (I'm using 52.128.23.153 as an example) on port 2262 in this case.

But when I try to access this from an internal zone, for example from the lan zone to this public IP it fails. How can I fix this redirect rule?

I'm thinking it might have something to do with having more than one NAT in the route. That is the route might look like this: lan -> (NAT) -> wan -> (NAT) -> srv.

I know the redirect rules are working for the external case but not from internal because I looked at iptables-save. For example I reset the firewall and try to access from an internal zone, all rules have zero counts:

root@OpenWrt:~# iptables-save -c | grep code
[0:0] -A zone_srv_postrouting -s 192.168.4.0/24 -d 192.168.4.62/32 -p tcp -m tcp --dport 22 -m comment --comment "!fw3: code (reflection)" -j SNAT --to-source 192.168.4.1
[0:0] -A zone_srv_prerouting -s 192.168.4.0/24 -d 52.128.23.153/32 -p tcp -m tcp --dport 2262 -m comment --comment "!fw3: code (reflection)" -j DNAT --to-destination 192.168.4.62:22
[0:0] -A zone_wan_prerouting -d 52.128.23.153/32 -p tcp -m tcp --dport 2262 -m comment --comment "!fw3: code" -j DNAT --to-destination 192.168.4.62:22

If I then access from an external IP, I see non-zero counts:

root@OpenWrt:~# iptables-save -c | grep code
[0:0] -A zone_srv_postrouting -s 192.168.4.0/24 -d 192.168.4.62/32 -p tcp -m tcp --dport 22 -m comment --comment "!fw3: code (reflection)" -j SNAT --to-source 192.168.4.1
[0:0] -A zone_srv_prerouting -s 192.168.4.0/24 -d 52.128.23.153/32 -p tcp -m tcp --dport 2262 -m comment --comment "!fw3: code (reflection)" -j DNAT --to-destination 192.168.4.62:22
[1:60] -A zone_wan_prerouting -d 52.128.23.153/32 -p tcp -m tcp --dport 2262 -m comment --comment "!fw3: code" -j DNAT --to-destination 192.168.4.62:22

Here is my firewall config:

config defaults
	option syn_flood '1'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'DROP'

config zone
	option name 'wan'
	option network 'wan wan6'
	option output 'ACCEPT'
	option masq '1'
	option mtu_fix '1'
	option input 'REJECT'
	option forward 'REJECT'

config zone
	option name 'vpn'
	option network 'vpn'
	option output 'ACCEPT'
	option mtu_fix '1'
	option masq '1'
	option input 'REJECT'
	option forward 'REJECT'

config zone
	option name 'lan'
	option network 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'

config zone
	option name 'guest'
	option network 'guest'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option input 'ACCEPT'

config zone
	option name 'apl'
	option network 'apl'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option input 'ACCEPT'

config zone
	option name 'srv'
	option network 'srv'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option input 'ACCEPT'

config forwarding
	option src 'guest'
	option dest 'wan'

config forwarding
	option src 'apl'
	option dest 'wan'

config forwarding
	option src 'srv'
	option dest 'wan'

config forwarding
	option src 'lan'
	option dest 'vpn'

config forwarding
	option src 'lan'
    option dest 'wan'

config rule
	option name 'Allow-DHCP-Renew'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-Ping'
	option src 'wan'
	option proto 'icmp'
	option icmp_type 'echo-request'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-IGMP'
	option src 'wan'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-DHCPv6'
	option src 'wan'
	option proto 'udp'
	option src_ip 'fc00::/6'
	option dest_ip 'fc00::/6'
	option dest_port '546'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-MLD'
	option src 'wan'
	option proto 'icmp'
	option src_ip 'fe80::/10'
	list icmp_type '130/0'
	list icmp_type '131/0'
	list icmp_type '132/0'
	list icmp_type '143/0'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config include
	option path '/etc/firewall.user'

config rule
	option name 'Guest DNS'
	option src 'guest'
	option proto 'tcp udp'
	option dest_port '53'
	option target 'ACCEPT'

config rule
	option name 'Guest DHCP'
	option src 'guest'
	option proto 'tcp udp'
	option dest_port '67-68'
	option target 'ACCEPT'

config rule
	option name 'Appliance DNS'
	option src 'apl'
	option proto 'tcp udp'
	option dest_port '53'
	option target 'ACCEPT'

config rule
	option name 'Appliance DHCP'
	option src 'apl'
	option proto 'tcp udp'
	option dest_port '67-68'
	option target 'ACCEPT'

config rule
	option name 'Server DNS'
	option src 'srv'
	option proto 'tcp udp'
	option dest_port '53'
	option target 'ACCEPT'

config rule
	option name 'Server DHCP'
	option src 'srv'
	option proto 'tcp udp'
	option dest_port '67-68'
	option target 'ACCEPT'

config redirect
	option name 'code'
	option target 'DNAT'
	option proto 'tcp udp'
	option src_dip '52.128.23.153'
	option src_dport '2262'
	option dest_ip '192.168.4.62'
	option dest_port '22'
	option dest 'srv'
	option src 'wan'

config redirect
	option name 'svn'
	option src 'wan'
	option target 'DNAT'
	option src_dip '52.128.23.153'
	option src_dport '3690'
	option dest_ip '192.168.4.62'
	option dest_port '3690'
	option proto 'tcp'
	option dest 'srv'

I also have set up VPN Policy Routing, which I don't think should be affecting this, but maybe. I have a VPN tunnel set up for the lan zone. I want the other zones to skip the VPN tunnel and just exit direct to WAN so I have:

Reach web server in DMZ from intranet - #4 by vgaetera

The VPN Policy Routing was part of the problem. I needed this because the VPN by default makes itself the default gateway. The rules to route certain zones direct to WAN interact badly with port forward + reflection/NAT loopback.

I found a way to make the VPN not grab the gateway and then to route only certain traffic through VPN.

With those rules removed, forwarding works from WAN and SRV zones (that is from the internet, and when accessing from within the same zone as the server).

But it still does not work when accessing from other zones, that is from LAN, APL or Guest.

Hopefully this information helps someone - but it isn't a complete solution for me.

1 Like

If you really need NAT loopback, create separate redirects for each source zone.
Otherwise, rebinding domain on the local DNS server is a preferred way to avoid redundant NAT.

Do you mean adding rules like:

config redirect
        option name 'code-guest'
        option target 'DNAT'
        option proto 'tcp'
        option src_dip '52.128.23.153'
        option src_dport '2262'
        option dest_ip '192.168.4.62'
        option dest_port '22'
        option src 'guest'
        option dest 'srv'

I tried this, with and without reflection, and it doesn't help.

1 Like

I feel like I'm getting closer but still not working.

According to this issue here: https://bugs.openwrt.org/index.php?do=details&task_id=1645

The NAT loopback feature adds some routing rules only for the zone that the server is in, and that you have to add corresponding rules yourself for the other zones.

I think this is what @vgaetera must have meant when he said to add separate redirects.

So I added the following to the custom rules:

. /lib/functions.sh
. /lib/functions/network.sh

network_get_subnet guest guest
network_get_ipaddr wan wan
network_get_ipaddr srv srv
codeserver=192.168.4.62
iptables -t nat -A zone_guest_prerouting  -s $guest -d $wan/32        -p tcp -m tcp --dport 2262 -j DNAT --to-destination $codeserver:22 -m comment --comment "$
iptables -t nat -A zone_guest_postrouting -s $guest -d $codeserver/32 -p tcp -m tcp --dport 22   -j SNAT --to-source      $srv           -m comment --comment "$

This create a parallel set of routing rules. This from iptables-save:

[0:0] -A zone_guest_postrouting -s 192.168.2.0/24   -d 192.168.4.62/32   -p tcp -m tcp --dport 22   -j SNAT --to-source      192.168.4.1
[0:0] -A zone_srv_postrouting   -s 192.168.4.0/24   -d 192.168.4.62/32   -p tcp -m tcp --dport 22   -j SNAT --to-source      192.168.4.1
[0:0] -A zone_guest_prerouting  -s 192.168.2.0/24   -d 52.128.23.153/32  -p tcp -m tcp --dport 2262 -j DNAT --to-destination 192.168.4.62:22
[0:0] -A zone_srv_prerouting    -s 192.168.4.0/24   -d 52.128.23.153/32  -p tcp -m tcp --dport 2262 -j DNAT --to-destination 192.168.4.62:22

The srv lines were added by the NAT loopback feature. The guest ones I added. But for some reason this still does not work.

Specify the remote inverted address !192.168.0.0/21 for each VPR policy.