Около 2,5 лет назад писал статью на тему автоматического переключения канала Интернет на резервный. Скрипт, конечно, и по сей день работает «на отлично», но его внешний вид и некоторые нюансы…
Итак, встала задача улучшить скрипт, максимально устранив побочные эффекты. Что ж, приступим.
В нашем распоряжении Mikrotik RB850Gx2, для которого мы будем писать скрипт (его работоспособность также проверена на моделях RB450G и RB951G-2HnD).
Присвойте скрипту имя, например, script-check-inet
Что же будем в нем использовать?
Итак, для начала определим необходимые переменные:
:local firstInterface "pppoe-rostelecom"; :local secondInterface "eth2-MTS"; :local pingTo "8.8.8.8"; :local prefix ">>> ";
где:
$firstInterface — имя нашего PPPoE-соединения основной линии связи.
$secondInterface — имя нашего Ethernet-соединения резервной линии.
$pingTo — IP-адрес ресурса, который будем «пинать».
$prefix — будем использовать для вывода логов, чтобы нам было виден нужный текст.
При изменении дистанции роута без очистки ARP таблицы будут возникать ошибки и так как в нашем коде есть несколько мест, откуда необходимо вызвать очистку данной таблицы, оформим код в функцию:
:local clearArp do={ :local dumplist [/ip arp find] :foreach i in=$dumplist do={ /ip arp remove $i } :log info ($prefix . "ARP cleaned"); }
Далее, для уменьшения строк кода, мы используем конструкцию `/ip route {}`, внутри которой нельзя вызывать другие корневые команды, например, `/interface pppoe-client…`. Поэтому мы создадим еще одну функцию, отвечающую за переподключение PPPoE-соединения:
# Function to reconnect PPPoE connection :local reconnectPPPoE do={ /interface pppoe-client set $nameInterface disable=yes; :delay 1s; /interface pppoe-client set $nameInterface disable=no; }
В нашем случае в качестве основной линии используется «Ростелеком», который имеет «фишку» в периодическом отключении Интернета при том, что внутренняя их линия работает без сбоев. Это достигли опытным путем, используя SIP-сервер от Ростелеком, который так ни разу и не упал.
В общем, создали функцию переподключения соединения. Идем дальше.
Перед основной частью добавим «защиту от дураков». Вдруг у нас интерфейс выключен, или дистанция не правильная…
Итак, проверяем активны ли интерфейсы и если нет — активируем их:
# Check FIRST interface /interface pppoe-client { :if ( [get $firstInterface disable] = true) do={ set $firstInterface disable=no; :delay 5s; } } # Check SECOND interface /interface ethernet { :if ( [get $secondInterface disable] = true) do={ set $secondInterface disable=no; } }
Но это еще не все! Дальше мы проверим выставленные дистанции роутов:
/ip route { # Set objects to variables :set firstInterface [find gateway=$firstInterface]; :set secondInterface [find gateway=$secondInterface]; # Check routes :if ( [get $firstInterface distance] != 2 ) do={ set $firstInterface distance=2; :log info ($prefix . "Distance for " . [get $firstInterface gateway] . " corrected"); } :if ( [get $secondInterface distance] != 1 && [get $secondInterface distance] != 3) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . [get $secondInterface gateway] . " corrected"); } # ... body ... }
Так как основная часть работает в `/ip route`, мы добавили «защиту от дураков» в ее начало.
В нашем случае используем 2 роута с `dst. address` равным `0.0.0.0/0`, шлюзом по имени интерфейса и «какой-то» дистанцией.
Что делает скрипт:
Вначале он присваивает переменным объекты для каждого из роутов, чтобы меньше кода писать.
Затем, проверяем дистанцию основного роута (Ростелеком). Мы используем значение «2».
После этого проверяем дистанцию запасного роута — он может принимать значение дистанций либо «1», либо «3». Это упрощает задачу с изменением дистанций, так как нет необходимости изменять дистанцию основного канала.
Проверили дистанции и выставили нужные. Что дальше?
Проверка пингом
Так как интерфейс может быть неактивен, то в первоначальном условии используем конструкцию не только проверки пинга, но и статуса интерфейса:
/ip route { # ... :if ( [get $firstInterface active] = false or [/ping $pingTo interface=[get $firstInterface gateway] count=5] = 0) do={ # ... } # ... }
То есть, если у интерфейс выключен или ни один пакет пинга не прошел — считаем, что Интернет отсох:
:if ( [get $firstInterface active] = false or [/ping $pingTo interface=[get $firstInterface gateway] count=5] = 0) do={ :log info ($prefix . "FIRST NO INTERNET!!!"); # Change distance :if ( [get $secondInterface distance] != 1 ) do={ set $secondInterface distance=1; :log info ($prefix . "Distance for " . [get $secondInterface gateway] . " changed"); $clearArp; } $reconnectPPPoE nameInterface=[get $firstInterface gateway]; }
Вначале выводим лог, что Инета нет, после чего изменяем дистанцию резервного канала и чистим таблицу ARP, вызвав функцию.
После этого вызываем функцию переподключения PPPoE-соединения. Так как имя соединения мы указываем в одном месте (чтобы не плодить переменные), функция написана с учетом принятия переменной, содержащей имя интерфейса. Таким образом, при вызове функции в параметр `nameInterface` мы передаем имя нужного нам PPPoE-интерфейса.
Интернет «появился», или как все вернуть назад
На этом-то этапе мы и заюзаем функцию `if else`:
/ip route { # ... :if ( [get $firstInterface active] = false or [/ping $pingTo interface=[get $firstInterface gateway] count=5] = 0) do={ # ... } else={ :log info ($prefix . "FIRST INTERNET CONNECTED"); # Change distance :if ( [get $secondInterface distance] != 3 ) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . [get $secondInterface gateway] . " changed"); $clearArp; } } # ... }
Как и в первой части функции, выводим сообщение о доступности сети, после чего изменяем дистанцию резервного канала и чистим таблицу ARP.
Как запускать
В поле `Name` введите имя записи, чтобы не запутаться.
Поле `Start Time` я выставил `00:00:00` для запуска ровно в полночь.
Интервал — 30 секунд
В поле `On Event` вписываем имя нашего скрипта — `script-check-inet`
И жмем «ОК».
Вот, собственно, и все!
Ниже под спойлером приведен полный код скрипта.
# Set local variables :local firstInterface "pppoe-rostelecom"; :local secondInterface "eth2-MTS"; :local pingTo "8.8.8.8"; :local prefix ">>> "; # Function to cleaning ARP table :local clearArp do={ :local dumplist [/ip arp find] :foreach i in=$dumplist do={ /ip arp remove $i } :log info ($prefix . "ARP cleaned"); } # Function to reconnect PPPoE connection :local reconnectPPPoE do={ /interface pppoe-client set $nameInterface disable=yes; :delay 1s; /interface pppoe-client set $nameInterface disable=no; } :log info ($prefix . "START PING to $pingTo"); # Check FIRST interface /interface pppoe-client { :if ( [get $firstInterface disable] = true) do={ set $firstInterface disable=no; :delay 5s; } } # Check SECOND interface /interface ethernet { :if ( [get $secondInterface disable] = true) do={ set $secondInterface disable=no; } } /ip route { # Set objects to variables :set firstInterface [find gateway=$firstInterface]; :set secondInterface [find gateway=$secondInterface]; # Check routes :if ( [get $firstInterface distance] != 2 ) do={ set $firstInterface distance=2; :log info ($prefix . "Distance for " . [get $firstInterface gateway] . " corrected"); } :if ( [get $secondInterface distance] != 1 && [get $secondInterface distance] != 3) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . [get $secondInterface gateway] . " corrected"); } # Check Internet :if ( [get $firstInterface active] = false or [/ping $pingTo interface=[get $firstInterface gateway] count=5] = 0) do={ :log info ($prefix . "FIRST NO INTERNET!!!"); # Change distance :if ( [get $secondInterface distance] != 1 ) do={ set $secondInterface distance=1; :log info ($prefix . "Distance for " . [get $secondInterface gateway] . " changed"); $clearArp; } $reconnectPPPoE nameInterface=[get $firstInterface gateway]; } else={ :log info ($prefix . "FIRST INTERNET CONNECTED"); # Change distance :if ( [get $secondInterface distance] != 3 ) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . [get $secondInterface gateway] . " changed"); $clearArp; } } } :log info ($prefix . "END PING to $pingTo");
ссылка на оригинал статьи https://habrahabr.ru/post/279567/
Добавить комментарий