Introduction to MikroTik RouterOS 7 Routing Filters
If you moved from RouterOS 6 to RouterOS 7, the first thing you noticed about routing is that everything changed. The old firewall-style routing filter rules are gone. In their place is a declarative filter chain — a script-like language that operates on route attributes rather than firewall chains. This change affects BGP, OSPF, RIP, and even your static route selection.
Policy-based routing (PBR) is separate but complementary. Where routing filters control which routes exist in the routing table, PBR controls which routing table traffic uses. Combined, they give you complete traffic engineering in your MikroTik network.
This guide covers:
- RouterOS 7 routing filter architecture and syntax
- BGP route filtering with AS path, prefix, and community filters
- Policy-based routing with mangle rules and routing marks
- Dual WAN traffic steering and failover
- Troubleshooting common filter and PBR issues
Understanding RouterOS 7 Routing Filter Architecture
RouterOS 7 introduces a unified routing filter engine used by all routing protocols. Filters are defined as independent chain rules and then attached to a protocol or VRF.
Filter Chains
Each routing protocol has up to four filter chains:
| Chain | Direction | Purpose |
|---|---|---|
in |
Inbound | Filter routes before they enter the routing table |
out |
Outbound | Filter routes before advertising to peers |
default-in |
Inbound | Applied when no in chain exists |
default-out |
Outbound | Applied when no out chain exists |
Filters are configured under /routing filter rule and referenced by name in the protocol configuration.
Route Attributes Available in Filters
RouterOS 7 routing filters operate on per-route attributes. The most commonly used ones:
dst-address— destination prefixgateway— next-hop addressdistance— administrative distancerouting-mark— associated routing tablebgp-as-path— BGP AS_PATH attributebgp-communities— BGP community listbgp-local-pref— BGP local preferencepref-src— preferred source addressscope— route scope for next-hop resolution
Filter Flow — Match and Action
Every filter rule has a condition (match) and an action. Rules are evaluated in order. The first matching rule with a terminal action (accept/reject) terminates the chain.
/routing filter rule
add chain=bgp-in \
if-dst-length=32 \
action=reject
add chain=bgp-in \
if-gateway="10.0.0.1" \
action=accept
If no rule matches, the implicit reject applies — the route is denied.
Basic Routing Filter Syntax
Filter rules use a consistent syntax. Here are the core building blocks:
Matching on Prefix
/routing filter rule
add chain=bgp-in if-dst-address="10.0.0.0/8" action=accept
# Reject /32 routes (host routes from BGP)
add chain=bgp-in if-dst-length=32 action=reject
# Accept only prefixes /24 or smaller
add chain=bgp-in if-dst-length=24-32 action=accept
Matching on BGP AS Path
# Accept only routes with origin AS 65001
add chain=bgp-in if-bgp-as-path="^65001$" action=accept
# Reject routes from AS 65099 (transit ISP)
add chain=bgp-in if-bgp-as-path=".*65099.*" action=reject
# Accept routes with exactly 2 AS hops
add chain=bgp-in if-bgp-as-path-length=2 action=accept
BGP Route Filtering with Routing Filters
Let’s put together a complete BGP filter setup. This example assumes a BGP session with an upstream ISP (AS 64500) and an internal peer (AS 65001).
Inbound Filter — Control What Enters Your Table
/routing filter rule
# Chain: bgp-in — applied to inbound BGP updates
# 1. Reject default route from internal peer
add chain=bgp-in \
if-dst-address="0.0.0.0/0" \
if-bgp-as-path="^65001$" \
action=reject
# 2. Accept specific prefixes from upstream (AS 64500)
add chain=bgp-in \
if-bgp-as-path="^64500$" \
if-dst-address="203.0.113.0/24" \
action=accept
# 3. Accept default route only from upstream
add chain=bgp-in \
if-dst-address="0.0.0.0/0" \
if-bgp-as-path="^64500$" \
action=accept
# 4. Accept internal routes from AS 65001
add chain=bgp-in \
if-bgp-as-path="^65001$" \
action=accept
# 5. Reject everything else (security)
add chain=bgp-in \
action=reject
Outbound Filter — Control What You Advertise
/routing filter rule
# Chain: bgp-out — applied to outbound BGP updates
# 1. Only advertise your own prefixes
add chain=bgp-out \
if-dst-address="198.51.100.0/24" \
action=accept
# 2. Don't advertise private ranges
add chain=bgp-out \
if-dst-address="10.0.0.0/8" \
action=reject
add chain=bgp-out \
if-dst-address="192.168.0.0/16" \
action=reject
# 3. Reject everything else
add chain=bgp-out \
action=reject
Attaching Filters to a BGP Connection
/routing bgp connection
add name=isp-upstream \
remote.address=203.0.113.1 \
remote.as=64500 \
local.role=ebgp \
routing-table=main \
.in.filter=bgp-in \
.out.filter=bgp-out
Note the syntax:
.in.filterand.out.filterreference the filter chain names you created.
Policy-Based Routing with Routing Marks and Mangle
Policy-based routing (PBR) lets you route traffic based on source address, protocol, port, packet size, or any combination — rather than just the destination.
Architecture
The PBR pipeline in RouterOS 7 is:
- Firewall mangle marks packets or connections
- Routing marks identify the routing table to use
- Separate routing tables contain the custom routes
- Routing rules direct marked traffic to the correct table
Step 1 — Mark Traffic with Mangle
/ip firewall mangle
# Mark all traffic from VLAN 10 (office) to use the office routing table
add chain=prerouting \
src-address=10.0.10.0/24 \
action=mark-routing \
new-routing-mark=office \
passthrough=yes
# Mark all HTTP/HTTPS traffic from VLAN 20 (guest) to use a filtered WAN
add chain=prerouting \
src-address=10.0.20.0/24 \
protocol=tcp \
dst-port=80,443 \
action=mark-routing \
new-routing-mark=guest-web \
passthrough=yes
# Mark all traffic to specific destination (VPN subnet) via dedicated table
add chain=prerouting \
dst-address=10.200.0.0/16 \
action=mark-routing \
new-routing-mark=vpn \
passthrough=yes
The passthrough=yes parameter makes the packet continue to subsequent mangle rules. Use passthrough=no if you want to stop processing after the match.
Connection Marking for Stateful Routing
For stateful PBR (recommended), mark connections instead of individual packets:
# Mark connections from the office VLAN
add chain=prerouting \
src-address=10.0.10.0/24 \
action=mark-connection \
new-connection-mark=office-conn \
passthrough=yes
# Route all packets in those connections
add chain=prerouting \
connection-mark=office-conn \
action=mark-routing \
new-routing-mark=office \
passthrough=no
Connection marking ensures that return traffic follows the same path.
Step 2 — Create Routing Tables
/routing table
add name=office fib
add name=guest-web fib
add name=vpn fib
# The 'fib' flag means the table participates in FIB resolution
# — required for the routes to be usable
Step 3 — Add Routes to Each Table
/ip route
# Office traffic goes via ISP1
add dst-address=0.0.0.0/0 \
gateway=172.16.1.1 \
routing-mark=office \
distance=1
# Guest web traffic goes via ISP2
add dst-address=0.0.0.0/0 \
gateway=172.16.2.1 \
routing-mark=guest-web \
distance=1
# VPN traffic via Wireguard interface
add dst-address=0.0.0.0/0 \
gateway=wg0 \
routing-mark=vpn \
distance=1
Step 4 — Routing Rules (Optional)
Routing rules direct traffic before the main routing table lookup. They are evaluated before standard destination-based routing:
/routing rule
# Send traffic from specific source to custom table
add src-address=10.0.10.0/24 table=office
# Send traffic to specific destination via VPN table
add dst-address=10.200.0.0/16 table=vpn
# Everything else uses the main table
add table=main
Use routing rules as an alternative to mangle-based marking when you only need source or destination-based PBR.
Dual WAN with Policy-Based Routing
A common homelab scenario: two ISPs where one handles general traffic and the other handles specific services (or backup).
Basic Dual WAN Setup
Assume WAN1 on ether1 (gateway 172.16.1.1) and WAN2 on ether2 (gateway 172.16.2.1).
# Default route via WAN1 (lower distance = preferred)
/ip route
add dst-address=0.0.0.0/0 \
gateway=172.16.1.1 \
distance=1
# Backup route via WAN2
add dst-address=0.0.0.0/0 \
gateway=172.16.2.1 \
distance=2
PBR for Specific Traffic via WAN2
Force certain traffic to use WAN2 even when WAN1 is up:
/ip firewall mangle
# Mark DNS and NTP traffic to use WAN2
add chain=prerouting \
protocol=udp \
dst-port=53,123 \
action=mark-routing \
new-routing-mark=wan2 \
passthrough=no
# Mark torrent traffic to use WAN2
add chain=prerouting \
protocol=tcp \
dst-port=6881-6999 \
action=mark-routing \
new-routing-mark=wan2 \
passthrough=no
/routing table
add name=wan2 fib
/ip route
add dst-address=0.0.0.0/0 \
gateway=172.16.2.1 \
routing-mark=wan2 \
distance=1
Failover with Route Checks
For production failover, add recursive next-hop checks:
/ip route
# WAN1 with recursive check
add dst-address=0.0.0.0/0 \
gateway=172.16.1.1 \
distance=1 \
check-gateway=ping
# WAN2 with recursive check
add dst-address=0.0.0.0/0 \
gateway=172.16.2.1 \
distance=2 \
check-gateway=ping
When check-gateway=ping is set, RouterOS periodically pings the gateway. If the ping fails, the route is disabled and traffic falls back to the next available route.
Advanced Filtering — BGP Communities and Local Preference
Setting Local Preference with Filters
Control inbound route preference before it reaches the routing table:
/routing filter rule
# Set high local-pref for routes tagged with community 64512:100
add chain=bgp-in \
if-bgp-communities="64512:100" \
set-bgp-local-pref=200 \
action=accept
# Set medium local-pref for routes from upstream
add chain=bgp-in \
if-bgp-as-path="^64500$" \
set-bgp-local-pref=150 \
action=accept
# Default local-pref for everything else
add chain=bgp-in \
action=accept
Manipulating BGP Communities
# Append a community to outbound routes
add chain=bgp-out \
if-dst-address="198.51.100.0/24" \
set-bgp-communities="64501:200" \
action=accept
# Strip communities from routes before advertising to upstream
add chain=bgp-out \
set-bgp-communities="" \
action=accept
Route Tagging with Routing Filters
Use routing filters as a route-map to tag routes for later PBR:
/routing filter rule
# Tag routes from specific BGP peer with a routing mark
add chain=bgp-in \
if-bgp-as-path="^65001$" \
set-routing-mark=internal \
action=accept
Troubleshooting Routing Filters and PBR
Verify Active Routes
# Show all routes
/routing route print
# Show routes in a specific routing table
/routing route print where routing-mark=office
# Show BGP routes specifically
/routing route print where protocol=bgp
Check What BGP Is Advertising
# Show advertised routes for a BGP session
/routing bgp session advertisements print \
session=isp-upstream
# Show received routes
/routing bgp session routes print \
session=isp-upstream
Debug PBR Traffic Flow
# Check which routing table a specific packet would use
# First check mangle hits
/ip firewall mangle print stats
# Then check the routing table lookup with routing mark
/routing route print where routing-mark=guest-web
# Verify routing rules are in order
/routing rule print
Common Pitfalls
1. Filter chain ordering matters
Rules are evaluated in the order they were created. An accept rule before a reject rule means the reject never runs. Always put specific match rules before general ones.
# WRONG: This accepts everything before the reject
add chain=bgp-in action=accept
add chain=bgp-in if-dst-address="10.0.0.0/8" action=reject
# RIGHT: Specific reject first
add chain=bgp-in if-dst-address="10.0.0.0/8" action=reject
add chain=bgp-in action=accept
2. Implicit reject at end of chain
If no rule matches, the route is rejected. Always end your chains with an explicit action=accept for the traffic you want to allow, or an explicit action=reject to block everything else.
3. Mangle rules need the correct chain
preroutingfor traffic coming into the router (both forwarded and locally destined)forwardonly for forwarded trafficoutputfor traffic generated by the router itself
For PBR, always use chain=prerouting so the routing mark is applied before the route lookup.
4. Routing table missing FIB flag
If you create a routing table without the fib flag, routes in that table won’t be resolvable:
# Check if your table has FIB enabled
/routing table print
# Enable it
/routing table set office fib=yes
Putting It All Together
RouterOS 7 routing filters and policy-based routing give you complete traffic engineering control. The key differences from RouterOS 6:
- Filters are declarative, not firewall-style — define the match condition and action
- PBR uses mangle + routing-mark + separate routing tables — no more
routing-markin static routes - Routing rules provide an alternative to mangle for simple source/destination-based PBR
Start simple: create a BGP inbound filter that blocks obvious garbage (host routes, bogons), then layer on policy routing for traffic segmentation. Always test with print commands before applying to production.
A well-configured routing filter chain keeps your routing table clean. Combined with PBR, you get per-client, per-protocol traffic control that rivals enterprise routers — all on MikroTik hardware.