Skip to content

IKEv2 Road Warrior — NAT Workaround

When multiple IKEv2 road-warrior clients connect from behind the same NAT device (corporate firewall, shared home router, mobile carrier NAT), they all appear to the RouterOS server as originating from a single public IP address. The standard generate-policy=yes setting keys dynamic IPsec policies on the remote IP alone, so the second client’s policy collides with and replaces the first client’s SA — the first client is silently disconnected.

Root cause: IKEv2 road-warrior policy collision when multiple clients share a public IP.

Solution: generate-policy=port-strict keys each dynamic policy on both the remote IP and its UDP source port (the NAT-T port on UDP/4500). Because each NATed client gets a distinct port mapping, policies no longer collide.

Additional problem: send-initial-contact=yes (the default) causes a reconnecting client to send a delete-notification that instructs the responder to clear all existing SAs for that identity — knocking out other clients sharing the same pre-shared key. Setting send-initial-contact=no disables this behaviour.

This topology is supported on RouterOS 6.45 and later. RouterOS 7.x is recommended for full IKEv2 mode-config stability.

Client A (NAT 203.0.113.1:4501) ──┐
├── RouterOS server 203.0.113.50
Client B (NAT 203.0.113.1:4502) ──┘ port-strict: A policy ≠ B policy

NAT-T wraps ESP in UDP/4500. The server tracks each client by (remote-ip, remote-port) rather than remote-ip alone.


RequirementNotes
RouterOS 6.45+ (7.x recommended)generate-policy=port-strict requires 6.45+
Public IP on the server WAN interfaceClients connect to this IP
UDP 500 and UDP 4500 reachable on serverMust not be blocked upstream
IP pool for VPN clientse.g. 10.10.10.0/24 not overlapping LAN
Pre-shared key or CA infrastructurePSK shown here; see See Also for certificates
WAN interface listThe masquerade rule uses out-interface-list=WAN; create it with /interface list add name=WAN and add your WAN interface: /interface list member add list=WAN interface=<your-wan>

/ip pool
add name=ike2-rw-pool ranges=10.10.10.2-10.10.10.254

Mode-config pushes a virtual IP, DNS, and split routes to the client:

/ip ipsec mode-config
add name=ike2-rw-cfg \
address-pool=ike2-rw-pool \
address-prefix-length=32 \
split-include=192.168.88.0/24 \
system-dns=no \
static-dns=192.168.88.1
  • split-include — routes pushed to the client (omit to route all traffic through the tunnel)
  • address-prefix-length=32 — client receives a /32 virtual IP
  • static-dns — DNS server pushed to the client
/ip ipsec policy group
add name=ike2-rw-group
/ip ipsec policy
add group=ike2-rw-group \
template=yes \
src-address=0.0.0.0/0 \
dst-address=0.0.0.0/0 \
proposal=ike2-rw-proposal \
level=unique

Note: ike2-rw-proposal is created in Step 4. If running these commands interactively, create the proposal first (Step 4), then return to create the policy template, or run Steps 3–4 as a batch.

  • level=unique — requires a unique SA per source address; this is the documented setting for NAT / multiple clients behind the same public IP. Without it, two clients sharing a NAT IP may share an SA instead of getting independent ones.

The template policy is instantiated per-client when generate-policy=port-strict creates dynamic entries.

/ip ipsec proposal
add name=ike2-rw-proposal \
auth-algorithms=sha256 \
enc-algorithms=aes-256-cbc,aes-128-cbc \
pfs-group=modp2048 \
lifetime=1h

nat-traversal=yes is essential — it enables NAT detection and UDP/4500 encapsulation:

/ip ipsec profile
add name=ike2-rw-profile \
hash-algorithm=sha256 \
enc-algorithm=aes-256,aes-128 \
dh-group=modp2048 \
nat-traversal=yes \
dpd-interval=30s \
dpd-maximum-failures=5
/ip ipsec peer
add name=ike2-rw-peer \
address=0.0.0.0/0 \
exchange-mode=ike2 \
profile=ike2-rw-profile \
passive=yes \
send-initial-contact=no

passive=yes — server waits for incoming connections; address=0.0.0.0/0 — accepts from any source IP. send-initial-contact=no prevents a reconnecting client from sending a delete-notification that would clear other clients’ SAs.

/ip ipsec identity
add peer=ike2-rw-peer \
auth-method=pre-shared-key \
secret=ChangeThisToAStrongSecret \
generate-policy=port-strict \
mode-config=ike2-rw-cfg \
policy-template-group=ike2-rw-group
ParameterValueWhy
generate-policy=port-strictport-strictPolicies keyed on IP + port — prevents collision
mode-configike2-rw-cfgAssigns virtual IP to each client
policy-template-groupike2-rw-groupLinks dynamic policies to the template

PSK identity limitation: With a shared PSK, all clients present the same identity to the server. port-strict separates their policies by port, but RouterOS cannot assign different mode-configs or ACLs to individual clients — every client gets the same pool and split routes. For per-user access control, use certificate or EAP authentication instead (see See Also).

Place before any default-drop rule:

