Skip to content

RouterOS NAT: masquerade, src-nat, dst-nat, port forwarding, and hairpin

RouterOS NAT: masquerade, src-nat, dst-nat, port forwarding, and hairpin

Section titled “RouterOS NAT: masquerade, src-nat, dst-nat, port forwarding, and hairpin”

Network Address Translation (NAT) in RouterOS rewrites IP addresses (and optionally ports) as packets traverse the router. All NAT rules live in /ip firewall nat and are evaluated on the first packet of each new connection only — subsequent packets follow the same translation automatically via connection tracking.

RouterOS has two NAT chains:

ChainPurpose
srcnatRewrites the source address of outgoing packets (masquerade, src-nat, netmap outbound)
dstnatRewrites the destination address of incoming packets (port forwarding, dst-nat, netmap inbound)

dstnat rules are evaluated before routing; srcnat rules are evaluated after routing. Connection tracking must be enabled for NAT to work correctly.

  • RouterOS v6.x or v7.x
  • At least one WAN interface with a public (or upstream-routable) IP address
  • Connection tracking enabled (default on most RouterOS configurations):
/ip firewall connection tracking
set enabled=yes
  • Know your WAN interface name (commonly ether1, pppoe-out1, or an interface list named WAN)

Both actions translate the source address of outgoing packets, but they differ in how the replacement address is determined.

Use action=masquerade when your WAN IP is dynamic (DHCP, PPPoE, or any address that changes). Masquerade automatically reads the current IP of the outgoing interface at translation time and adapts when the IP changes. This makes it slightly slower at high connection rates due to the interface-IP lookup on each new connection.

/ip firewall nat
add chain=srcnat action=masquerade out-interface=ether1 \
comment="Masquerade all LAN traffic to dynamic WAN IP"

Use action=src-nat when your WAN IP is static. You specify the exact replacement address with to-addresses. This avoids the per-connection interface-IP lookup and performs better at scale.

/ip firewall nat
add chain=srcnat action=src-nat out-interface=ether1 \
src-address=192.168.88.0/24 to-addresses=203.0.113.10 \
comment="Static src-nat to fixed public IP"

Rule of thumb: Prefer src-nat with a static to-addresses whenever possible. Fall back to masquerade only when the WAN IP can change unpredictably.


dst-nat (destination NAT) redirects inbound connections from the router’s public IP/port to an internal host. This is the standard mechanism for port forwarding.

Mandatory matchers for a port-forward rule:

  • chain=dstnat
  • in-interface — the WAN interface (prevents forwarding of internal traffic)
  • protocoltcp or udp (required when matching ports)
  • dst-port — the external port(s) to intercept

Forward external TCP/80 to an internal web server:

/ip firewall nat
add chain=dstnat action=dst-nat in-interface=ether1 \
protocol=tcp dst-port=80 \
to-addresses=192.168.88.10 to-ports=80 \
comment="Port forward: WAN:80 -> 192.168.88.10:80"

Forward to a different internal port (port remapping):

/ip firewall nat
add chain=dstnat action=dst-nat in-interface=ether1 \
protocol=tcp dst-port=8443 \
to-addresses=192.168.88.10 to-ports=443 \
comment="Port remap: WAN:8443 -> 192.168.88.10:443"

Multiple services on the same host (separate rules per service):

/ip firewall nat
add chain=dstnat action=dst-nat in-interface=ether1 \
protocol=tcp dst-port=80 \
to-addresses=192.168.88.10 to-ports=80 \
comment="HTTP forward"
add chain=dstnat action=dst-nat in-interface=ether1 \
protocol=tcp dst-port=443 \
to-addresses=192.168.88.10 to-ports=443 \
comment="HTTPS forward"
add chain=dstnat action=dst-nat in-interface=ether1 \
protocol=udp dst-port=5060 \
to-addresses=192.168.88.20 to-ports=5060 \
comment="SIP UDP forward"

NAT rules are evaluated top-to-bottom and the first matching rule wins. Order rules from most-specific to least-specific.

Tip: Using an interface list instead of a hard-coded interface name makes rules portable across configurations:

/ip firewall nat
add chain=dstnat action=dst-nat in-interface-list=WAN \
protocol=tcp dst-port=3389 \
to-addresses=192.168.88.50 to-ports=3389 \
comment="RDP forward via WAN list"

action=netmap provides static 1:1 address translation — every IP in a source range maps to a corresponding IP in the target range. It is used when you have a block of public IPs and want each to map directly to an internal host, in both directions.

Because RouterOS has no single “bidirectional” NAT action, you create two paired rules: one dstnat rule for inbound and one srcnat rule for outbound.

Single-host 1:1 NAT (one public IP ↔ one internal host):

/ip firewall nat
# Inbound: public IP 203.0.113.20 -> internal 192.168.88.20
add chain=dstnat action=netmap in-interface=ether1 \
dst-address=203.0.113.20 \
to-addresses=192.168.88.20 \
comment="1:1 NAT inbound - host 20"
# Outbound: internal 192.168.88.20 -> public IP 203.0.113.20
add chain=srcnat action=netmap out-interface=ether1 \
src-address=192.168.88.20 \
to-addresses=203.0.113.20 \
comment="1:1 NAT outbound - host 20"

Full-subnet 1:1 NAT (/24 block):

