Mikrotik: скрипт переключения на резервный канал интернета

Mikrotik: скрипт переключения на резервный канал интернета

406
ПОДЕЛИТЬСЯ

Скрипт изготовлен специально как более гибкое средство мониторинга, так как остальные варианты, в частности check-gateway, не совершенно корректны для меня. Сходу скажу , каналы доступны по-одному, никакого load-balance здесь не будет. Желаю поделиться своим скриптом для перехода на резервный веб, когда основной теряется, и возврату на основной, как лишь он вновь заработает. Оба канала — PPP соединения (в моем случае один проводной, 2-ой — 3G свисток).

Можно придумать, когда и пинги не являются показателем работы, но эти случаи я опускаю, в скрипте можно указать хоть какой иной метод проверки, под ситуацию. Остальные индивидуальности: резервный канал — мобильная сеть, и он подключается лишь при отсутствии основного канала, в остальное время интерфейс выключен. При возврате обратно на основной канал корректно проверяется его работоспособность. Я проверяю, пингуя несколько наружных адресов. Ну и route-distance у интерфейсов изменяются динамически и постоянно не равны, что дает возможность одновременной работы каналов, но трафик направляется лишь на один из их. Основной принцип прост: поднятый VPN канал не значит, что веб через него работает. Методика, хорошая от пинга с указанием интерфейса.

При осознании можно просто переработать скрипт, раз провайдеры либо один из их, дает статику.

В конце будет скрипт полностью. Итак, я поочередно обрисую, какая настройка нужна для работы скрипта, а потом по кускам обрисую главные моменты работы.

Допустим, есть 2 PPP соединения ISP1 — основной, и ISP2 — резервный, оба настроены и работают по-отдельности. Настраиваем обычные вещи, как NAT, маркировка пакетов и соединений для ответов по тому же интерфейсу, откуда пришел запрос, маршрутов для помеченных пакетов: Выставляем на их dial-on-demand=no и add-default-route=yes, потом устанавливаем у ISP2 параметр default-route-distance на единицу больше, чем у ISP1.

Подготовка/ip firewall mangle
add action=mark-connection chain=forward connection-mark=no-mark
in-interface=ISP1 new-connection-mark=ISP1 passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP1 in-interface=
bridge-local new-routing-mark=to_ISP1 passthrough=no

add action=mark-connection chain=forward connection-mark=no-mark
in-interface=ISP2 new-connection-mark=ISP2 passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP2 in-interface=
bridge-local new-routing-mark=to_ISP2 passthrough=no

/ip firewall nat
add action=masquerade chain=srcnat out-interface=ISP1
add action=masquerade chain=srcnat out-interface=ISP2

/ip route
add distance=1 gateway=ISP1 routing-mark=to_ISP1
add distance=1 gateway=ISP2 routing-mark=to_ISP2
Эти данные, как и имена интерфейсов, необходимо поменять на свои. Это не вся настройка, но обо всем по-порядку. Также представим, что локальный адресок роутера 192.168.xx.yy, а сабсеть 192.168.xx.0/24.

Переменныеglobal FailoverTimes;
global FailoverLastTime;
global FailoverLastBackTime;

local ifMain "ISP1";
local ifRes "ISP2";

local scriptName "Failover";
local state 0;
local pingNum 0;
local pingRes;
local routeDist;
local routeDist2;
local tmp;

local ip { x.x.x.x; y.y.y.y; z.z.z.z };
local pingSrcAddr 192.168.xx.yy;
Определяем переменные: пишем наименования интерфейсов в ifMain и ifRes, локальный адресок роутера в pingSrcAddr (дальше будет понятно, для чего он нужен), и 3 наружных адреса, которые будут пинговаться для проверки канала в массив ip.

Single instanceif ( [len [/system script job find where script=$scriptName]] > 1) do= { error "single instance" };
delay 15;
Delay на вариант пуска при старте RouterOS, даем время подняться соединениям. Разрешим запускаться лишь одной копии скрипта.

Разглядим их. В нескончаемом цикле он анализирует текущее состояние по переменной state и выполняет нужные деяния. Пропустим незначительно, и перейдем к основной части. Скрипт работает нескончаемо, точнее, пока его не приостановят либо не случится ошибка.

