Please help me analyze my PCC load balancing configuration

I have two ISPs at a branch office connected to a MikroTik RB750Gr3 running RouterOS v7.23.7 ​​(long-term release):

Icon Plus Dedicated ISP (20 Mbps bandwidth, 1 static public IP)

IndiHome Broadband ISP (100 Mbps bandwidth, no public IP)

This branch MikroTik also connects to a WireGuard server and the head office to access local IP addresses.

I have been experimenting with various configurations found online, but I do not yet fully grasp the underlying concepts. Here is my current configuration, please help me analyze it:

/interface list
add name=WAN
add name=LAN

/interface bridge
add name=bridge1_local

/interface bridge port
add bridge=bridge1_local interface=ether3
add bridge=bridge1_local interface=ether4
add bridge=bridge1_local interface=ether5

/interface list member
add list=WAN interface=ether1_isp_icon_plus
add list=WAN interface=ether2_isp_indihome
add list=LAN interface=bridge1_local
add list=LAN interface=ether3
add list=LAN interface=ether4
add list=LAN interface=ether5

/interface wireguard
add name=wg_to_server_hypernet
add name=wg_to_server_melsa
add name=wg_to_server_headoffice

/ip address
add address=103.xx.x.xx6/30 interface=ether1_isp_icon_plus
add address=192.168.1.2/24 interface=ether2_isp_indihome
add address=192.168.15.0/24 interface=bridge1_local
add address=10.5.5.114/24 interface=wg_to_server_hypernet
add address=10.6.6.114/24 interface=wg_to_server_melsa
add address=10.7.7.114/24 interface=wg_to_headoffice

/ip pool
add name=pool_local ranges=192.168.15.2-192.168.15.254

/ip dhcp-server network
add name=dhcp-local interface=bridge1_local address-pool=pool_local lease-time=1d disable=no

/ip dns
set-server=1.1.1.1,8.8.8.8 allow-remote-requests=yes

/routing table
add name=to_icon_plus fib
add name=to_indihome fib

/ip route
add dst-address=0.0.0.0/0 gateway=103.xx.x.xx5 distance=1 check-gateway=ping comment="main gateway icon plus"
add dst-address=0.0.0.0/0 gateway=192.168.1.1 distance=2 comment="backup gateway indihome"

add dst-address=0.0.0.0/0 gateway=103.xx.x.xx5 distance=1 check-gateway=ping routing-table=to_icon_plus comment="routing table icon plus"
add dst-address=0.0.0.0/0 gateway=192.168.1.1 distance=1 check-gateway=ping routing-table=to_indihome comment="routing table indihome"

add dst-address=10.10.1.0/24 gateway=10.5.5.1 distance=1 check-gateway=ping comment="to server"
add dst-address=10.10.1.0/24 gateway=10.6.6.1 distance=2 check-gateway=ping comment="to server"
add dst-address=10.10.1.0/24 gateway=10.7.7.1 distance=3 comment="to server"

add dst-address=10.10.2.0/24 gateway=10.5.5.1 distance=1 check-gateway=ping comment="to headoffice"
add dst-address=10.10.2.0/24 gateway=10.6.6.1 distance=2 check-gateway=ping comment="to headoffice"
add dst-address=10.10.2.0/24 gateway=10.7.7.1 distance=3 comment="to headoffice"

/ip firewall address-list
add list=no-pcc address=192.168.15.0/24 comment="local"
add list=no-pcc address=10.5.5.0/24 comment="wg hypernet"
add list=no-pcc address=10.6.6.0/24 comment="wg melsa"
add list=no-pcc address=10.7.7.0/24 comment="wg headoffice"
add list=no-pcc address=10.10.1.0/24 comment="server"
add list=no-pcc address=10.10.2.0/24 comment="headoffice"

/ip firewall mangle
add chain=prerouting in-interface-list=LAN dst-address-list=no-pcc action=accept comment="bypass local"

add chain=prerouting in-interface=ether1_isp_icon_plus connection-state=new connection-mark=no-mark dst-address-type=!local action=mark-connection new-connection-mark=conn_icon_plus passthrough=no
add chain=prerouting in-interface=ether2_isp_indihome connection-state=new connection-mark=no-mark dst-address-type=!local action=mark-connection new-connection-mark=conn_indihome passthrough=no

add chain=prerouting in-interface-list=LAN connection-mark=no-mark dst-address-type=!local per-connection-classifier=both-addresses:3/0 action=mark-connection new-connection-mark=conn_icon_plus passthrough=yes
add chain=prerouting in-interface-list=LAN connection-mark=no-mark dst-address-type=!local per-connection-classifier=both-addresses:3/1 action=mark-connection new-connection-mark=conn_indihome passthrough=yes
add chain=prerouting in-interface-list=LAN connection-mark=no-mark dst-address-type=!local per-connection-classifier=both-addresses:3/2 action=mark-connection new-connection-mark=conn_indihome passthrough=yes

