Yet another Hairpin problem/question

Hi Everyone, hope you are dooing well and wish you a happy new year filled with health, joy and wealth :slight_smile:

This is yet another post about Hairpin. i've been looking through the documentation and reading a lot of post forums about it (most of them dating 3 to 4 years now) and still havin some problems with Hairpin.

The problem :

I havei been hosting a lot of game servers for years now for me, my family and friends using Pterodactyl panel on my homelab (2 Node proxmox).
Everything works fine, dstnat is working perfectly and never had any problem.

I have been creating some new game servers these days that do not want to connect via the lan ip but needs the public IP which is for me and my family a typical Hairpin problem because packets get's out of the network to come back from outside it.

To simplify things here is a simple diagram of the network stack

[Internet] <---> [Public IP] ISP BOX [(DMZ) Interface : 192.168.1.254/24] <---> [192.168.1.9/24] CCR2004 [192.168.2.1/24] <---> Switches/devices

Bellow is my filter rules and the relevant firewall rules (removed those not connected to the problem)

As i said everything works fine, i can connect to the game servers either by their local address or the public address for those refusing to aknowledge the local one.
I wanted to create a hairpin to prevent the outside/inside so added the masquerades but :

  • If i put Out-address as XXX (nat, bradge etc etc) as i've seen in a lot of examples nothing works. Only way to make it work is either as an IN interface or nothing
  • When testing a tcp/udp scan with nmap with hairpin activate i can see the counter going up which is encouraging
  • When i connect to the game server i seen the packet counter going up once and then ... nothing ... no traffic registered, no packets going up.

This is the part where i'm confused : i expected to see packets constantly incrementing with the communication between my pc and the game server but nothing. So does it work ? How can i know it ? In logs i get nothing apart from the initial connection.

--- Filter

/ip firewall filter
add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked
add action=drop chain=input comment="defconf: drop invalid" connection-state=invalid
add action=accept chain=input comment="defconf: accept ICMP" protocol=icmp
add action=accept chain=input comment="defconf: accept to local loopback (for CAPsMAN)" dst-address=127.0.0.1
add action=accept chain=input comment="Allowing wireguard port acces to te router" dst-port=13231 protocol=udp
add action=accept chain=input comment="[Wireguard] Protonvpn" dst-port=13232 protocol=udp
add action=accept chain=input comment="[Wireguard] Protonvpn" dst-port=13233 protocol=udp
add action=accept chain=input comment="Acceptation connexion vers routeur depuis la liners Family" src-address-list=liners_family
add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN log=yes log-prefix="[DROP-NO-LAN]”
add action=accept chain=forward comment="defconf: accept in ipsec policy" ipsec-policy=in,ipsec
add action=accept chain=forward comment="defconf: accept out ipsec policy" ipsec-policy=out,ipsec
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-state=established,related hw-offload=yes
add action=accept chain=forward comment="defconf: accept established,related, untracked" connection-state=established,related,untracked
add action=accept chain=forward comment="Acceptation trafic vers r\C3\A9seau local depuis wireguard" dst-address=192.168.2.0/24 in-interface=homeliners-wireguard-interface log=yes log-prefix="[input-liners_family]”
add action=drop chain=forward comment="defconf: drop invalid" connection-state=invalid
add action=drop chain=forward comment="defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat connection-state=new in-interface-list=WAN

--- Firewall

/ip firewall nat
add action=masquerade chain=srcnat comment="Hairpin for the game server" disabled=yes dst-address-list=free_ipv4_public dst-port=28000-28199 log=yes log-prefix="[FW][PTEROS-0-HAIRPIN-TCP]" protocol=tcp src-address-list=liners_family to-ports=28000-28199
add action=masquerade chain=srcnat comment="Hairpin for the game server" disabled=yes dst-address-list=free_ipv4_public dst-port=28200-28250 log=yes log-prefix="[FW][PTEROS-1-HAIRPIN-TCP]" protocol=tcp src-address-list=liners_family to-ports=28200-28250
add action=masquerade chain=srcnat comment="Hairpin for the game server" disabled=yes dst-address-list=free_ipv4_public dst-port=28000-28199 log=yes log-prefix="[FW][PTEROS-0-HAIRPIN-UDP]" protocol=udp src-address-list=liners_family to-ports=28000-28199
add action=masquerade chain=srcnat comment="Hairpin for the game server" disabled=yes dst-address-list=free_ipv4_public dst-port=28200-28250 log=yes log-prefix="[FW][PTEROS-1-HAIRPIN-UDP]" protocol=udp src-address-list=liners_family to-ports=28200-28250
add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=out,none log=yes log-prefix="[DEFAULT-MASQUERADE]" out-interface-list=WAN
add action=dst-nat chain=dstnat comment="[tiguemmi][lxc] pteros-0" dst-port=28000-28199 in-interface-list=WAN log=yes log-prefix="[FW][PTEROS-0]" protocol=tcp to-addresses=192.168.2.250 to-ports=28000-28199
add action=dst-nat chain=dstnat comment="[tiguemmi][lxc] pteros-0" dst-port=28000-28199 in-interface-list=WAN log=yes log-prefix="[FW][PTEROS-0]" protocol=udp to-addresses=192.168.2.250 to-ports=28000-28199
add action=dst-nat chain=dstnat comment="[tiguemmi][lxc] pteros-1" dst-port=28200-28250 in-interface-list=WAN log=yes log-prefix="[FW][PTEROS-1]" protocol=tcp to-addresses=192.168.2.248 to-ports=28200-28250
add action=dst-nat chain=dstnat comment="[tiguemmi][lxc] pteros-1" dst-port=28200-28250 in-interface-list=WAN log=yes log-prefix="[FW][PTEROS-1]" protocol=udp to-addresses=192.168.2.248 to-ports=28200-28250

Did you read this ? See below for a Hairpin-NAT example and step-by-step packetflow

Your hairpin-rules are not correct I think, the hairpin part is never referring to any public IP.
(like you refer : dst-address-list=free_ipv4_public → in your case you do not have a true public IP right, only the 192.168.1.9 on the “WAN” of the device?

Try adding something like (and disable the 4 rules that you call “hairpin”

/ip firewall nat
add action=masquerade chain=srcnat dst-address=192.168.2.0/24 out-interface=LAN protocol=tcp src-address=192.168.2.0/24
add action=masquerade chain=srcnat dst-address=192.168.2.0/24 out-interface=LAN protocol=udp src-address=192.168.2.0/24

1 Like

I already read the documentation :slight_smile:

I use an address list not the ip directly which is the same thing. The address list is a variable that contains the IP address (see dst-address-list). Easier to maintain an upgrade an IP without having to upgrade a lot of lines.

free_ipv4_public contains both the real public IP and the WAN if you want.

As i said it seems to work for simple tcp/udp packets i send on the relevant port as i see the “hairpin” rules counter go up. Event when i connect to a game server using the “Real Public IP”.

For the sake of the example let’s assume the following :

  • For my router it’s WAN interface is the sfp-plusplus2 (192.168.1.9)
  • The real IP from the ISP router is 82.XXX.XXX.XXX

free_ipv4_public contains : 82.XXX.XXX.XXX

Which means whenever a query (tcp/udp) goes to 82.XXX.XXX.XXX on port 28002 (let’s settle on one port range for the moment : 28100 to 28199) ; with my rules, i see the rules counter increment when i do an nmap test with tcp/udp scan on the port 28002 and address 82.XXX.XXX.XXX

I even see it increment when i connect to a game server using the 82.XXX.XXX.XXX but after that event if i stay in game i see nothing.

I’ll try your solution when i get home

Please take a look at the firewall flowchart The ultimate Mikrotik iptables flowchart - RouterOS / Useful user articles - MikroTik community forum

The masquerade rule, which is a SRCNAT rule, is at the end of the flowchart, at the box #25. At this point, the packet has already went through the DSTNAT manipulation, which is way earlier at #10. Which means the value of the destination address, dst-address, has already been rewritten by DSTNAT, and no longer contains the original public IP address. At #25 the value you see in dst-address (and also what is dst-address-list uses to compare) is the LAN IP address of the servers, these:

image

That's why the condition in the masquerade rule should be dst-address=192.168.2.0/24, like what @jvanhambelgium posted above:

or dst-address-list=local_server_list, with local_server_list containing 192.168.2.248 and 192.168.2.250.

Also those masquerade rules don't need to-ports=.

1 Like

Don’t rely too much on the counters. I might be the first packets that increments the counter, but then perhaps something gets offloaded (depending on your hardware-device) and then your counters do not increment anymore since the CPU does not see them anymore (switch-chip L3 offload)

@jvanhambelgium @CGGXANNX Ok thank you :slight_smile: as soon as i hit the house i’ll check and try with what you taught me and shown me. I’ll keep in touch with you about it.

Thanks for the help and informations

@jvanhambelgium @CGGXANNX

Thank you for you patience and kindness with me.I reread the documentation to better understand it

Just to illustrate here is the diagram of the situation :

So when my friend on the outside reach the server IP with my public address : everything works fine

The game we are playing is one of those steam games expecting the “public” address, that’s why i wanted to hairpin this. I read both your rightful answer and did by them. I made two masquerade rules where

  • src -address-list = local_lan_ip_addresses (192.168.2.0/24)
  • dst-address-list = lan_game_servers (both the server ips)
  • out-interface-list = LAN (LAN in my router is an address list encompassing bridge. So instead of out-interface bridge i preferred the list to have it clearly readable in one eye swoop)

So far nothing went awry and everything works fine. I’m not minding the token counter as you advised However even with logs enabled : i do not see masquerade logs for the 2 rules i made only the default one logging.

I’ll keep it like this and monitor in the next days.