Содержимое: как сделать так, чтобы компьютер отвечал в интернете на все свои IP-адреса по всем своим интерфейсам, каждый из которых имеет шлюз по умолчанию. Касается и серверов, и десктопов.
Ключевые слова: policy routing, source based routing
Лирика: Есть достаточно статей про policy routing в Linux. Но они чаще всего разбирают общие, более тонкие и сложные случаи. Я же разберу тривиальный сценарий следующего вида:
![](https://habrastorage.org/r/w1560/files/5e6/b09/738/5e6b0973886b4ab689b8acea335ca0da.png)
Нашему компьютеру (серверу) доступно три интерфейса. На каждом интерфейсе шлюз ему выдал IP (статикой или по dhcp, не важно) и сказал «весь трафик шли мне».
Если мы оставим эту конфигурацию как есть, то будет использоваться принцип «кто последний встал, того и дефолтный шлюз». На картинке выше, если последним поднимется нижний интерфейс (241), то в него будет отправляться весь трафик. Если к нашему серверу придёт запрос на первый интерфейс (188), то ответ на него всё равно пойдёт по нижнему. Если у маршрутизатора/провайдера есть хотя бы минимальная защита от подделки адресов, то ответ просто дропнут, как невалидный (с точки зрения 241.241.241.1 ему прислали из сети 241.241.241.0/24 пакет с src 188.188.188.188, чего, очевидно, быть не должно).
Другими словами, в обычном варианте будет работать только один интерфейс. Чтобы сделать ситуацию хуже, если адреса получены по dhcp, то обновление аренды на других интерфейсах может перезаписать шлюз по умолчанию, что означает, что тот интерфейс, который работал, работать перестанет, а начнёт работать другой интерфейс. Удачной стабильной работы вашему серверу, так сказать.
Общее решение — policy routing. Довольно объёмная и интересная штука позволяющая выделывать всевозможные чудеса. Из этих чудес мы (в рамках этой статьи) оставим только одно: «отсылать каждому маршрутизатору трафик с „его“ интерфейса». То есть классический source-based routing в самой примитивной форме.
Обзор решения с высоты птичьего полёта:
Мы задаём три варианта маршрутизации трафика: «всё в eth0», «всё в eth1», «всё в eth2», дальше формулируем правила: трафик с IP первого интерфейса отправлять через первый вариант, трафик со второго IP — через второй вариант, третий IP — через третий.
В результате мы получаем такую конструкцию:
Краткое содержимое:
Утилита ip (её официальное название iproute2) имеет специальную команду — ip rule для управления policy routing. А добавление маршрутов (ip route add) может принимать аргумент с названием таблицы. Ядро ничего про названия таблиц не знает, а вот iproute2 требует использования имени из своего конфига —
Мы назовём наши таблицы маршрутизации eth0-route, eth1-route, eth2-route. Возьмём случайные символичные номера — 100, 101, 102.
Для каждой таблицы зададим маршрут по умолчанию (можно и не по умолчанию — фантазия в ваших руках). Эти правила ничего не сломают на сервере и их можно делать на живую. Пока мы не скажем использовать эти таблицы — это всего лишь байтики в памяти и они на работу маршрутизации не влияют (то есть мы не останемся внезапно без коннективити в середине процесса).
Теперь интимный момент: внимательно перечитать написанное. После включения правил опечатка в таблице может оставить сервер без коннективити.
Всё. С этого момента сервер начнёт отвечать по всем трём адресам. Приятная новость: policy routing имеет более высокий приоритет, чем dhcp'шный default route, так что обновление аренды не сломает маршрутизацию.
Неприятная новость: если при обновлении аренды вам выдадут другой адрес, то он перестанет работать. Это можно исправить либо сделав правило с маской (
(debian/ubuntu)
Отличное место для этих правил — в /etc/network/interfaces. Для нужного интерфейса пишем:
Замечание о маршрутизации: source routing в общем случае открывает уязвимость в системе (вам шлют пакет с фальшивым source, а вы его передаёте дальше, как будто так и надо), но эта узявимость уровня directly connected. Кроме того, если вы ограничиваетесь только своим адресом в from в правилах, то линукс такого не пропустит, плюс вам надо иметь включенную маршрутизацию. На обычном сервере такая конфигурация совершенно безопасна и довольно надёжна.
Замечание о load-balancing: Прописав все три адреса в A-записи DNS-зоны, вы получите бесплатный round-robin по всем трём интерфейсам без бондинга и поэтесс. Так как обрабатываться они будут одним и тем же сервером и одним и тем же приложением, это позволит балансировать даже stateful-сессии.
Ключевые слова: policy routing, source based routing
Лирика: Есть достаточно статей про policy routing в Linux. Но они чаще всего разбирают общие, более тонкие и сложные случаи. Я же разберу тривиальный сценарий следующего вида:
![](https://habrastorage.org/files/5e6/b09/738/5e6b0973886b4ab689b8acea335ca0da.png)
Нашему компьютеру (серверу) доступно три интерфейса. На каждом интерфейсе шлюз ему выдал IP (статикой или по dhcp, не важно) и сказал «весь трафик шли мне».
Если мы оставим эту конфигурацию как есть, то будет использоваться принцип «кто последний встал, того и дефолтный шлюз». На картинке выше, если последним поднимется нижний интерфейс (241), то в него будет отправляться весь трафик. Если к нашему серверу придёт запрос на первый интерфейс (188), то ответ на него всё равно пойдёт по нижнему. Если у маршрутизатора/провайдера есть хотя бы минимальная защита от подделки адресов, то ответ просто дропнут, как невалидный (с точки зрения 241.241.241.1 ему прислали из сети 241.241.241.0/24 пакет с src 188.188.188.188, чего, очевидно, быть не должно).
Другими словами, в обычном варианте будет работать только один интерфейс. Чтобы сделать ситуацию хуже, если адреса получены по dhcp, то обновление аренды на других интерфейсах может перезаписать шлюз по умолчанию, что означает, что тот интерфейс, который работал, работать перестанет, а начнёт работать другой интерфейс. Удачной стабильной работы вашему серверу, так сказать.
Решение
Общее решение — policy routing. Довольно объёмная и интересная штука позволяющая выделывать всевозможные чудеса. Из этих чудес мы (в рамках этой статьи) оставим только одно: «отсылать каждому маршрутизатору трафик с „его“ интерфейса». То есть классический source-based routing в самой примитивной форме.
Обзор решения с высоты птичьего полёта:
Мы задаём три варианта маршрутизации трафика: «всё в eth0», «всё в eth1», «всё в eth2», дальше формулируем правила: трафик с IP первого интерфейса отправлять через первый вариант, трафик со второго IP — через второй вариант, третий IP — через третий.
В результате мы получаем такую конструкцию:
source: 188.188.188.188 | в таблицу eth0-route | default via 188.188.1 | via eth0 |
source: 75.75.75.75 | в таблицу eth1-route | default via 75.75.75.1 | via eth1 |
source: 241.241.241.241 | в таблицу eth2-route | default via 241.241.241.1 | via eth2 |
Краткое содержимое:
- Настроить утилиту iproute2 с помощью конфига (внезапно, у неё есть конфиг!) — дать имена трём таблицам маршрутизации
- Настроить маршруты в трёх таблицах маршрутизации — точнее, задать дефолтные машруты
- Указать правила, по которым трафик будет распределяться по трём таблицам маршрутов
Шаги подробнее
Утилита ip (её официальное название iproute2) имеет специальную команду — ip rule для управления policy routing. А добавление маршрутов (ip route add) может принимать аргумент с названием таблицы. Ядро ничего про названия таблиц не знает, а вот iproute2 требует использования имени из своего конфига —
/etc/iproute2/rt_tables
(которая сопоставляет имена абстрактным числам, которые понимает ядро). Значения «0» и значения ниже 255 (254, 253) заняты, остальные можно использовать.Настройка iroute2
Мы назовём наши таблицы маршрутизации eth0-route, eth1-route, eth2-route. Возьмём случайные символичные номера — 100, 101, 102.
echo 100 eth0-route >>/etc/iproute2/rt_tables echo 101 eth1-route >>/etc/iproute2/rt_tables echo 102 eth2-route >>/etc/iproute2/rt_tables
Настройка маршрутов в трёх таблицах маршрутизации
Для каждой таблицы зададим маршрут по умолчанию (можно и не по умолчанию — фантазия в ваших руках). Эти правила ничего не сломают на сервере и их можно делать на живую. Пока мы не скажем использовать эти таблицы — это всего лишь байтики в памяти и они на работу маршрутизации не влияют (то есть мы не останемся внезапно без коннективити в середине процесса).
ip route add default via 188.188.188.1 dev eth0 table eth0-route ip route add default via 75.75.75.1 dev eth1 table eth1-route ip route add default via 241.241.241.1 dev eth2 table eth2-route
Теперь интимный момент: внимательно перечитать написанное. После включения правил опечатка в таблице может оставить сервер без коннективити.
Включение policy routing
ip rule add from 188.188.188.188 lookup eth0-route ip rule add from 75.75.75.75 lookup eth1-route ip rule add from 241.241.241.241 lookup eth2-route
Всё. С этого момента сервер начнёт отвечать по всем трём адресам. Приятная новость: policy routing имеет более высокий приоритет, чем dhcp'шный default route, так что обновление аренды не сломает маршрутизацию.
Неприятная новость: если при обновлении аренды вам выдадут другой адрес, то он перестанет работать. Это можно исправить либо сделав правило с маской (
from 241.241.241.0/24
), либо прибив адрес гвоздями в конфиге dhcp-сервера (вообще, серверам не принято выдавать динамические адреса по DHCP...)Отлитие в конфигах
(debian/ubuntu)
Отличное место для этих правил — в /etc/network/interfaces. Для нужного интерфейса пишем:
iface eth0 inet static address 188.188.188.188 netmask 255.255.255.0 broadcast: 188.188.188.255 post-up ip route add default via 188.188.188.1 dev eth0 table eth0-route post-up ip rule add from 188.188.188.188 lookup eth0-route
Примечания
Замечание о маршрутизации: source routing в общем случае открывает уязвимость в системе (вам шлют пакет с фальшивым source, а вы его передаёте дальше, как будто так и надо), но эта узявимость уровня directly connected. Кроме того, если вы ограничиваетесь только своим адресом в from в правилах, то линукс такого не пропустит, плюс вам надо иметь включенную маршрутизацию. На обычном сервере такая конфигурация совершенно безопасна и довольно надёжна.
Замечание о load-balancing: Прописав все три адреса в A-записи DNS-зоны, вы получите бесплатный round-robin по всем трём интерфейсам без бондинга и поэтесс. Так как обрабатываться они будут одним и тем же сервером и одним и тем же приложением, это позволит балансировать даже stateful-сессии.