add chain=prerouting in-interface-list=LAN connection-mark=conn_icon_plus action=mark-routing new-routing-mark=to_icon_plus passthrough=no
add chain=prerouting in-interface-list=LAN connection-mark=conn_indihome action=mark-routing new-routing-mark=to_indihome passthrough=no

add chain=output connection-mark=conn_icon_plus action=mark-routing new-routing-mark=to_icon_plus passthrough=no
add chain=output connection-mark=conn_indihome action=mark-routing new-routing-mark=to_indihome passthrough=no

/ip firewall nat
add chain=srcnat out-interface=ether1_isp_icon_plus acton=masquerade
add chain=srcnat out-interface=ether2_isp_indihome acton=massquerade

Hi,

Good start, you have already got dual WAN with PCC and WireGuard failover in one config. I went through it and found several things to fix, ordered from the most critical.

1. Typos and syntax errors

  • In the NAT rules, acton should be action, and massquerade should be masquerade.
  • set-server= is not valid. The correct form is:
/ip dns set servers=1.1.1.1,8.8.8.8 allow-remote-requests=yes
  • The WireGuard interface is named wg_to_server_headoffice, but the IP address line uses wg_to_headoffice. The names must match.

2. Bridge IP address
192.168.15.0/24 is the network address, not a host address. Use the .1 address as the gateway:

/ip address add address=192.168.15.1/24 interface=bridge1_local

3. DHCP server
The line you posted mixes two different menus. You need both the server and the network:

/ip dhcp-server add name=dhcp-local interface=bridge1_local address-pool=pool_local lease-time=1d disabled=no
/ip dhcp-server network add address=192.168.15.0/24 gateway=192.168.15.1 dns-server=192.168.15.1

4. No firewall filter rules (important)
There is no /ip firewall filter at all, and DNS allow-remote-requests is enabled. That means your router is open to the internet (WinBox, DNS resolver, etc.). At minimum, add these input rules and a final drop:

/ip firewall filter add chain=input connection-state=established,related action=accept
/ip firewall filter add chain=input connection-state=invalid action=drop
/ip firewall filter add chain=input in-interface-list=LAN action=accept
/ip firewall filter add chain=input protocol=udp dst-port=13231 action=accept comment="wireguard, change to your listen-port"
/ip firewall filter add chain=input in-interface-list=!LAN action=drop

Also make sure the WireGuard interfaces are allowed (add them to the LAN list or accept them explicitly), otherwise the tunnels will be blocked by the last rule.

5. Mangle: WAN connection marking
In the two rules with in-interface=ether1/ether2, you used dst-address-type=!local. That is the opposite of what is needed. These rules are meant to catch connections coming in from the ISP to the router itself, so that the replies go back out through the same ISP. Change it to dst-address-type=local (or remove it).

6. Extend the no-pcc list
Traffic from LAN to 192.168.1.0/24 (the Indihome modem) and to your Icon Plus /30 subnet currently goes through PCC. Because the custom routing tables only contain a default route, this traffic can fail. Add them to the bypass list:

/ip firewall address-list add list=no-pcc address=192.168.1.0/24 comment="indihome modem"
/ip firewall address-list add list=no-pcc address=103.xx.x.xx4/30 comment="icon plus subnet"

7. Failover inside the PCC routing tables
Your to_icon_plus and to_indihome tables only have one route each, so if one ISP goes down, the connections marked for it will not fail over. Add a backup route with a higher distance in each table:

/ip route add dst-address=0.0.0.0/0 gateway=192.168.1.1 distance=2 routing-table=to_icon_plus
/ip route add dst-address=0.0.0.0/0 gateway=103.xx.x.xx5 distance=2 routing-table=to_indihome

Note: check-gateway=ping only checks the next hop (the modem/ISP gateway), not the real internet. If the modem is alive but the upstream is dead, failover will not trigger. For more reliable detection, use Netwatch or recursive routes.

8. PCC ratio
With 3/0, 3/1 and 3/2 you are sending 1/3 of connections to Icon Plus and 2/3 to Indihome. That is fine if it is intentional, just make sure it matches your bandwidth.

9. WireGuard routes

  • Make sure the peers on both sides have the right allowed-address. On the router side it must include 10.10.1.0/24 and 10.10.2.0/24, and on the server side it must include 192.168.15.0/24, otherwise the return traffic is dropped.
  • Instead of gateway=10.5.5.1, you can point the route directly to the interface (for example gateway=wg_to_server_hypernet).
  • If each tunnel is supposed to go out through a specific ISP, add a specific route for each tunnel's endpoint IP, because by default the WireGuard traffic from the router follows the main default route.

10. NAT
Using the interface list is cleaner and works with both WANs:

/ip firewall nat add chain=srcnat out-interface-list=WAN action=masquerade

Also, ether3 to ether5 are already bridge ports, so listing them in the LAN list is not needed. The bridge itself is enough.

Please apply these step by step and test after each change. If something still does not work, post the output of /ip route print and /ip firewall mangle print and I will take a look.

Thank you very much for the very detailed explanation. Please excuse the typo in the config, I typed it out manually based on my configuration. I'll give it a try

Next time export it.

See here:

Forum rules - #5 by gigabyte091