Hairpin NAT Problem RB493G

Hi all,

I am having some trouble getting hairpin NAT working. I have a RB493.

ether2 has been renamed as WAN. ether3-9 are part of a bridge that is named LAN.

LAN has ip address 192.168.175.1/24.

I have a host (192.168.175.100) that has MySQL on port 3306.

Internet connection is a static IP assigned to WAN (ether2).

Here are my NAT rules:

/ip firewall nat
add action=log chain=srcnat disabled=no log-prefix="Log SRC-NAT MySQL" port=3306 protocol=tcp
add action=log chain=dstnat disabled=no log-prefix="Log DST-NAT MySQL" port=3306 protocol=tcp
add action=dst-nat chain=dstnat comment="Allow incoming MySQL" disabled=no dst-port=3306 in-interface=WAN protocol=tcp to-addresses=192.168.175.100
add action=dst-nat chain=dstnat comment="Local DST-NAT for MySQL" disabled=no dst-address=1.2.3.4 port=3306 protocol=tcp to-addresses=192.168.175.100
add action=dst-nat chain=dstnat comment="Allow incoming HTTP" disabled=no dst-port=80 in-interface=WAN protocol=tcp to-addresses=192.168.175.100
add action=masquerade chain=srcnat comment="Masquerade (NAT) all local traffic to WAN" disabled=no out-interface=WAN
add action=masquerade chain=srcnat comment="Hairpin NAT for MySQL" disabled=no dst-address=192.168.175.100 dst-port=3306 out-interface=LAN protocol=tcp src-address=192.168.175.0/24

Here are my FILTER rules:

/ip firewall filter
add action=log chain=input disabled=no log-prefix="Log INPUT MySQL" port=3306 protocol=tcp
add action=log chain=output disabled=no log-prefix="Log OUTPUT MySQL" port=3306 protocol=tcp
add action=log chain=forward disabled=no log-prefix="Log FORWARD MySQL" port=3306 protocol=tcp
add action=accept chain=forward comment="Allow Local LAN to Local LAN connections" disabled=no dst-address=192.168.175.0/24 src-address=192.168.175.0/24
add action=accept chain=input disabled=no dst-port=22 protocol=tcp src-address=192.168.175.0/24
add action=reject chain=input disabled=no dst-port=22 protocol=tcp reject-with=icmp-network-unreachable
add action=accept chain=input disabled=no dst-port=8291 protocol=tcp src-address=192.168.175.0/24
add action=reject chain=input disabled=no dst-port=8291 protocol=tcp reject-with=icmp-network-unreachable
add action=accept chain=input comment="Allow established connections" connection-state=established disabled=no
add action=accept chain=input comment="Allow related connections" connection-state=related disabled=no
add action=reject chain=input disabled=no in-interface=WAN reject-with=icmp-network-unreachable

Here is the log from a connection attmept from 192.168.175.103->1.2.3.4 (MySQL, tcp/3306):

10:20:36 firewall,info Log PREROUTING MySQL prerouting: in:LAN out:(none), src-mac 00:1d:60:bc:a8:e7, proto TCP (SYN), 192.168.175.103:58440->1.2.3.4:3306, len 60 
10:20:36 firewall,info Log DST-NAT MySQL dstnat: in:LAN out:(none), src-mac 00:1d:60:bc:a8:e7, proto TCP (SYN), 192.168.175.103:58440->1.2.3.4:3306, len 60 
10:20:36 firewall,info Log FORWARD MySQL forward: in:LAN out:WAN, src-mac 00:1d:60:bc:a8:e7, proto TCP (SYN), 192.168.175.103:58440->192.168.175.100:3306, len 60 
10:20:36 firewall,info Log POSTROUTING MySQL postrouti: in:(none) out:WAN, proto TCP (SYN), 192.168.175.103:58440->192.168.175.100:3306, len 60 
10:20:36 firewall,info Log SRC-NAT MySQL srcnat: in:(none) out:WAN, proto TCP (SYN), 192.168.175.103:58440->192.168.175.100:3306, len 60

It seems to me the problem is the RB dst-nats the public IP back to private IP, but then thinks the packet should go out the WAN interface. Any ideas what I am doing wrong?

Thank you,
James

Check the order of your rules. Hairpin nat should be toward the top.

I work with the OP. After months of fighting this, we have finally figured it all out. Our issue was compounded by using IPSec VPN. Our IPSec rules have a src-address of 192.168.175.0/24 and a dst-address of 192.168.0.0/16 (so the source subnet is in the same range as the destination).

There are some missing details on the Wiki page that might be helpful for other people. The following is a working configuration for us.

There needs to be a firewall filter rule to allow LAN-to-LAN:

/ip firewall filter add action=accept chain=forward comment="Allow Local LAN to Local LAN connections" disabled=no dst-address=192.168.175.0/24 src-address=192.168.175.0/24

Then, we have our NAT rules near the top (the order matters!):

/ip firewall nat
add action=masquerade chain=srcnat comment="Hairpin NAT for MySQL" disabled=no dst-address=192.168.175.100 dst-port=3306 out-interface=LAN protocol=tcp src-address=192.168.175.0/24
add action=dst-nat chain=dstnat comment="Local DST-NAT for MySQL" disabled=no dst-address=1.2.3.4(WAN IP) port=3306 protocol=tcp to-addresses=192.168.175.100
add action=accept chain=srcnat comment="Do not NAT packets headed for IPSec tunnels" disabled=no dst-address=192.168.0.0/16 src-address=192.168.175.0/24

Finally, we needed an additional IPSec policy specifically for the local subnet. If this rule doesn’t exist, the local subnet (192.168.175.0/24) cannot ping the router (192.168.175.1), and the router cannot ping the local subnet. This results in the Hairpin traffic never reaching the local subnet. It is important that this rule is BEFORE the “normal” IPSec policy (otherwise it hits the normal policy first and sends the packets across the wire). This needs to be done via SSH/Console/Telnet, there doesn’t appear to be a way to change the order of the policies via Winbox.

NOTE: The only way we got this to work on a “live” setup was after adding the follow rule, disable the IPSec peer and policy, flush installed-ca and kill remote-peers, then re-enable the peer and policy (force the tunnel down and back up).

/ip ipsec policy
add action=none disabled=no dst-address=192.168.175.0/24 dst-port=any ipsec-protocols=esp level=require priority=0 proposal=default protocol=all sa-dst-address=0.0.0.0 sa-src-address=0.0.0.0 src-address=192.168.175.1/24 src-port=any tunnel=yes place-before=0

On a side note, we where only able to bring up the IPSec tunnel from one direction (remote to local). Going the other direction resulted in the tunnel never successfully being established. This rule fixes that:

/ip firewall filter add action=accept chain=input disabled=no protocol=ipsec-esp src-address-list=4.3.2.1(remote public IP)