Part 1: Netwatch Monitoring
π§ Objective
To monitor the health of 3 WAN interfaces (ISP1, ISP2, ISP3) using Netwatch, dynamically managing routing rules and default routes based on link status. This ensures that the PCC load balancing setup only uses active upstreams.
π§± Step 1: Rename Interfaces
/interface ethernet
set [ find default-name=ether1 ] name=ether1::ISP1
set [ find default-name=ether2 ] name=ether2::ISP2
set [ find default-name=ether3 ] name=ether3::ISP3
set [ find default-name=ether4 ] name=ether4::MGMT
set [ find default-name=ether5 ] name=ether5::LAN
π Step 2: Configure PPPoE on ISP3
/interface pppoe-client
add disabled=no interface=ether3::ISP3 name=pppoe-out1 user=ppp1
π§ Step 3: Create Routing Tables
These are used for PCC and route isolation.
/routing table
add name=isp1
add name=isp2
add name=isp3
π‘ Step 4: DHCP Clients with Scripts for ISP1 & ISP2
This handles setting the gateway and Netwatch src-address
dynamically.
ISP1:
/ip dhcp-client
add interface=ether1::ISP1 default-route-tables=isp1 script="
if ((\$\"bound\")=1) do={
:local gwip (\$\"gateway-address\")
:local ip (\$\"lease-address\")
ip route set gateway=\$gwip [find comment=\"isp1\"]
tool netwatch set src-address=\$ip [find name=\"isp1_check\"]
routing rule set src-address=\$ip [find table=isp1]
}"

if (($"bound")=1) do={
:local gwip ($"gateway-address")
:local ip ($"lease-address")
ip route set gateway=$gwip [find comment="isp1"]
tool netwatch set src-address=$ip [find name="isp1_check"]
routing rule set src-address=$ip [find table=isp1]
}
ISP2:
add interface=ether2::ISP2 default-route-tables=isp2 script="
if ((\$\"bound\")=1) do={
:local gwip (\$\"gateway-address\")
:local ip (\$\"lease-address\")
ip route set gateway=\$gwip [find comment=\"isp2\"]
tool netwatch set src-address=\$ip [find name=\"isp2_check\"]
routing rule set src-address=\$ip [find table=isp2]
}"

if (($"bound")=1) do={
:local gwip ($"gateway-address")
:local ip ($"lease-address")
ip route set gateway=$gwip [find comment="isp2"]
tool netwatch set src-address=$ip [find name="isp2_check"]
routing rule set src-address=$ip [find table=isp2]
}
πΆ Step 5: PPPoE Dynamic IP Update Script (ISP3)
Add a script and scheduler to update Netwatch when the PPPoE IP changes:
Script:
/system script
add name=pppoe-check source="
interface/pppoe-client/monitor pppoe-out1 once do={
:local ip (\$\"local-address\")
:local current [/tool netwatch get value-name=src-address isp3_check]
:if (\$current != \$ip) do={
/tool netwatch set src-address=\$ip [find name=\"isp3_check\"]
/routing/rule set src-address=\$ip [find table=isp3]
}
}"

interface/pppoe-client/monitor pppoe-out1 once do={
:local ip ($"local-address")
:local current [/tool netwatch get value-name=src-address isp3_check]
:if ($current != $ip) do={
/tool netwatch set src-address=$ip [find name="isp3_check"]
/routing/rule set src-address=$ip [find table=isp3]
}
}
Scheduler (runs every 10s):
/system scheduler
add interval=10s name=pppoe-check on-event=pppoe-check
π£οΈ Step 6: Add Static Routes for Monitoring
Routes to 8.8.8.8 in each routing table to allow Netwatch testing.
/ip route
add dst-address=8.8.8.8/32 gateway=10.200.22.1 routing-table=isp1 comment=isp1
add dst-address=8.8.8.8/32 gateway=10.200.23.1 routing-table=isp2 comment=isp2
add dst-address=8.8.8.8/32 gateway=pppoe-out1 routing-table=isp3 comment=isp3

Also a default route for ISP3:
add dst-address=0.0.0.0/0 gateway=pppoe-out1 routing-table=isp3 comment=isp3-gw

π¦ Step 7: Netwatch Configuration
ISP1:
/tool netwatch
add name=isp1_check host=8.8.8.8 src-address=10.200.22.196 interval=3s \
type=icmp ignore-initial-up=yes ignore-initial-down=yes \
down-script="ip dhcp-client set add-default-route=no [ find interface=ether1::ISP1 ]" \
up-script="ip dhcp-client set add-default-route=yes [ find interface=ether1::ISP1 ]"
UP Script
ip dhcp-client set add-default-route=yes [ find interface=ether1::ISP1 ]
DOWN Script
ip dhcp-client set add-default-route=no [ find interface=ether1::ISP1 ]
ISP2:
add name=isp2_check host=8.8.8.8 src-address=10.200.23.197 interval=3s \
type=icmp ignore-initial-up=yes ignore-initial-down=yes \
down-script="ip dhcp-client set add-default-route=no [ find interface=ether2::ISP2 ]" \
up-script="ip dhcp-client set add-default-route=yes [ find interface=ether2::ISP2 ]"
UP Script
ip dhcp-client set add-default-route=yes [ find interface=ether2::ISP2 ]
DOWN Script
ip dhcp-client set add-default-route=no [ find interface=ether2::ISP2 ]
ISP3:
add name=isp3_check host=8.8.8.8 src-address=203.0.113.2 interval=3s \
type=icmp ignore-initial-up=yes ignore-initial-down=yes \
down-script="ip route disable [find comment=\"isp3-gw\"]" \
up-script="ip route enable [find comment=\"isp3-gw\"]"
UP Script
ip route enable [find comment="isp3-gw"]
DOWN Script
ip route disable [find comment="isp3-gw"]
π Step 8: Add Routing Rules for PCC/Source IPs
Ensure traffic uses correct routing tables.
/routing rule
add src-address=10.200.22.196 dst-address=8.8.8.8/32 table=isp1
add src-address=10.200.23.197 dst-address=8.8.8.8/32 table=isp2
add src-address=203.0.113.2 dst-address=8.8.8.8/32 table=isp3
β Final Notes
- Netwatch checks are ICMP-based and monitor 8.8.8.8 from each WAN’s public IP.
- Down events prevent routes or disable DHCP default-route behavior to avoid blackholing traffic.
- Each interface’s IP is dynamically updated for Netwatch via DHCP scripts or PPPoE script.
Part 2: PCC (Per Connection Classifier) Load Balancing
Here is a complete guide to adding PCC (Per Connection Classifier) load balancing on MikroTik using your existing WAN failover and Netwatch setup. The guide includes:
- PCC setup steps
- Line-by-line explanation of each mangle rule
- Why each rule matters
π― Objective
Use MikroTikβs PCC (Per Connection Classifier) to distribute LAN traffic across three ISPs (ISP1, ISP2, ISP3) based on source and destination addresses and ports, while preserving session consistency and allowing automatic failover via Netwatch.
π§ Prerequisites
- 3 WAN links (ether1::ISP1, ether2::ISP2, pppoe-out1 for ISP3)
- Routing tables already set:
isp1
,isp2
,isp3
- Netwatch configured to detect WAN health
- Default routes present in each table
π§© Step-by-Step PCC Setup with Explanation
π 1. Mark New Incoming Connections from Each ISP
/ip firewall mangle
add chain=prerouting action=mark-connection in-interface=ether1::ISP1 connection-mark=no-mark connection-state=new new-connection-mark=isp1_conn passthrough=yes


β
What it does:
When new connections arrive from ISP1
, they’re tagged with isp1_conn
so they can be tracked and routed properly.
(Same for ISP2 and ISP3 below.)
add chain=prerouting action=mark-connection in-interface=ether2::ISP2 connection-mark=no-mark connection-state=new new-connection-mark=isp2_conn passthrough=yes
add chain=prerouting action=mark-connection in-interface=pppoe-out1 connection-mark=no-mark connection-state=new new-connection-mark=isp3_conn passthrough=yes
π 2. Mark Routing for Outbound Traffic Originating from the Router (Output Chain)
add chain=output action=mark-routing connection-mark=isp1_conn new-routing-mark=isp1 passthrough=yes


β
What it does:
When the router itself (e.g., for DNS, NTP) initiates traffic that matches an existing marked connection (isp1_conn
), route it via the isp1
table.
(Same logic for ISP2 and ISP3.)
add chain=output action=mark-routing connection-mark=isp2_conn new-routing-mark=isp2 passthrough=yes
add chain=output action=mark-routing connection-mark=isp3_conn new-routing-mark=isp3 passthrough=yes
π 3. Apply PCC to LAN β Internet Traffic
These are the core PCC load balancing rules.
add chain=prerouting action=mark-connection in-interface=LAN connection-mark=no-mark connection-state=new dst-address-type=!local new-connection-mark=isp1_conn per-connection-classifier=both-addresses-and-ports:3/0 passthrough=yes




β
What it does:
For new LAN-to-Internet traffic thatβs not going to the router itself (!local
), this classifies 1 in every 3 connections to go through ISP1
.
The both-addresses-and-ports
method ensures session stickiness (for web browsing, downloads, etc.).
(Same logic for the other two classifiers.)
add chain=prerouting action=mark-connection in-interface=LAN connection-mark=no-mark connection-state=new dst-address-type=!local new-connection-mark=isp2_conn per-connection-classifier=both-addresses-and-ports:3/1 passthrough=yes
add chain=prerouting action=mark-connection in-interface=LAN connection-mark=no-mark connection-state=new dst-address-type=!local new-connection-mark=isp3_conn per-connection-classifier=both-addresses-and-ports:3/2 passthrough=yes
π§ Why 3/x
?
This evenly splits traffic 3 ways:
3/0
β ISP13/1
β ISP23/2
β ISP3
You can adjust these ratios depending on bandwidth or priorities.
π 4. Apply Routing Marks to Traffic from LAN Based on Connection Mark
add chain=prerouting action=mark-routing in-interface=LAN connection-mark=isp1_conn new-routing-mark=isp1 passthrough=yes


β
What it does:
This ensures that LAN traffic previously classified to go out via ISP1 is forced to use the isp1
routing table (which points to ISP1’s gateway).
(Same for the others.)
add chain=prerouting action=mark-routing in-interface=LAN connection-mark=isp2_conn new-routing-mark=isp2 passthrough=yes
add chain=prerouting action=mark-routing in-interface=LAN connection-mark=isp3_conn new-routing-mark=isp3 passthrough=yes
β Summary of PCC Rule Roles
Rule Purpose | Chain | Description |
---|---|---|
Mark incoming connections from WANs | prerouting | So return traffic uses correct interface |
Mark routing for router-originated traffic | output | Ensures correct WAN is used for DNS, etc. |
Classify and mark new LAN connections | prerouting + PCC | Evenly split LAN traffic over 3 WANs |
Mark routing for LAN traffic | prerouting | Direct LAN connections to appropriate WAN based on PCC mark |
Full Mangle Rules
/ip firewall mangle
add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new in-interface=ether1::ISP1 new-connection-mark=isp1_conn passthrough=yes
add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new in-interface=ether2::ISP2 new-connection-mark=isp2_conn passthrough=yes
add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new in-interface=pppoe-out1 new-connection-mark=isp3_conn passthrough=yes
add action=mark-routing chain=output connection-mark=isp1_conn new-routing-mark=isp1 passthrough=yes
add action=mark-routing chain=output connection-mark=isp2_conn new-routing-mark=isp2 passthrough=yes
add action=mark-routing chain=output connection-mark=isp3_conn new-routing-mark=isp3 passthrough=yes
add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local in-interface=LAN new-connection-mark=isp1_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:3/0
add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local in-interface=LAN new-connection-mark=isp2_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:3/1
add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local in-interface=LAN new-connection-mark=isp3_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:3/2
add action=mark-routing chain=prerouting connection-mark=isp1_conn in-interface=LAN new-routing-mark=isp1 passthrough=yes
add action=mark-routing chain=prerouting connection-mark=isp2_conn in-interface=LAN new-routing-mark=isp2 passthrough=yes
add action=mark-routing chain=prerouting connection-mark=isp3_conn in-interface=LAN new-routing-mark=isp3 passthrough=yes
β οΈ Tips & Best Practices
- Connection tracking must be enabled (
/ip firewall connection tracking
) β it is by default. - Always monitor performance and tune the classifier method if you have session-sensitive apps.
- You may wish to include failover logic with Netwatch to disable classifiers or NAT rules dynamically (advanced).