State 0 if ($state = 0) do= {
do {
if ($pingNum >= 3) do= { set $pingNum 0; }
if ([ping ($ip->$pingNum) count=1] = 0) do= {
set $pingRes [ping ($ip->0) count=2];
set $pingRes ($pingRes+[ping ($ip->1) count=2]);
set $pingRes ($pingRes+[ping ($ip->2) count=2]);
if ($pingRes = 0) do= {
set $FailoverLastTime "$[/system clock get date] $[/system clock get time]";
set $FailoverTimes ([tonum $FailoverTimes] + 1)
set $state 1;
log info "$scriptName: state changed 0->1";
}
}
set $pingNum ($pingNum + 1);
if ($state = 0) do= { delay 15 };
} while ($state = 0);
}
Здесь агрессивно указано, что адресов в массиве — 3. Раз в 15 секунд проверяем поочередно один из 3-х указанных адресов, раз ответа нет — проверяем все 3 адреса. Раз это не так, придется подправить. Состояние 0 — когда работает основной канал. Глухо — инициируем переход на резервный канал.

State 1 if ($state = 1) do= {
if ( [/interface l2tp-client get $ifMain default-route-distance] > 10) do= {
/interface ppp-client set $ifRes default-route-distance=1;
}
/interface enable $ifRes;
beep frequency=2000 length=250ms;
delay 500ms;
beep frequency=2000 length=250ms;
delay 500ms;
delay 6;
/interface disable $ifMain;
set $routeDist ([/interface ppp-client get $ifRes default-route-distance] + 1);
/interface l2tp-client set $ifMain default-route-distance=$routeDist;
/interface enable $ifMain;
set $state 2;
log info "$scriptName: state changed 1->2";
}
Состояние 1 — переключение каналов. В примере — ISP1 это l2tp-client, а ISP2 — ppp-client. Раз остальные, необходимо их подправить в строчках с default-route-distance. Тут принципиально, какие конкретно PPP соединения употребляются.

Это достаточное время для меня, за которое 3G соединение поднимается. За это время текущие соединения и новейшие висят в таймаутах, пока основной VPN еще не разорвался, и минимизируются ответы роутера типа dest unreachable. Опосля включения резервного канала ждем 7 секунд.

Звуковая индикация на любителя, может и ночкой сработать. Раз не необходимо — убираем.

За счет этого имеем возможность ожидать возврата основного канала без помех для работы веба через резерв. Дальше основной канал отключается, его default-route-distance устанавливается на 1 больше, чем у резервного, и он врубается обратно.

Забегая вперед, при переходе обратно на основной канал и выключении резерва его default-route-distance опять возрастет на 1. С каждым переключением route distance у PPP соединений поочередно возрастает. Для того, чтоб они не уходили очень далековато, тут проверяется текущее значение и происходит сброс на 1 при превышении 10 (цифра не имеет значения, взято для примера, на теоретическом уровне максимум около 250).

State 2 if ($state = 2) do= {
do {
if ( [len [interface find where name=$ifMain and running] ] = 1) do= {
set $pingRes [ping ($ip->0) src-address=$pingSrcAddr count=2];
set $pingRes ($pingRes+[ping ($ip->1) src-address=$pingSrcAddr count=2]);
set $pingRes ($pingRes+[ping ($ip->2) src-address=$pingSrcAddr count=2]);
if ($pingRes > 0) do= {
set $state 3;
log info "$scriptName: state changed 2->3";
}
}
if ($state = 2) do= { delay 15 };
} while ($state = 2);
}
Раз он не подключился, ничего не поделать, все условия для него сделаны, и практически нас интересует лишь основной канал. Стоит отметить, что состояние резерва не любопытно. Состояние 2 — ожидание восстановления основного канала.

Разбираемся, как же огласить роутеру посылать такие пинги через основной канал, даже когда его маршрут неактивен (route distance больше, чем у резервного): Допускается, что он постоянно есть, по другому для чего роутер нужен. Здесь употребляется пинг с локального адреса роутера. Тут ожидается поднятие VPN основного канала, и опосля этого через него при активном резерве происходят пробы пинга наружных адресов. Я не употреблял наружный адресок основного канала, поэтому что провайдер его дает динамическим. Изготовлено это сложновато, но верно. Раз писать ping xx.xx.xx.xx interface=$ifMain, то по словам разрабов, это может как работать, так и нет.

Донастройка/ip firewall mangle
add action=mark-routing chain=output comment=Failover_script_rule
dst-address=!192.168.xx.0/24 new-routing-mark=to_ISP1 passthrough=no
protocol=icmp src-address=192.168.xx.yy

