Skip to content

Instantly share code, notes, and snippets.

@zoilomora
Last active February 5, 2021 08:32
Show Gist options
  • Save zoilomora/72a516714bbe1fd25a07867b543bca40 to your computer and use it in GitHub Desktop.
Save zoilomora/72a516714bbe1fd25a07867b543bca40 to your computer and use it in GitHub Desktop.
Failover with MikroTik

# ------------------- header -------------------# Base script created by # Script by Tomas Kirnak, version 1.0.7 # If you use this script, or edit and # re-use it, please keep the header intact. # # For more information and details about # this script please visit the wiki page at # http://wiki.mikrotik.com/wiki/Failover_Scripting

# Script modifications and adaptations done by Nikola Bencic v1.0.0 # 26.oct.2018. in Reykjavik # ------------------- header -------------------

# Fill the WAN interface gateways ( ip address or interface name - preferred is IP address, but for dhcp-client WAN use interface name ) :local GatewayISP1 ether2 :local GatewayISP2 ether1 :local GatewayISP3 ether3 :local GatewayISP4 ether5 :local GatewayISP5 192.168.1.1

# Mark routes with comments and fill their marks ( unique comments only !!! ) :local RouteISP1 [/ip route find comment="WAN1"]; :local RouteISP2 [/ip route find comment="WAN2"]; :local RouteISP3 [/ip route find comment="WAN3"]; :local RouteISP4 [/ip route find comment="WAN4"]; :local RouteISP5 [/ip route find comment="WAN5"];

# Check hosts: opendns, googledns, 0.ntp.europe.pool ( you can add more hosts if needed - but remember to chec firewall rules and whitelist them if you need ) :local PingTargets {"208.67.222.222";"8.8.8.8";"185.19.184.35"}

# Amount of ping fails needed to declare route as faulty :local FailTreshold 3

# Define the distance increase of a route when it fails ( number should be count of ISPs or greater ) :local DistanceIncrease 5

# -------------- stop editing here --------------

# Declare the global variables :global PingFailCountISP1 :global OriginalRouteDistanceISP1 :global PingFailCountISP2 :global OriginalRouteDistanceISP2 :global PingFailCountISP3 :global OriginalRouteDistanceISP3 :global PingFailCountISP4 :global OriginalRouteDistanceISP4 :global PingFailCountISP5 :global OriginalRouteDistanceISP5

# 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 } :if ([:typeof $PingFailCountISP3] = "nothing") do={ :set PingFailCountISP3 0 } :if ([:typeof $PingFailCountISP4] = "nothing") do={ :set PingFailCountISP4 0 } :if ([:typeof $PingFailCountISP5] = "nothing") do={ :set PingFailCountISP5 0 }

# This variable will be used to keep results of individual ping attempts :local PingResult

# Clean routing table just in case we have a leftover route /ip route remove [find comment="FAILOVER CHECK ROUTE"]

########################################################################################### # Check ISP1

# Check remote ip 3 times :foreach k,pingTarget in=$PingTargets do={

:delay 1s; /ip route add comment="FAILOVER CHECK ROUTE" distance=1 gateway=$GatewayISP1 dst-address=$pingTarget :local testRouteID [/ip route find comment="FAILOVER CHECK ROUTE"]; /log warning "Pinging $pingTarget thru $GatewayISP1"

:set PingResult [ping $pingTarget count=1];

:if ([/ip route get $testRouteID gateway-status] ~"unreachable"=true) do={

:set PingFailCountISP1 ($PingFailCountISP1 + 1) /log error "Ping to $pingTarget thru $GatewayISP1 FAILED - unreachable gw"

} else={

# remote ping failed, increase fail count isp +1 :if ($PingResult = 0) do={ :set PingFailCountISP1 ($PingFailCountISP1 + 1) /log error "Ping to $pingTarget thru $GatewayISP1 FAILED - no ping" };

# remote ping passed, decrease fail count isp -1 :if ($PingResult = 1) do={ :if ($PingFailCountISP1 > 0) do={ :set PingFailCountISP1 ($PingFailCountISP1 - 1) }; };

}

/ip route remove [find comment="FAILOVER CHECK ROUTE"]

};

#if ping to all 3 hosts failed, declare route as faulty :if ($PingFailCountISP1 >= $FailTreshold) do={

:if ([:typeof $OriginalRouteDistanceISP1] = "nothing") do={

:set OriginalRouteDistanceISP1 [/ip route get $RouteISP1 distance]

}

:if ($OriginalRouteDistanceISP1 + $DistanceIncrease != [/ip route get $RouteISP1 distance]) do={

/log warning "ISP1 has a problem en route to $pingTarget - increasing distance of route." /ip route set $RouteISP1 distance=($OriginalRouteDistanceISP1 + $DistanceIncrease) /log warning "ISP1 Route distance increase finished."

}

}