/ip firewall nat
add chain=dstnat action=netmap in-interface=ether1 \
dst-address=203.0.113.0/24 \
to-addresses=192.168.88.0/24 \
comment="1:1 subnet NAT inbound"
add chain=srcnat action=netmap out-interface=ether1 \
src-address=192.168.88.0/24 \
to-addresses=203.0.113.0/24 \
comment="1:1 subnet NAT outbound"

Both subnets must be the same size. RouterOS maps addresses positionally — the Nth address in the source range maps to the Nth address in the target range.

Important: After adding or changing netmap rules, clear existing connections so the new rules apply to active flows:

/ip firewall connection remove [find]

Hairpin NAT solves the problem of internal clients reaching an internal server using the server’s public IP. Without hairpin NAT, the dstnat rule rewrites the destination but the server receives a connection from an internal IP, sends the reply directly back on the LAN, and the client never gets the translated response — the session breaks.

How hairpin works: A second srcnat rule matches LAN-originated traffic destined for the internal server (after dstnat has rewritten the destination). It masquerades the source address to the router’s LAN IP, so the server sends all replies back through the router, which can then deliver them correctly to the client.

Full hairpin NAT example (web server at 192.168.88.10, public IP 203.0.113.5):

/ip firewall nat
# Step 1: Port forward (applies to both internet clients AND LAN hairpin)
add chain=dstnat action=dst-nat in-interface=ether1 \
protocol=tcp dst-port=80,443 dst-address=203.0.113.5 \
to-addresses=192.168.88.10 \
comment="Port forward: WAN -> web server"
# Step 2: Hairpin srcnat - rewrite source so server replies via router
add chain=srcnat action=masquerade \
src-address=192.168.88.0/24 \
dst-address=192.168.88.10 \
protocol=tcp dst-port=80,443 \
comment="Hairpin NAT: LAN clients using public IP for internal server"

The hairpin srcnat rule should be placed before the general masquerade rule to ensure it matches first.

Why the dst-address in hairpin srcnat is the internal IP: By the time srcnat evaluates the packet, dstnat has already rewritten the destination from the public IP to the internal server IP. So the hairpin srcnat rule must match on the post-dstnat destination (192.168.88.10), not the original public IP.

Alternative using src-nat with explicit gateway IP (avoids masquerade overhead):

/ip firewall nat
add chain=srcnat action=src-nat \
src-address=192.168.88.0/24 \
dst-address=192.168.88.10 \
protocol=tcp dst-port=80,443 \
to-addresses=192.168.88.1 \
comment="Hairpin NAT: explicit LAN gateway as source"

Here to-addresses is the router’s LAN interface IP (e.g. 192.168.88.1).

Check NAT rule hit counters — bytes/packets should increment during traffic:

/ip firewall nat print stats

Inspect active connections — confirm translation is occurring:

/ip firewall connection print where dst-address~"192.168.88.10"

Use torch to watch real-time traffic on the WAN interface:

/tool torch interface=ether1 src-address=0.0.0.0/0

Test port forwarding from an external host (or use an external IP checker):

/tool fetch url="http://203.0.113.5:80" output=none

Verify hairpin NAT from a LAN client:

/ping 203.0.113.5 count=4

If ping fails but curl http://203.0.113.5 works, check that only the relevant ports are NAT’d and that ICMP is allowed through separately.

Port forward not working — traffic not reaching internal host

  1. Confirm the dstnat rule has in-interface set to the correct WAN interface (not the LAN interface).
  2. Check that connection-state is not inadvertently blocking traffic in the firewall filter forward chain before NAT takes effect. dstnat runs before routing, but filter forward runs after.
  3. Verify the internal host has the router as its default gateway — otherwise reply packets bypass the router.
  4. Check rule hit counters with /ip firewall nat print stats. Zero hits means the rule is not matching.

masquerade/src-nat not translating outbound traffic

  1. Confirm out-interface matches the actual WAN interface name exactly.
  2. Verify connection tracking is enabled: /ip firewall connection tracking print.
  3. Check that the srcnat rule position is correct — rules above it may be matching first.

Hairpin NAT not working — internal clients cannot reach server via public IP

  1. Confirm the hairpin srcnat rule is present and placed before the general masquerade rule.
  2. The dst-address in the hairpin srcnat must match the internal server IP (post-dstnat rewrite), not the public IP.
  3. Clear affected connections after rule changes: /ip firewall connection remove [find].
  4. Verify hit counters on both the dstnat and hairpin srcnat rules increment during a test from a LAN client.

NAT not applying after rule change

NAT decisions are cached per connection on the first packet. Existing connections are not affected by new rules. Clear connections to force re-evaluation:

/ip firewall connection remove [find]

Checking NAT order of operations

RouterOS processes packets in this order for a forwarded packet:

  1. RAW prerouting
  2. dstnat (NAT chain, prerouting)
  3. Routing decision
  4. Filter forward chain
  5. srcnat (NAT chain, postrouting)
  6. RAW postrouting

This means dstnat rewrites happen before filter forward rules see the packet — the filter chain sees the already-translated destination address.

  • Firewall and QoS Case Studies — advanced firewall with masquerade and interface lists
  • RouterOS documentation: /ip firewall nat — full parameter reference
  • RouterOS documentation: /ip firewall connection — connection tracking table inspection