Универсальный скрипт переключения 2-х каналов интернета Mikrotik

от автора

Около 2,5 лет назад писал статью на тему автоматического переключения канала Интернет на резервный. Скрипт, конечно, и по сей день работает «на отлично», но его внешний вид и некоторые нюансы…

Итак, встала задача улучшить скрипт, максимально устранив побочные эффекты. Что ж, приступим.

image

В нашем распоряжении Mikrotik RB850Gx2, для которого мы будем писать скрипт (его работоспособность также проверена на моделях RB450G и RB951G-2HnD).

Для начала, добавим новый скрипт

image

Присвойте скрипту имя, например, 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.

Как запускать

Создадим запись в шедулере

image

image

В поле `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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *