FIREWALL / NAT
NAT.
Three NAT scenarios cover almost every small-router setup: masquerade for LAN-to-internet, dst-nat for inbound port forwarding, and hairpin for "let LAN clients reach my public IP." The forward chain still does the filtering — NAT only changes addresses.
1. Masquerade — typical SOHO
One rule. Anything leaving on a WAN-list interface gets the WAN's source
address. masquerade is a special form of src-nat
that auto-discovers the egress IP, which is what you want behind a
dynamic-IP ISP. If you have a static public IP and want it pinned, swap
masquerade for src-nat to-addresses=<your-ip>.
# typical SOHO masquerade (v6)
/ip firewall nat
add chain=srcnat action=masquerade out-interface-list=WAN \
comment="mtkf: masquerade WAN egress" # typical SOHO masquerade (v7)
/ip/firewall/nat
add chain=srcnat action=masquerade out-interface-list=WAN \
comment="mtkf: masquerade WAN egress" 2. dst-nat (port forwarding)
Inbound on the WAN interface, matching a specific port, gets rewritten to
an internal IP and (optionally) port. Pair with the
forward chain's
connection-nat-state=!dstnat drop rule, which the dst-nat
flag bypasses cleanly.
# port-forward 443 to an internal HTTPS server (v6)
/ip firewall nat
add chain=dstnat action=dst-nat \
in-interface-list=WAN protocol=tcp dst-port=443 \
to-addresses=192.168.88.10 to-ports=443 \
comment="mtkf: dst-nat 443 -> internal https" # port-forward 443 to an internal HTTPS server (v7)
/ip/firewall/nat
add chain=dstnat action=dst-nat \
in-interface-list=WAN protocol=tcp dst-port=443 \
to-addresses=192.168.88.10 to-ports=443 \
comment="mtkf: dst-nat 443 -> internal https" Order matters. Place this rule above any catch-all src-nat / masquerade in the same table. RouterOS evaluates NAT rules top-to-bottom and stops on the first match.
3. Hairpin NAT
Hairpin NAT solves a specific paper-cut: a LAN client (say, your laptop
on 192.168.88.50) tries to reach
https://<your-public-ip> — which is your own router.
Without hairpin, the dst-nat rewrites the destination to
192.168.88.10 but the reply from .10 goes
directly to .50, bypassing the router and breaking the
connection (the client expects a reply from the public IP, not from a
LAN peer).
Fix: src-nat the LAN-to-LAN-via-public-IP traffic so replies go back through the router and get translated correctly.
# hairpin NAT — let LAN clients reach the public IP via the internal address (v6)
# the trick: src-nat the inbound dst-natted connection so the reply goes back through the router
/ip firewall nat
# (1) the existing dst-nat rule from above stays as-is
# (2) src-nat the LAN-to-LAN-via-public-IP traffic
add chain=srcnat action=masquerade \
src-address=192.168.88.0/24 dst-address=192.168.88.10 \
protocol=tcp dst-port=443 \
comment="mtkf: hairpin srcnat for 443" # hairpin NAT — let LAN clients reach the public IP via the internal address (v7)
# the trick: src-nat the inbound dst-natted connection so the reply goes back through the router
/ip/firewall/nat
# (1) the existing dst-nat rule from above stays as-is
# (2) src-nat the LAN-to-LAN-via-public-IP traffic
add chain=srcnat action=masquerade \
src-address=192.168.88.0/24 dst-address=192.168.88.10 \
protocol=tcp dst-port=443 \
comment="mtkf: hairpin srcnat for 443" Many small-router guides skip hairpin because "use the LAN IP from inside" is a workable workaround. We cover it because the LAN-IP workaround breaks split-DNS setups and any container/VM that shipped with the public hostname baked in.
A note on IPv6
There's no IPv6 NAT in the rules above and there shouldn't be. NAT66 / NPTv6 exist as protocols but the right answer for IPv6 is to route a delegated prefix to LAN clients and let them have routable global addresses. The IPv6 forward chain drops new WAN-ingress traffic — that's the membrane that NAT incidentally provides on IPv4.