Is there really no NAT loopback/reflection/hairpinning anymore?

  • ATTENTION! As of November 1, 2020, you are not able to reply to threads 6 months after the thread is opened if there are more than 500 posts in the thread.
    Threads will not be locked, so posts may still be edited by their authors.
    Just start a new thread on the topic to post if you get an error message when trying to reply to a thread.

Conundrum1911

New Around Here
So a bit at wits end regarding this -- I've spent more than a few days looking back and forth for a definitive answer on if NAT loopback/reflection/hairpinning is still possible on these routers (specifically the RT-AC86U on my case), and there doesn't seem to be an answer. Apparently there was an implementation in Merlin in the past, but was removed due to issues. There is some talk of things reverting back to using "Asus' implementation", but since Day 1 I've never had this working on mine, stock FW or Merlin.

So I guess what I am after is, is this a dead issue with no resolution, or is there some way to get this working still in 2020? Even setting up a dnsmasq file and trying to force things over doesn't even seem to work (everything still resolves to my external IP via no-ip).
 

ColinTaylor

Part of the Furniture
I don't know what the current state is with the RT-AC86U (as I don't have one) but as far as I know hair-pinning should work. Don't confuse name resolution with hair-pinning though as they're two completely different things.

Maybe you can give us a description of your current configuration.
 

Conundrum1911

New Around Here
I know they aren't the same, but given NAT loopback doesn't seem possible I was also trying to see if I could accomplish similar via local DNS.

Current config is fairly simple..TC7650 cable modem plugged directly into the AC86U (no double NAT). DDNS via no-ip has been working flawlessly for years, everything resolves fine from the outside with port forwards, etc.

This also all worked fine on my old Netgear WNDR3700 I had years ago...it just never has since I replaced it with the Asus router. I've also bought the same router for my parents when theirs died (since aside from this it has been a great router), and it has never worked on theirs either. Both are on 384.19, and it looks like "hardware acceleration" is on, although there also doesn't appear to be a way to turn that off either in the name of debugging.
 

ColinTaylor

Part of the Furniture
What port forwarding rules have you setup on the router and how are you testing it?

I've just tried it on my router and it works fine with an FTP connection. Obviously it's not a particularly good comparison because I'm running a completely different version of the firmware.
 

Conundrum1911

New Around Here
I've had port forwarding set up for over a year for a number of different applications, some pointing to a Windows PC (for RDP), others for things like qBittorrent, Tautulli, SSH, Home Assistant, etc. All work fine from the outside, etc, but I can't resolve anything from the inside using my DDNS, I have to use either local hostnames of the PCs or their internal 192.168.10.0/24 IPs on my local subnet.

Considering your router is still running a version below 384.4 it predates the removal of Merlins' NAT loopback option, so maybe that's why it still works for you. Every post I see on this forum, reddit, etc about the AC86U though seems split between a few saying it works, and a ton saying it never has for them.
 

ColinTaylor

Part of the Furniture
Like I said, name resolution issues are a different subject. I'm just testing using my WAN IP address. If you post the output of iptables-save -t nat I can compare it to mine and see if there are any significant differences.
 

ColinTaylor

Part of the Furniture
FYI I just tested this again (using an external DNS name) with an internal web server (http and https) and RDP and both worked fine. I don't think my firmware has the "special Merlin loopback" because I think it predates that. Seeing your iptables output would confirm whether that's the case.
 

dave14305

Part of the Furniture

Conundrum1911

New Around Here
From iptables -- Redacted external IP and my port forwards since I don't want to expose that to the public (all forwards from outside work perfectly though)

