Один VPN-сервер, два устройства и две страны: как IPv6 обходит туннель
Недавно мне написал пользователь с довольно странной проблемой.
Ситуация выглядела примерно так:
На телефоне подключаюсь к серверу в Германии — Gemini открывается и нормально работает. На компьютере выбираю этот же сервер — Gemini пишет, что сервис недоступен в моём регионе. Клиент тот же, подписка та же, сервер тот же. Что происходит?
На первый взгляд звучит как какая-то ерунда.
Ну серьёзно. Есть один сервер. У сервера один внешний IP-адрес. Оба устройства подключились именно к нему. Значит, сайты должны видеть одну и ту же страну.
Логично?
Логично.
Но неееееет!
Телефон для Google действительно находился в Германии, а компьютер каким-то образом продолжал частично находиться в России. Причём обычная проверка IP-адреса показывала Германию на обоих устройствах.
Сначала я, конечно, подумал на всё стандартное: кэш браузера, cookies, регион Google-аккаунта, DNS, кривой клиент, кривой сервер, кривой пользователь. В общем, классический набор.
В итоге оказалось, что проблема действительно была в VPN-клиенте. Только не в том смысле, что он не подключался. Он подключался. Туннель работал. Трафик ходил через Германию.
Просто не весь.
Давайте разбираться.
Небольшое уточнение: исходная проблема пришла от реального пользователя, но тогда у меня не было возможности собрать с его компьютера вообще все логи и сетевые дампы. Поэтому позже я воспроизвёл ту же ситуацию на отдельной Windows-машине. IP-адреса в статье сокращены, а вывод некоторых команд немного очищен, чтобы не тащить сюда лишнее.
Что у нас было
Есть обычный VPN-сервер в Германии.
На телефоне:
Android;
подключение через системный VPN-интерфейс;
весь трафик устройства уходит в туннель;
Gemini работает.
На компьютере:
Windows;
тот же сервер;
тот же протокол;
подключение успешно;
большинство сайтов работает;
проверка IP показывает Германию;
Gemini считает, что пользователь находится в России.
Схематично всё выглядело так:
Телефон
│
└── VPN-туннель ── Германия ── интернет
Компьютер
│
└── VPN-туннель ── Германия ── интернетПо крайней мере, именно так всё должно было работать.
Первое подозрение: Google просто запомнил регион
Это самое очевидное объяснение.
Google знает о пользователе довольно много. Есть история входов, настройки аккаунта, cookies, язык системы, часовой пояс и куча других данных. Поэтому вполне возможно, что дело вообще не в сети.
Проверяем:
Открываем Gemini в режиме инкогнито.
Полностью выходим из Google-аккаунта.
Открываем страницу в другом браузере.
Создаём чистый профиль браузера.
Удаляем cookies и данные сайта.
Перезапускаем браузер.
Никаких изменений.
На телефоне всё работает. На компьютере — сообщение о недоступности в регионе.
Теория с аккаунтом пока не отпадает полностью, но становится подозрительной. Если проблема сохраняется в чистом браузере без авторизации, надо смотреть сеть.
Вторая мысль: это DNS
DNS почему-то стал универсальным ответом почти на любую проблему с VPN.
Не открывается сайт? DNS.
Открывается не тот сайт? DNS.
Работает на телефоне, но не работает на компьютере? Ну явно DNS.
Иногда это действительно так. Например, браузер может использовать собственный DNS-over-HTTPS и отправлять DNS-запросы не туда, куда рассчитывает VPN-клиент. В Chrome функция Secure DNS действительно может выполнять запросы через DNS-over-HTTPS.
Но здесь важно понимать одну вещь: утечка DNS сама по себе ещё не означает, что соединение с сайтом тоже идёт мимо VPN.
DNS отвечает на вопрос:
Какой IP-адрес у
gemini.google.com?
А уже после этого компьютер подключается к полученному IP.
Условно:
DNS-запрос:
gemini.google.com → 142.250.xxx.xxx
HTTPS-соединение:
компьютер → 142.250.xxx.xxxМожно отправить DNS-запрос российскому провайдеру, а само соединение установить через немецкий VPN-сервер.
Это плохо с точки зрения приватности и иногда влияет на CDN, балансировку и доступность отдельных ресурсов. Но просто российского DNS недостаточно, чтобы уверенно объяснить российскую геолокацию исходящего соединения.
Тем не менее проверяем.
nslookup gemini.google.comЗатем смотрим, какие DNS-серверы вообще назначены системе:
Get-DnsClientServerAddressНа тестовой машине действительно оставался DNS домашнего роутера.
Подозрительно? Да.
Причина проблемы? Как выяснилось, нет.
Я временно поменял DNS, отключил Secure DNS в браузере, очистил кэш:
ipconfig /flushdnsПерезапустил клиент и браузер.
Gemini всё ещё считал компьютер российским.
Хорошо. Идём дальше.
Проверяем внешний IP
Открываем первый попавшийся сервис проверки IP.
Германия.
Открываем второй.
Тоже Германия.
На телефоне Германия, на компьютере Германия. Сервер один и тот же. Казалось бы, на этом расследование можно заканчивать и писать пользователю что-нибудь универсальное:
Попробуйте очистить кэш, переустановить приложение и перезагрузить роутер.
Но это не ответ. Это просто способ избавиться от пользователя.
Я решил проверить адрес не через браузер, а напрямую:
curl.exe https://ifconfig.co/ipПолучаю IP немецкого сервера.
Всё правильно.
И здесь я уже почти вернулся к версии с Google-аккаунтом, но потом решил отдельно проверить IPv4 и IPv6.
curl.exe -4 https://ifconfig.co/ipРезультат:
91.xxx.xxx.xxxНемецкий адрес VPN-сервера.
Теперь IPv6:
curl.exe -6 https://ifconfig.co/ipИ вот здесь внезапно появляется:
2a00:xxxx:xxxx:xxxx::xxxxIPv6-адрес российского интернет-провайдера.
Ну вот и приехали.
Компьютер одновременно находился в двух странах:
IPv4 → VPN → Германия
IPv6 → напрямую → РоссияVPN не был выключен. Он не отвалился. Сервер тоже работал нормально.
Просто клиент отправлял через него IPv4-трафик, а IPv6 продолжал ходить через обычное подключение.
Почему обычная проверка IP нас обманула
На самом деле сервис проверки IP ничего не обязан нам врать.
Он показывает адрес того соединения, через которое мы открыли его страницу.
Если конкретное соединение установилось по IPv4, сервис покажет немецкий IPv4. Это не доказывает, что IPv6 тоже проходит через VPN.
Получается примерно так:
Проверка IP:
браузер → IPv4 → VPN → Германия
Другой сервис:
браузер → IPv6 → провайдер → РоссияПри этом пользователь видит на странице проверки красивую надпись:
Your location: GermanyИ совершенно справедливо думает, что VPN работает на всём устройстве.
А он работает. Но только для части соединений.
Именно поэтому проверка через один сайт — так себе диагностика. Она подтверждает только то, что один конкретный запрос вышел с конкретного адреса.
Откуда вообще взялся IPv6
У большинства домашних пользователей сейчас не обязательно есть отдельный «белый» IPv4-адрес. Провайдер может выдавать серый IPv4 за CGNAT, но при этом назначать полноценный IPv6.
В результате на компьютере одновременно существуют оба стека:
IPv4: 192.168.1.45
IPv6: 2a00:xxxx:xxxx:xxxx::xxxxСайт тоже может иметь оба адреса:
A → IPv4
AAAA → IPv6Когда приложение получает несколько вариантов адреса, оно должно решить, через какой из них подключаться. Для таких сценариев существует механизм Happy Eyeballs: клиент может параллельно или с небольшой задержкой пробовать разные доступные адреса и использовать подходящее соединение. Это описано в RFC 8305.
Если IPv6 доступен и нормально работает, приложение вполне может выбрать его.
И получается очень неприятная схема:
┌── IPv4 ── VPN ── Германия
Приложение ── выбор ───┤
└── IPv6 ── провайдер ── РоссияДля пользователя всё выглядит случайным.
Один сайт работает.
Другой считает его российским.
Третий сначала открывается, а после обновления страницы перестаёт.
Четвёртый работает в одном браузере, но не работает в приложении.
На самом деле никакой магии нет. Просто разные соединения выбирают разные маршруты.
Почему на телефоне всё было нормально
На Android VPN-приложение использовало системный VPN-интерфейс. С точки зрения системы создаётся виртуальный сетевой интерфейс, через который приложение может обрабатывать направленный в него трафик.
TUN-интерфейс как раз и нужен для того, чтобы работать не только как настройка прокси внутри отдельных приложений, а как виртуальный сетевой интерфейс. В документации Xray TUN описан именно как интерфейс, трафик которого передаётся на обработку Xray.
На компьютере же клиент был запущен в режиме системного прокси.
А системный прокси и полноценный VPN-интерфейс — это не одно и то же.
Очень грубо:
Системный прокси:
программа сама должна использовать прокси
TUN:
операционная система направляет сетевой трафик
в виртуальный интерфейсЕсли приложение не использует системные настройки прокси, создаёт собственные соединения или выбирает маршрут, который клиент не перехватывает, часть трафика может уйти напрямую.
В нашем случае IPv4-соединения браузера успешно проходили через локальный прокси и дальше через VPN-сервер.
А для IPv6 в таблице маршрутизации продолжал существовать нормальный прямой маршрут через физический сетевой адаптер.
Смотрим таблицу маршрутизации
В Windows маршруты можно посмотреть стандартными средствами.
Общий вариант:
route printОтдельно IPv6:
route print -6Либо через PowerShell:
Get-NetRoute -AddressFamily IPv6 |
Sort-Object RouteMetric, InterfaceMetricGet-NetRoute показывает записи из таблицы IP-маршрутизации, включая префикс назначения, следующий узел и метрики.
Нас особенно интересует маршрут по умолчанию:
DestinationPrefix : ::/0
NextHop : fe80::1
InterfaceAlias : Ethernet
RouteMetric : 256::/0 — это маршрут по умолчанию для IPv6.
И направлен он был не в VPN-интерфейс, а в обычный Ethernet через домашний роутер.
То есть Windows прямо говорила:
Не знаешь, куда отправлять IPv6-пакет? Отправляй провайдеру.
Что она и делала.
Для IPv4 при этом работал локальный прокси, поэтому большая часть привычного браузерного трафика успешно уходила через Германию.
А что насчёт WebRTC, QUIC и прочих страшных слов
Пока я искал причину, я также проверил несколько популярных версий.
WebRTC
Через WebRTC браузер действительно может раскрывать информацию о сетевых интерфейсах и адресах. Но в нашем случае проблема проявлялась при обычном HTTPS-запросе к сервису.
То есть WebRTC мог быть отдельной утечкой, но не объяснял основной маршрут соединения.
QUIC
QUIC работает поверх UDP, поэтому при прокси-режимах он всегда вызывает подозрения.
Я временно отключил QUIC в браузере и повторил проверку. Проблема осталась.
Значит, в этом конкретном случае дело было не в нём.
DNS-over-HTTPS
Secure DNS тоже отключили. DNS перевели на резолвер внутри туннеля.
Ничего не изменилось, пока не был исправлен сам IPv6-маршрут.
И вот это, как мне кажется, важный момент всей истории.
Когда VPN работает странно, очень легко начать хаотично отключать вообще всё:
IPv6;
QUIC;
WebRTC;
Secure DNS;
антивирус;
брандмауэр;
расширения браузера;
роутер;
соседский холодильник.
Иногда после этого проблема действительно исчезает. Только мы так и не узнаём, что именно её исправило.
Нормальная диагностика — это когда мы меняем одну вещь, повторяем тест и фиксируем результат.
Иначе получается не расследование, а сетевой шаманизм.
Как мы это исправили
Самый нормальный вариант — включить в клиенте TUN-режим и убедиться, что он действительно обрабатывает нужный трафик.
После переключения клиента в TUN проверяем всё заново:
curl.exe -4 https://ifconfig.co/ip
curl.exe -6 https://ifconfig.co/ipВ идеальном варианте оба запроса должны показать адреса нужной выходной точки.
Например:
IPv4: 91.xxx.xxx.xxx
IPv6: 2a01:xxxx:xxxx::xxxxОба адреса относятся к Германии.
Но есть важный нюанс.
У VPN-сервера может вообще не быть настроенного IPv6-выхода. В таком случае у клиента есть два более-менее нормальных варианта:
Передавать IPv6-трафик через туннель и выпускать его с сервера корректным способом.
Не давать приложениям использовать прямой IPv6, чтобы они перешли на IPv4 через VPN.
А вот так делать не надо:
IPv4 → VPN
IPv6 → напрямуюЭто худший вариант, потому что внешне всё вроде бы работает, но реальный маршрут зависит от конкретного сайта и приложения.
В нашем случае после включения TUN клиент начал корректно перехватывать трафик. Там, где полноценный IPv6-выход отсутствовал, клиент не оставлял прямой IPv6-маршрут наружу, и приложения использовали IPv4 через туннель.
После перезапуска браузера Gemini открылся.
Без очистки аккаунта.
Без переустановки Windows.
Без жертвоприношения роутера.
Можно ли просто отключить IPv6
Да, как диагностический тест — вполне.
Например, можно временно снять галочку IPv6 в свойствах сетевого адаптера или отключить привязку через PowerShell:
Disable-NetAdapterBinding `
-Name "Ethernet" `
-ComponentID ms_tcpip6Microsoft тоже описывает управление привязкой IPv6 через Disable-NetAdapterBinding.
После этого повторяем проверку.
Если проблема исчезла, мы почти наверняка нашли направление, в котором надо копать.
Но я бы не превращал отключение IPv6 во всеобщее универсальное решение.
Проблема не в существовании IPv6. Проблема в том, что VPN-клиент обработал одну семью адресов и оставил другую гулять напрямую.
Правильнее исправить маршрутизацию.
Отключить IPv6 навсегда — это примерно как снять дверь с машины, потому что сломался замок. Да, теперь замок не мешает. Но решение всё-таки странное.
Чтобы вернуть IPv6:
Enable-NetAdapterBinding `
-Name "Ethernet" `
-ComponentID ms_tcpip6Почему один и тот же сервер не означает один и тот же результат
После этой истории я окончательно перестал воспринимать фразу «подключены к одному серверу» как достаточное описание проблемы.
Один сервер — это только одна часть маршрута.
На результат также влияют:
режим работы клиента;
таблица маршрутизации;
IPv4 и IPv6;
DNS;
настройки браузера;
правила разделения трафика;
отдельные маршруты для приложений;
UDP;
локальные прокси;
виртуальные сетевые адаптеры;
сохранённые соединения;
настройки самой операционной системы.
Два устройства могут использовать одну подписку, один профиль и одну VPN-ноду, но фактически отправлять трафик совершенно по-разному.
Например:
Телефон:
весь трафик → TUN → VPN
Компьютер:
браузерный IPv4 → системный прокси → VPN
IPv6 → напрямую
часть приложений → напрямуюС точки зрения интерфейса в обоих случаях горит зелёная надпись:
ConnectedНо Connected означает только то, что клиент смог установить соединение с сервером.
Это не означает автоматически, что:
весь трафик проходит через сервер;
DNS проходит через сервер;
IPv6 проходит через сервер;
UDP проходит через сервер;
каждое приложение использует туннель;
прямой выход заблокирован при падении VPN.
Зелёная кнопка — не доказательство маршрута.
Маршрут надо проверять.
Чек-лист: сайт видит не ту страну
Теперь, когда пользователь говорит мне:
На телефоне работает, а на компьютере нет,
я не предлагаю сразу переустанавливать клиент.
Сначала проверяем по порядку.
1. Убедиться, что выбран действительно один сервер
Иногда профили называются одинаково, но ведут на разные ноды.
Проверяем IP сервера, домен, порт и идентификатор профиля.
2. Проверить IPv4 отдельно
curl.exe -4 https://ifconfig.co/ip3. Проверить IPv6 отдельно
curl.exe -6 https://ifconfig.co/ipЕсли второй запрос показывает адрес провайдера — уже интересно.
Если он завершается ошибкой, это само по себе не обязательно проблема. Возможно, IPv6 через эту конфигурацию просто недоступен, и система использует IPv4.
Главное, чтобы он не уходил напрямую тогда, когда пользователь рассчитывает на полный туннель.
4. Посмотреть маршруты
route print
route print -6Или:
Get-NetRoute -AddressFamily IPv4
Get-NetRoute -AddressFamily IPv65. Проверить режим клиента
Нас интересует, что реально включено:
системный прокси;
TUN;
VPN;
split tunneling;
global;
rule;
direct;
bypass LAN.
Названия отличаются от клиента к клиенту, но смысл один.
6. Проверить DNS
Get-DnsClientServerAddress
nslookup gemini.google.comВ браузере отдельно смотрим Secure DNS.
7. Полностью перезапустить браузер
Не просто закрыть окно.
Браузер может продолжать работать в фоне и сохранять старые соединения.
На время теста можно завершить все процессы:
taskkill /F /IM chrome.exe8. Проверить чистый профиль
Если оба IP уже правильные, но один конкретный сервис продолжает видеть старый регион, только тогда имеет смысл переходить к:
cookies;
аккаунту;
региональным настройкам;
кэшу;
данным сайта;
отдельному профилю браузера.
То есть сначала доказываем, что сеть работает правильно. Потом уже обвиняем Google.
Что из этого должен вынести разработчик VPN-сервиса
Для пользователя VPN — это одна кнопка.
Он не обязан знать, чем системный прокси отличается от TUN, что такое ::/0 и почему существуют A- и AAAA-записи.
Он нажал кнопку. Она стала зелёной. Значит, по его совершенно нормальной логике VPN включён.
Поэтому недостаточно просто успешно поднять соединение с сервером.
Клиенту желательно уметь проверять хотя бы базовые вещи:
есть ли прямой IPv6-маршрут;
совпадает ли внешний IPv4 с ожидаемым;
совпадает ли внешний IPv6 с ожидаемым;
используется ли нужный DNS;
не остался ли трафик вне туннеля;
корректно ли применились маршруты;
что произойдёт при падении соединения.
В идеале пользователь вообще не должен узнавать о проблеме от Gemini или банковского приложения.
Клиент сам может показать что-нибудь вроде:
Обнаружен прямой IPv6-маршрут.
Часть приложений может подключаться в обход VPN.И предложить понятное действие.
Потому что ответ:
У вас утечка IPv6, настройте маршрутизацию,
технически правильный, но обычному человеку он не даёт вообще ничего.
Итог
VPN-сервер был один.
Подключение было активно на обоих устройствах.
IPv4-адрес на обоих устройствах показывал Германию.
Но компьютер продолжал отправлять IPv6-трафик напрямую через российского провайдера.
Из-за этого разные сайты и приложения могли видеть разные страны, хотя пользователь выбирал один и тот же сервер.
Финальная схема выглядела так:
До исправления:
IPv4 → VPN → Германия
IPv6 → провайдер → Россия
После исправления:
IPv4 → TUN → VPN → Германия
IPv6 → TUN → VPN или блокировка прямого выходаГлавный вывод довольно простой:
VPN подключён — ещё не значит, что весь трафик действительно идёт через VPN.
Поэтому при странной геолокации не надо сразу удалять аккаунты, менять DNS двадцать раз и переустанавливать браузер.
Сначала две команды:
curl.exe -4 https://ifconfig.co/ip
curl.exe -6 https://ifconfig.co/ipИногда они объясняют всю проблему быстрее, чем час переписки с поддержкой.