Стандартная маршрутизация в Linux принимает решение о пути пакета на основе IP-адреса назначения. Ядро просматривает таблицу маршрутизации, находит наиболее подходящий маршрут и отправляет пакет через указанный интерфейс на шлюз. Source-based routing, или маршрутизация по источнику, меняет эту логику: выбор маршрута зависит от адреса отправителя, метки пакета, входящего интерфейса и других критериев. Этот механизм реализуется через подсистему iproute2 - команды ip rule и несколько таблиц маршрутизации ip route.
Практическая ценность подхода проявляется в трёх типовых сценариях. Первый - сервер с несколькими интернет-каналами (multi-WAN), где трафик от бухгалтерии должен уходить через одного провайдера, а трафик разработчиков - через другого. Второй - изоляция сред: пакеты от контейнеров или приложений принудительно направляются через VPN-туннель, тогда как остальной трафик идёт напрямую. Третий - защита от IP-спуфинга через проверку обратного пути (RPF), когда маршрутизатор блокирует пакеты с поддельными адресами отправителя. Все примеры в статье проверены на Ubuntu 24.04 и Debian 12, актуальны на август 2026 года и готовы к применению в продуктивной среде.
Что такое source-based routing и зачем он нужен
Policy-Based Routing (PBR) - это метод выбора маршрута на основе политик, заданных администратором. В отличие от классической маршрутизации, где решение принимается по полю destination IP, PBR анализирует source IP, метку пакета (fwmark), протокол, порт и другие атрибуты. Linux реализует PBR через правила (rules) и таблицы маршрутизации. Правило указывает условие отбора пакетов и номер таблицы, которую нужно использовать для таких пакетов. Таблица содержит маршруты - стандартные записи вида «сеть назначения через шлюз».
Архитектура маршрутизации Linux включает три зарезервированные таблицы: local (приоритет 0, маршруты для локальных адресов и loopback), main (приоритет 32766, основная таблица) и default (приоритет 32767, запасная таблица). Когда ядро обрабатывает пакет, оно последовательно проверяет правила в порядке возрастания приоритета. Первое совпадение определяет таблицу. Если ни одно правило не сработало, используется таблица main. Файл /etc/iproute2/rt_tables сопоставляет числовые идентификаторы таблиц с именами, что упрощает конфигурацию.
Отличие от обычной маршрутизации: таблицы и правила
Обычная маршрутизация оперирует одной таблицей main. Команда ip route add 10.0.0.0/8 via 192.168.1.1 добавляет запись в main, и все пакеты в сеть 10.0.0.0/8 пойдут через шлюз 192.168.1.1 независимо от адреса отправителя. PBR вводит дополнительный уровень: правило ip rule add from 192.168.10.0/24 table 100 говорит ядру - для пакетов с source IP из подсети 192.168.10.0/24 использовать таблицу 100. Если в таблице 100 есть маршрут по умолчанию через другого провайдера, трафик уйдёт туда.
Иерархия принятия решения выглядит так: пакет приходит в ядро, ядро проверяет список правил ip rule show от меньшего приоритета к большему, находит первое совпадение по условиям (from, fwmark, iif, tos и другим), извлекает номер таблицы из правила, ищет маршрут в этой таблице. Если маршрут найден - пакет отправляется. Если нет - ядро возвращается к следующему правилу. Приоритеты задаются параметром pref или priority. Правила без явного приоритета получают номера автоматически с шагом 32765, 32764 и так далее.
Типовые сценарии использования source-based routing
Сценарии, где PBR решает задачи, неразрешимые обычной маршрутизацией:
- Multi-WAN с балансировкой и резервированием. Сервер подключён к двум провайдерам. Трафик от подсети 192.168.1.0/24 уходит через ISP1, от 192.168.2.0/24 - через ISP2. При отказе одного канала весь трафик переключается на оставшийся. Подробная настройка в разделе «Практический сценарий: Multi-WAN с балансировкой нагрузки».
- Сегментация сети по источникам. Отдел разработки и продакшен-среда используют разные шлюзы для выхода в интернет. Правила ip rule привязывают подсети к конкретным таблицам маршрутизации.
- Маршрутизация на основе приложений через fwmark. HTTP-трафик от определённого сервиса помечается меткой в iptables и направляется через VPN-туннель. Остальной трафик сервиса идёт через основной шлюз. Разобрано в разделе «Гибкая маршрутизация с помощью fwmark».
- Защита от IP-спуфинга. Strict Reverse Path Forwarding проверяет, что пакет с адресом источника из внутренней сети пришёл именно с внутреннего интерфейса. Пакеты с поддельным source IP блокируются. Конфигурация в разделе «Защита от IP-спуфинга с помощью source-based routing».
Подготовка системы и базовые понятия
Перед настройкой убедитесь, что пакет iproute2 установлен. Команда ip -V покажет версию. На Ubuntu и Debian пакет входит в базовую установку. Для маркировки пакетов потребуется iptables или nftables - они также предустановлены в большинстве дистрибутивов. Работать рекомендуется через консоль сервера или IPMI: ошибка в правилах может разорвать сетевое соединение, и удалённый SSH-доступ станет недоступен. Держите резервную сессию или консоль открытой.
Необходимые утилиты и файлы конфигурации
Основной инструмент - утилита ip из пакета iproute2. Файл /etc/iproute2/rt_tables содержит сопоставление номеров и имён таблиц. Формат записи: номер и имя через пробел. Зарезервированные номера: 0 (unspec), 253 (default), 254 (main), 255 (local). Пользовательские таблицы размещайте в диапазоне от 1 до 252. Пример добавления двух таблиц для провайдеров:
# /etc/iproute2/rt_tables
100 isp1
200 isp2
После редактирования файла имена становятся доступны в командах ip route и ip rule. Перезагрузка не требуется.
Просмотр текущих правил и таблиц маршрутизации
Диагностика исходного состояния помогает понять, что изменится после настройки. Команда ip rule show выводит список правил с приоритетами:
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
Правило 0 направляет все локальные пакеты в таблицу local. Правило 32766 - всё остальное в main. Правило 32767 - запасной default. Команда ip route show table main показывает содержимое основной таблицы. ip route show table local - таблицу локальных адресов (loopback, IP-адреса интерфейсов, broadcast).
Настройка маршрутизации по IP-адресу источника с ip rule
Этот сценарий - основа PBR. Задача: трафик от подсети 192.168.10.0/24 должен уходить через шлюз 10.0.0.1 провайдера ISP1, а трафик от 192.168.20.0/24 - через шлюз 10.0.1.1 провайдера ISP2. Сервер имеет два внешних интерфейса: eth0 с адресом 10.0.0.2/24 и eth1 с адресом 10.0.1.2/24.
Создание и именование дополнительных таблиц
Добавьте в /etc/iproute2/rt_tables строки:
100 isp1
200 isp2
Номера 100 и 200 выбраны произвольно, но должны быть уникальны и не пересекаться с зарезервированными. Имена isp1 и isp2 будут использоваться в командах.
Добавление маршрутов в пользовательские таблицы
Каждая таблица должна содержать маршрут по умолчанию через своего провайдера и маршрут к локальной сети шлюза для обеспечения доступности next-hop:
ip route add 10.0.0.0/24 dev eth0 table isp1
ip route add default via 10.0.0.1 dev eth0 table isp1
ip route add 10.0.1.0/24 dev eth1 table isp2
ip route add default via 10.0.1.1 dev eth1 table isp2
Маршрут к сети шлюза обязателен. Если его нет, ядро не сможет разрешить next-hop и вернёт ошибку «Network is unreachable». Команда ip route show table isp1 подтвердит добавление записей.
Создание правил ip rule для выбора таблицы
Свяжите адреса источников с таблицами:
ip rule add from 192.168.10.0/24 table isp1 priority 100
ip rule add from 192.168.20.0/24 table isp2 priority 200
Параметр priority задаёт порядок обработки. Меньшее число - более высокий приоритет. Правило для конкретного IP-адреса: ip rule add from 192.168.10.5/32 table isp1 priority 50. После добавления проверьте список: ip rule show.
Проверка и отладка конфигурации
Команда ip route get симулирует выбор маршрута для пакета с заданными параметрами. Укажите адрес назначения и адрес источника:
ip route get 8.8.8.8 from 192.168.10.5
Вывод покажет выбранную таблицу, шлюз и интерфейс. Если маршрут идёт через eth0 и шлюз 10.0.0.1 - настройка работает. Для захвата пакетов используйте tcpdump -i eth0 host 8.8.8.8 и инициируйте трафик с нужного адреса источника.
Гибкая маршрутизация с помощью fwmark (iptables/nftables)
Маркировка пакетов (fwmark) расширяет возможности PBR: решение о маршруте принимается не только по source IP, но и по порту, протоколу, UID процесса. Метка - это целое число, которое netfilter записывает в поле mark пакета, а ip rule проверяет условием fwmark.
Маркировка пакетов в iptables
Метки устанавливаются в цепочке PREROUTING таблицы mangle, чтобы повлиять на маршрутизацию. Пример: направить HTTP-трафик от подсети 192.168.10.0/24 через ISP2, а остальной трафик этой подсети - через ISP1:
iptables -t mangle -A PREROUTING -s 192.168.10.0/24 -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -s 192.168.10.0/24 -p tcp --dport 443 -j MARK --set-mark 1
Правило ip rule для метки 1:
ip rule add fwmark 1 table isp2 priority 150
Порядок правил важен: сначала проверяется fwmark, затем from. Сохраните правила iptables: iptables-save > /etc/iptables/rules.v4 (Debian/Ubuntu).
Маркировка пакетов в nftables
Современная альтернатива iptables. Создайте таблицу mangle с цепочкой mark:
nft add table inet mangle
nft add chain inet mangle mark { type filter hook prerouting priority mangle \; }
nft add rule inet mangle mark ip saddr 192.168.10.0/24 tcp dport { 80, 443 } meta mark set 1
Правило ip rule с fwmark работает одинаково для iptables и nftables. Проверка меток: nft list ruleset.
Совместное использование ip rule и fwmark
Можно комбинировать критерии в одном правиле. Например, трафик от 192.168.10.5 с меткой 2 направить в таблицу vpn:
ip rule add from 192.168.10.5 fwmark 2 table vpn priority 50
Ядро проверяет все условия правила. Если пакет удовлетворяет и from, и fwmark - применяется указанная таблица. Если нет - поиск продолжается по следующим правилам. Приоритеты расставляйте так, чтобы более специфичные правила имели меньший номер priority.
Практический сценарий: Multi-WAN с балансировкой нагрузки
Сервер с двумя внешними интерфейсами: eth0 подключён к ISP1 (шлюз 10.0.0.1), eth1 к ISP2 (шлюз 10.0.1.1). Внутренняя сеть - 192.168.0.0/24. Задача: балансировать исходящий трафик клиентов между провайдерами, при отказе одного канала весь трафик направлять через оставшийся, и закрепить бухгалтерию (192.168.0.10-192.168.0.20) за ISP1.
Настройка таблиц для каждого провайдера
Создайте таблицы в /etc/iproute2/rt_tables:
100 isp1
200 isp2
Заполните маршрутами:
ip route add 10.0.0.0/24 dev eth0 table isp1
ip route add default via 10.0.0.1 dev eth0 table isp1
ip route add 10.0.1.0/24 dev eth1 table isp2
ip route add default via 10.0.1.1 dev eth1 table isp2
Балансировка исходящего трафика с помощью multipath
Добавьте в таблицу main маршрут по умолчанию с несколькими nexthop:
ip route add default scope global nexthop via 10.0.0.1 dev eth0 weight 1 nexthop via 10.0.1.1 dev eth1 weight 1
Параметр weight задаёт пропорцию распределения. При равных весах трафик делится 50/50. Современные ядра (5.x и 6.x) балансируют per-flow: все пакеты одного TCP-соединения идут через один и тот же шлюз. Это предотвращает проблемы с асимметричной маршрутизацией и сохранением сессий.
Обеспечение отказоустойчивости (failover)
Ядро само отслеживает доступность nexthop через механизм neighbour detection. Если шлюз 10.0.0.1 перестаёт отвечать на ARP-запросы, ядро исключает его из multipath и весь трафик идёт через 10.0.1.1. Для более быстрого обнаружения можно настроить скрипт с периодическим ping и удалением/добавлением nexthop. Альтернатива - keepalived с отслеживанием состояния интерфейсов. Базовая проверка: ping -I eth0 10.0.0.1 и ping -I eth1 10.0.1.1.
Привязка внутренних подсетей к конкретным WAN-каналам
Для бухгалтерии создайте правило, указывающее таблицу isp1:
ip rule add from 192.168.0.10/28 table isp1 priority 50
Трафик от этих адресов всегда пойдёт через ISP1, независимо от балансировки в таблице main. Остальные клиенты продолжат использовать multipath.
Защита от IP-спуфинга с помощью source-based routing
IP-спуфинг - атака, при которой злоумышленник отправляет пакеты с поддельным адресом отправителя. Маршрутизатор, не проверяющий источник, пропускает такие пакеты. Source-based routing позволяет реализовать strict Reverse Path Forwarding - проверку, что пакет с адресом источника из определённой сети пришёл через интерфейс, который действительно подключён к этой сети.
Принцип Reverse Path Forwarding (RPF)
Strict RPF требует, чтобы маршрут к адресу источника пакета проходил через тот же интерфейс, с которого пакет пришёл. Ядро Linux поддерживает RPF через параметр rp_filter, но он работает только для таблицы main. PBR даёт более гибкий контроль: можно создать правила для каждого интерфейса и явно указать, какие сети ожидаются на каждом входном интерфейсе.
Настройка правил для проверки источника
Предположим, внутренний интерфейс eth1 подключён к сети 192.168.1.0/24. Создайте правило, которое для пакетов, пришедших с eth1 и имеющих source IP из этой сети, использует таблицу main:
ip rule add from 192.168.1.0/24 iif eth1 table main priority 100
Добавьте завершающее правило, блокирующее всё остальное с eth1:
ip rule add iif eth1 unreachable priority 101
Теперь пакет с source IP 192.168.1.5, пришедший с eth1, пройдёт проверку и будет обработан. Пакет с source IP 10.0.0.5, пришедший с eth1, попадёт под правило 101 и будет отброшен. Аналогичные правила создаются для каждого интерфейса. Проверка: попытка отправить пакет с поддельным адресом с другого интерфейса не пройдёт.
Сохранение конфигурации и автозапуск
Правила ip rule и маршруты в пользовательских таблицах не сохраняются после перезагрузки. Для production-среды настройки должны применяться автоматически.
Сохранение через /etc/network/interfaces (Debian/Ubuntu)
Для систем с ifupdown добавьте post-up команды в описание интерфейса:
auto eth0
iface eth0 inet static
address 10.0.0.2/24
post-up ip route add default via 10.0.0.1 dev eth0 table isp1
post-up ip rule add from 192.168.10.0/24 table isp1 priority 100
Использование systemd-networkd
Создайте файл /etc/systemd/network/10-eth0.network:
[Match]
Name=eth0
[Network]
Address=10.0.0.2/24
[Route]
Destination=0.0.0.0/0
Gateway=10.0.0.1
Table=100
[RoutingPolicyRule]
From=192.168.10.0/24
Table=100
Priority=100
После создания файла выполните systemctl restart systemd-networkd. Настройки применятся и сохранятся при перезагрузке.
Создание собственного systemd-сервиса
Универсальный метод для любого дистрибутива с systemd. Создайте скрипт /usr/local/bin/pbr-setup.sh с командами ip route и ip rule. Сделайте его исполняемым: chmod +x /usr/local/bin/pbr-setup.sh. Создайте юнит /etc/systemd/system/pbr-setup.service:
[Unit]
Description=Setup Policy-Based Routing
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/pbr-setup.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Активируйте: systemctl enable pbr-setup.service && systemctl start pbr-setup.service. Сервис выполнит скрипт после поднятия сети.
Диагностика и решение типичных проблем
Ошибки при настройке PBR возникают по трём основным причинам: неверный порядок правил, отсутствие маршрута к шлюзу в пользовательской таблице и асимметричная маршрутизация.
Инструменты отладки: ip route get, tcpdump, логи ядра
Команда ip route get - главный инструмент. Она показывает полный путь пакета: выбранную таблицу, шлюз, интерфейс. Запускайте с разными параметрами:
ip route get 8.8.8.8 from 192.168.10.5 iif eth1 mark 1
tcpdump фиксирует реальное движение пакетов. Запустите на всех интерфейсах одновременно, чтобы отследить вход и выход: tcpdump -i any host 8.8.8.8 -n. Логи martian-пакетов включаются через echo 1 > /proc/sys/net/ipv4/conf/all/log_martians. Записи появятся в dmesg или syslog.
Частые ошибки и их решение
Ошибка «RTNETLINK answers: Network is unreachable». Возникает при добавлении маршрута с next-hop, который недоступен напрямую. Решение: добавьте маршрут к сети шлюза в ту же таблицу перед default-маршрутом.
Правило ip rule не срабатывает. Проверьте приоритеты: более специфичное правило должно иметь меньший номер priority. Проверьте синтаксис условия - from принимает префикс сети, а не диапазон. Команда ip rule show выведет все правила с приоритетами.
Трафик уходит, но ответы не возвращаются. Это асимметричная маршрутизация: пакеты уходят через eth0, а ответы приходят на eth1. Решение: настройте connection tracking (conntrack), чтобы ядро запоминало, через какой интерфейс ушёл запрос, и ожидало ответ на том же интерфейсе. Альтернатива - привязка внутренних подсетей к конкретным WAN-каналам через ip rule, как описано в разделе Multi-WAN.
Потеря связности после применения правил. Если вы работаете по SSH и добавили правило, которое перенаправляет ваш трафик в таблицу без маршрута, соединение разорвётся. Работайте через консоль сервера или добавьте правило для вашего IP-адреса, возвращающее трафик в main, до применения общих правил.