Вы переключаете VPN в режим полного туннеля, большая часть трафика подчиняется, а затем один адрес отвечает напрямую — принтер, NAS, промежуточный хост в 10.x. Очевидное объяснение — туннель протекает, поэтому вы снижаете метрику маршрута по умолчанию, и ничего не меняется. Эта неудачная попытка и есть подсказка: маршрут по умолчанию — запасной вариант, а не приговор. Поиск маршрута берёт самый длинный совпадающий префикс ещё до того, как сравнить метрики, поэтому /24 продолжает побеждать /0, какое бы число вы ни привязали к любому из них. Как только этот порядок ясен, утечка обычно оказывается маршрутом, который кто-то установил намеренно, или префиксом, который клиент никогда не собирался покрывать.

Что маршрут по умолчанию на самом деле обещает

0.0.0.0/0 совпадает с любым адресом назначения IPv4 при нуле битов специфичности. Он существует, чтобы хост мог добраться до всего, что его таблица не описывает лучше. Маршрут к 192.168.1.0/24 несёт 24 совпадающих бита, поэтому для 192.168.1.40 он побеждает раньше, чем кто-либо сравнит метрики, административное расстояние или что там клиент называет полем приоритета. То же правило действует в FIB Linux, таблице маршрутизации Windows и стеке BSD за macOS.

В обычной локальной сети такое поведение — это функция. Локальная подсеть подключена напрямую, принтер продолжает работать, пока туннель несёт ваш трафик, и ничего не настроено неправильно. Вопрос, который стоит задать, не в том, почему этот адрес идёт напрямую, а в том, какой префикс в какой таблице ему соответствует и кто его там разместил.

Полумаршруты по умолчанию, или почему запись по умолчанию может выглядеть нетронутой

Самый чистый пример — redirect-gateway def1 в OpenVPN. Вместо удаления и замены маршрута по умолчанию он добавляет 0.0.0.0/1 и 128.0.0.0/1. Вместе пара покрывает весь IPv4, каждый префикс длиннее /0, и оба побеждают маршрут маршрутизатора по умолчанию, оставляя его на месте. Запустите после этого список маршрутов, и исходный маршрут по умолчанию всё ещё там, всё ещё указывает на ваш шлюз; просто он никогда не выбирается для интернет-назначений.

wg-quick из WireGuard достигает того же результата через маршрутизацию по политике. Когда AllowedIPs покрывает 0.0.0.0/0, он создаёт собственную таблицу, отправляет туда помеченные пакеты и добавляет правило, игнорирующее префикс нулевой длины основной таблицы. Основная таблица сохраняет свою запись по умолчанию, а правила решают, что для трафика, принадлежащего интерфейсу, сначала спрашивают таблицу туннеля.

Обе схемы требуют ровно одного маршрута вне туннеля: маршрута к хосту — к собственному адресу VPN-сервера через исходный шлюз. Без него клиент пытался бы нести пакеты, строящие туннель, внутри туннеля, и соединение никогда бы не завершилось. Поэтому маршрут, не относящийся к туннелю, в вашей таблице нормален даже в строгом профиле полного туннеля. Маршруты, которые стоит поставить под сомнение, — это те, что покрывают назначения, которые вам действительно важны.

Метрики решают ничью, префиксы решают победителей

Метрики сравниваются только среди кандидатов, одинаково хорошо совпадающих с назначением. В Linux ip route get 10.20.5.9 выводит победителя вместе с его таблицей, а ip route show table all перечисляет кандидатов, включая любые полумаршруты по умолчанию и любую таблицу, на которую указывает правило. В Windows Find-NetRoute -RemoteIPAddress 10.20.5.9 возвращает выбранный маршрут и адрес источника, а Get-NetRoute показывает сырую таблицу — но эффективная стоимость — это метрика маршрута плюс метрика интерфейса, поэтому медленный адаптер может проиграть ничью, которую вроде бы выигрывает.