/ip route rule
add action=lookup-only-in-table routing-mark=to_ISP1 src-address=
192.168.xx.yy/32 table=to_ISP1
Трафик пингов, используемый здесь, является необычным. Традиционно, в таковых вариантах за src-address роутер берет адресок интерфейса, по которому уйдет пакет. Дальше, таковой трафик метится с routing-mark основного канала, и пакеты идут по основному каналу за счет маршрута с меткой. Указывая локальный адресок роутера как src-address, мы как бы выносим его за тот же NAT, за которым посиживает локалка. Это output трафик, исходящий от самого роутера на наружный адресок.

2-ое правило также нужно. Без него, раз вдруг основной канал опять свалится, то пинги, даже помеченные to_ISP1, пойдут по маршруту без метки резервного канала, что приведет к неправильному возврату на основной канал. Так вот раз в это время основной канал отключится, то пинги начнут проходить по резерву. Чтоб было незначительно яснее, представим, что state=2, основной канал поднят, но трафик через него не идет. Так работает RouterOS, раз канал не подключен, то маршруты, даже помеченные, отключаются. На пинги в таком случае уйдет 6 секунд. 2-ое правило это исключает.

Отмечаем, что пинги в локалку с роутера не метятся и работают как традиционно.

State 3 if ($state = 3) do= {
/interface disable $ifRes;
set $routeDist ([/interface l2tp-client get $ifMain default-route-distance] + 1);
/interface ppp-client set $ifRes default-route-distance=$routeDist;
set $state 0;
set $FailoverLastBackTime "$[/system clock get date] $[/system clock get time]";
log info "$scriptName: state changed 3->0";
beep frequency=500 length=500ms;
}
Опосля того, как пинги по основному каналу начали проходить, довольно выключить резервный VPN, и будет употребляться основной. Обращаем внимание на тип PPP соединений, и меняем по-необходимости. Дальше, меняем default-route-distance у резервного на 1 больше, чем у основного, и подаем звуковой сигнал. Состояние 3 — переход на основной канал.

На этом цикл замыкается и происходит возврат в состояние 0.

Сейчас о том, как при запуске скрипта он выяснит текущее состояние:

Initial stateset $routeDist [/interface l2tp-client get $ifMain default-route-distance];
set $routeDist2 [/interface ppp-client get $ifRes default-route-distance];
if ($routeDist < $routeDist2) do= {
if ( [/interface get $ifMain running] = true) do= { set $state 0; } else= { set $state 1; }
} else= {
if ( [/interface get $ifMain disabled] = true) do= { /interface enable $ifMain; }
if ($routeDist > $routeDist2 and [/interface get $ifRes disabled] = false) do= {
set $state 2;
} else= { set $state 3; }
}

log info "$scriptName: initial state $state";
Тут логика также сложновата на 1-ый взор. Анализируются 3 параметра: запущен ли ISP1, запущен ли ISP2, и соотношение default route distance у их. Исходные состояния 1 и 3 являются необычными, и молвят о неверной настройке, но скрипт в таком случае сам все восстанавливает, пусть время от времени и методом ненадобных переключений.

состояние: Вот это доп. Исключенное состояниеУ меня есть еще одно состояние, которое я исключил, т.к. не разрешит доменное имя, а будет продолжать применять DNS резерва. оно разрешает в локальный адресок. И раз не посодействовать скрипту с разрешением, указывая определенный DNS, то даже опосля доступности сети ISP1 он никогда не подключится, т.к. быстрее всего оно вряд ли необходимо большинству. Мой ISP1 подключает VPN по имени, не по IP, для разрешения этого имени необходимо применять DNS этого же провайдера, т.к.

if ($state = 2) do= {
do {
if (([ping DNSip1 count=1] > 0) or ([ping DNSip2 count=1] > 0)) do= {
set $tmp 0;
do { resolve VPNaddress server=DNSip1; } on-error= { };
do { resolve VPNaddress server=DNSip2; } on-error= { };
do { resolve VPNaddress } on-error= { set $tmp 1; };
if ($tmp = 0) do= {
set $state 3;
log info "$scriptName: state changed 2->3";
delay 5;
}
}
if ($state = 2) do= { delay 15 };
} while ($state = 2);
}
Все состояния ниже соответственно смещаются на +1. Заместо DNSip1, DNSip2 и VPNaddress подставляем нужные данные.

На остальных версиях — не обещаю, и простите за отсутствие ‘:’ перед командами. Вот в принципе и все, создано и отлажено на 6.26 и RB951G-2HnD.

