Policy-Based Routing (PBR): как способ починить домашний интернет

—

от автора

Весной 2025 я заметил, что у меня начались регулярные проблемы с доступом в интернет. Сайты открывались, но часть изображений не загружалась. Некоторые мобильные приложения периодически жаловались на сеть. При этом те же ресурсы нормально работали через мобильный интернет или VPN.

Я поднял на домашнем роутере OpenVPN-клиент. Проблемные ресурсы заработали. Зато российские сервисы начали видеть зарубежный IP-адрес, а задержка увеличилась.

Получилось, что VPN не устранил проблему, а лишь подменил одно другим.

В итоге я разделил трафик с помощью Policy-Based Routing: основной внешний трафик домашней сети направил в VPN, а исключения оставил на обычном WAN подключении. Ниже — устройство этой схемы, конфигурация OpenWrt.

Почему VPN-тунель решил задачу только наполовину

OpenVPN-сервер отправляет клиенту redirect-gateway def1, весь трафик идет через VPN.

Меняем правила:

  • внешний трафик пойдет через VPN;

  • российские ресурсы — напрямую через провайдера;

  • при падении VPN устройства в сети не должны полностью терять интернет.

Часть задачи можно решить статическими маршрутами. Но я не хотел вручную поддерживать файл /etc/hosts в акуальном состоянии.

На помошь пришел Policy-Based Routing (PBR), как решение которое позволяет выбирать путь с дополнительным условиям. В Linux правила ip rule могут учитывать, например, адрес источника и метку пакета, а затем направлять поиск маршрута в нужную таблицу.

Разделяй и властвуй

Для реализации решения нам понадобится довольно мощное и современное железо. Роутер, который вам дал в аренду ваш провайдер скорее всего не подойдет.

В статье я буду использовать Banana Pi BPI-R4 с официальное сборкой OpenWrt.

Обозначение

Назначение

lan

Домашняя сеть 192.168.0.0/24

wan

Основное проводное подключение к провайдеру

vpn, tun0

Логический интерфейс и устройство OpenVPN-туннеля

Упрощённая схема выбора пути для внешнего IPv4-трафика из LAN:

LAN 192.168.0.0/24        |  OpenWrt на BPI-R4        |        +-- login.beeline.ru ------> wan        +-- адрес из ru.zone ------> main --> обычный uplink        +-- остальные назначения -> vpn (tun0) --> VPN-сервер

VPN здесь работает поверх обычного подключения: до публичного адреса VPN-сервера роутер добирается через провайдера. Туннель даёт дополнительный путь для пользовательского трафика.

Главная идея: VPN не должен владеть default route

Ключевое решение оказалось таким:

Не заставлять OpenVPN менять default route, а выбор VPN перенести в отдельные PBR-политики.

OpenVPN через netifd

В моей сборке туннель запускается через netifd, поэтому его параметры находятся в /etc/config/network:

config interface 'vpn'option proto 'openvpn'option config '/etc/openvpn/client.ovpn'option pull_filter 'ignore "redirect-gateway"'option dev 'tun0'option defaultroute '0'

pull_filter заставляет клиент игнорировать полученную от сервера директиву redirect-gateway. defaultroute '0' запрещает netifd устанавливать полученный шлюз как обычный default route. Эти настройки действуют на разных уровнях, поэтому я оставил обе.

Явное dev 'tun0' связывает логический интерфейс vpn с устройством туннеля, которое затем использует PBR. Проверять нужно и интерфейс, и фактические маршруты: две настройки в конфигурации сами по себе не показывают результат.

Firewall для выхода через VPN

Выбранный маршрут должен быть разрешён firewall. VPN у меня находится в отдельной зоне. Часть /etc/config/firewall, отвечающая за выход из LAN:

config zoneoption name 'vpn'list network 'vpn'option input 'REJECT'option output 'ACCEPT'option forward 'REJECT'option masq '1'option mtu_fix '1'config forwardingoption src 'lan'option dest 'vpn'

lan -> vpn разрешает пересылку, а masq '1' подменяет адрес источника на адрес туннеля. В этой схеме VPN-серверу не требуется обратный маршрут к каждому домашнему адресу.

Обычный выход lan -> wan с NAT также остаётся разрешённым: через него работают прямые исключения и запасной путь при недоступности VPN.

Три политики PBR

Конфигурация /etc/config/pbr:

