-
-
Save renderorange/0ace1f2f16535b602df550c11e6f88fe to your computer and use it in GitHub Desktop.
Mikrotik RouterOS Script to do proper uplink failover/failback (more reliable than Netwatch)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Mikrotik RouterOS Script to do proper uplink failover/failback (more reliable than Netwatch). | |
# Supports both Generic and PPPoE interfaces. | |
# Policy: read, write, policy, test | |
### Configuration ### | |
# Define Names of all Interfaces that will be checked: | |
:local "interface-names" { "internet-speedy";"internet-biznet" }; | |
# Define Gateway Addresses of Interfaces with static gateways: | |
#:local "interface-gateways" { "internet-tunnel1"="192.168.100.1" }; | |
:local "interface-gateways" { "" }; | |
# Define whether Order of priority follows fixed ordering of "interface-names" array (otherwise uses default route distance): | |
:local "fixed-priority" yes; | |
# Define whether will automatically failback to higher priority interfaces (when connectivity is restored): | |
:local "auto-failback" yes; | |
### Script ### | |
# Get IDs of Generic Interfaces: | |
:local "interface-ids"; | |
:if ($"fixed-priority" = yes) do={ | |
:global "cached-interface-ids"; | |
:if ([:typeof $"cached-interface-ids"] = "array" && [:len $"cached-interface-ids"] = [:len $"interface-names"]) do={ | |
:set "interface-ids" $"cached-interface-ids"; | |
} else={ | |
:set "interface-ids" [:toarray ""]; | |
:foreach idx,"interface-name" in=$"interface-names" do={ | |
:local "interface-id" [/interface find where name=$"interface-name"]; | |
if ($"interface-id"!="") do={ | |
:set ($"interface-ids"->$idx) [:tostr $"interface-id"]; | |
} | |
} | |
:set "cached-interface-ids" $"interface-ids"; | |
} | |
} else={ | |
:set "interface-ids" [:toarray ""]; | |
:foreach idx,"interface-name" in=$"interface-names" do={ | |
:local "interface-id" [/interface find where name=$"interface-name"]; | |
# Priority Order by Default-Route Distance: | |
:if ([/interface get $"interface-id" type] = "pppoe-out") do={ | |
:local "interface-route-distance" [/interface pppoe-client get $"interface-id" default-route-distance]; | |
:set ($"interface-ids"->[:tostr $"interface-route-distance"]) [:tostr $"interface-id"]; | |
} else={ | |
:local "interface-gw-address" ($"interface-gateways"->$"interface-name"); | |
:local "interface-route-id" [/ip route find dst-address="0.0.0.0/0" gateway=$"interface-gw-address" routing-mark~"^(main)\?\$"]; | |
:if ([:len $"interface-route-id"] = 1) do={ | |
:local "interface-route-distance" [/ip route get $"interface-route-id" distance]; | |
:set ($"interface-ids"->[:tostr $"interface-route-distance"]) [:tostr $"interface-id"]; | |
} | |
} | |
} | |
} | |
:local "request-failback" no; | |
# Loop over each Generic Interface in defined order: | |
:foreach idx,"interface-id" in=$"interface-ids" do={ | |
:if (![/interface get $"interface-id" disabled]) do={ | |
# Get Configured Gateway Address: | |
:local "interface-gateway-address"; | |
:local "interface-name" [/interface get $"interface-id" name]; | |
:if ([/interface get $"interface-id" type] = "pppoe-out") do={ | |
/interface pppoe-client monitor $"interface-id" once do={ :set "interface-gateway-address" $"remote-address" }; | |
} else={ | |
:set "interface-gateway-address" ($"interface-gateways"->$"interface-name"); | |
} | |
# Get Current Gateway State: | |
:local "interface-gateway-state"; | |
:local "interface-nexthop-id" [/ip route nexthop find address=$"interface-gateway-address"]; | |
:if ([:len $"interface-nexthop-id"] = 1) do={ | |
:set "interface-gateway-state" [/ip route nexthop get $"interface-nexthop-id" gw-state]; | |
} else={ | |
:set "interface-gateway-state" "unreachable"; | |
} | |
# Ensure interface-gateway-states global variable is defined: | |
:global "interface-gateway-states"; | |
:if ([:typeof $"interface-gateway-states"] != "array") do={ | |
:set "interface-gateway-states" [:toarray ""]; | |
} | |
#:log warn ("name: " . $"interface-name" . ", distance: " . $idx . ", gw-state: " . $"interface-gateway-state"); | |
# Detect Change in Gateway Connectivity / State: | |
:if ($"interface-gateway-state" != $"interface-gateway-states"->$"interface-id" ) do= { | |
:log info ($"interface-name" . ": Internet Gateway State changed from \"" . $"interface-gateway-states"->$"interface-id" . "\" to \"" . $"interface-gateway-state" . "\""); | |
:set ($"interface-gateway-states"->$"interface-id") $"interface-gateway-state"; | |
:if ($"interface-gateway-state" = "unreachable") do={ | |
:log info "------- INTERNET GATEWAY DOWN -------"; | |
/interface disable $"interface-id"; | |
/interface enable $"interface-id"; | |
} else={ | |
:if ($"interface-gateway-state" = "reachable") do={ | |
:log info "------- INTERNET GATEWAY UP -------"; | |
:set "request-failback" $"auto-failback" | |
} else={ | |
/log warn ("------- INTERNET GATEWAY ".$"interface-gateway-state"." -------"); | |
} | |
} | |
#/system script run UpdateDynamicIP; | |
} else={ | |
if ($"request-failback" = yes) do={ | |
# Failback by disabling and re-enabling interface | |
:if ($"interface-gateway-state" = "reachable") do={ | |
:log info "------- INTERNET GATEWAY FAILBACK -------"; | |
/interface disable $"interface-id"; | |
/interface enable $"interface-id"; | |
:set "request-failback" no | |
} | |
} | |
} | |
} | |
} | |
# Add self (this script) to system scheduler | |
:if ([:len [/system scheduler find name=NetwatchGatewayStates]] = 0) do={ | |
/system scheduler add interval=5s name=NetwatchGatewayStates on-event=NetwatchGatewayStates \ | |
policy=read,write,policy,test start-time=startup; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment