Весной 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.
Обозначение | Назначение |
|---|---|
| Домашняя сеть |
| Основное проводное подключение к провайдеру |
| Логический интерфейс и устройство 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-сервера роутер добирается через провайдера. Туннель даёт дополнительный путь для пользовательского трафика.
Пакеты OpenWrt
Для реализации этой схемы на роутере понадобятся:
Пакет | Назначение |
|---|---|
| Создаёт политики, правила и таблицы маршрутизации |
| OpenVPN-клиент, который поднимает VPN-туннель. |
| DNS/DHCP-сервис с поддержкой |
| Позволяет PBR читать адресный список по |
dnsmasq-full устанавливается взамен обычного dnsmasq. Порядок замены и требования к доменным политикам описаны в документации PBR.
ip-full устанавливается автоматически вместе с pbr.
Главная идея: 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 zone option name 'vpn' list network 'vpn' option input 'REJECT' option output 'ACCEPT' option forward 'REJECT' option masq '1' option mtu_fix '1' config forwarding option 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 policy option name 'Beeline login' option interface 'wan' option dest_addr 'login.beeline.ru' config policy option name 'Bypass IPs' option interface 'ignore' option dest_addr 'file:///www/iplist/ru.zone' config policy option name 'Default VPN' option interface 'vpn' option src_addr '192.168.0.0/24'
Порядок правил имеет значение.
Политика | Что совпадает | Как выбирается путь |
|---|---|---|
| Адреса домена | Через проводной |
| Адрес назначения из | Пропустить дальнейшую обработку PBR и использовать обычную маршрутизацию |
| Источник из | Через |
Почему для RU bypass выбран ignore
ignore здесь означает выход из обработки политиками PBR. В моей конфигурации основным подключением остаётся WAN, адреса из списка идут через него.
У портала провайдера другая задача: login.beeline.ru относится к проводному подключению Билайн. Поэтому он закреплён именно за wan, а его правило стоит выше общего списка исключений. Это нужно, чтобы мы могли залогинится у провайдера.
Файл /www/iplist/ru.zone должен существовать на роутере до загрузки политики. Это обычный текстовый список IPv4-сетей в CIDR, по одной сети на строку.
Пример формата:
5.8.0.0/13 5.16.0.0/12 31.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.git cd ipcc python3 ./ipcc --help
IPCC запускается из исходников и использует стандартную библиотеку Python. В этой схеме развёртывание сводится к размещению репозитория на компьютере и запуску утилиты из его каталога.
Генерация списка
Для российских IPv4-сетей указываю код страны RU и имя выходного файла:
python3 ./ipcc --country RU --output ru.zone
Утилита загружает данные RIR из интернета и записывает результат в ru.zone в текущем каталоге. После успешного завершения проверяю число строк и начало файла:
wc -l ru.zone # ~8500 head -n 5 ru.zone
Каждая строка должна содержать IPv4-сеть в формате CIDR, как в примере выше. Число сетей меняется вместе с исходными данными.
Параметр | Назначение |
|---|---|
| Выбрать страну по двухбуквенному коду; по умолчанию используется |
| Указать путь к выходному файлу |
| Сформировать IPv6-список вместо IPv4; для показанного RU bypass используется IPv4 |
| Включить подробное журналирование |
Передача списка на роутер
Нужно передать файл 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, какой выпускать напрямую и что делать при отключении туннеля.

