Линукс и 2+ провайдера. Advanced mode. Хитрости и тонкости. Подключение к интернет через нескольких провайдеров давно уже не редкость.
Раньше рассматриваемая тема называлась "линукс и два провайдера", но в суровой реальности провайдеров может быть и больше.
Также хочется вместо термина "провайдер" использовать "канал", поскольку некоторые люди путаются - "провайдер у меня один", а вот шлюзов (и адресов в различных подсетях) он предоставляет несколько (ну, так бывает нужно по разным мотивам).
Написано на эту тему также уже довольно много. Но есть что дополнить, поэтому приступим.
Вводная часть.
Итак, первое что требуется сделать для указания правил маршрутизации - это указать, к чему эти правила будут приводить. А приводить они должны к тому, что пакеты пойдут по разным шлюзам, такчто, вполне логично описать эти самые шлюзы.
Делается это с использованием набора пользовательских таблиц маршрутизации. Для удобства использования, им можно присваивать admin-friendly имена, что делается прописыванием строк в файл /etc/iproute2/rt_tables. Строки имеют вид
примерно такой:
100 northtelecom
101 southtelecom
Таблицы можно не именовать, а использовать во всех подкомандах команды ip безликие цифры, в этом случае ничего в файл rt_tables прописывать не потребуется. Также, часть документации таблицы именует именами вида "Т1", "Т2" что тоже, на мой взгляд, слабо удобно при просмотре правил.
Отлично, таблицы поименованы. Далее требуется указать шлюзы, на которые требуется отправлять траффик.
Обычно для этого прописывается пара команд в системный файл-скрипт, который отвечает за "подъем" маршрутизатора.
На PC-маршрутизаторе вполне можно для этих целей использовать непосредственно /etc/rc.local. Итак, команды указания маршрутов в интернет выглядят так:
ip route add default via northtelecom_gw table northtelecom
ip route add default via southtelecom_gw table southtelecom
Отлично! Мы создали две таблицы маршрутизации, внесли в них значения двух шлюзов для маршрутизации траффика.
Теперь эти таблицы надо начать применять. Обдуманное применение таблиц маршрутизации заключается в использовании команды "ip rule" и выглядит это примерно так:
ip rule add from northtelecom_ip lookup northtelecom
ip rule add from southtelecom_ip lookup southtelecom
На самом деле, это еще не совсем обдуманное применение, поскольку не указаны приоритеты правил (забегая вперед, уточним что они указываются использованием "ip rule add .... pref <priority>").
Посмотрим на получившийся результат:
debian:~# ip ru
0: from all lookup local
32764: from southtelecom_ip lookup southtelecom
32765: from northtelecom_ip lookup northtelecom
32766: from all lookup main
32767: from all lookup default
Что означает полученный вывод ?
Он означает, что пакет при выборе пути к месту назначения будет сначала проверен правилом 0, который отправит поиск маршрута в таблицу "local". Просмотр таблицы "local" проводится на предмет "не является ли получателем" локальная машина, широковещательный или сетевой адреса интерфейсов маршрутизатора. Соответствующими маршрутами заполняют эту таблицу команды конфигурации сетевых интерфейсов.
Далее, пакет будет проверен нашими двумя правилами 32764 и 32765, на предмет адреса отправителя, и, в случае совпадения, будет отправлен в соответствующую таблицу маршрутизации.
Если адрес отправителя не совпадет с southtelecom_ip или northtelecom_ip, поиск маршрута пойдет с использованием таблицы "main", а таблица "default" (пока еще?) дистрибутивно чиста. Как вы могли заметить, если маршрут не будет найден в таблице, на которую поиск будет завернут правилом, то поиски-проверки будут продолжены со следующего правила.
Таблица "main" - это основная таблица, используемая по-умолчанию всеми командами-программами, связанными с маршрутизацией. При поднятии интерфейсов в неё прописываются маршруты к подсетям интерфейсов, стартовые скрипты также заносят в эту таблицу значение шлюза по-умолчанию. Использование команды "route add" аминистратором также может дополнить таблицу машрутами. В общем случае, таблица "main" всегда содержит подходящий для пакета маршрут.
Закончим вводную часть и приступим к "первому сложному моменту".
Некоторые источники рекомендуют использовать еще и примерно такие команды при описании таблиц провайдеров:
ip route add northtelecom_net dev northtelecom_if src northtelecom_ip table northtelecom
ip route add southtelecom_net dev southtelecom_if src southtelecom_ip table southtelecom
Во-первых, хочется заметить что можно упростить команду, опустив уточнение "src northtelecom_ip", поскольку таблица будет выбираться путем сравнения IP-адреса отправителя пакета с некоторым значением, т.е. адрес уже будет задан.
А во-вторых, объясню более подробно, зачем нужны соответствующие маршруты.
Без дополнительной команды таблица northtelecom содержит следующие маршруты (да, пока, только один маршрут):
debian:~# ip ro sh table northtelecom
default via northtelecom_gw dev northtelecom_if
В этом случае, пакетик, который отправляется с адреса northtelecom_ip к одному из хостов в сети провайдера northtelecom будет послан не непосредственно к этому хосту, а отправлен на машрутизатор провайдера. Конечно же, лучше отправлять пакет непосредственно к хосту, для чего требуется соответствующее уточнение таблиц маршрутизации командами
ip route add northtelecom_net dev northtelecom_if table northtelecom
ip route add southtelecom_net dev southtelecom_if table southtelecom
Вроде бы всё здорово, пакеты в непосредственно присоединенные сети провайдеров идут напрямую к хостам. Но неужели у нас отсутствует локальная сеть ? Чаще всего она у нас есть. Но что же произойдет, если из локальной сети будет произведено обращение к одному из адресов northtelecom_ip или southtelecom_ip ?
Посмотрим еще раз на вывод команды "ip rule"
debian:~# ip ru
0: from all lookup local
32764: from southtelecom_ip lookup southtelecom
32765: from northtelecom_ip lookup northtelecom
32766: from all lookup main
32767: from all lookup default
В соответствии с правилами, маршрут отправки пакетов, исходящих с адресов northtelecom_ip или southtelecom_ip, будет производиться в таблицах northtelecom и southtelecom, что в конечном счете приведет к отправке пакета в сторону шлюза провайдера. Поскольку это слегка не то, что нам нужно, придется дополнить таблицы еще парочкой маршрутов.
ip route add local_net dev local_if table northtelecom
ip route add local_net dev local_if table southtelecom
А если у нас есть еще и внутренние маршрутизаторы, и подсетки, скрытые за ними, то всё аналогично, требуется добавление каждой подсетки в каждую таблицу провайдеров.
ip route add local_net2 via local_router2 table northtelecom
ip route add local_net3 via local_router2 table northtelecom
ip route add local_net2 via local_router2 table southtelecom
ip route add local_net3 via local_router2 table southtelecom
Много правил, много команд, легко запутаться, потерять, забыть что-то прописать ... Неужели нельзя проще ?
Как показывает практика, можно, но не всегда.
Легкий трюк, который в большинстве случаев помогает, выглядит так:
ip rule add lookup main pref 1000
ip ro add default via (northtelecom|southtelecom)_gw table default
ip ro delete default table main
Что он означает? Посмотрим на таблицу правил командой "ip rule" (да, я уже перенабрал набор правил с указанием приоритетов)
debian:~# ip ru
0: from all lookup local
1000: from all lookup main
3000: from southtelecom_ip lookup southtelecom
3010: from northtelecom_ip lookup northtelecom
32766: from all lookup main
32767: from all lookup default
Теперь для исходящих пакетов до поиска маршрутов в таблицах провайдеров будет осуществлен поиск маршрута в таблице "main", а она, как было написано выше, содержит максимум информации о маршрутах, в том числе и маршруты в локальные сети, как присоединенные напрямую, так и через внутренние маршрутизаторы. Чтобы поиск маршрута в таблице "main" не закончился отправкой пакета в "шлюз по умолчанию", мы перенесем его указание в таблицу "default".
Всё, теперь локальные и присоединенные сети вносить в таблицы "southtelecom" и "northtelecom" необходимости нет. Достаточно эти таблицы сформировать командами
ip route add northtelecom_net dev northtelecom_if src northtelecom_ip table northtelecom
ip route add southtelecom_net dev southtelecom_if src southtelecom_ip table southtelecom
Но этот легкий трюк имеет некоторый побочный эффект, который иногда ограничивает его прямое применение.
Допустим, что от хоста из сети southtelecom_net идет обращение к northtelecom_ip. Поскольку теперь маршрут приоритетно ищется в таблице main, то ответные пакеты с адреса northtelecom_ip будут отправлены через интерфейс southtelecom_if непосредственно к обратившемуся хосту. C одной стороны, это можеть быть здорово, пакеты пойдут кратчайшим путем, мы можем ожидать более высокой скорости передачи ответа и массы других полезных вещей, но с другой стороны это может оказаться не тем, чего от нас ожидал наш провайдер southtelecom, который ограничил нас на нашем порту коммутатора парой MAC+IP...
В общем, вышеописаный трюк - это трюк для слегка ленивых. Оптимальным методом решения задачи является создание третьей пользовательской таблицы "localnets" и указание в ней части маршрутов (маршрутов к локальным сетям) из таблицы main. Приведу набор команд, формирующих решение, а анализ того, как будет определяться маршрут для пакетов оставлю пытливым умам читателей.
ip route add local_net dev local_if table localnets
ip route add local_net2 via local_router2 table localnets
ip route add local_net3 via local_router2 table localnets
ip route add southtelecom_net dev southtelecom_if table southtelecom
ip route add default via southtelecom_gw table southtelecom
ip route add northtelecom_net dev northtelecom_if table northtelecom
ip route add default via northtelecom_gw table northtelecom
ip rule add lookup table localnets pref 1000
ip rule add from southtelecom_ip lookup southtelecom pref 3000
ip rule add from northtelecom_ip lookup northtelecom pref 3010
debian:~# ip ru
0: from all lookup local
1000: from all lookup localnets
3000: from southtelecom_ip lookup southtelecom
3010: from northtelecom_ip lookup northtelecom
32766: from all lookup main
32767: from all lookup default
В качестве завершения, хочется отметить то, для чего между правилом 1000 и правилами 30хх оставлен свободный диапазон.
Используя этот диапазон, очень удобно разделять, по какому из каналов пойдет траффик хостов локальной сети (без специальных правил он пойдет с использованием шлюза по умолчанию из таблицы "main") или специально маркированный траффик (описано во второй части).
Например, это делается так:
ip ru add from 192.168.0.100 lookup southtelecom pref 2000
ip ru add from 192.168.0.128/25 lookup northtelecom pref 2010
Названия провайдеров "southtelecom" и "northtelecom" являются вымышленными, совпадения, если таковые имеются, случайны.