Вот маленький пример, но лишь первой части: Он инспектирует, запущен ли этот скрипт, ну и дополнительно отсылает мне по почте IP адресок, когда тот изменяется. В моей конфигурации в связке с сиим скриптом работает еще один, который запускается по-расписанию раз в минутку.

Скрипт-мониторglobal FailoverDisabled;

if ( [len [/system script job find where script="Failover"]] = 0 and $FailoverDisabled != 1) do= {
do { execute script="Failover"; } on-error= { log info "$scriptName: Failed to execute Failover" };
}
Также, за счет расписания, при непредвиденных перезагрузках роутера скрипт будет автоматом запущен опять. Глобальной переменной можно отключить пуск скрипта Failover.

Скрипт Failover целикомglobal FailoverTimes;
global FailoverLastTime;
global FailoverLastBackTime;

local ifMain "ISP1";
local ifRes "ISP2";

local scriptName "Failover";
local state 0;
local pingNum 0;
local pingRes;
local routeDist;
local routeDist2;
local tmp;

local ip { x.x.x.x; y.y.y.y; z.z.z.z };
local pingSrcAddr 192.168.xx.yy;

if ( [len [/system script job find where script=$scriptName]] > 1) do= { error "single instance" };
delay 15;

set $routeDist [/interface l2tp-client get $ifMain default-route-distance];
set $routeDist2 [/interface ppp-client get $ifRes default-route-distance];
if ($routeDist < $routeDist2) do= {
if ( [/interface get $ifMain running] = true) do= { set $state 0; } else= { set $state 1; }
} else= {
if ( [/interface get $ifMain disabled] = true) do= { /interface enable $ifMain; }
if ($routeDist > $routeDist2 and [/interface get $ifRes disabled] = false) do= {
set $state 2;
} else= { set $state 3; }
}

log info "$scriptName: initial state $state";

do {
if ($state = 0) do= {
do {
if ($pingNum >= 3) do= { set $pingNum 0; }
if ([ping ($ip->$pingNum) count=1] = 0) do= {
set $pingRes [ping ($ip->0) count=2];
set $pingRes ($pingRes+[ping ($ip->1) count=2]);
set $pingRes ($pingRes+[ping ($ip->2) count=2]);
if ($pingRes = 0) do= {
set $FailoverLastTime "$[/system clock get date] $[/system clock get time]";
set $FailoverTimes ([tonum $FailoverTimes] + 1)
set $state 1;
log info "$scriptName: state changed 0->1";
}
}
set $pingNum ($pingNum + 1);
if ($state = 0) do= { delay 15 };
} while ($state = 0);
}
# endof if state = 0

if ($state = 1) do= {
if ( [/interface l2tp-client get $ifMain default-route-distance] > 10) do= {
/interface ppp-client set $ifRes default-route-distance=1;
}
/interface enable $ifRes;
beep frequency=2000 length=250ms;
delay 500ms;
beep frequency=2000 length=250ms;
delay 500ms;
delay 6;
/interface disable $ifMain;
set $routeDist ([/interface ppp-client get $ifRes default-route-distance] + 1);
/interface l2tp-client set $ifMain default-route-distance=$routeDist;
/interface enable $ifMain;
set $state 2;
log info "$scriptName: state changed 1->2";
}

if ($state = 2) do= {
do {
if ( [len [interface find where name=$ifMain and running] ] = 1) do= {
set $pingRes [ping ($ip->0) src-address=$pingSrcAddr count=2];
set $pingRes ($pingRes+[ping ($ip->1) src-address=$pingSrcAddr count=2]);
set $pingRes ($pingRes+[ping ($ip->2) src-address=$pingSrcAddr count=2]);
if ($pingRes > 0) do= {
set $state 3;
log info "$scriptName: state changed 2->3";
}
}
if ($state = 2) do= { delay 15 };
} while ($state = 2);
}
# endof if state = 2

if ($state = 3) do= {
/interface disable $ifRes;
set $routeDist ([/interface l2tp-client get $ifMain default-route-distance] + 1);
/interface ppp-client set $ifRes default-route-distance=$routeDist;
set $state 0;
set $FailoverLastBackTime "$[/system clock get date] $[/system clock get time]";
log info "$scriptName: state changed 3->0";
beep frequency=500 length=500ms;
}
# bad programming protection
delay 1;
} while= ( true ); habrahabr.ru

НЕТ КОММЕНТАРИЕВ

ОСТАВЬТЕ ОТВЕТ

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.