Усложним предыдущий пример. Требуется обеспечить доступ в Интернет при следующих условиях:
Используется устройство NSG–1700 с двумя опциями LTE/3G. Как и в предыдущем примере, работоспособность каждого из каналов контролируется с помощью ping. Особенность данной задачи в том, что столь сложный и разветвлённый алгоритм выбора канала связи (к тому же, как правило, уникальный для каждого заказчика) уже невозможно описать последовательной иерархией метрик или каких-либо иных критериев. Чтобы предусмотреть все возможные варианты, придётся ниже написать полноценный скрипт с большим числом условных переходов.
Предварительные настройки:
Для отправки пингов на 3 контрольных хоста (каждого из операторов) по фиксированным маршрутам удобнее использовать маршрутизацию на основе установленных правил и раздельные таблицы маршрутизации. Таблица маршрутизации для Ethernet заполняется при помощи стартового скрипта (можно вместо этого использовать протокол static в механизме динамической маршрутизации). Интерфейсы LTE получают маршруты по DHCP и пишут их каждый в свою таблицу.
port : eth0 : : adm-state = "up" : : ifAddress : : : prefix = "192.168.1.1/24" : eth1 : : ifAddress : : : prefix = "123.45.67.89/24" : m1 : : type = "lte" : : ifAddress : : : configurable = "dhcp" : : : dhcp-options : : : : default-gw-table = 2 : m2 : : type = "lte" : : provider : : : main : : : : attempts = 0 : : : aux : : : : attempts = 1 : : ifAddress : : : configurable = "dhcp" : : : dhcp-options : : : : default-gw-table = 3 services : daemons : : table1setup : : : adm-state = "up" : : : auto-restart = "false" : : : command = "ip route add default via 123.45.67.90 table 1"
Далее, каждый из пингов отправляется по своей таблице; если это не удалось сделать (соединение LTE отсутствует, и в этой таблице нет маршрутов), то следующим правилом он уничтожается, чтобы случайно не маршрутизировался по остальным правилам куда не надо.
ip : rule : : 1 : : : to = "123.45.67.77/32" : : : table = "1" : : 2 : : : to = "123.45.67.77/32" : : : type = "blackhole" : : 3 : : : to = "34.12.56.98/32" : : : table = "2" : : 4 : : : to = "34.12.56.98/32" : : : type = "blackhole" : : 5 : : : to = "54.67.89.12/32" : : : table = "3" : : 6 : : : to = "54.67.89.12/32" : : : type = "blackhole"
Если устройство пропускает через себя транзитный трафик, необходим NAT на каждом из интерфейсов. Если весь трафик локальной сети должен уходить только в туннель, то это не требуется (равно как и очистка таблиц NAT при помощи conntrack -F ниже в скриптах).
ip : nat : : POSTROUTING : : : 1 : : : : out-interface = "eth1" : : : : target = "SNAT" : : : : to-source = "123.45.67.89" : : : 2 : : : : out-interface = "m1" : : : : target = "MASQUERADE" : : : 3 : : : : out-interface = "m2" : : : : target = "MASQUERADE"
Скрипт для выбора канала связи, о котором говорилось выше, удобно оформить в виде генератора событий. В рамках обработчика событий он будет изображать из себя некий виртуальный датчик с именем channel и возможными состояниями nil, eth, m1 и m2, соответственно. Поскольку этот громоздкий скрипт удобнее писать по строкам и с отступами, то для начала, создаём такую ветвь конфигурации:
services : event-handler : : event-generators : : : mypoopyrouteselector : : : : enable = true : : : : command = "\n"
Поле, содержащее последовательности \n или \r, при редактировании в Web-интерфейсе преобразуется в двумерное текстовое окно; при работе в консольной командной оболочке его можно открыть в текстовом редакторе при помощи команды command=_edit или command=_fullscreen. Вставляем туда сам скрипт в удобочитаемом виде:
PING0=123.45.67.77; PING1=34.12.56.98; PING2=54.67.89.12; COUNT0=2; COUNT1=2; COUNT2=2; TO0=3; TO1=5; TO2=5; echo channel:nil; CHANNEL=NIL; while true; do if ping -c$COUNT0 -i$TO0 $PING0 > /dev/null; then echo channel:eth; CHANNEL=ETH; else if [ x$CHANNEL = xM2 ] > /dev/null; then if ping -c$COUNT2 -i$TO2 $PING2 > /dev/null; then echo channel:m2; CHANNEL=M2; else if ping -c$COUNT1 -i$TO1 $PING1 > /dev/null; then echo channel:m1; CHANNEL=M1; else echo channel:nil; CHANNEL=NIL; fi; fi; else if ping -c$COUNT1 -i$TO1 $PING1 > /dev/null; then echo channel:m1; CHANNEL=M1; else if ping -c$COUNT2 -i$TO2 $PING2 > /dev/null; then echo channel:m2; CHANNEL=M2; else echo channel:nil; CHANNEL=NIL; fi; fi; fi; fi; done;
Число пингов и интервалы между ними можно варьировать, чтобы минимизировать время реагирования, но избежать ложных срабатываний. При выходе из редактора получится длинная трудночитаемая строка, но при последующем редактировании она снова преобразуется в двумерный текст.
Обработчик событий реагирует на события этого датчика, т.е. на переход из одного состояния в другое, следующим образом:
services : event-handler : : 1 : : : virt-sensor = "channel" : : : prev-state = "other" : : : state = "eth" : : : script = "ip route del default; ip route add default dev eth1 via 123.45.67.90; conntrack -F; logger -tCHANNELS switched to Ethernet;" : : 2 : : : virt-sensor = "channel" : : : prev-state = "other" : : : state = "m1" : : : script = "ip route del default; ip route add default dev $(nsgsh -q .system.get-iface-name=m1); conntrack -F; logger -tCHANNELS switched to LTE-1;" : : 3 : : : virt-sensor = "channel" : : : prev-state = "other" : : : state = "m2" : : : script = "ip route del default; ip route add default dev $(nsgsh -q .system.get-iface-name=m2); conntrack -F; logger -tCHANNELS switched to LTE-2;" : : 4 : : : virt-sensor = "channel" : : : prev-state = "other" : : : state = "nil" : : : script = "ip route del default; logger -tNO CHANNELS available!!!;"
Скрипты здесь можно записывать в строку или, как это было сделано выше, в несколько строк. Для наглядности добавлена запись событий в syslog.
Дополнительные замечания:
Задавать маршрут через широковещательный интерфейс только с помощью его имени (dev) вместо явного указания следующего шлюза (via) — не очень корректно, но для существующих интерфейсов LTE в устройствах NSG это допустимо. Да и то приходится определять имя интерфейса динамически, поскольку оно не тождественно имени физического порта и может изменяться в зависимости от конкретного чипа LTE и порядка падения/поднятия интерфейсов. Более корректно было бы определить шлюз по умолчанию, назначенный для выбранного интерфейса LTE (а также интерфейса Ethernet, если он настраивается динамически по DHCP) и указать его явным образом. В ближайших версиях NSG Linux будет предложен простой механизм для этой цели.
В более простых конфигурациях (Ethernet+LTE или 2×LTE) данную конфигурацию можно упростить, но, в принципе, она работоспособна и так — маршрут в отсутствующий интерфейс просто никогда не будет назначаться.
Явное указание APN, имени пользователя и пароля в современных сотовых сетях не требуется. Если они указаны оператором явно (сети 2G, некоторые устаревшие сети 3G или услуга VPN сотовых операторов), они указываются в узлах provider.main и provider.aux, соответственно.
Обязательно задание пароля для доступа к устройству.
В целях безопасности настоятельно рекомендуется также: