Эмулятор Android и VPN на хосте: снимок DNS и NAT
Гость находится за NAT эмулятора и хранит список DNS, скопированный при загрузке. Поднимите туннель до загрузки, а затем проверьте разрешение имён изнутри гостя.
Когда эмулятор не может достучаться до сборочного сервера, который в браузере на хосте открывается без проблем, полезный вопрос не в том, работает ли VPN. Вопрос в том, какого из двух механизмов не хватает гостю. Виртуальное устройство запускает собственный сетевой стек за NAT эмулятора, а его список резолверов — это копия, снятая при загрузке, поэтому туннель, поднятый позже, меняет путь хоста, но не меняет то, о чём спрашивает гость. Понимание этих двух слоёв объясняет почти любой вариант этой проблемы.
Гость — это отдельный стек за фиксированным NAT
Пользовательская сеть QEMU размещает гостя на 10.0.2.15 со шлюзом на 10.0.2.2 и службой DNS на 10.0.2.3-10.0.2.6. Исходящий трафик гостя не пересылается ядром хоста; он преобразуется в сокеты на стороне хоста, по одному на каждое соединение гостя, и эти сокеты выходят из адресного пространства хоста. Поэтому соединение гостя наследует решение маршрутизации хоста в момент создания, и поэтому в таблице маршрутов гостя нет записи, которая называла бы ваш туннель.
Тот же механизм даёт гостю полезный обходной путь. 10.0.2.2 — это псевдоним интерфейса loopback хоста, поэтому служба, слушающая 127.0.0.1 на ноутбуке, доступна из гостя по этому адресу. Всё, что клиент открывает только на loopback, можно достать этим путём, и это основа описанной ниже схемы с прокси.
Чего гость не может — это наблюдать сеть хоста так, будто она является его пространством имён. Здесь нет моста, нет общей таблицы маршрутов и нет унаследованного резолвера; есть только служба NAT, принимающая его пакеты, и снимок, который сделал загрузчик при старте виртуального устройства.
DNS — это снимок, а не живая картина
При запуске эмулятор читает список резолверов хоста и передаёт гостю до четырёх серверов, доступных по адресам 10.0.2.3-10.0.2.6. Если в этот момент туннель был опущен, в снимке окажется тот публичный резолвер, которым пользовался ноутбук, и гость продолжает спрашивать его всю свою жизнь. Публичные имена разрешаются, внутренние — нет, и все симптомы указывают на VPN, тогда как настоящим устаревшим значением является список резолверов, снятый за несколько минут до поднятия туннеля.
Поэтому исправление порядка — не фольклор. Сначала подключите туннель, затем перезапустите эмулятор, и предпочитайте холодную загрузку восстановлению снимка, потому что образ быстрой загрузки может вернуться с ранее сохранённым состоянием резолверов и маршрутов. Если нужно увидеть, что гость использует сейчас, спрашивайте устройство, а не хост:
adb shell dumpsys connectivity | grep -i -A2 dns
adb shell ping -c 1 build.internal.exampleЭмулятор также принимает явный резолвер при запуске, что полезно, когда вы точно знаете, какой сервер должен использовать гость, и опасно, когда этот сервер публичный:
emulator -avd Pixel_API_34 -dns-server 203.0.113.53У публичного резолвера нет копии внутренней зоны, поэтому привязка одного превращает проблему порядка в постоянный отказ. Если профиль продвигает приватный DNS, позвольте туннелю его поставлять и холодно загрузите гостя вместо жёсткого прописывания адреса.
Маршрутизация следует за хостом в момент соединения
Поскольку соединения гостя становятся сокетами хоста, решение о маршрутизации принимается при создании сокета. Поднятый до запроса туннель означает, что запрос уходит через туннель с учётом маршрутов профиля. Профиль split-tunnel, несущий только выбранные префиксы, охотно оставит вашу приватную подсеть снаружи, поэтому доступность хоста — это гипотеза о госте, а не результат. Существующие соединения гостя сохраняют путь, с которым были созданы, поэтому клиент, переподключающийся заново, не всегда чинит зависшую загрузку внутри эмулятора.
Проверка относится к интерфейсу хоста, а не к статистике гостя:
sudo tcpdump -ni tun0 'host 203.0.113.10'Если запрос гостя появляется на интерфейсе туннеля, маршрутизация работает, и проблема выше. Если он появляется на физическом интерфейсе, профиль не покрывает этот префикс, и никакая перенастройка на стороне гостя этого не изменит.
Когда клиент предлагает только локальный прокси
Некоторые коммерческие клиенты вовсе не создают системный туннель. Они открывают HTTP- или SOCKS-слушатель на 127.0.0.1 и ожидают, что приложения будут им пользоваться. Гость не видит этот сокет напрямую, но эмулятору можно указать использовать его, либо через расширенные элементы управления (Settings, затем Proxy, ручной режим, хост 127.0.0.1 и порт, который печатает клиент), либо при запуске:
emulator -avd Pixel_API_34 -http-proxy http://127.0.0.1:8080Скопируйте порт из окна клиента; угаданный порт тихо отказывает и выглядит как сломанный туннель. Помните, что это покрывает: настройку HTTP-прокси соблюдают приложения, знающие о прокси, а сырые сокеты её полностью игнорируют. Если рабочая нагрузка — это нативный бинарник, открывающий собственные соединения, путь через прокси не эквивалентен туннелю, и сравнение нужно проводить явно.
Докажите результат изнутри гостя
Разделите разрешение имён и маршрутизацию двумя тестами в одной сессии. Имя, которое не разрешается, пока числовой адрес отвечает, — это проблема снимка DNS, и решение — порядок или явный резолвер. Если не работают оба, это указывает на маршрутизацию или политику, и следующая проверка — не запущено ли приложение в рабочем профиле с собственными сетевыми ограничениями.
Наконец, знайте, на каком госте вы тестируете. Очень старые образы эмулятора поставлялись без поддержки tun, которая нужна клиенту VPN на стороне гостя, поэтому ни один профиль там не мог работать; текущие образы её включают. Клиент, который не работает на текущем образе, — это проблема клиента или профиля, и пересборка туннеля хоста результата не изменит.