RouterOS gheto Load Balancer

GLBer is a program that creates the configuration for a Mikrotik RouterOS Load Balancer.

The confiuration is suitable for a router with many uplinks that serves many end users
eg: a workplace with many adsl lines

glber.sh takes as input the names of the point-to-point named interfaces and creates the RouterOS configuration commands and a RouterOS script that runs every 10 minutes.

glber.sh
#!/bin/bash
#Creates the configuration commands and a RouterOS script used to put together a Ghetto Load BalanER --GLBer
#GLBer balances traffic among n named interfaces 
#GLBer NATs and masquerades 
#GLBer watches if the interfaces have Internet Access every 10 minutes and adjusts
#g0 2014 , there is a post about GLBer at http://alog.ipduh.com

GATEWAYS="${1}"
ECMP=""
MIKAR=""
GW_COUNT=0

for GW in ${GATEWAYS}; do
 ((GW_COUNT+=1))
done

echo "glber.sh:hola , I will create the configuration for ${GW_COUNT} named interfaces" 

POS=0
for GW in ${GATEWAYS}; do
 ((POS+=1))

 if [ ${POS} -eq ${GW_COUNT} ] 
 then
  ECMP+="${GW}"
  MIKAR+="\"${GW}\""
 else
  ECMP+="${GW},"
  MIKAR+="\"${GW}\";"
 fi 
done

echo ""
echo "###commands###"
echo ""

echo "/ip route"
echo "add dst-address=0.0.0.0/0 gateway=$ECMP check-gateway=ping comment=lbercur ;"

echo "/ip firewall nat"
for GW in ${GATEWAYS}; do
 echo "add chain=srcnat out-interface=${GW} action=masquerade comment=GLBer_${GW}_nat ;"
done

echo "/ip firewall mangle"
for GW in ${GATEWAYS}; do
 echo "add chain=input in-interface=${GW} action=mark-connection new-connection-mark=${GW}_c comment=GLBer_mangle ;"
 echo "add chain=output connection-mark=${GW}_c action=mark-routing new-routing-mark=to_${GW} comment=GLBer_mangle ;"
done

echo "/ip route"
for GW in ${GATEWAYS}; do
 echo "add dst-address=0.0.0.0/0 gateway=${GW} routing-mark=to_${GW} comment=GLBER ;"
done 

echo ""
echo "###script###"
echo "###add the following script to the router 'script-repository' and name it glber###"
echo ""

read -r -d '' SCR <<- 'SCRIPTT'
:local goodgates " "
:local gcount 0
:local coma ""
:local lbercur "lbercur"
:local lbertmp "lbertmp"
:local pong "8.8.8.8"

:foreach g in=$gateways do={
:if ( $gcount > 0 ) do={ :set coma (",") }
:if ([/ping $pong interface="$g" count=3] = 0) do={  }  else={ 
:set $goodgates ( "$goodgates" . "$coma" . "$g" ) ;
:set gcount ($gcount+1) 
}
};

/ip route set [find comment=$lbercur] distance=2  
/ip route add dst-address=0.0.0.0/0 gateway=$goodgates check-gateway=ping comment="$lbertmp"
/ip route remove [find comment=$lbercur] 
/ip route add dst-address=0.0.0.0/0 gateway=$goodgates check-gateway=ping comment="$lbercur"
/ip route remove [find comment=$lbertmp] 

}
SCRIPTT
echo "{ "
echo "#GLBer -- g0 2014 -- alog.ipduh.com"
echo ":local gateways { ${MIKAR} };"
echo "${SCR}"
echo ""
echo "###schedule script###"
echo "/system scheduler add name=glber policy=read,write,test interval=10m ;"




I think that the easiest way to use glber.sh is to ssh to the RouterOS system from a system that runs bash. Or ssh to a system that has bash and the RouterOS system from the same machine ... whatever .... The good thing is that RouterOS understands the line endings used in all the popular operating systems.

Example Usage

Assume we want to create an uplink balancer for the interfaces alpha, beta , gama , delta ,epsilon

These interfaces may be VPNs, PPoE Tunnels, etc
$ wget kod.ipduh.com/lib/glber.sh
$ chmod 755 glber.sh
$ ./glber.sh "alpha beta gama delta epsilon"


You need to ssh or winbox to the mikrotik RouterOS system and copy the configuration that the glber.sh outputs.

The configuration for the alpha beta gama delta epsilon balancing
 ./glber.sh "alpha beta gama delta epsilon"
glber.sh:hola , I will create the configuration for 5 named interfaces

###commands###

