Same goal as Cisco PBR — route by who sent the packet, not just where it's going — but RouterOS gets there two different ways, and knowing which to reach for is half the battle. Routing rules for the simple src/dst cases; mangle routing-marks for anything that needs connection awareness. The twin of the Cisco Policy-Based Routing post.
RouterOS gives you two policy-routing tools, and picking the right one avoids a lot of pain. Routing rules (/routing rule) are the lightweight option: match on source/destination/interface and send the lookup to a different routing table — fast, stateless, perfect for "this subnet uses that table." Mangle routing-marks are the powerful option: mark a packet (or a whole connection) in the firewall, then route by that mark — which lets you decide on anything the firewall can match (port, protocol, connection state) and, crucially, keep a flow pinned to one WAN for its whole life.
The distinction that matters: routing rules can't see connections, so for dual-WAN you almost always want the mangle + connection-mark + routing-mark pattern, so a reply comes back the same way the request left. This is the RouterOS counterpart to Cisco PBR — one set ip next-hop route-map there, two distinct mechanisms here.
01
When the decision is purely "this source uses that path," routing rules are the cleanest tool: a route in a named table, and a rule that points matching traffic at it.
# A default route living in its own routing table (v7 uses routing-table=). /routing table add name=via-ISP-B fib /ip route add dst-address=0.0.0.0/0 gateway=203.0.113.9 routing-table=via-ISP-B # Rule: guest source -> look up in via-ISP-B instead of main. /routing rule add src-address=10.10.20.0/24 action=lookup-only-in-table table=via-ISP-B # Everything else falls through to the main table normally.
lookup vs lookup-only-in-table
action=lookup tries your table and then falls back to main if there's no match — handy, but it means a dead gateway in your table silently reverts to the default path (sometimes good, sometimes a surprise). action=lookup-only-in-table is strict: match this table or drop, no fallback. For dual-WAN failover you usually want lookup so a dead ISP-B falls back to ISP-A; for hard isolation (guest must never use ISP-A) you want lookup-only-in-table. Choose deliberately.
02
When the decision depends on ports, protocols, or must survive as a connection, mark the connection once and the packets thereafter, then route by the mark. This is the workhorse pattern for real dual-WAN.
/ip firewall mangle # 1. Mark the CONNECTION on the first packet (here: SIP/RTP). add chain=prerouting protocol=udp port=5060,10000-20000 \ connection-state=new action=mark-connection new-connection-mark=voip-conn passthrough=yes # 2. Turn the connection-mark into a ROUTING-mark on every packet of it. add chain=prerouting connection-mark=voip-conn \ action=mark-routing new-routing-mark=via-ISP-A passthrough=no # 3. A default route in that routing table. /ip route add dst-address=0.0.0.0/0 gateway=198.51.100.1 routing-table=via-ISP-A
The two-step — mark-connection on the new packet, then mark-routing from that connection-mark — is the pattern that keeps a flow on one WAN for its entire life. Match only per-packet and a mid-connection classification change (or an unmarked return packet) can split a flow across both links, breaking stateful NAT and anything the far end tracks. Marking the connection once and deriving the routing-mark from it guarantees symmetry. It's the single most important habit in RouterOS policy routing, and the reason mangle beats bare routing rules for dual-WAN.
03
Policy-routed traffic still has to be NATed out the interface it's actually leaving by, and replies must find their way back. Two rules people forget, and the symptoms are baffling.
/ip firewall nat add chain=srcnat out-interface=ISP-A-link action=masquerade add chain=srcnat out-interface=ISP-B-link action=masquerade # Each WAN NATs what leaves it. Without the matching masquerade, traffic # policy-routed out ISP-B leaves with an ISP-A-shaped source and is dropped. # For inbound-initiated flows on the secondary WAN, also mark connections # arriving on ISP-B so their replies route back out ISP-B (same 2-step).
04
Every stage has a counter. Walk them in order — mangle hits, the mark on the connection, the route chosen — and the break is always at one identifiable step.
/ip firewall mangle print stats # do the mark rules have packet counts? /ip firewall connection print where connection-mark=voip-conn /routing rule print # rules present and ordered /ip route print where routing-table=via-ISP-A # From a marked host, a traceroute should exit the intended WAN gateway.
| Symptom | Where to look |
|---|---|
| Traffic ignores the policy | Mangle rule not matching (wrong chain/port), or route in wrong table. |
| Flow splits across both WANs | Marked per-packet, not per-connection — use the 2-step connection→routing mark. |
| Leaves right WAN, no replies | Missing per-WAN masquerade, or return path not marked. |
| Dead ISP-B kills guest internet | lookup-only-in-table with no fallback — switch to lookup. |
Takeaways
lookup falls back to main; lookup-only-in-table doesn't. Choose based on whether a dead path should fail over or stay isolated.NOCTIS builds connection-aware policy routing on MikroTik and Cisco across Crete — guest, VoIP, and backups each on the right path, pinned per-connection, with clean NAT and failover.
Book a Discovery Call →