Community discussions

MikroTik App
 
kgmuzu
just joined
Topic Author
Posts: 12
Joined: Tue Dec 17, 2013 9:38 pm

automaitic failover, multiple ISPs / gateways

Thu Mar 20, 2014 12:54 pm

You have N gateways / ISPs and you want to use them all but your ISPs are not reliable and connections go down from time to time? Then the following script might help or inspire you.

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

#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
And the second part is the script:
####################################################
#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!!
 
masriat
just joined
Posts: 3
Joined: Sat Oct 23, 2010 2:53 pm

Re: automaitic failover, multiple ISPs / gateways

Wed Mar 26, 2014 2:19 am

Thanks for the script
but i have one question if i have multiple isps and I want to load balance them but block download from 1 line and forward it to the others
I can do it by using firewall IP > Firewall > filter > add make content .exe for example and action drop
but if someone tries to download from a server which give him one connection , if that connection is from that line then he won't be able to download ... and that line is 4 times bigger than the other ones ... so it requests 4 connections