/ip firewall filter
add chain=input protocol=udp dst-port=500 action=accept \
comment="IKEv2 IKE"
add chain=input protocol=udp dst-port=4500 action=accept \
comment="IKEv2 NAT-T"
add chain=input protocol=ipsec-esp action=accept \
comment="IPsec ESP (non-NATed clients)"

UDP 4500 carries NAT-T encapsulated ESP — this is the critical rule for NATed clients. UDP 500 handles the initial handshake before NAT is detected.

Prevents the server’s masquerade rule from rewriting VPN-encrypted packets:

/ip firewall nat
add chain=srcnat action=accept ipsec-policy=out,ipsec \
comment="IPsec bypass — must be before masquerade"

Note: place-before=0 is only valid when at least one NAT rule already exists; on an empty table it raises an error. Add this rule first (or omit place-before), then add the masquerade rule after it. Confirm ordering with /ip firewall nat print.

If VPN clients need internet access routed through the server, first ensure the WAN interface list exists and includes your WAN interface, then add the masquerade rule after the bypass:

/interface list add name=WAN
/interface list member add list=WAN interface=ether1
/ip firewall nat
add chain=srcnat src-address=10.10.10.0/24 \
out-interface-list=WAN action=masquerade \
comment="VPN client internet access"

Replace ether1 with your actual WAN interface name.


/ip ipsec active-peers print

Multiple clients behind the same NAT should show the same remote IP but different ports:

# STATE REMOTE-ADDRESS REMOTE-PORT ...
0 established 203.0.113.1 4501
1 established 203.0.113.1 4502
/ip ipsec installed-sa print

Each client should have its own SA pair (enc + auth for each direction). The src-address and dst-address fields in dynamic policies confirm port-strict is working:

/ip ipsec policy print where dynamic=yes
/ip ipsec mode-config print

Each connected client appears with its assigned virtual IP under remote-address.

/ip ipsec policy print detail

Byte/packet counters on dynamic policies increment as traffic flows.

From a connected client, ping the server LAN address or a host behind it:

ping 192.168.88.1

If the ping succeeds, the tunnel is up and policy matching is working.


Second client disconnects first client on connect

Section titled “Second client disconnects first client on connect”

Cause: send-initial-contact=yes on the peer (default). The second client sends a delete-SA notification for the shared identity.

Fix: Set send-initial-contact=no on the peer:

/ip ipsec peer set [find name=ike2-rw-peer] send-initial-contact=no

Cause 1: No NAT bypass rule. The masquerade rule rewrites the source of returning traffic, breaking the IPsec policy check on the return path.

Fix: Confirm the bypass rule is present and placed before masquerade:

/ip firewall nat print

Cause 2: IPsec policy template dst-address does not include the LAN subnet.

Fix: Edit the template policy to match traffic destined for the LAN:

/ip ipsec policy set [find template=yes] dst-address=192.168.88.0/24

NAT-T not activating (ESP blocked mid-path)

Section titled “NAT-T not activating (ESP blocked mid-path)”

Symptom: active-peers print shows remote-port=500 or peers disappear after Phase 1.

Cause: An ISP or intermediate NAT device blocks IP protocol 50 (raw ESP).

Fix: nat-traversal=yes in the profile is required. Verify it is active:

/ip ipsec profile print where name=ike2-rw-profile

Once NAT-T activates, all ESP is wrapped in UDP/4500. The established counter in active-peers print detail should increment.

Symptom: policy print dynamic shows no entries after client connects.

Cause: generate-policy is not port-strict or the identity is not matched.

Check:

/ip ipsec identity print detail
/log print where topics~"ipsec"

Ensure generate-policy=port-strict appears on the identity. IKEv2 debug logs reveal whether Phase 2 negotiation reaches the policy-generation step.

Client roams (IP changes) and tunnel breaks

Section titled “Client roams (IP changes) and tunnel breaks”

Cause: IKEv2 does not natively support MOBIKE on all RouterOS versions.

Workaround: Configure a short DPD interval so the server detects the dead peer quickly and clears the SA, allowing the client to reconnect:

/ip ipsec profile set ike2-rw-profile dpd-interval=30s dpd-maximum-failures=3

FastTrack bypasses IPsec policy — tunnel up but traffic does not flow

Section titled “FastTrack bypasses IPsec policy — tunnel up but traffic does not flow”

Symptom: VPN clients connect successfully (active-peers print shows established), but traffic does not pass through the tunnel or arrives unencrypted on the server.

Cause: RouterOS FastTrack (/ip firewall filter action=fasttrack-connection) fast-forwards established connections at the hardware/driver level, bypassing the IPsec policy lookup. Packets that should be encrypted are forwarded in the clear.

Fix: Add accept rules for IPsec-matched traffic before the FastTrack rule in the forward chain:

/ip firewall filter
add chain=forward action=accept ipsec-policy=in,ipsec place-before=0 \
comment="IPsec inbound — skip FastTrack"
add chain=forward action=accept ipsec-policy=out,ipsec place-before=1 \
comment="IPsec outbound — skip FastTrack"

Verify the rule order — the accept rules must appear before any action=fasttrack-connection rule:

/ip firewall filter print
/system logging add topics=ipsec action=memory
/log print where topics~"ipsec"

Remove the log rule when done:

/system logging remove [find topics~"ipsec"]