30 сентября, 2015

Mikrotik WAN Failover и «повисающий» SIP

Не так давно довелось столкнуться с довольно тривиальной ситуацией, решений для которой в рунете сходу найти, к моему удивлению, не удалось. Ситуация: есть офис, раздачей интернетов в котором занимается роутер под управлением RouterOS. В офисе есть некоторое количество SIP-телефонов, а также резервный канал доступа в интернет на случай проблем с основным.

И вроде ничего не предвещало, как говорится, но вылез один очень неприятный момент: при переключении на резервный канал те самые SIP-телефоны напрочь отказывались заново устанавливать соединение с PBX (находящейся за пределами офиса), что заставляло обитателей того офиса вручную перезагружать каждый телефон, да еще и стараться побыстрее заметить, что телефоны отвалились, чтоб не пропустить какой-нибудь важный звонок.

В чем же дело? Всё довольно просто: переключение на резервный канал было реализовано с помощью двух маршрутов с destination 0.0.0.0/0 и разным значением distance в зависимости от приоритета, а также с настроенными соответствующим образом опциями check-gateway. Но переключение переключением, а вот таблицу NAT роутер при этом не очищал, т.к. его об этом никто и не просил, что приводило к «повисанию» UDP-соединений на том дефолте, который по несчастью отвалился.

Избежать подобной ситуации можно несколькими способами (мне попадались даже разного рода скриптовые ады, отключающие-включающие connection tracking, и прочие душераздирающие зрелища), но лично я решил сделать решение без ненужного скриптинга, максимально простое и лаконичное.

Итак, из руководства к RouterOS нам известно, что check-gateway проверяет доступность шлюза каждые 10 секунд, и если ответа на проверочный запрос не поступило в течение 10 секунд, то проверка считается неудачной. А после двух таких таймаутов подряд шлюз помечается как недоступный, тут-то и происходит переключение на резерв.

Для простоты будем считать, что период переключения каналов у нас равен 30 секундам. Тогда создадим netwatch, который будет пингать нашу телефонную станцию с интервалом в 15 секунд (половина от тех 30, чтоб наверняка). И да, разумеется наша телефонная станция должна в нормальной ситуации отвечать на пинг:

/tool netwatch add host=адрес_нашей_PBX interval=15s timeout=1s up-script="/ip firewall connection remove [find where dst-address~\"5060\"]" down-script="/ip firewall connection remove [find where dst-address~\"5060\"]"

Теперь, если вдруг у нас пропадет пинг до IP-АТС, из таблицы соединений будет вычищено всё, что шло на порт 5060. Но вообще я бы рекомендовал написать в up/down скрипт что-нибудь такое:

/ip firewall connection remove [find where src-address~"192.168.88."]

Под 192.168.88 подразумевается диапазон локальных офисных ip-адресов. Это сбросит все клиентские соединения (при этом не затронув те, что инициировал сам роутер, например L2TP-туннель до провайдера или еще что-нибудь), что в свою очередь сделает процесс переключения на резервный канал и обратно более «бесшовным» для пользователей, т.к. коннекты будут оборваны и пакеты точно не будут пытаться идти по умершему маршруту.

Не стоит беспокоиться насчет того, что такое решение может рвать соединения излишне часто: netwatch-скрипты выполняются по одному разу (единожды, когда пингуемый хост стал виден, и точно так же единожды, когда из виду пропал), а не циклично.
Данное решение было всячески оттестировано с пристрастием (и без), и никаких проблем выявлено не было.