:if ($PingFailCountISP1 < $FailTreshold) do={

/log warning "ISP1 is working ok"

:if ([:typeof $OriginalRouteDistanceISP1] != "nothing") do={

:if ([/ip route get $RouteISP1 distance] != $OriginalRouteDistanceISP1) do={

/log warning "ISP1 can reach $pingTarget again - bringing back original distance of route."

# if ISP1RecoverSpeed is "fast" - recovery of ISP will be FASTER - but there is potential danger of flapping in route ( if its unstable ISP ) # # if ISP1RecoverSpeed is "slow" - recovery of ISP will be SLOWER - but there we are safe in case of unstable ISP :local ISP1RecoverSpeed "slow";

:if ($ISP1RecoverSpeed = "slow") do={

/log warning "ISP1 recovery is set to SLOW" :for pingRun from 0 to 2 do={ :if ($PingFailCountISP1 > 0) do={ :set PingFailCountISP1 ($PingFailCountISP1 - 1) } }

} else={

/log warning "ISP1 recovery is set to FAST" :set PingFailCountISP1 0

}

:if ($PingFailCountISP1 = 0) do={

/ip route set $RouteISP1 distance=($OriginalRouteDistanceISP1) /log warning "ISP1 Route distance returned to original value."

}

}

}

}

########################################################################################### # Check ISP2

# Check remote ip 3 times :foreach k,pingTarget in=$PingTargets do={

:delay 1s; /ip route add comment="FAILOVER CHECK ROUTE" distance=1 gateway=$GatewayISP2 dst-address=$pingTarget :local testRouteID [/ip route find comment="FAILOVER CHECK ROUTE"]; /log warning "Pinging $pingTarget thru $GatewayISP2"

:set PingResult [ping $pingTarget count=1];

:if ([/ip route get $testRouteID gateway-status] ~"unreachable"=true) do={

:set PingFailCountISP2 ($PingFailCountISP2 + 1) /log error "Ping to $pingTarget thru $GatewayISP2 FAILED - unreachable gw"

} else={

# remote ping failed, increase fail count isp +1 :if ($PingResult = 0) do={ :set PingFailCountISP2 ($PingFailCountISP2 + 1) /log error "Ping to $pingTarget thru $GatewayISP2 FAILED - no ping" };

# remote ping passed, decrease fail count isp -1 :if ($PingResult = 1) do={ :if ($PingFailCountISP2 > 0) do={ :set PingFailCountISP2 ($PingFailCountISP2 - 1) }; };

}

/ip route remove [find comment="FAILOVER CHECK ROUTE"]

};

#if ping to all 3 hosts failed, declare route as faulty :if ($PingFailCountISP2 >= $FailTreshold) do={

:if ([:typeof $OriginalRouteDistanceISP2] = "nothing") do={

:set OriginalRouteDistanceISP2 [/ip route get $RouteISP2 distance]

}

:if ($OriginalRouteDistanceISP2 + $DistanceIncrease != [/ip route get $RouteISP2 distance]) do={

/log warning "ISP2 has a problem en route to $pingTarget - increasing distance of route." /ip route set $RouteISP2 distance=($OriginalRouteDistanceISP2 + $DistanceIncrease) /log warning "ISP2 Route distance increase finished."

}

}

:if ($PingFailCountISP2 < $FailTreshold) do={

/log warning "ISP2 is working ok"

:if ([:typeof $OriginalRouteDistanceISP2] != "nothing") do={

:if ([/ip route get $RouteISP2 distance] != $OriginalRouteDistanceISP2) do={

/log warning "ISP2 can reach $pingTarget again - bringing back original distance of route."

# if ISP2RecoverSpeed is "fast" - recovery of ISP will be FASTER - but there is potential danger of flapping in route ( if its unstable ISP ) # # if ISP2RecoverSpeed is "slow" - recovery of ISP will be SLOWER - but there we are safe in case of unstable ISP :local ISP2RecoverSpeed "slow";

:if ($ISP2RecoverSpeed = "slow") do={

/log warning "ISP2 recovery is set to SLOW" :for pingRun from 0 to 2 do={ :if ($PingFailCountISP2 > 0) do={ :set PingFailCountISP2 ($PingFailCountISP2 - 1) } }

} else={

/log warning "ISP2 recovery is set to FAST" :set PingFailCountISP2 0

}

:if ($PingFailCountISP2 = 0) do={

/ip route set $RouteISP2 distance=($OriginalRouteDistanceISP2) /log warning "ISP2 Route distance returned to original value."

}

}

}

}

########################################################################################### # Check ISP3

# Check remote ip 3 times :foreach k,pingTarget in=$PingTargets do={