Три случая охватывают большую часть того, что вы увидите:

  • Одно и то же назначение, /24 против /0: /24 побеждает по длине префикса, что бы ни говорили метрики.
  • Одно и то же назначение, два /24 с разными метриками: побеждает более низкая эффективная метрика.
  • Одно и то же назначение, две таблицы, одна выбрана правилом политики: правило сначала выбирает таблицу, и сравнение префиксов происходит внутри неё.

Третий случай — это место, где заканчивается большинство таких отчётов. Правило ip rule в Linux или фильтр VPN-трафика Windows может отправить трафик в таблицу, которая никогда не содержала маршрута туннеля по умолчанию, а основная таблица всё это время выглядит безупречно.

Кому обычно принадлежит обход

Начинайте с профиля, а не с таблицы. Конфигурации split-tunnel и настройки VPN на уровне приложений намеренно оставляют именованные префиксы вне туннеля; рабочий профиль, защищающий одну частную подсеть и пускающий общий обзор напрямую, ведёт себя так, как задумано, и его переопределение нарушает политику, которую он должен был обеспечивать. Руководство Microsoft по маршрутизации VPN проводит ту же границу между принудительно туннелированным трафиком и явными исключениями, что делает его полезной перекрёстной проверкой перед любым редактированием.

Затем ищите остатки. Маршрут, указывающий на интерфейс, которого больше нет, или на адрес из предыдущей сети, появляется после подключения и отключения док-станций, USB-адаптеров и мостов контейнеров. Удалить его один раз — не конец: профили подключений NetworkManager, файлы netplan и постоянные маршруты Windows устанавливают его снова при следующем подключении или загрузке, поэтому найдите файл или профиль, который им владеет, прежде чем что-то менять.

Затем проверьте IPv6 отдельно. ip -6 route и Get-NetRoute -AddressFamily IPv6 хранят отдельное состояние, отдельные маршруты по умолчанию и отдельное поведение клиента. Аудит только IPv4 ничего не говорит о хосте с двойным стеком, который тихо предпочитает нативный путь IPv6, и одно это может заставить клиента выглядеть как split-tunnel.

Подтвердите собственным потоком приложения

Запишите назначение, которое приложение действительно использует, затем спросите систему о пути, который оно выбрало бы из адреса источника, к которому это приложение привязано. ip route get 10.20.5.9 from 192.168.1.40 отвечает на это в Linux; Find-NetRoute -RemoteIPAddress 10.20.5.9 плюс локальный адрес делает это в Windows. Сравните результат с тем, что утверждает профиль. Страница публичного IP в браузере проверяет одно назначение по одному протоколу и ничего не говорит о частном адресе, с которого началась жалоба.

Исправьте самый узкий объект, объясняющий разницу. Если исключение утверждено, запишите префикс и оставьте его; если нет, удалите маршрут или добавьте явный более узкий маршрут через туннель вместо снижения метрики маршрута по умолчанию. Снижение метрики не может обогнать более длинный префикс, а редактирование маршрута по умолчанию, пока два маршрута /1 владеют интернет-путём, тоже ничего не меняет.

Затем подключите туннель заново и повторите тот же запрос. Клиенты переустанавливают маршруты при каждом подключении, поэтому изменение, которое держится только до следующего рукопожатия, не проверено. Если путь всё ещё расходится, оставшиеся подозреваемые — правило политики, вторая таблица или фильтр на стороне клиента, а не маршрут по умолчанию.

  • Назовите назначение и адрес источника, которые приложение действительно использует.
  • Выведите победивший маршрут с помощью ip route get или Find-NetRoute.
  • Проверьте, владеет ли интернет-путём пара /1 или таблица политики.
  • Перечислите каждый префикс длиннее /0, соответствующий назначению.
  • Повторите проверку IPv6 отдельно.
  • Проследите оставшийся маршрут обратно к файлу, профилю или фильтру, который его устанавливает.
  • Подключитесь заново и выполните запрос ещё раз, прежде чем называть это исправленным.

Если все семь согласуются с профилем, туннель делает именно то, для чего был настроен, и исправление относится к профилю, а не к метрике.

Похожие статьи