Code:
# Generated by iptables-save v1.4.15 on Mon Oct 19 09:10:39 2020
*nat
:PREROUTING ACCEPT [94950:7308060]
:INPUT ACCEPT [37469:3054573]
:OUTPUT ACCEPT [81569:6770070]
:POSTROUTING ACCEPT [44454:3092773]
:DNSFILTER - [0:0]
:GAME_VSERVER - [0:0]
:LOCALSRV - [0:0]
:PCREDIRECT - [0:0]
:PUPNP - [0:0]
:VSERVER - [0:0]
:VUPNP - [0:0]
-A PREROUTING -d <external IP>/32 -j GAME_VSERVER
-A PREROUTING -d <external IP>/32 -j VSERVER
-A POSTROUTING -o eth0 -j PUPNP
-A POSTROUTING ! -s <external IP>/32 -o eth0 -j MASQUERADE
-A POSTROUTING -s 192.168.10.0/24 -d 192.168.10.0/24 -o br0 -j MASQUERADE
-A PUPNP -s 192.168.10.12/32 -p tcp -m tcp --sport 32400 -j MASQUERADE --to-ports 15078
-A PUPNP -s 192.168.10.10/32 -p tcp -m tcp --sport 32400 -j MASQUERADE --to-ports 11054
<port fowards were here -- redacted>
-A VSERVER -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.10.10:80
-A VSERVER -j VUPNP
-A VUPNP -p tcp -m tcp --dport 15078 -j DNAT --to-destination 192.168.10.12:32400
-A VUPNP -p tcp -m tcp --dport 11054 -j DNAT --to-destination 192.168.10.10:32400
COMMIT
# Completed on Mon Oct 19 09:10:39 2020
The VUPNP entries all seem to have been added automatically by Plex
 

ColinTaylor

Part of the Furniture
I think John’s fork is the only one that still has it.
Thanks for the extra info Dave. It looks like John changed the default NAT loopback from Merlin's to Asus' back in 2016. So I've been testing with Asus' method. There's no GUI option to change the method, only via nvram variables.
 

ColinTaylor

Part of the Furniture
From iptables -- Redacted external IP and my port forwards since I don't want to expose that to the public (all forwards from outside work perfectly though)
:
The VUPNP entries all seem to have been added automatically by Plex
I can't comment on the way Plex has setup its forwarding through UPnP because I don't have a Plex server. But your NAT loopback is the same as mine and access to the web server on port 80 should work. I've just setup the same forwarding rule here and it's identical to yours, and it works fine with my external IP address.

Do bear in mind that from the web server's perspective the source address of the incoming connection will be 192.168.10.1, not the original source address (e.g.
192.168.10.99). Just something to remember if you're looking in the log files or setting up access control lists.
 
Last edited:

dave14305

Part of the Furniture
Are there hits on the rule? iptables -t nat -nvL POSTROUTING
 

Conundrum1911

New Around Here
Are there hits on the rule? iptables -t nat -nvL POSTROUTING
Code:
Chain POSTROUTING (policy ACCEPT 32881 packets, 2280K bytes)
 pkts bytes target     prot opt in     out     source               destination         
37433 2854K PUPNP      all  --  *      eth0    0.0.0.0/0            0.0.0.0/0           
22240 1944K MASQUERADE  all  --  *      eth0   !<external_IP>      0.0.0.0/0           
28684 2833K MASQUERADE  all  --  *      br0     192.168.10.0/24      192.168.10.0/24
 

Conundrum1911

New Around Here
I've actually tried this now too without any luck:


Here's my modified code, saved as NAT-START in /jffs/scripts:

Code:
#!/bin/sh
# Add Hairpin NAT rules, so that accessing some internal servers
# uses the Internet external IP address as far as the server is concerned.

ExternalIP=`ifconfig eth0 | grep 'inet addr' | cut -d: -f2 | awk '{print $1}'`
logger "Changing firewall to use NAT Hairpinning with external IP $ExternalIP for certain servers."

# Start a new chain for our NAT reflection or hairpinning rules.
if ! iptables --table nat --new HairPin ; then
  logger "Hairpin rules already in place, doing nothing."
  exit
fi

