Скрипт переключения маршрутов по умолчанию при использовании DualWAN

Есть роутер с двумя каналами интернет. Есть маркированные маршруты, для отдельных сегментов внутренней сети, для выхода в интернет только через указанный шлюз. Все остальные должны иметь доступ к интернет через основного провайдера, при его отсутствии – через резерв.

Простая проверка шлюза провайдера – Check Gateway имеет существенный недостаток – проверяется только шлюз провайдера, но часто бывает так – что он доступен, а вот сети и хосты за ним – недоступны. Поэтому будем проверять доступность внешних общедоступных хостов и уже сходя из этого, принимать решение – какой канал использовать для доступа к интернет.

Итак, имеем настроенный роутер, основной внешний адрес 2.2.2.2, резервный 3.3.3.2. Внутренняя сеть – 172.16.0.0/24.

Настроим доступ к роутеру через оба внешних адреса, доступ с роутера через оба провайдера

/ip firewall address-list
add address=172.16.0.0/24 list=LAN
add address=0.0.0.0/8 list=BOGON
add address=10.0.0.0/8 list=BOGON
add address=127.0.0.0/8 list=BOGON
add address=169.254.0.0/16 list=BOGON
add address=172.16.0.0/12 list=BOGON
add address=192.0.0.0/24 list=BOGON
add address=192.0.2.0/24 list=BOGON
add address=192.88.99.0/24 list=BOGON
add address=192.168.0.0/16 list=BOGON
add address=198.18.0.0/15 list=BOGON
add address=198.51.100.0/24 list=BOGON
add address=203.0.113.0/24 list=BOGON
add address=224.0.0.0/4 list=BOGON
add address=240.0.0.0/4 list=BOGON
add address=255.255.255.255 list=BOGON

/ip firewall mangle
add action=mark-connection chain=prerouting comment="allow access to RB" connection-state=new dst-address=2.2.2.2 in-interface=ether1 new-connection-mark=Prerouting/GW/2.2.2.1 passthrough=no
add action=mark-connection chain=prerouting connection-state=new dst-address=3.3.3.2 in-interface=ether2 new-connection-mark=Prerouting/GW/3.3.3.1 passthrough=no
add action=mark-routing chain=output connection-mark=Prerouting/GW/2.2.2.1 new-routing-mark=Next-Hop/2.2.2.1 passthrough=no
add action=mark-routing chain=prerouting connection-mark=Prerouting/GW/3.3.3.1 new-routing-mark=Next-Hop/3.3.3.1 passthrough=no
add action=mark-routing chain=output comment="allow out from RB" dst-address-list=!BOGON new-routing-mark=Next-Hop/2.2.2.1 passthrough=no src-address=2.2.2.2
add action=mark-routing chain=output dst-address-list=!BOGON new-routing-mark=Next-Hop/3.3.3.1 passthrough=no src-address=3.3.3.2
add action=mark-routing chain=prerouting comment="allow access over NAT" connection-mark=Prerouting/GW/2.2.2.1 in-interface=!ether1 new-routing-mark=Next-Hop/2.2.2.1 passthrough=no
add action=mark-routing chain=prerouting connection-mark=Prerouting/GW/3.3.3.1 in-interface=!ether2 new-routing-mark=Next-Hop/3.3.3.1 passthrough=no

/ip route
add distance=1 gateway=2.2.2.1 routing-mark=Next-Hop/2.2.2.1
add distance=1 gateway=3.3.3.1 routing-mark=Next-Hop/3.3.3.1
add comment=WAN1 distance=1 gateway=2.2.2.1
add comment=WAN2 distance=2 gateway=3.3.3.1
add distance=254 gateway=Br-Loopback pref-src=2.2.2.2

/ip route rule
add action=lookup-only-in-table routing-mark=Next-Hop/2.2.2.1 table=Next-Hop/2.2.2.1
add action=lookup-only-in-table routing-mark=Next-Hop/3.3.3.1 table=Next-Hop/3.3.3.1

Теперь настроим доступ из внутренней сети через оба шлюза. К примеру, ПК с адресом 172.16.0.100 должен использовать только резервный канал, ПК с адресом 172.16.0.200 – только основной

/ip firewall address-list
add address=172.16.0.200 list=via/2.2.2.2
add address=172.16.0.100 list=via/3.3.3.2

