Выберите продукт

Debian/Ubuntu: rp_filter, reverse path filtering и policy routing без сюрпризов

Разбираем, как в Debian/Ubuntu работает reverse path filtering, почему в dmesg появляются martian source и как правильно сочетать rp_filter с policy routing через ip rule и ip route на сервере с несколькими интерфейсами и uplink без плавающих сбоев.
Debian/Ubuntu: rp_filter, reverse path filtering и policy routing без сюрпризов

На Linux-серверах с двумя и более интерфейсами, несколькими IP-адресами, разными шлюзами или отдельной сетью для бэкапов рано или поздно всплывает одна и та же история: часть трафика ходит, часть внезапно пропадает, в dmesg появляются сообщения про martian source, а после очередного reboot ситуация повторяется. Особенно часто это встречается на Debian/Ubuntu, когда администратор настраивает policy routing через ip rule и ip route, но забывает про rp_filter.

Reverse path filtering — это механизм ядра Linux для проверки, что обратный маршрут к адресу источника пакета выглядит корректно с точки зрения таблицы маршрутизации. В простых конфигурациях он помогает отсекать спуфинг и явно неверный трафик. Но как только у сервера появляется асимметрия маршрутов, несколько uplink'ов, несколько source IP или VRF-подобная логика через policy routing, слишком строгая проверка начинает ломать вполне легитимные пакеты.

Проблема в том, что многие смотрят на rp_filter как на магический переключатель: 0 — плохо, 1 — безопасно, 2 — непонятно. На практике всё зависит от вашей схемы маршрутизации. Если у вас один интерфейс и один default route, можно годами не вспоминать про этот параметр. Если же входящий пакет приходит по одному интерфейсу, а лучший обратный маршрут по main-таблице указывает на другой, строгий rp_filter такой пакет отбросит.

Ниже разберём, как это работает в Debian/Ubuntu, где искать симптомы, как правильно проверять lookup ядра и как сочетать rp_filter с policy routing так, чтобы сеть после перезагрузки вела себя предсказуемо.

Что такое rp_filter и почему он мешает именно на multi-homed сервере

rp_filter — это проверка обратного пути. Когда на интерфейс приходит пакет, ядро пытается понять: если бы я сейчас отправлял ответ к IP-адресу источника, через какой интерфейс я бы его послал?

В строгом режиме ядро ожидает, что обратный маршрут укажет именно на тот интерфейс, на который пакет пришёл. Если пакет пришёл на eth1, а lookup показывает, что назад к источнику лучше идти через eth0, пакет может быть отброшен ещё до того, как его увидит приложение.

Это логично для защиты от подмены адресов, но конфликтует с асимметричной маршрутизацией. Асимметрия сама по себе не всегда ошибка. Например, сервер может принимать трафик по одному адресу на eth1, а основной исходящий маршрут держать через eth0. Или трафик должен уходить через разного провайдера в зависимости от source IP. В таких схемах без policy routing одно только наличие нескольких интерфейсов уже создаёт условия, при которых strict reverse path filtering начинает ломать нормальный трафик.

Значения параметра rp_filter

  • 0 — проверка отключена;
  • 1 — strict mode, строгая проверка обратного пути;
  • 2 — loose mode, мягкая проверка.

В loose mode ядро проверяет не то, что лучший обратный путь проходит через тот же интерфейс, а лишь то, что источник в принципе достижим через таблицы маршрутизации. Для реальных серверов это часто компромиссный вариант: базовая защита остаётся, но допустимая асимметрия не ломается на ровном месте.

Короткое практическое правило: rp_filter=1 хорошо подходит для простых одноинтерфейсных хостов, а на серверах с несколькими uplink чаще нужен режим 2 или точечное отключение на конкретном интерфейсе.

Где смотреть текущие значения и как понять, что виноват именно rp_filter

На Debian и Ubuntu параметры доступны через sysctl и /proc/sys. Проверять нужно не только глобальные значения, но и настройки конкретных интерфейсов. Частая ошибка — изменить только net.ipv4.conf.all.rp_filter и считать, что этого достаточно.

sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.default.rp_filter
sysctl net.ipv4.conf.eth0.rp_filter
sysctl net.ipv4.conf.eth1.rp_filter

То же самое через /proc:

cat /proc/sys/net/ipv4/conf/all/rp_filter
cat /proc/sys/net/ipv4/conf/default/rp_filter
cat /proc/sys/net/ipv4/conf/eth0/rp_filter
cat /proc/sys/net/ipv4/conf/eth1/rp_filter

Если сеть ведёт себя странно, сразу смотрите и маршруты, и правила:

ip addr
ip route show table main
ip rule show
ip route get 203.0.113.10
ip route get 203.0.113.10 from 198.51.100.20 iif eth1

Последняя команда особенно полезна: она моделирует, как ядро воспринимает путь для конкретного исходного адреса и входного интерфейса. Если расчётный обратный путь не совпадает с вашей реальной схемой, причина уже почти найдена.

Ещё один важный источник информации — журнал ядра. Сообщения про martian source как раз намекают, что пакет пришёл с точки зрения текущей маршрутизации «не оттуда».

dmesg | grep -i martian
journalctl -k | grep -i martian

Типичный сценарий: входящий пакет пришёл на eth1 с адреса, для которого main-таблица считает лучшим обратным маршрутом eth0. При активном strict mode пакет отбрасывается, а в логах появляется характерная запись.

Диагностика policy routing в терминале с командами ip rule, ip route и сообщениями ядра

Почему одной main-таблицы недостаточно

По умолчанию Linux использует основную таблицу маршрутизации — main. Для простого сервера этого достаточно: один default gateway, локальные connected route, всё предсказуемо. Но как только сервер должен отправлять ответы через разные провайдеры в зависимости от source IP или интерфейса, main перестаёт отражать реальную сетевую логику.

Например, у сервера есть два адреса и два шлюза:

  • eth0198.51.100.10/24, шлюз 198.51.100.1;
  • eth1203.0.113.10/24, шлюз 203.0.113.1.

Если в main оставить только один default route, то ответы с адреса 203.0.113.10 могут уходить через eth0. Удалённая сторона может отбросить такие пакеты из-за anti-spoofing у провайдера, а локальный strict rp_filter будет считать входящие пакеты на eth1 неправильными. В итоге ломается и вход, и выход.

Здесь и нужна policy routing: отдельные таблицы и правила, которые говорят ядру, какую таблицу использовать для определённого source IP, интерфейса или другого признака. Если у вас такой сервер развёрнут на VDS, эти правила особенно важно сохранить в постоянной конфигурации, чтобы не ловить плавающие инциденты после рестарта сети или узла.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Базовая схема policy routing через ip rule и ip route

Самый распространённый случай — source-based routing. Мы хотим, чтобы трафик, исходящий с конкретного адреса, всегда уходил через соответствующий шлюз. Тогда обратный путь становится симметричным, а поведение приложений — предсказуемым.

Сначала добавим имена таблиц:

echo '100 uplink1' >> /etc/iproute2/rt_tables
echo '200 uplink2' >> /etc/iproute2/rt_tables

Теперь наполним таблицы маршрутами:

ip route add 198.51.100.0/24 dev eth0 src 198.51.100.10 table uplink1
ip route add default via 198.51.100.1 dev eth0 table uplink1
ip route add 203.0.113.0/24 dev eth1 src 203.0.113.10 table uplink2
ip route add default via 203.0.113.1 dev eth1 table uplink2

Дальше — правила:

ip rule add from 198.51.100.10/32 table uplink1 priority 1000
ip rule add from 203.0.113.10/32 table uplink2 priority 1001

И проверка:

ip rule show
ip route show table uplink1
ip route show table uplink2
ip route get 1.1.1.1 from 198.51.100.10
ip route get 1.1.1.1 from 203.0.113.10

Теперь ядро знает, что ответ с каждого адреса должен идти через свой uplink. Это не обходной манёвр, а явное описание сетевой политики. После такой настройки вероятность конфликтов с rp_filter заметно снижается.

Что делать с main-таблицей

main всё равно остаётся важной. Обычно в ней оставляют connected routes и один основной default route либо минимальный набор маршрутов, если логика вынесена в policy routing. Важно, чтобы это не создавало неожиданных путей для служебного трафика самого хоста.