:delay 1s; /ip route add comment="FAILOVER CHECK ROUTE" distance=1 gateway=$GatewayISP3 dst-address=$pingTarget :local testRouteID [/ip route find comment="FAILOVER CHECK ROUTE"]; /log warning "Pinging $pingTarget thru $GatewayISP3"

:set PingResult [ping $pingTarget count=1];

:if ([/ip route get $testRouteID gateway-status] ~"unreachable"=true) do={

:set PingFailCountISP3 ($PingFailCountISP3 + 1) /log error "Ping to $pingTarget thru $GatewayISP3 FAILED - unreachable gw"

} else={

# remote ping failed, increase fail count isp +1 :if ($PingResult = 0) do={ :set PingFailCountISP3 ($PingFailCountISP3 + 1) /log error "Ping to $pingTarget thru $GatewayISP3 FAILED - no ping" };

# remote ping passed, decrease fail count isp -1 :if ($PingResult = 1) do={ :if ($PingFailCountISP3 > 0) do={ :set PingFailCountISP3 ($PingFailCountISP3 - 1) }; };

}

/ip route remove [find comment="FAILOVER CHECK ROUTE"]

};

#if ping to all 3 hosts failed, declare route as faulty :if ($PingFailCountISP3 >= $FailTreshold) do={

:if ([:typeof $OriginalRouteDistanceISP3] = "nothing") do={

:set OriginalRouteDistanceISP3 [/ip route get $RouteISP3 distance]

}

:if ($OriginalRouteDistanceISP3 + $DistanceIncrease != [/ip route get $RouteISP3 distance]) do={

/log warning "ISP3 has a problem en route to $pingTarget - increasing distance of route." /ip route set $RouteISP3 distance=($OriginalRouteDistanceISP3 + $DistanceIncrease) /log warning "ISP3 Route distance increase finished."

}

}

:if ($PingFailCountISP3 < $FailTreshold) do={

/log warning "ISP3 is working ok"

:if ([:typeof $OriginalRouteDistanceISP3] != "nothing") do={

:if ([/ip route get $RouteISP3 distance] != $OriginalRouteDistanceISP3) do={

/log warning "ISP3 can reach $pingTarget again - bringing back original distance of route."

# if ISP3RecoverSpeed is "fast" - recovery of ISP will be FASTER - but there is potential danger of flapping in route ( if its unstable ISP ) # # if ISP3RecoverSpeed is "slow" - recovery of ISP will be SLOWER - but there we are safe in case of unstable ISP :local ISP3RecoverSpeed "slow";

:if ($ISP3RecoverSpeed = "slow") do={

/log warning "ISP3 recovery is set to SLOW" :for pingRun from 0 to 2 do={ :if ($PingFailCountISP3 > 0) do={ :set PingFailCountISP3 ($PingFailCountISP3 - 1) } }

} else={

/log warning "ISP3 recovery is set to FAST" :set PingFailCountISP3 0

}

:if ($PingFailCountISP3 = 0) do={

/ip route set $RouteISP3 distance=($OriginalRouteDistanceISP3) /log warning "ISP3 Route distance returned to original value."

}

}

}

}

########################################################################################### # Check ISP4

# Check remote ip 3 times :foreach k,pingTarget in=$PingTargets do={

:delay 1s; /ip route add comment="FAILOVER CHECK ROUTE" distance=1 gateway=$GatewayISP4 dst-address=$pingTarget :local testRouteID [/ip route find comment="FAILOVER CHECK ROUTE"]; /log warning "Pinging $pingTarget thru $GatewayISP4"

:set PingResult [ping $pingTarget count=1];

:if ([/ip route get $testRouteID gateway-status] ~"unreachable"=true) do={

:set PingFailCountISP4 ($PingFailCountISP4 + 1) /log error "Ping to $pingTarget thru $GatewayISP4 FAILED - unreachable gw"

} else={

# remote ping failed, increase fail count isp +1 :if ($PingResult = 0) do={ :set PingFailCountISP4 ($PingFailCountISP4 + 1) /log error "Ping to $pingTarget thru $GatewayISP4 FAILED - no ping" };

# remote ping passed, decrease fail count isp -1 :if ($PingResult = 1) do={ :if ($PingFailCountISP4 > 0) do={ :set PingFailCountISP4 ($PingFailCountISP4 - 1) }; };

}

/ip route remove [find comment="FAILOVER CHECK ROUTE"]

};

#if ping to all 3 hosts failed, declare route as faulty :if ($PingFailCountISP4 >= $FailTreshold) do={

:if ([:typeof $OriginalRouteDistanceISP4] = "nothing") do={

:set OriginalRouteDistanceISP4 [/ip route get $RouteISP4 distance]

}

