Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save renderorange/0ace1f2f16535b602df550c11e6f88fe to your computer and use it in GitHub Desktop.
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)
# 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