Если у вас параллельно решается задача исходящего NAT для нескольких адресов, полезно отдельно посмотреть материал про управление SNAT для нескольких IP через nftables: он хорошо дополняет схему с source-based routing.

Когда достаточно loose mode, а когда лучше отключать rp_filter точечно

На практике есть три рабочих подхода, и выбор зависит не от «правильного мнения», а от вашей топологии.

Оставить strict mode

Подходит, если сервер реально одноhomed, асимметрии нет, а policy routing либо отсутствует, либо выстроена так, что обратный lookup всегда совпадает с входным интерфейсом. Это самый строгий режим, но он плохо переносит сложные сети.

Перейти на loose mode

Это практичный выбор для многих продакшн-серверов с двумя интерфейсами, вторичными адресами или нестандартной маршрутизацией. Входящий пакет не будет отброшен только потому, что лучший ответ идёт через другой интерфейс, если источник вообще достижим.

sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.default.rp_filter=2
sysctl -w net.ipv4.conf.eth0.rp_filter=2
sysctl -w net.ipv4.conf.eth1.rp_filter=2

Это изменение временное и пропадёт после перезагрузки, если не записать его в постоянную конфигурацию.

Отключить rp_filter на конкретном интерфейсе

Иногда один интерфейс участвует в специфичной схеме: туннель, VPN, overlay, VRRP или отдельная backhaul-сеть. Тогда разумнее не ослаблять проверку везде, а отключить её только там, где она объективно мешает.

sysctl -w net.ipv4.conf.eth1.rp_filter=0

Это почти всегда лучше, чем глобально отключать фильтр на всём хосте без понимания последствий.

Как сделать настройки постоянными в Debian/Ubuntu

Чтобы параметры не исчезли после перезагрузки, создайте отдельный файл в /etc/sysctl.d/. Так сопровождать систему заметно проще, чем править общий /etc/sysctl.conf.

cat > /etc/sysctl.d/60-rp-filter.conf << 'EOF'
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.eth0.rp_filter = 2
net.ipv4.conf.eth1.rp_filter = 0
EOF
sysctl --system

Если интерфейсы создаются динамически, значение default особенно важно: оно будет применяться к новым интерфейсам. Но уже существующие интерфейсы лучше настраивать явно, иначе можно получить смесь старых и новых значений.

Сами маршруты и правила тоже должны переживать reboot. Конкретный способ зависит от того, чем вы управляете сетью: ifupdown, netplan, systemd-networkd или NetworkManager. Важно не только прописать команды, но и убедиться, что они применяются в правильном порядке: сначала адреса и connected routes, затем таблицы, затем ip rule.

Схема сервера с двумя uplink, двумя интерфейсами и source-based routing

Практический сценарий: два провайдера и два публичных IP

Представим сервер, у которого два интерфейса. На eth0 опубликован основной сайт, на eth1 — API для партнёров с отдельным IP и отдельным uplink. Приложения слушают оба адреса. Если оставить одну default route через eth0, то ответы с адреса на eth1 могут уходить через eth0, часть клиентов увидит таймауты, а ядро начнёт логировать martian source.

Правильный путь здесь такой:

  1. описать отдельные таблицы для каждого uplink;
  2. добавить source-based rules для каждого адреса;
  3. проверить ip route get для обоих source IP;
  4. перевести rp_filter в 2 или отключить его на проблемном интерфейсе, если схема заведомо асимметрична;
  5. протестировать входящий и исходящий трафик отдельно.

Если подобная схема используется ещё и для site-to-site-связности, полезно проверить, как policy routing сочетается с туннелями — об этом отдельно есть статья про site-to-site на WireGuard.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Как диагностировать проблему пошагово

Если подозреваете конфликт между rp_filter и policy routing, не меняйте всё сразу. Лучше пройти короткий чек-лист.

  1. Посмотрите адреса и интерфейсы: ip addr.
  2. Проверьте правила: ip rule show.
  3. Проверьте main и дополнительные таблицы: ip route show table all.
  4. Смоделируйте route lookup для конкретного source: ip route get ... from ... iif ....
  5. Проверьте журнал ядра на martian source.
  6. Временно переключите проблемный интерфейс в rp_filter=0 или 2 и сравните поведение.
  7. Если проблема исчезла, доведите policy routing до состояния, в котором логика маршрутизации описана явно и сохраняется после перезагрузки.

