Some requirements and assumptions:
The IP address for the WAN interfaces must be static. If it is set by DHCP an dynamic route is installed which cannot be altered by a script.
You would have to run this script regularly like every 5min.
I did some basic test which where satisfying. I dont know how good connections adopt when they are changed to another gateway. Some might recover quickly some might take a bit longer depending on protocol and application of course.
I am sure there are plenty of variations possible of how to implement this failover script. I would be happy to see other solutions. E.g. you could manipulate the PCC rules depending what gateway went down also.
Hint: a good way how to work with MikroTik scripts is to use UltraEdit and set "View as" to Perl. This helps a lot!!! To apply the script I copy the whole text simply into the script window in Mikrotik Winbox, apply. Go to the command line and print the script. Then you see syntax problems.
Credits: I adopted a script I found in this thread http://forum.mikrotik.com/viewtopic.php?f=9&t=39365 have a look at it!
The first part is setting up firewall rules and routes
Code: Select all
#NAT: I would try to rid of double nat-ting. In this example I NAT first in the Mikrotik and then in the gateway=modem
/ip firewall nat
add action=masquerade chain=srcnat comment="Masq for GW1" disabled=no out-interface=ether1
add action=masquerade chain=srcnat comment="Masq for GW2" disabled=no out-interface=ether2
add action=masquerade chain=srcnat comment="Masq for GW2" disabled=no out-interface=ether3
#IP routes
#sope>target-scope stops next hop lookup (if next hop lookup would be not successful then the routing entry would be disabled)
#LB...load balancing
/ip route
add comment="LB" disabled=no distance=1 dst-address=0.0.0.0/0 gateway=10.1.11.254 routing-mark=GW0 scope=30 target-scope=10
add comment="LB" disabled=no distance=1 dst-address=0.0.0.0/0 gateway=192.168.20.1 routing-mark=GW1 scope=30 target-scope=10
add comment="LB" disabled=no distance=1 dst-address=0.0.0.0/0 gateway=192.168.30.1 routing-mark=GW2 scope=30 target-scope=10
#PCC marking with double weight on gateway 2
/ip firewall mangle
add action=mark-connection chain=prerouting comment="markForward" disabled=no in-interface=ether5 new-connection-mark=GW0 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/0
add action=mark-connection chain=prerouting comment="markForward" disabled=no in-interface=ether5 new-connection-mark=GW1 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/1
add action=mark-connection chain=prerouting comment="markForward" disabled=no in-interface=ether5 new-connection-mark=GW1 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/2
add action=mark-connection chain=prerouting comment="markForward" disabled=no in-interface=ether5 new-connection-mark=GW2 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/3
#this is connection marking for traffic originating the router especially interesting for proxy and hotspot traffic!
#/ip firewall mangle
#add action=mark-connection chain=output comment="markOutput" disabled=no connection-mark=no-mark disabled=no new-connection-mark=GW0 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/0
#add action=mark-connection chain=output comment="markOutput" disabled=no connection-mark=no-mark disabled=no new-connection-mark=GW1 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/1
#add action=mark-connection chain=output comment="markOutput" disabled=no connection-mark=no-mark disabled=no new-connection-mark=GW1 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/2
#add action=mark-connection chain=output comment="markOutput" disabled=no connection-mark=no-mark disabled=no new-connection-mark=GW2 passthrough=yes per-connection-classifier=both-addresses-and-ports:4/2
#to mark incoming connections to be sent out via the same gateway as they came in
/ip firewall mangle
add action=mark-connection chain=input comment="markInput" connection-mark=no-mark disabled=no in-interface=ether1 new-connection-mark=GW0 passthrough=yes
add action=mark-connection chain=input comment="markInput" connection-mark=no-mark disabled=no in-interface=ether2 new-connection-mark=GW1 passthrough=yes
add action=mark-connection chain=input comment="markInput" connection-mark=no-mark disabled=no in-interface=ether2 new-connection-mark=GW2 passthrough=yes
#marking for routing
/ip firewall mangle
add action=mark-routing chain=prerouting comment="markRouting" connection-mark=GW0 disabled=no in-interface=ether5 new-routing-mark=GW0 passthrough=yes
add action=mark-routing chain=prerouting comment="markRouting" connection-mark=GW1 disabled=no in-interface=ether5 new-routing-mark=GW1 passthrough=yes
add action=mark-routing chain=prerouting comment="markRouting" connection-mark=GW2 disabled=no in-interface=ether5 new-routing-mark=GW2 passthrough=yes
Code: Select all
####################################################
#Gateway checking based using host/server ping
#Tested for RouterOS 6.7
#This version is for n gateways
#
#set variables
:local pingcount 3
#not good to rely on a DNS server
#:local pingip [:resolve "www.google.com"]
:local pingip 8.8.8.8
#how many gateways do you use
:local numberOfGateways 3
#list all IP addresses of your gateways
:local Gateways "10.1.11.254,192.168.20.1,192.168.30.1"
#create an "GatewayArray"
:local GatewayAR [:toarray $Gateways]
#holds a boolean describing if the respective gateway is up or down
:local GatewayState
:local GatewayStateAR [:toarray $GatewayState]
:local numberOfActiveGWs 0
:local alldown true
:local allup true
#Setup a log file
#If not necessary it can stay commented out
#:if ([/system logging action print count-only where name=GatewaysCheck]=0) do={/system logging action add name=GatewaysCheck target=disk disk-file-name="Gateways Check" disk-lines-per-file=10000}
#:if ([/system logging print count-only where action=GatewaysCheck]=0) do={/system logging add topics=script action=GatewaysCheck}
#install manually a nonsense route in the routing table. this route will be manipulated later to make the test pings. the gateway IP is random. you can put any number there
:if ([/ip route print count-only where dst-address="$pingip/32"]=0) do={/ip route add dst-address=($pingip) gateway=(192.168.1.254) comment="helper route for pinging"};
:put "PRINT routing table; after install helper route";
:put [/ip route print];
#ping e.g. 8.8.8.8 through all gateways listed above
:foreach k in $GatewayAR do={
#Test Gateways:
:put "Testing gateway $k now";
/ip route set [find dst-address="$pingip/32"] disabled=no gateway=$k comment="Checking $k gateway";
:delay 1000ms;
:put ("PRINT gateway-status".[/ip route get [find dst-address="$pingip/32"] gateway-status]);
#if a link is congested the link/gateway might be fine but the ping takes too long --> set a longer timeout
#:local pingresult [/ping interval=3s $pingip count=$pingcount];
:local pingresult [/ping $pingip count=$pingcount];
:put "my pingresult=$pingresult";
#Mark in array through which gateway you can ping
:if ($pingresult=0) do={:set GatewayStateAR ($GatewayStateAR, false); :set allup false; :log info ("gateway $k DOWN / $pingcount")};
:if ($pingresult>0) do={:set GatewayStateAR ($GatewayStateAR, true); :set alldown false; :log info ("gateway $k UP / $pingcount")};
}
#:put $GatewayStateAR;
#remove helper routing table entry to route. was used to route pings through the two gateways
/ip route remove [find dst-address="$pingip/32"];
##########################################
#manipulate routes and packet marks to direct traffic only to the active gateways
##########################################
#count active gateways to Internet
:for o from=0 to=($numberOfGateways-1) do={:if ($GatewayStateAR->$o=true) do={set numberOfActiveGWs ($numberOfActiveGWs+1)}};
:put "number of active GWs $numberOfActiveGWs";
#set routing marks for all active interfaces
:if (allup=true) do={
:log info ("HOTFAILOVER all gateways up");
#enable marking rules in mangle table and enable the routing based on routing marks
:local count 0:
#test if any routing mark has to be restored to original, if not no changes
:for p from=0 to=($numberOfGateways-1) do={:foreach i in=[/ip firewall mangle find comment="markRouting"&&(connection-mark=("GW$p"))] do={
:put "inside allup";
:put [/ip firewall mangle get $i new-routing-mark];
:if (!(([/ip firewall mangle get $i new-routing-mark])=("GW$p"))) do={[/ip firewall mangle set $i new-routing-mark=("GW".$p)];}
}}
} else={:if (alldown=false) do={
:log info ("HOTFAILOVER at least one gateway down");
:local alternativeGW 0;
:local x 0;
:while ($GatewayStateAR->$x=false) do={:set x ($x + 1);};
:set alternativeGW $x;
:put "(first GW is GW0) next alternative GW = GW$alternativeGW";
:if (($x + 1)=$numberOfGateways) do={:set x 0;} else={set x ($x + 1);}
:put "next (next GW to test) x = $x";
:for m from=0 to=($numberOfGateways-1) do={
:put "inside FOR LOOP m= $m";
:foreach n in=[/ip firewall mangle find ((comment="markRouting")&&(connection-mark=("GW$m")))] do={
:if ($GatewayStateAR->$m=true) do={
:put "inside FOR LOOP active GW$m";
#the following line ensures that the an active gateway keeps its routing mark (no changes for active gateways)
[/ip firewall mangle set $n new-routing-mark=("GW".$m)];} else={
:put "inside FOR LOOP else statement GW$m";
[/ip firewall mangle set $n new-routing-mark=("GW".$alternativeGW)];
:while ($GatewayStateAR->$x=false) do={:if (($x + 1)=$numberOfGateways) do={:set x 0;} else={set x ($x + 1);}};
:set alternativeGW $x;
:put "inside FOR LOOP next alternative GW = GW$alternativeGW";}
}}} else={:put "!all gateways to Internet are down!"; :log info ("HOTFAILOVER all gateways down");}
}
:put "ROUTING TABLE after";
:put [/ip route print];
improve it and post what you have!!