config pbr 'config'option enabled '1'option strict_enforcement '0'option procd_reload_delay '5'option uplink_interface 'wan'option resolver_set 'dnsmasq.nftset'config policyoption name 'Beeline login'option interface 'wan'option dest_addr 'login.beeline.ru'config policyoption name 'Bypass IPs'option interface 'ignore'option dest_addr 'file:///www/iplist/ru.zone'config policyoption name 'Default VPN'option interface 'vpn'option src_addr '192.168.0.0/24'

Порядок правил имеет значение.

Политика

Что совпадает

Как выбирается путь

Beeline login

Адреса домена login.beeline.ru

Через проводной wan

Bypass IPs

Адрес назначения из ru.zone

Пропустить дальнейшую обработку PBR и использовать обычную маршрутизацию

Default VPN

Источник из 192.168.0.0/24

Через vpn для оставшегося внешнего трафика

Почему для RU bypass выбран ignore

ignore здесь означает выход из обработки политиками PBR. В моей конфигурации основным подключением остаётся WAN, адреса из списка идут через него.

У портала провайдера другая задача: login.beeline.ru относится к проводному подключению Билайн. Поэтому он закреплён именно за wan, а его правило стоит выше общего списка исключений. Это нужно, чтобы мы могли залогинится у провайдера.

Файл /www/iplist/ru.zone должен существовать на роутере до загрузки политики. Это обычный текстовый список IPv4-сетей в CIDR, по одной сети на строку.

Пример формата:

5.8.0.0/135.16.0.0/1231.128.0.0/10...

ip-full предоставляет полноценную команду ip, которой PBR управляет таблицами и правилами маршрутизации. Для доменных политик при resolver_set 'dnsmasq.nftset' нужен dnsmasq-full с поддержкой nftset: он наполняет адресные наборы по DNS-ответам.

Для чтения локального адресного списка через file:// нужен curl. Требования к пакетам описаны в документации PBR.

Получение регионального файла через IPCC

Для получения файла ru.zone я использовал собственное решение — IPCC. Утилита загружает статистику региональных интернет-регистраторов (RIR), выбирает сети по коду страны и агрегирует их в список CIDR.

Скачивание и запуск

Нужно будет склонировать репозиторий:

git clone git@github.com:woodger/ipcc.gitcd ipccpython3 ./ipcc --help

IPCC запускается из исходников и использует стандартную библиотеку Python. В этой схеме развёртывание сводится к размещению репозитория на компьютере и запуску утилиты из его каталога.

Генерация списка

Для российских IPv4-сетей указываю код страны RU и имя выходного файла:

python3 ./ipcc --country RU --output ru.zone

Утилита загружает данные RIR из интернета и записывает результат в ru.zone в текущем каталоге. После успешного завершения проверяю число строк и начало файла:

wc -l ru.zone # ~8500head -n 5 ru.zone

Каждая строка должна содержать IPv4-сеть в формате CIDR, как в примере выше. Число сетей меняется вместе с исходными данными.

Параметр

Назначение

--country RU

Выбрать страну по двухбуквенному коду; по умолчанию используется RU

--output ru.zone

Указать путь к выходному файлу

--ipv6

Сформировать IPv6-список вместо IPv4; для показанного RU bypass используется IPv4

--verbose

Включить подробное журналирование

Передача списка на роутер

Нужно передать файл ru.zone на роутер по пути /www/iplist/ru.zone, который уже указан в политике Bypass IPs как file:///www/iplist/ru.zone.

Что происходит при отказе VPN

У меня намеренно установлено:

option strict_enforcement '0'

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

Цена такого решения — выход трафика напрямую при отказе VPN. Если требуется обязательная передача только через туннель, нужен другой режим и проверка блокировки при его отключении. Значение strict_enforcement и порядок политик описаны в документации PBR.

Итог

В результате RU-трафик идет через WAN. OpenVPN только поднимает дополнительный путь. Firewall разрешает и маскарадует трафик. dnsmasq превращает доменные исключения в наборы адресов. PBR решает, какую таблицу использовать конкретному пакету из LAN. Доменные правила при этом не будут зависеть от DNS.

Такая схема сохраняет обычный выход в интернет и задаёт исключения явно: какой трафик направлять в VPN, какой выпускать напрямую и что делать при отключении туннеля.

Ссылки

ссылка на оригинал статьи https://habr.com/ru/articles/1089258/