:if ($OriginalRouteDistanceISP4 + $DistanceIncrease != [/ip route get $RouteISP4 distance]) do={

/log warning "ISP4 has a problem en route to $pingTarget - increasing distance of route." /ip route set $RouteISP4 distance=($OriginalRouteDistanceISP4 + $DistanceIncrease) /log warning "ISP4 Route distance increase finished."

}

}

:if ($PingFailCountISP4 < $FailTreshold) do={

/log warning "ISP4 is working ok"

:if ([:typeof $OriginalRouteDistanceISP4] != "nothing") do={

:if ([/ip route get $RouteISP4 distance] != $OriginalRouteDistanceISP4) do={

/log warning "ISP4 can reach $pingTarget again - bringing back original distance of route."

# if ISP4RecoverSpeed is "fast" - recovery of ISP will be FASTER - but there is potential danger of flapping in route ( if its unstable ISP ) # # if ISP4RecoverSpeed is "slow" - recovery of ISP will be SLOWER - but there we are safe in case of unstable ISP :local ISP4RecoverSpeed "slow";

:if ($ISP4RecoverSpeed = "slow") do={

/log warning "ISP4 recovery is set to SLOW" :for pingRun from 0 to 2 do={ :if ($PingFailCountISP4 > 0) do={ :set PingFailCountISP4 ($PingFailCountISP4 - 1) } }

} else={

/log warning "ISP4 recovery is set to FAST" :set PingFailCountISP4 0

}

:if ($PingFailCountISP4 = 0) do={

/ip route set $RouteISP4 distance=($OriginalRouteDistanceISP4) /log warning "ISP4 Route distance returned to original value."

}

}

}

}

########################################################################################### # Check ISP5

# Check remote ip 3 times :foreach k,pingTarget in=$PingTargets do={

:delay 1s; /ip route add comment="FAILOVER CHECK ROUTE" distance=1 gateway=$GatewayISP5 dst-address=$pingTarget :local testRouteID [/ip route find comment="FAILOVER CHECK ROUTE"]; /log warning "Pinging $pingTarget thru $GatewayISP5"

:set PingResult [ping $pingTarget count=1];

:if ([/ip route get $testRouteID gateway-status] ~"unreachable"=true) do={

:set PingFailCountISP5 ($PingFailCountISP5 + 1) /log error "Ping to $pingTarget thru $GatewayISP5 FAILED - unreachable gw"

} else={

# remote ping failed, increase fail count isp +1 :if ($PingResult = 0) do={ :set PingFailCountISP5 ($PingFailCountISP5 + 1) /log error "Ping to $pingTarget thru $GatewayISP5 FAILED - no ping" };

# remote ping passed, decrease fail count isp -1 :if ($PingResult = 1) do={ :if ($PingFailCountISP5 > 0) do={ :set PingFailCountISP5 ($PingFailCountISP5 - 1) }; };

}

/ip route remove [find comment="FAILOVER CHECK ROUTE"]

};

#if ping to all 3 hosts failed, declare route as faulty :if ($PingFailCountISP5 >= $FailTreshold) do={

:if ([:typeof $OriginalRouteDistanceISP5] = "nothing") do={

:set OriginalRouteDistanceISP5 [/ip route get $RouteISP5 distance]

}

:if ($OriginalRouteDistanceISP5 + $DistanceIncrease != [/ip route get $RouteISP5 distance]) do={

/log warning "ISP5 has a problem en route to $pingTarget - increasing distance of route." /ip route set $RouteISP5 distance=($OriginalRouteDistanceISP5 + $DistanceIncrease) /log warning "ISP5 Route distance increase finished."

}

}

:if ($PingFailCountISP5 < $FailTreshold) do={

/log warning "ISP5 is working ok"

:if ([:typeof $OriginalRouteDistanceISP5] != "nothing") do={

:if ([/ip route get $RouteISP5 distance] != $OriginalRouteDistanceISP5) do={

/log warning "ISP5 can reach $pingTarget again - bringing back original distance of route."

# if ISP5RecoverSpeed is "fast" - recovery of ISP will be FASTER - but there is potential danger of flapping in route ( if its unstable ISP ) # # if ISP5RecoverSpeed is "slow" - recovery of ISP will be SLOWER - but there we are safe in case of unstable ISP :local ISP5RecoverSpeed "slow";

:if ($ISP5RecoverSpeed = "slow") do={

/log warning "ISP5 recovery is set to SLOW" :for pingRun from 0 to 2 do={ :if ($PingFailCountISP5 > 0) do={ :set PingFailCountISP5 ($PingFailCountISP5 - 1) } }

} else={

/log warning "ISP5 recovery is set to FAST" :set PingFailCountISP5 0

}

:if ($PingFailCountISP5 = 0) do={

/ip route set $RouteISP5 distance=($OriginalRouteDistanceISP5) /log warning "ISP5 Route distance returned to original value."

}

}

}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment