Fault tolerance su Mikrotik

Stanco degli approcci limitati e poco parametrici mi sono messo al lavoro.

Dopo una estate di prove ecco lo script per la fault tolerance su Mikrotik.

L’idea è semplice:

– ho un router mikrotik

– su questo router ho N connettività (adsl, 3g, satellite,…)

– su questo router ho una rete LAN

– voglio che la rete LAN navighi in internet

– voglio che l’uscita sia automaticamente direzionata sull’uscita migliore (nell’ordine)

Fin qui niente di strano. Basta usare le distanze nelle varie rotte di default. Ma….

– se una connessione ha un ip dinamico? La rotta è fornita e ha sempre distanza pari a uno.

– se una connessione è funzionante verso il router adiacente ma fallisce all’interno della rete del provider?

Volevo uno script che fosse il più parametrico possibile.

Eccolo!

Il funzionamento è semplice.

Prendo un router Mikrotik.

Collego nella porta N la mia rete LAN e la configuro come solito. Se voglio metto il DHCP server, il DNS,…

Collego nella porta 1 la prima connessione. Configuro questa connessione affinchè funzioni la navigazione ad internet (metto la rotta se ho in ip statico, metto il masquerade,…).

Quando la prima connessione funziona e la rete LAN naviga stacco la prima connessione e procedo con la seconda.

Collego la seconda connessione e la configuro affinchè funzioni la navigazione ad internet (metto la rotta se ho in ip statico, metto il masquerade,…).

Procedo in questo modo con tutte le connessioni che ho a disposizione.

A questo punto copio lo script (System > Script)e lo metto in esecuzione ogni secondo (System > Schedule).

L’unica configurazione necessaria è cambiare la riga :local wanInterfaces “wan1,wlan1,wan3,3g”; indicando i nomi delle interfacce che ho usato (le connessioni) nell’ordine che desidero vengano utilizzate.

# Multi interfaces fault tolerance (with or without dhcp)
# Vittore Zen (http://www.zen.pn.it)
#
# Instructions:
# 1. Configure router as usual.
# 2. Check that when only a single wan is connect you must browse internet. E.g. disconnect all wans, connect wan2 and check connection from your client.
# 2. Set variable wanInterfaces in script with all wans interface names. Order is relevant.
# 3. Put script in scheduler every 1 sec.
# All done.`

:local wanInterfaces "wan1,wlan1,wan3,3g";

### ###
### Don't modify below ###
### ###

:local PingTarget 8.8.8.8
:local intfinder ""
:local gw ""
:local wanInterfacesArray [:toarray $wanInterfaces];
:local distance ""
:local tag "Created by zen-ft script"
:local mark2
:local i
:local mark

#
# function that find interface associated to a route
#

:local getRouteInterface do={
:local intfinder ""
:set $intfinder [:tostr [/ip route get number=$1 value-name=gateway-status]]
:set $intfinder [:pick $intfinder ([:find $intfinder "via"]+5) [:len $intfinder]]
:return $intfinder
}

#
# function that replace a value in an array
#

:local replaceValue do={
:local array $1
:local position $2
:local newValue $3
:for i from=0 to=([:len $array]-1) do={
:if ($i=$position) do={
:if ($i=0) do {
:set $newArray {$newValue}
} else {
:set $newArray ($newArray, $newValue)
}
} else {
:if ($i=0) do {
:set $newArray { [:pick $array $i]}
} else {
:set $newArray ($newArray, [:pick $array $i])
}
}
}
:return $newArray
}

#
# Check default routes and market routes (if needed)
#

:foreach counter in=[/ip route find dst-address=0.0.0.0/0 disabled=no comment!=$tag] do={
:set $intfinder [$getRouteInterface $counter]
:put ("interface: ".$intfinder)
:set $ftPos [:find $wanInterfacesArray $intfinder]
:if ($ftPos!=-1) do={
:set distance ($ftPos+1)
:put ("distance: ".$distance);
:foreach counter2 in=[/ip route find dst-address=0.0.0.0/0 disabled=no] do={
:set $intfinder2 [$getRouteInterface $counter2]
:if ($intfinder2=$intfinder) do={
:set gw [ /ip route get number=$counter2 value-name=gateway]
/ip route add dst-address=0.0.0.0/0 disabled=no distance=$distance gateway=$gw comment=$tag
/ip route add dst-address=0.0.0.0/0 disabled=no distance=$distance gateway=$gw comment=$tag routing-mark=$intfinder
/ip route remove numbers=$counter2
}
}
}
}

:for i from=0 to=([:len $wanInterfacesArray]-1) do={
:set $mark [:pick $wanInterfacesArray $i]
:put ("mark: ".$mark)
:foreach counter2 in=[/ip route find dst-address=0.0.0.0/0 disabled=no ] do={
:set mark2 [ /ip route get number=$counter2 value-name=routing-mark]
:if ($mark2=$mark) do={
:set $PingResult [ping $PingTarget count=1 routing-table=$mark]
:set gw [ /ip route get number=$counter2 value-name=gateway]
:put ("gw: ".$gw)
:foreach counter3 in=[/ip route find dst-address=0.0.0.0/0 disabled=no gateway=$gw ] do={
:if ($PingResult = 0) do={
:set $distance ($i+1)
} else {
:set $distance (10*($i+1))
}
/ip route set number=$counter3 distance=$distance
}
}
}
}

Una nota.

Molti script che si trovano in rete usano l’approccio del ping sull’interfaccia (/ping interface=wan3 8.8.8.8). Ma questo non funziona. Come si legge nel manuale RouterOS il parametro interfaccia funziona solo se l’host utilizzato è ipv6. Quindi tutti questi script non funzionano se non in casi particolari.

Ovviamente sono benvenuti tutti i miglioramenti a questo script.