Очень полезно параллельно запускать tcpdump на обоих интерфейсах. Если SYN приходит на eth1, а ответный SYN-ACK уходит через eth0, у вас классическая асимметрия.

tcpdump -ni eth0 host 203.0.113.50
tcpdump -ni eth1 host 203.0.113.50

Частые ошибки в реальных конфигурациях

Одна default route на сервере с двумя независимыми uplink

Это самый частый источник проблем. Система вроде работает, пока трафик не начинает зависеть от source IP.

Настроили ip rule, но забыли маршруты в таблице

Правило само по себе ничего не даёт, если в соответствующей таблице нет connected route и default gateway. Очень часто забывают маршрут до собственной подсети через интерфейс.

Поменяли только all и default, но не конкретный интерфейс

Из-за этого после тестов остаётся неожиданный набор значений: глобально уже loose mode, а на интерфейсе всё ещё strict.

Отключили rp_filter навсегда, не разобравшись с маршрутизацией

Иногда это допустимо, но чаще просто маскирует реальную проблему. Через какое-то время появляется новый адрес, новый сервис или туннель — и старая неявная логика снова ломается.

Не проверили поведение после reboot

Временные команды через ip и sysctl -w отлично помогают в диагностике, но исчезают после перезагрузки. Если не оформить изменения постоянно, вы получите плавающий инцидент.

Рекомендуемый практический подход

  1. Сначала описать желаемую исходящую политику через ip rule и отдельные таблицы.
  2. Проверить выбор маршрута командами ip route get для каждого source IP.
  3. Если схема асимметрична по дизайну, перевести rp_filter в 2.
  4. Если особый случай касается одного интерфейса, отключить фильтр только на нём.
  5. Зафиксировать всё в постоянной конфигурации и обязательно проверить результат после reboot.

Такой подход лучше, чем бинарный выбор между «строго везде» и «выключить всё». Он сохраняет управляемость и делает сетевую схему понятной не только вам сегодня, но и коллеге через полгода.

Итог

rp_filter не является врагом policy routing — проблемы начинаются, когда серверу навязывают слишком простую модель маршрутизации в сложной сети. Если у вас один интерфейс, strict mode обычно полезен. Если сервер использует несколько source IP, несколько шлюзов или асимметричные пути, нужно явно описать политику через ip rule и ip route, а затем подобрать подходящий режим reverse path filtering.

Если вы видите martian source в dmesg, не считайте это «глюком ядра». Чаще всего это честный сигнал о том, что текущая таблица маршрутизации не совпадает с реальным движением трафика. Приведите в порядок policy routing, сохраните настройки на постоянной основе и отдельно проверьте всё после перезагрузки — тогда сеть будет вести себя предсказуемо и без неприятных сюрпризов в продакшне.

Поделиться статьей

Вам будет интересно

Docker published ports в Debian/Ubuntu: как исправить too many colons in address и bind address already in use OpenAI Статья написана AI (GPT 5)

Docker published ports в Debian/Ubuntu: как исправить too many colons in address и bind address already in use

Разбираем типичные ошибки при публикации портов Docker в Debian и Ubuntu: неверный синтаксис параметра -p, too many colons in addr ...
Debian/Ubuntu: как исправить overlayfs invalid argument и operation not permitted в rootless Docker OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить overlayfs invalid argument и operation not permitted в rootless Docker

Если rootless Docker на Debian или Ubuntu падает с ошибками overlayfs invalid argument или operation not permitted, причина обычно ...
Debian/Ubuntu: как исправить Apache AH00558 Could not reliably determine the server's fully qualified domain name OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Apache AH00558 Could not reliably determine the server's fully qualified domain name

Предупреждение Apache AH00558 на Debian и Ubuntu обычно не ломает сайт, но показывает, что серверу не хватает базовой настройки. Р ...