MikroTik: Avoid Asymmetric Routing Between the ISP and the VPN
Mark inbound connections, force their replies back through the same Internet access, and preserve VPN policy routing without breaking published services.
A VPN tunnel and a standard Internet connection can perfectly coexist on a MikroTik. The difficulty arises when a local network server is published on the ISP's address while a policy routing rule sends part of the outgoing traffic into the VPN. The request arrives via the ISP, but the response might leave through the tunnel. The client then sees two different paths, and the connection fails.
The symptom is misleading : the website, mail server, camera, or NAS responds to some clients, but not to others. The dst-nat seems correct, ports are open, and the service is listening. Yet, the first SYN packet enters through the public WAN while the SYN-ACK takes the VPN. The remedy consists of memorizing the origin of the connection, then imposing the same Internet access to all its return packets.
Why a default route is not enough
RouterOS chooses a route for each packet. If the router has a rule that directs a server, a VLAN, or certain destinations to a VPN table, this rule can also apply to the response of a published service. A valid default route in main therefore does not prevent a more precise mangle rule from sending the packet elsewhere.
Connection tracking, or conntrack, provides the necessary memory. The first packet received on the WAN is associated with a connection. A connection mark remains attached to both directions of this flow. When the server responds, RouterOS retrieves this mark and can apply a routing mark that selects a table dedicated to the ISP.
| Element | Role | Duration |
|---|---|---|
connection-mark=from-isp | Identifies the flow entered by the ISP, in both directions | For the entire duration of the tracked connection |
routing-mark=via-isp | Requests a route lookup in the table via-isp | The processed packet |
Table via-isp | Contains the forced exit path to the ISP | As long as the route is active |
Scenario and names used
The following commands target RouterOS 7. Adapt the names before pasting them. The addresses 192.0.2.0/24 belong to a block reserved for documentation and should not be used as an actual configuration.
| Name in the guide | Meaning | Example to replace |
|---|---|---|
ether1 | Physical interface connected to the ISP | WAN, SFP, or modem |
LAN | List of internal interfaces | bridge, VLAN, or existing list |
from-isp | Mark of connections received from the ISP | Free and unique name |
via-isp | Routing table reserved for return via the ISP | Free and unique name |
192.0.2.1 | ISP gateway in the static example | Actually provided gateway |
The published server must already be functioning with an dst-nat rule. The guide does not modify either this translation or the rules that select the traffic allowed to use the VPN.
Before starting : backup and observation
A bad routing mark can interrupt access to the router. Work from the LAN, enable Safe Mode in WinBox or with Ctrl+X, then export the configuration.
/export file=avant-routage-symetrique
/system backup save name=avant-routage-symetrique
Then identify the routes, the mangle rules, and the firewall order. A general rule placed before the new rules can intercept the packet first.
/ip route print detail
/routing rule print detail
/ip firewall mangle print detail
/ip firewall filter print detail
/interface list member print
Check in particular that ether1 is indeed the ISP's WAN and that the internal interfaces belong to the LAN list. If you are already using marks named from-isp or via-isp, choose other names.
1. Create the ISP routing table
In RouterOS 7, a custom table must be declared before being used as new-routing-mark. The fib option allows its use for actual packet forwarding.
/routing table
add fib name=via-isp
With a static gateway, add a default route in this table. The @main suffix indicates that the gateway must be resolved using the main table.
/ip route
add dst-address=0.0.0.0/0 gateway=192.0.2.1@main \
routing-table=via-isp distance=1 \
comment="Symmetry: return via ISP"
Do not keep 192.0.2.1 : replace it with the gateway actually visible in /ip route print detail. If the WAN receives its gateway via DHCP, use the script presented later instead.
2. Mark incoming connections
The first rule applies only to a new, not yet marked connection that enters through the ISP's WAN. It sets from-isp on the entire connection. It does not decide the route yet.
/ip firewall mangle
add chain=prerouting action=mark-connection \
in-interface=ether1 connection-state=new connection-mark=no-mark \
new-connection-mark=from-isp passthrough=yes \
comment="Symmetry: connections received from ISP"
If multiple Internet connections are active, create a distinct mark per connection. Avoid a rule that is too broad, based only on in-interface-list=WAN : it would no longer allow knowing which WAN should carry the response.
3. Send LAN responses back through the ISP
The second rule only matches packets returning from the LAN and belonging to a marked connection. The first incoming packet, arriving on ether1, therefore cannot be accidentally sent to the return table. The dst-address-type=!local exclusion also avoids marking traffic destined for the router itself.
/ip firewall mangle
add chain=prerouting action=mark-routing \
in-interface-list=LAN connection-mark=from-isp \
dst-address-type=!local new-routing-mark=via-isp passthrough=no \
comment="Symmetry: LAN replies via ISP"
This rule covers responses from a server behind dst-nat. The mangle policy routing rules to the VPN must come after this exception or explicitly exclude connection-mark=from-isp.
4. Cover services hosted on the router
A service terminated on the MikroTik itself — for example, a VPN server — does not traverse prerouting on the way back. Its packet is created locally and goes through the output chain. Therefore, add this rule if the router itself responds to connections received from the ISP.
/ip firewall mangle
add chain=output action=mark-routing \
connection-mark=from-isp dst-address-type=!local \
new-routing-mark=via-isp passthrough=no \
comment="Symmetry: router replies via ISP"
This rule does not authorize any new service. The input firewall remains responsible for access to the router, and administration should not be exposed directly to the Internet.
5. Prevent FastTrack from ignoring the custom table
In RouterOS, FastTrack processes packets with the main table. A connection that must use via-isp therefore must not enter the general FastTrack rule. Add a targeted acceptance of marked connections and place it immediately before the first fasttrack-connection rule.
/ip firewall filter
add chain=forward action=accept connection-mark=from-isp \
connection-state=established,related \
comment="Symmetry: exclude these connections from FastTrack"
In WinBox, move this rule above FastTrack. From the command line, first display the numbers, then use move with the visible numbers rather than assuming a location.
/ip firewall filter print where action=fasttrack-connection
/ip firewall filter print where comment~"Symmetry"
WAN with gateway assigned by DHCP
A custom route does not always automatically follow the DHCP client's gateway change. However, RouterOS exposes the bound and gateway-address variables to the client's event script. The following script only creates or updates the route with the comment via-isp-dhcp : it does not touch other routes.
Open IP → DHCP Client, edit the client of ether1, then paste this code into the Script field on the advanced tab.
:local routeComment "via-isp-dhcp"
:local routeId [/ip route find where comment=$routeComment]
:if ($bound = 1) do={
:local nextHop ($"gateway-address" . "@main")
:if ([:len $routeId] = 0) do={
/ip route add dst-address=0.0.0.0/0 gateway=$nextHop \
routing-table=via-isp distance=1 comment=$routeComment
} else={
/ip route set $routeId gateway=$nextHop disabled=no
}
:log info ("Route via-isp updated: " . $nextHop)
} else={
:if ([:len $routeId] > 0) do={
/ip route disable $routeId
}
:log warning "Route via-isp desactivee: bail DHCP absent"
}
After saving, renew the lease during a maintenance window or rerun the script from the interface. Verify that the route appears active in the via-isp table. The script disables this route if the lease disappears, rather than silently keeping an old gateway.
Case of a PPPoE connection
With PPPoE, the route's gateway can be the logical interface itself. No DHCP script is necessary then.
/ip route
add dst-address=0.0.0.0/0 gateway=pppoe-out1 \
routing-table=via-isp distance=1 \
comment="Symmetry: PPPoE return via ISP"
Replace pppoe-out1 with the actual name of the interface. If the VPN also uses PPPoE or a dynamic interface, identify each entry with a unique comment.
Verify the result
Test from a truly external network : mobile connection, remote site, or monitoring probe. A request sent from the same LAN might use hairpin NAT and does not validate the complete Internet path.
/routing table print
/ip route print detail where routing-table=via-isp
/ip firewall mangle print stats
/ip firewall filter print stats
/ip firewall connection print detail where connection-mark=from-isp
/log print where message~"via-isp"
For a successful incoming connection, the counters must tell a coherent story :
- the
mark-connectionrule increases upon external opening ; - the connection appears with
connection-mark=from-isp; - the LAN's
mark-routingrule increases when the server responds ; - the default route of
via-ispis active ; - the established connection is not captured by FastTrack.
A limited capture on the ISP's interface and the VPN's interface allows confirming the result without ambiguity. The server's response packet must exit through the ISP, not through the tunnel.
/tool sniffer quick interface=ether1 ip-protocol=tcp
/tool torch interface=ether1
Filter if possible on the address or port of the tested service to avoid a capture that is too large. Do not leave a permanent sniffer on a production router.
Troubleshooting : the most frequent causes
| Symptom | Probable cause | Check |
|---|---|---|
| No connection is marked | Wrong WAN interface or rule placed after a rule that stops processing | Compare in-interface and mangle counters |
| The mark exists, but the response goes through the VPN | Priority VPN rule or LAN interfaces missing from the list | Check the order and /interface list member print |
The via-isp table is inactive | Wrong or unresolved gateway | Verify the @main suffix and the DHCP lease |
| The first packet works then the flow breaks | FastTrack takes over the connection | Place the marked acceptance before FastTrack |
| The router responds poorly, but the server works | Missing output rule | Add the routing mark for local responses |
| IPv4 works, IPv6 does not | The configuration only covers the IPv4 firewall | Build a separate IPv6 policy with its own routes and rules |
Also avoid an rp-filter=strict setting in an intentionally multi-path architecture. Strict reverse path filtering can reject valid packets when multiple tables and multiple outputs coexist. Evaluate this parameter in the full context of the firewall before modifying it.
Cleanly remove the configuration
If the result is not compliant, Safe Mode can cancel the session's changes. For manual removal, use comments and unique names rather than rule numbers that might have changed.
/ip firewall filter remove [find where comment="Symmetry: exclude these connections from FastTrack"]
/ip firewall mangle remove [find where comment~"Symmetry:"]
/ip route remove [find where comment="Symmetry: return via ISP"]
/ip route remove [find where comment="Symmetry: PPPoE return via ISP"]
/ip route remove [find where comment="via-isp-dhcp"]
/routing table remove [find where name=via-isp]
If you added the script to the DHCP client, also empty its Script field. Do not delete the table as long as a rule or a route still references it.
Conclusion
The coexistence of an ISP and a VPN poses no problem as long as each connection retains a predictable return path. In RouterOS, the correct combination is simple : a connection mark memorizes the entry WAN, a routing mark processes the responses, and a dedicated table contains the exit path.
Reliability mainly depends on details : creating the table before using it, resolving the dynamic gateway, placing the exception before the VPN policy routing, keeping these flows out of FastTrack, and checking the counters from an external network. Once these points are controlled, published services respond via the ISP without depriving the rest of the network of its VPN tunnel.
Discussion
Comments
No comment yet. Feel free to open the discussion with a question or your own experience.