# ------------------- header ------------------- # Script improved to check two different hosts and act with PCC Load Balancer # (Original Script by Tomas Kirnak) # If you edit this script, please share it with the community! # Author: Denis Barbazza (denis . barbazza [at] gmail . com) # VERSION=2.3 # http://www.farlock.org/mikrotik/mikrotik-load-balancer-and-failover-and-traffic-prioritization/ # ChangeLog # 2.3 - 21/10/16 - Bugfix, when main ISP comes back we close alle the connection on ISP2, not clean but necessary because of some connections not tracked (udp, needs more testing) # we leave this feature commented out, needs testing. # we close also connections from outside to lan, sometimes internal initated connection takes this mark, needs testing # 2.2 - 11/05/16 - If one connection hangs, drop connection on it (udp and tcp), when main connection # fails or comes back we reset also connections without mark (these because of the default route weight) # 2.1 - 17/03/16 - Improved ping check based on script made by Gregory Sloop (gregs @ sloop.net) # 2.0 - 01/03/16 - now we manage also the rule used with PCC load balancer # 1.5 - 01/12/15 - Check two different hosts, just to be sure # 1.0.7 - Original Script by Tomas Kirnak (t.kirnak @ atris.sk) # The script in case of a faulting link increase the default route # and disable the marking rule based on PCC that it found on mangle/prerouting chain # # Use ips for ping target, the script may not work with fqdn # # if you want you can disable every marking rule, and not only PCC, simply editing the four line that search for rule to be disabled: # :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP1 && (per-connection-classifier)."" != ""] do=\ # and remove the part of PCC value: # :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP1 ] do=\ # REMEMBER: you must edit the rule in 4 places (enable/disable ISP1 and enable/diable ISP2) # # Search in script rule starting with "### OPTIONAL", here you can enable or disable some features, # based on your needs. # # For more information and details about # this script please visit the wiki page at # http://wiki.mikrotik.com/wiki/Failover_Scripting # ------------------- header ------------------- # ------------- start editing here ------------- # Edit the variables below to suit your needs # Please fill the WAN interface names :local InterfaceISP1 ISP_1 :local InterfaceISP2 ISP_2 # Please fill the gateway IPs (or interface names in case of PPP) :local GatewayISP1 10.39.1.14 :local GatewayISP2 172.31.29.1 # Routing mark of each interface :local RoutingMarkISP1 ISP1_Route :local RoutingMarkISP2 ISP2_Route # Connection mark of each interface :local ConnMarkISP1 to_ISP1 :local ConnMarkISP2 to_ISP2 # Connection mark of each interface, from outside to local network :local ConnMarkISP1_LAN from_ISP1_to_LAN :local ConnMarkISP2_LAN from_ISP2_to_LAN # Please fill the ping check host - currently: resolver1.opendns.com :local PingTarget1 208.67.222.222 # Second ping check host - currently google secondary DNS :local PingTarget2 8.8.4.4 # This can be used to make sure that the RTT is above this threshold. Ping replies that take longer than # this to return will be counted as no reply. Adapt it to your lines :local PingInterval 500ms; # How many pings to send for our test :local PingCount 5; # Size of the pick packets [Don't make them too large.] :local PingSize 28; # How many pings minimum must we get back to consider the pipe "up" - fewer than this - consider it down. # This is for the single check! So we send PingCount packet and we must receive at least PingReturnThreshold # to consider the line up :local PingReturnThreshold 2; # Please fill how many times the check can fail before fail-over happens, # In may case I run the script once every 10 minute, so one is enough # Or you can run it once a minute so increase it :local FailTreshold 3 # Define the distance increase of a route when it fails :local DistanceIncrease 20 # Editing the script after this point may break it # -------------- stop editing here -------------- # Declare the global variables :global PingFailCountISP1 :global PingFailCountISP2 # This inicializes the PingFailCount variables, in case this is the 1st time the script has ran :if ([:typeof $PingFailCountISP1] = "nothing") do={:set PingFailCountISP1 0} :if ([:typeof $PingFailCountISP2] = "nothing") do={:set PingFailCountISP2 0} # These variables will be used to keep results of individual ping attempts :local PingResult1 :local PingResult2 # Check ISP1 # :set PingResult1 [ping $PingTarget1 count=1 interface=$InterfaceISP1 routing-table=$RoutingMarkISP1] :set PingResult1 [/ping $PingTarget1 interface=$InterfaceISP1 routing-table=$RoutingMarkISP1 interval=$PingInterval count=$PingCount size=$PingSize]; #:put $PingResult1 # :set PingResult2 [ping $PingTarget2 count=1 interface=$InterfaceISP1 routing-table=$RoutingMarkISP1] :set PingResult2 [/ping $PingTarget2 interface=$InterfaceISP1 routing-table=$RoutingMarkISP1 interval=$PingInterval count=$PingCount size=$PingSize]; #:put $PingResult2 # If both fails we consider router down :if (($PingResult1 < $PingReturnThreshold) && ($PingResult2 < $PingReturnThreshold)) do={ :if ($PingFailCountISP1 < ($FailTreshold+2)) do={ :set PingFailCountISP1 ($PingFailCountISP1 + 1) :if ($PingFailCountISP1 = $FailTreshold) do={ :log warning "ISP1 has a problem en route to $PingTarget1 or $PingTarget2 - increasing distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP1 && static && !routing-mark] do=\ # {:log info "Increase distance route $i"} {/ip route set $i distance=([/ip route get $i distance] + $DistanceIncrease)} # Disable PCC rules :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP1 && (per-connection-classifier)."" != ""] do=\ {/ip firewall mangle disable $i } ### OPTIONAL - Disable all rule, not the only ones regarding PCC # :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP1 ] do=\ # {/ip firewall mangle disable $i } :log warning "Route distance increase finished." # close ISP1 connection foreach i in=[/ip firewall connection find connection-mark=$ConnMarkISP1] do= {/ip firewall connection remove $i } foreach i in=[/ip firewall connection find connection-mark=$ConnMarkISP1_LAN] do= {/ip firewall connection remove $i } # close connection without mark foreach i in=[/ip firewall connection find (connection-mark)."" = "" ] do= {/ip firewall connection remove $i } :log warning "Closed connection $ConnMarkISP1 , $ConnMarkISP1_LAN and without mark" } } } # If almost one is ok we consider the line up :if (($PingResult1 > $PingReturnThreshold) || ($PingResult2 > $PingReturnThreshold)) do={ :if ($PingFailCountISP1 > 0) do={ :set PingFailCountISP1 ($PingFailCountISP1 - 1) :if ($PingFailCountISP1 = ($FailTreshold -1)) do={ :log warning "ISP1 can reach $PingTarget1 or $PingTarget2 again - bringing back original distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP1 && static && !routing-mark] do=\ # {:log info "Decrease distance route $i"} {/ip route set $i distance=([/ip route get $i distance] - $DistanceIncrease)} # Reenable PCC rules :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP1 && (per-connection-classifier)."" != ""] do=\ {/ip firewall mangle enable $i } ### OPTIONAL - Enable all rule, not the only ones regarding PCC # :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP1 ] do=\ # {/ip firewall mangle enable $i } :log warning "Route distance decrease finished." # close connection without mark foreach i in=[/ip firewall connection find (connection-mark)."" = "" ] do= {/ip firewall connection remove $i } ### OPTIONAL - If you want you can close all the connection on the line 2 to force reconnection on line 1 # foreach i in=[/ip firewall connection find connection-mark=$ConnMarkISP2] do= {/ip firewall connection remove $i } # foreach i in=[/ip firewall connection find connection-mark=$ConnMarkISP2_LAN] do= {/ip firewall connection remove $i } :log warning "Closed connection without mark" } } } # Check ISP2 # :set PingResult1 [ping $PingTarget1 count=1 interface=$InterfaceISP2 routing-table=$RoutingMarkISP2] :set PingResult1 [/ping $PingTarget1 interface=$InterfaceISP2 routing-table=$RoutingMarkISP2 interval=$PingInterval count=$PingCount size=$PingSize]; #:put $PingResult1 # :set PingResult2 [ping $PingTarget2 count=1 interface=$InterfaceISP2 routing-table=$RoutingMarkISP1] :set PingResult2 [/ping $PingTarget2 interface=$InterfaceISP2 routing-table=$RoutingMarkISP2 interval=$PingInterval count=$PingCount size=$PingSize]; #:put $PingResult2 :if (($PingResult1 < $PingReturnThreshold) && ($PingResult2 < $PingReturnThreshold)) do={ :if ($PingFailCountISP2 < ($FailTreshold+2)) do={ :set PingFailCountISP2 ($PingFailCountISP2 + 1) :if ($PingFailCountISP2 = $FailTreshold) do={ :log warning "ISP2 has a problem en route to $PingTarget1 and $PingTarget2 - increasing distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP2 && static && !routing-mark] do=\ # {:log info "Increase distance route $i"} {/ip route set $i distance=([/ip route get $i distance] + $DistanceIncrease)} # Disable PCC rules :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP2 && (per-connection-classifier)."" != ""] do=\ {/ip firewall mangle disable $i } ### OPTIONAL - Disable all rule, not the only ones regarding PCC # :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP2 ] do=\ # {/ip firewall mangle disable $i } :log warning "Route distance increase finished." # close ISP2 connection foreach i in=[/ip firewall connection find connection-mark=$ConnMarkISP2] do= {/ip firewall connection remove $i } foreach i in=[/ip firewall connection find connection-mark=$ConnMarkISP2_LAN] do= {/ip firewall connection remove $i } :log warning "Closed connection $ConnMarkISP2 and $ConnMarkISP2_LAN" ### OPTIONAL - Close connection without mark to force reopen, should not be necessary # foreach i in=[/ip firewall connection find (connection-mark)."" = "" ] do= {/ip firewall connection remove $i } } } } :if (($PingResult1 > $PingReturnThreshold) || ($PingResult2 > $PingReturnThreshold)) do={ :if ($PingFailCountISP2 > 0) do={ :set PingFailCountISP2 ($PingFailCountISP2 - 1) :if ($PingFailCountISP2 = ($FailTreshold -1)) do={ :log warning "ISP2 can reach $PingTarget1 or $PingTarget2 again - bringing back original distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP2 && static && !routing-mark] do=\ # {:log info "Decrease distance route $i"} {/ip route set $i distance=([/ip route get $i distance] - $DistanceIncrease)} # Reenable PCC rules :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP2 && (per-connection-classifier)."" != ""] do=\ {/ip firewall mangle enable $i } ### OPTIONAL - Disable all rule, not the only ones regarding PCC # :foreach i in=[/ip firewall mangle find chain=prerouting && new-connection-mark=$ConnMarkISP2 ] do=\ # {/ip firewall mangle enable $i } :log warning "Route distance decrease finished." } } }