# SEVER1 RULES
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 8123 -j SNAT --to-source $ExternalIP
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 10000 -j SNAT --to-source $ExternalIP
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 8181 -j SNAT --to-source $ExternalIP
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 9000 -j SNAT --to-source $ExternalIP

# SERVER2 RULES
iptables --table nat --append HairPin -d 192.168.10.11 -p tcp -m tcp --dport 8080 -j SNAT --to-source $ExternalIP

# Hook the Hairpinning rules into the main POSTROUTING table just after the
# policy rule.  Also do some common prerequisite tests for hairpinning (has
# to be from LAN to LAN).
iptables --table nat --insert POSTROUTING 2 -o br0 -s 192.168.10.0/24 -d 192.168.10.0/24 -j HairPin

logger "Hairpin rules added."
I can also manually apply it to iptables, yet still hairpinning doesn't seem to work.
 

dave14305

Part of the Furniture
Why not add a temporary log rule at the end of the HairPin chain to see what's traversing it? Something like this:
Code:
iptables -t nat -A HairPin -j LOG --log-prefix "[HairPin] " --log-tcp-sequence --log-tcp-options --log-ip-options
 

eibgrad

Very Senior Member
I've actually tried this now too without any luck:


Here's my modified code, saved as NAT-START in /jffs/scripts:

Code:
#!/bin/sh
# Add Hairpin NAT rules, so that accessing some internal servers
# uses the Internet external IP address as far as the server is concerned.

ExternalIP=`ifconfig eth0 | grep 'inet addr' | cut -d: -f2 | awk '{print $1}'`
logger "Changing firewall to use NAT Hairpinning with external IP $ExternalIP for certain servers."

# Start a new chain for our NAT reflection or hairpinning rules.
if ! iptables --table nat --new HairPin ; then
  logger "Hairpin rules already in place, doing nothing."
  exit
fi

# SEVER1 RULES
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 8123 -j SNAT --to-source $ExternalIP
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 10000 -j SNAT --to-source $ExternalIP
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 8181 -j SNAT --to-source $ExternalIP
iptables --table nat --append HairPin -d 192.168.10.10 -p tcp -m tcp --dport 9000 -j SNAT --to-source $ExternalIP

# SERVER2 RULES
iptables --table nat --append HairPin -d 192.168.10.11 -p tcp -m tcp --dport 8080 -j SNAT --to-source $ExternalIP

# Hook the Hairpinning rules into the main POSTROUTING table just after the
# policy rule.  Also do some common prerequisite tests for hairpinning (has
# to be from LAN to LAN).
iptables --table nat --insert POSTROUTING 2 -o br0 -s 192.168.10.0/24 -d 192.168.10.0/24 -j HairPin

logger "Hairpin rules added."
I can also manually apply it to iptables, yet still hairpinning doesn't seem to work.
Seems to me that NAT loopback implementation is unnecessarily complex.

All NAT loopback is supposed to do is SNAT (w/ the LAN ip of the router!) any traffic directed at the public IP on the WAN that gets redirected back into the LAN. Pretty simple. So it always takes a combination of DNAT + SNAT to implement it. And the port forwards typically provide the DNAT.

Code:
LAN_IP="$(nvram get lan_ipaddr)"
LAN_NET="$LAN_IP/$(nvram get lan_netmask)"
iptables -t nat -I POSTROUTING -s $LAN_NET -o br0 -d $LAN_NET -j SNAT --to $LAN_IP
 

ColinTaylor

Part of the Furniture
All NAT loopback is supposed to do is SNAT (w/ the LAN ip of the router!) any traffic directed at the public IP on the WAN that gets redirected back into the LAN.
That's exactly what the router's default MASQUERADE rule does. MASQUERADE is a special form of SNAT. The only difference with this alternate version is that it's NATing the router's WAN address instead of it's LAN address (hence the need to use SNAT instead of MASQUERADE). The author of the script apparently needed to do this to solve some issue with his BitTorrent server, but otherwise it shouldn't normally be necessary.
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top