/ip firewall mangle
add action=mark-routing chain=prerouting disabled=yes dst-address-list=!BOGONS new-routing-mark=Next-Hop/2.2.2.1 passthrough=no src-address-list=via/2.2.2.2
add action=mark-routing chain=prerouting disabled=yes dst-address-list=!BOGONS new-routing-mark=Next-Hop/3.3.3.1 passthrough=no src-address-list=via/3.3.3.2

/ip firewall nat
add action=src-nat chain=srcnat disabled=yes routing-mark=Next-Hop/2.2.2.1 src-address-list=via/2.2.2.2 to-addresses=2.2.2.2
add action=src-nat chain=srcnat disabled=yes routing-mark=Next-Hop/3.3.3.1 src-address-list=via/3.3.3.2 to-addresses=3.3.3.2

Весь остальной трафик от внутренних хостов сети 172.16.0.0/24 будет не маркирован и для доступа к сети будет использоваться активный маршрут по умолчанию. Будем использовать скрипт, который меняет приоритет маршрута по умолчанию для не маркированного трафика при отсутствии ping на несколько общедоступных адресов. А так же будет отправлять уведомление в телеграмм. Важное условие для работы скрипта – наличие комментариев у маршрутов, т.к. он ищет маршруты для изменения их distance именно по этим комментариям

add comment=WAN1 distance=1 gateway=2.2.2.1
add comment=WAN2 distance=2 gateway=3.3.3.1

Переменные скрипта:

MainIf - основной интерфейс 
RsrvIf - резервный интерфейс
PingCount - количество запросов Ping до каждого проверочного хоста
PingTargets - проверочные хосты
CrierBotID - id для телеграмм бота

Сам скрипт:

#Main interface name
:global MainIf ether1
#Failover interface name
:global RsrvIf ether2
:local PingCount 3
:local PingTargets {8.8.8.8; 77.88.8.8; 1.1.1.1} 
:local host
:local MainIfInetOk false
:local RsrvIfInetOk false
:local MainPings 0
:local RsrvPings 0
foreach host in=$PingTargets do={
:local res [/ping $host count=$PingCount interface=$MainIf]
:set MainPings ($MainPings + $res)
:local res [/ping $host count=$PingCount interface=$RsrvIf]
:set RsrvPings ($RsrvPings + $res)
:delay 1
}
:set MainIfInetOk ($MainPings >= 1)
:set RsrvIfInetOk ($RsrvPings >= 1)
:put "MainIfInetOk=$MainIfInetOk"
:put "RsrvIfInetOk=$RsrvIfInetOk"
:local MainGWDistance [/ip route get [find comment="WAN1"] distance]
:local RsrvGWDistance [/ip route get [find comment="WAN2"] distance]
:put "MainGWDistance=$MainGWDistance"
:put "RsrvGWDistance=$RsrvGWDistance"
if ($MainIfInetOk && ($MainGWDistance >= $RsrvGWDistance)) do={
/ip route set [find comment="WAN1"] distance=1
/ip route set [find comment="WAN2"] distance=2
:put "Switched to main internet connection"
:log warning "RB1 message - MAIN Gateway is active";
/tool fetch url="http://crierbot.appspot.com/CrierBotID/send?message=RB1%20allert%20MAIN%20Gateway%20active" keep-result=no;
}
if (!$MainIfInetOk && $RsrvIfInetOk && ($MainGWDistance <= $RsrvGWDistance)) do={ /ip route set [find comment="WAN1"] distance=2
/ip route set [find comment="WAN2"] distance=1
:put "Switched to reserve internet connection"
:delay 2;
:log warning "RB1 message - RESERVE Gateway is active";
/tool fetch url="http://crierbot.appspot.com/CrierBotID/send?message=RB1%20allert%20RESERVE%20Gateway%20active" keep-result=no;
}

Проверка работы скрипта – все проверочные хосты доступны – основной шлюз WAN1

Доступны не все проверочные хосты, а только один из них – основной шлюз WAN1

Недоступны все проверочные хосты – основной шлюз WAN2

Добавляем скрипт в планировщик с нужным интервалом проверки, в логах можно видеть результаты его работы

/system scheduler
add interval=30s name=checkGW on-event=checkGW start-time=startup