Правки в ядрах
В keqdroid два ядра: Xray-core 26.10.10 и mihomo 1.19.32. Оба собраны из исходников с правками: в готовых сборках нет того, на что опирается приложение, а ещё в них есть несколько ошибок, которые здесь исправлены. Сами правки лежат в tool/patches/.
Ядра собираются свежими версиями Go, в которых закрыты известные уязвимости самого Go: mihomo – на Go 1.26.10, Xray – на Go 1.27.3.
| Правка | Ядро | Где | Что даёт |
|---|---|---|---|
| версия клиента REALITY | mihomo | везде | серверы с минимальной версией клиента больше не отказывают в подключении |
| отпечаток Firefox | mihomo | везде | REALITY с fp=firefox работает на обновлённых серверах |
| кто открыл соединение | оба | Android | работают маршруты для отдельных приложений |
| список пакетов | mihomo | Android | TUN в mihomo вообще запускается |
| приложения вне VPN | оба | Android | исключённое приложение не может узнать адрес сервера |
| UDP по адресу | оба | везде | UDP идёт туда, куда указывают маршруты |
| TUN на ПК | Xray | Windows, Linux | раздельное туннелирование, честный пинг, аккуратная остановка |
| TUN на Linux | оба | Linux | TUN работает при строгой проверке обратного пути, WebRTC не выдаёт адрес, серверы на компьютере отвечают тем, кто подключается снаружи |
| память geo-баз | mihomo | везде | правила geosite и geoip занимают меньше памяти |
REALITY на mihomo
Версия клиента REALITY
Клиент REALITY сообщает серверу свою версию, а владелец сервера может задать минимально допустимую. mihomo всегда называет одну и ту же старую версию, и поменять её нельзя. Если на сервере минимальная версия выше, сервер отказывает, причём молча, как это принято у REALITY. Клиент пишет «REALITY authentication failed», хотя ключ и адрес верные, а тот же сервер через Xray работает.
Правка называет ту же версию, что и Xray в приложении. Эта версия ни от чего не защищает, она только сообщает серверу, какую редакцию протокола понимает клиент, а mihomo её понимает.
Отпечаток Firefox
С сентября 2026 года новые версии Xray на сервере отклоняют подключения без постквантового ключа. В mihomo отпечаток fp=firefox снят со старого Firefox, где такого ключа ещё нет. Поэтому, как только владелец сервера обновлял Xray, все серверы REALITY с fp=firefox на mihomo переставали работать с той же ошибкой «REALITY authentication failed». Отпечатки Chrome этот ключ содержат, но там, где пользуются приложением, фильтры режут подключения с отпечатком Chrome.
Правка ставит отпечаток свежего Firefox, такой же, как у Xray. Её уберут, когда mihomo сам обновит отпечаток.
Android
Кто открыл соединение
Маршрут для отдельного приложения срабатывает, только если ядро знает, какое приложение открыло соединение. Начиная с Android 10 система не сообщает это обычным программам. Ядро ничего не находило, и ни один маршрут по приложениям не работал.
Узнать, чьё это соединение, может только приложение, которому принадлежит VPN. Поэтому ядро спрашивает об этом keqdroid, а тот – систему. Никто другой на телефоне этим каналом воспользоваться не может.
В Xray заодно исправлено, о каком соединении спрашивать. Для сайтов, которые ядро определило по имени, оно и спрашивало по имени, а система знает соединения только по адресам, поэтому ответа не было.
Список пакетов
При запуске TUN на Android mihomo читает список установленных приложений. Начиная с Android 11 обычным приложениям это запрещено, и TUN не запускался, хотя приложение показывало «Подключено»: прокси работал, а через туннель не проходил ни один пакет.
Этот список нужен mihomo только для его собственного раздельного туннелирования, а в keqdroid раздельным туннелированием занимается система. Правка не читает список, если он не нужен.
Приложения вне VPN
Приложение, которое раздельное туннелирование вывело из VPN, всё равно может само отправить данные в туннель: на Android для этого не нужно особых прав. Тогда его данные пойдут через ваш сервер, и приложение узнает адрес сервера. Так, например, банковское приложение может заметить VPN.
Система знает о таких соединениях и на вопрос о владельце отвечает «не ваше». В этом случае keqdroid называет владельца особым именем, и первое правило конфига отклоняет соединение.
Закрыты и обходные пути:
- соединение отклоняется до того, как успело открыться, иначе сам факт открытия уже показывал бы, что туннель работает;
- через DNS туннеля такое приложение могло спросить, с какого адреса пришёл запрос, и получить адрес сервера. Теперь на запросы без известного владельца ядро отвечает только адресами сайтов;
- системный DNS в режиме «Только выбранные» тоже приходит без владельца, но приложение его узнаёт и пропускает, иначе выбранные приложения остались бы без DNS.
Если keqdroid не ответил ядру, соединение проходит: отклоняется только то, что система прямо назвала чужим.
UDP по адресу назначения
Оба ядра выбирали путь для UDP один раз, по первому пакету. Если игра сначала обращалась к адресу, которого нет в ваших маршрутах, её голосовой чат так и шёл по этому пути, куда бы его ни отправлял маршрут. А если первый пакет попадал под «Напрямую», мимо туннеля уходило и всё остальное.
С настройкой «UDP по маршрутам для каждого адреса» путь выбирается по маршрутам отдельно для каждого адреса. По умолчанию она выключена: звонки, игры и торренты, которые соединяются друг с другом напрямую, могут перестать это делать, если маршруты разведут их по разным путям.
TUN на ПК
Раньше TUN на Windows и Linux работал на sing-box, теперь это собственный TUN Xray. То, что умел sing-box, пришлось добавить в Xray:
- Раздельное туннелирование. «Только выбранные» отправляет все остальные программы мимо туннеля, а у Xray не было способа сказать «любая программа, кроме этих». На Windows имена программ теперь сравниваются без учёта регистра, иначе
winwordне совпадал сWINWORD.EXE. На экране «Соединения» видно, какая программа открыла соединение. - Пинг. При включённом TUN любой адрес, даже несуществующий, отвечал на
pingпримерно за 1 мс, потому что отвечал сам туннель. Теперьpingиtracertпоказывают настоящую сеть. - UDP-таймаут. Настройка «UDP-таймаут» в TUN закрывает UDP-сессии, которые простаивают. В Xray такого не было.
- Остановка. На Windows ядро останавливалось так резко, что не успевало за собой убрать: в настройках сети и в кэше DNS оставались следы туннеля. Теперь ядро останавливается так же, как по Ctrl+C.
- Счётчики трафика. Приложение читает их у ядра по локальному адресу, к которому может обратиться любая программа на компьютере. Раньше Xray отдавал там ещё и лишние сведения о себе, теперь только счётчики.
TUN на Linux
Здесь три правки. Первая есть только в Xray, две другие – в обоих ядрах.
- Строгая проверка обратного пути. Xray записывал маршрут в туннель в основную таблицу маршрутов Linux. Если в системе включена строгая проверка обратного пути (
rp_filter=1, её часто включают для защиты), Linux выбрасывал каждый ответ от сервера. По основной таблице ответ на этот адрес ушёл бы в туннель, а пришёл он через сетевую карту, поэтому Linux считал пакет поддельным. Ядро запускалось, но трафик не шёл. Теперь маршрут лежит в отдельной таблице, к которой ведут правила, как у sing-box и mihomo. Заодно в туннель попадает DNS-запрос к любому адресу, а не только к адресам из/etc/resolv.conf. - WebRTC. Браузер отправляет запросы WebRTC в том числе с адреса каждой сетевой карты. В mihomo такой запрос проходил мимо туннеля, и сайт видел ваш настоящий адрес. Теперь в обоих ядрах всё, что отправляют программы обычного пользователя, идёт в туннель.
- Ответы на подключения снаружи. Если на компьютере работает сервер, к которому подключаются из интернета, например Sunshine для стриминга игр, то в Xray его ответы уходили в туннель и до того, кто подключился, не доходили. Теперь оба ядра помечают ответы на соединения, начатые снаружи, и такие ответы уходят тем же путём, каким пришёл запрос. Так же делает sing-box. Для этого нужен nftables. Если его нет, ядро пишет предупреждение в журнал, и ответы по-прежнему уходят в туннель. Исключение – UDP-сервер, который не указывает, с какого адреса отвечать: его ответы тоже уходят в туннель. Sunshine адрес указывает.
Память geo-баз
mihomo при первом же правиле geosite или geoip навсегда оставлял в памяти список, которым не пользуется ни одно правило, а каждый загруженный список доменов хранил в двух копиях. Правка убирает и то, и другое. С российскими сайтами, рекламой и geoip:ru ядро занимает примерно на 2,5 МБ меньше памяти, с большими списками – около 5 МБ. На маршрутизацию это не влияет.