/ip route
add dst-address=0.0.0.0/0 gateway=alpha,beta,gama,delta,epsilon check-gateway=ping comment=lbercur ;
/ip firewall nat
add chain=srcnat out-interface=alpha action=masquerade comment=GLBer_alpha_nat ;
add chain=srcnat out-interface=beta action=masquerade comment=GLBer_beta_nat ;
add chain=srcnat out-interface=gama action=masquerade comment=GLBer_gama_nat ;
add chain=srcnat out-interface=delta action=masquerade comment=GLBer_delta_nat ;
add chain=srcnat out-interface=epsilon action=masquerade comment=GLBer_epsilon_nat ;
/ip firewall mangle
add chain=input in-interface=alpha action=mark-connection new-connection-mark=alpha_c comment=GLBer_mangle ;
add chain=output connection-mark=alpha_c action=mark-routing new-routing-mark=to_alpha comment=GLBer_mangle ;
add chain=input in-interface=beta action=mark-connection new-connection-mark=beta_c comment=GLBer_mangle ;
add chain=output connection-mark=beta_c action=mark-routing new-routing-mark=to_beta comment=GLBer_mangle ;
add chain=input in-interface=gama action=mark-connection new-connection-mark=gama_c comment=GLBer_mangle ;
add chain=output connection-mark=gama_c action=mark-routing new-routing-mark=to_gama comment=GLBer_mangle ;
add chain=input in-interface=delta action=mark-connection new-connection-mark=delta_c comment=GLBer_mangle ;
add chain=output connection-mark=delta_c action=mark-routing new-routing-mark=to_delta comment=GLBer_mangle ;
add chain=input in-interface=epsilon action=mark-connection new-connection-mark=epsilon_c comment=GLBer_mangle ;
add chain=output connection-mark=epsilon_c action=mark-routing new-routing-mark=to_epsilon comment=GLBer_mangle ;
/ip route
add dst-address=0.0.0.0/0 gateway=alpha routing-mark=to_alpha comment=GLBER ;
add dst-address=0.0.0.0/0 gateway=beta routing-mark=to_beta comment=GLBER ;
add dst-address=0.0.0.0/0 gateway=gama routing-mark=to_gama comment=GLBER ;
add dst-address=0.0.0.0/0 gateway=delta routing-mark=to_delta comment=GLBER ;
add dst-address=0.0.0.0/0 gateway=epsilon routing-mark=to_epsilon comment=GLBER ;

###script###
###add the following script to the router 'script-repository' and name it glber###

{ 
#GLBer -- g0 2014 -- alog.ipduh.com
:local gateways { "alpha";"beta";"gama";"delta";"epsilon" };
:local goodgates " "
:local gcount 0
:local coma ""
:local lbercur "lbercur"
:local lbertmp "lbertmp"
:local pong "8.8.8.8"

:foreach g in=$gateways do={
:if ( $gcount > 0 ) do={ :set coma (",") }
:if ([/ping $pong interface="$g" count=3] = 0) do={  }  else={ 
:set $goodgates ( "$goodgates" . "$coma" . "$g" ) ;
:set gcount ($gcount+1) 
}
};

/ip route set [find comment=$lbercur] distance=2  
/ip route add dst-address=0.0.0.0/0 gateway=$goodgates check-gateway=ping comment="$lbertmp"
/ip route remove [find comment=$lbercur] 
/ip route add dst-address=0.0.0.0/0 gateway=$goodgates check-gateway=ping comment="$lbercur"
/ip route remove [find comment=$lbertmp] 

}

###schedule script###
/system scheduler add name=glber policy=read,write,test interval=10m ;





In RouterOS the routing table is flushed every 10 minutes and then there is a good chance ( depending on the number of the uplinks ) to reset the masqueraded connections.

The RouterOS glber script runs every 10 minutes and resets the equal cost multipath route therefore I think that it raises the chance for the masqueraded connections to reset in a 10 minutes period.

In a 5 uplinks setup the chance for a connection to not reset should fall to 4% ( I did not have the patience to verify it yet ). Adding three lines of code to glber should raise the probability that a connection does not reset every 10 minutes up to 1/uplinks. I did not need it in my setup. I wanted outgoing connections to reset often. The thing needed a session was in the internal network and resetting facebook connections was considered a feature.

It is possible ( though not-tested enough) to balance differently per uplink.
eg: 25% of connections from alpha , 25% of connections from beta and 50% of connections from gama would be
$ ./glber.sh "alpha beta gama gama"
and 75% of the connections from alpha and 25% of the connections from beta would be
$ ./glber.sh "alpha alpha alpha beta"




Mikrotik RouterOS Uplink Balancer