РоссияНовостьМодели
Unbihex 0.2.3: разбор узких мест TCP, 938 подключений/с в REALITY и консольный клиент для роутеров и NAS
Недавно я выложил первую статью про Unbihex — легковесный VPN‑клиент и сетевое ядро на Rust, которое потребляет 12–18 МБ оперативной памяти. Ожидал дежурной критики и пары десятков просмотров, но публикация собрала больше 2000 прочтений, репозиторий перевалил за сотню звёзд, а в комментарии и Issues пришли практикующие сетевики и системные инженеры.
За прошедший день проект дорос до версии 0.2.3. Мы закрыли основные запросы сообщества (роутеры, NAS, JSON‑конфиги, TUI в стиле btop), добавили постквантовый обмен ключами, выложили тестовый сервер и провели глубокий аудит производительности, в результате которого обогнали Xray и sing‑box по скорости обработки новых соединений.
Но перед техническим разбором сразу закрою вопрос, который активно обсуждали в прошлый раз.
Под первой статьёй в комментариях справедливо заметили: «А почему у вас в contributors висит Claude?»
Отвечаю прямо и без прикрас: да, при разработке активно использовался Claude. Я пишу проект в одиночку, и перекладывать на LLM рутину — парсинг заголовков, шаблонный код сериализации serde, заготовки юнит‑тестов и первичную вёрстку интерфейса — это нормальная инженерная практика, которая экономит недели жизни.
При этом нейросеть не способна сама спроектировать рабочий сетевой стек. Она не поймает за тебя дедлок при двунаправленном копировании, не увидит в ss -tinm, почему ядро Linux зажимает окно приёма TCP на длинном линке, и не поймёт, почему кеш REALITY всегда мажет мимо из‑за плавающих байтов GREASE. Архитектура, отладка сетевых затыков, работа с системными вызовами и сведение всех узлов в рабочий бинарник — это ручная работа и постоянные тесты на стендах. ИИ здесь выступает как толковый напарник‑кодогенератор, а не волшебная кнопка «сделать VPN».
Самое крутое, что случилось после первой статьи — в репозиторий пришёл контрибьютор, который поднял изолированный стенд на 16 ядрах с эмуляцией задержек через netem, прогнал наш движок в паре «клиент ↔ сервер» против свежих Xray 26.10.29 и sing‑box 1.14.4 и принёс детальный пулл‑реквест (PR #18) с разбором трёх критических узких мест.
1. Окна ipstack по 16 КБ и сброс MSS до 536 байт
В режиме виртуального адаптера (TUN) пакеты внутри пространства пользователя разбирает библиотека ipstack. Оказалось, что в TcpConfig::default() у ipstack 1.0.1 размер окон приёма и отправки по умолчанию равен всего 16 КБ, а TCP‑опция MSS при установке соединения вообще не объявляется.
- Окно в 16 КБ даёт жёсткий потолок скорости 16 КБ / RTT на одно соединение. При пинге 100 мс это жалкие 1.3 Мбит/с.
- Отсутствие объявленного MSS заставляет вторую сторону считать, что MSS = 536 байт (стандартный минимум по RFC), из‑за чего трафик нарезается на мелкие пакеты и оверхед возрастает в ~2.5 раза.
Решение (perf/tun-tcp-windows): мы расширили окна до 256 КБ и явно прописали MSS = MTU - 60 (минус 60 байт вместо 40 — чтобы с запасом укладываться не только в IPv4, но и в IPv6 с опциями). На скачивание это дало прирост пропускной способности до 16 раз.
2. Почему клиент выдавал 0.85 МБ/с при пинге 50 мс
На локальном стенде без задержек (RTT ≈ 0) один поток через Unbihex спокойно выдавал 437 МБ/с. Но стоило добавить через netem реальную задержку в 50 мс (типичный пинг до европейского VPS), как скорость одного потока падала до 0.85–5.9 МБ/с, в то время как Xray в тех же условиях качал 88.6 МБ/с.
Диагностика сокетов через ss -tinm показала картину: у клиентского сокета rcv_wnd ≈ 50 КБ и rwnd_limited 73.8%. Ядро Linux не увеличивало буфер приёма автоматически, потому что асинхронный цикл в Rust вычитывал данные из сокета в userspace быстрее, чем приходили новые пакеты из сети. В итоге ОС считала, что приложению хватает крошечного буфера, и не разгоняла окно TCP под широкий канал.
Решение (perf/socket-buffers): добавили явную настройку буферов SO_RCVBUF и SO_SNDBUF (по 4 МиБ) через tune_tcp() на сокетах туннеля. Без единой другой правки скорость одного потока на RTT 50 мс взлетела до 85.8–93.5 МБ/с — вровень с Xray. Также внутри самого диспетчера сделали адаптивный буфер перекладки данных: соединение стартует со скромных 16 КБ (чтобы не тратить память на «молчащих» коннектах) и автоматически вырастает до 64 КБ, как только поток заполняется целиком.
3. Серверный REALITY: кеширование формы dest без GREASE
В проекте появился test_server — серверная реализация VLESS и REALITY на том же ядре unbihex-engine (собирается с флагом --features server). Он нужен для честных замеров пары «свой клиент ↔ свой сервер» без сторонних прослоек.
Чтобы неотличимо маскироваться под чужой сайт (dest), сервер REALITY при подключении своего клиента отправляет его ClientHello на реальный dest, читает ответ, забирает оттуда выбранный шифр, группу ключей и точные размеры TLS‑записей, после чего упаковывает свои EncryptedExtensions…Finished ровно в те же длины с помощью стандартного паддинга TLS 1.3 (RFC 8446 § 5.4).
Проблема первого варианта была в том, что сервер ходил на dest по сети на каждое рукопожатие, теряя 2–15 мс на ожидание ответа. В итоге сервер выдавал TTFB 5.57–6.86 мс и переваривал только 255–276 подключений в секунду.
Решение (perf/reality-shape-cache): мы закешировали структурную форму ответа dest на 10 минут по стабильным полям ClientHello (SNI, шифры, группы, ALPN, key_share). Главная тонкость — при вычислении ключа кеша пришлось принудительно вычищать плавающие байты GREASE (0x?a?a), которые Chrome меняет при каждом новом соединении (иначе кеш никогда не попадает).
Все замеры проводились на одном 16-ядерном стенде (loopback, протокол REALITY, задержка RTT 50 мс вносилась через netem на порту туннеля)
По итогу:
- На многопоточной прокачке (8 потоков) мы выдаём 1163 МБ/с (~9.3 Гбит/с), опережая Xray (806 МБ/с) почти на 45%.
- Время до первого байта (TTFB) сократилось до 1.98 мс.
- По скорости приёма новых подключений (938 conn/s) серверное ядро Unbihex обогнало и Xray (448 conn/s), и sing‑box (563 conn/s) почти в два раза.
Большую часть тикетов, которые читатели открыли после первой публикации, мы уже закрыли в релизах 0.2.2—0.2.3.
1. Консольный клиент unbihex‑cli, TUI как в btop и сборка под NAS
Владельцы домашних серверов, NAS (QNAP, Synology) и Linux‑машин без графики просили отдельный бинарник и конфигурацию через JSON (Issues #8, #12).
Теперь в релизах выходят статические сборки unbihex-cli под x86_64, ARM64 (aarch64) и ARMv7, которые работают на любом дистрибутиве Linux и в Docker без зависимостей:
- unbihex-cli — минималистичный демон для роутеров, NAS и скриптов. Умеет работать как с подписками, так и с прямым конфигом ядра (unbihex-cli run -c config.json, перечитывает конфиг по SIGHUP). Флаг --lan открывает прокси для всех устройств домашней сети с автогенерацией пароля.
- unbihex-cli-tui — та же консоль, но с полноэкранным интерактивным интерфейсом в духе btop: список серверов, проверка пинга, живые графики скорости, статистика трафика напрямую и через VPN, переключение нод на лету по Enter.
2. Режим «чистого туннеля» для роутеров (‑no‑routes)
В комментариях к прошлой статье пользователь @V1tol спросил: «Можно ли запустить клиент в режиме „я соединился, вот тебе tun интерфейс, больше ничего не умею“? Хочу поднять на роутере и маршрутизировать вручную».
Сделано. Команда unbihex-cli connect --mode tun --no-routes (или "auto_route": false в JSON) просто поднимает интерфейс unbihex0 и вообще не трогает системные таблицы маршрутизации и DNS. Дополнительно мы вынесли флаги --interface <имя> (привязка сокетов ядра к физическому линку) и --mark <n> (SO_MARK для разруливания трафика через ip rule fwmark).
Пример запуска на роутере, чтобы весь трафик локальной сети br-lan шёл в туннель, а сам роутер ходил напрямую:
Bash
sudo unbihex-cli connect --mode tun --no-routes --mark 255
ip route add default dev unbihex0 table 100
ip rule add iif br-lan lookup 100
sysctl -w net.ipv4.ip_forward=13. Постквантовый обмен ключами X25519MLKEM768 и защита от Replay‑атак
- Отпечаток Chrome и ML‑KEM-768: Клиент и сервер теперь из коробки поддерживают гибридный постквантовый обмен ключами X25519MLKEM768 и формируют приветствие ClientHello с отпечатком современного Chrome (JA4 t13d1516h2_8daaf6152771_…), а не стандартный палевный отпечаток rustls.
- Анти‑Replay на сервере: В отличие от Xray, проверку расхождения часов клиента (max_time_diff_ms, по умолчанию 120 с) в нашем сервере нельзя случайно выключить нулём, а все принятые client_random запоминаются на двойное окно времени. Если цензор запишет легитимный пакет и попробует воспроизвести его повторно, сервер прозрачно перенаправит его на настоящий маскировочный сайт dest, как обычного сканера.
4. Починка маршрутизации на Windows, Sniffing и честный хотсвап
- Windows DNS и Microsoft Store (Issue #2): Чтобы Windows не сливала DNS‑запросы мимо туннеля через Smart Multi‑Homed Name Resolution, в режиме TUN добавляется правило NRPT, заворачивающее весь DNS строго внутрь туннеля. А если у пользователя поверх стоит локальный AdGuard, антивирус или включён DoH в браузере (из‑за чего приложения приходят уже на реальные IP вместо fake-ip), встроенный Sniffing вытаскивает доменное имя из первых байтов TLS SNI или заголовка HTTP Host, так что доменные правила маршрутизации продолжают работать. Для приложений из Microsoft Store (WhatsApp, Xbox, Почта) в режиме системного прокси появилась кнопка снятия loopback‑ограничений Windows.
- Бесшовный хотсвап без утечки пакетов: Переключение серверов в клиенте и TUI происходит внутри работающего процесса без сноса TUN‑интерфейса и переписывания системных маршрутов. Во время смены ноды ни один пакет не выскакивает наружу с домашнего IP — можно переключать локации прямо во время работы с чувствительными зарубежными сервисами, не рискуя словить бан сессии.
- Журнал аварийного отката: Все изменения системных маршрутов и прокси перед применением пишутся в routes-journal.json и proxy-journal.json. Если компьютер внезапно выключится по питанию или процесс убьют через диспетчер задач, при следующем старте (или по команде unbihex cleanup) сеть автоматически вернётся в исходное состояние.
Базовое клиентское ядро, десктопный/мобильный клиент, консольные утилиты и тестовый сервер для замеров остаются полностью открытыми в репозитории под лицензией MPL 2.0:
github.com/crystalmasta/unbihex
Сейчас в работе поддержка оставшихся протоколов (Trojan, VMess, Hysteria2 — пока такие серверы из подписок аккуратно отображаются в списке серым цветом с указанием причины), сборка под macOS и серверная инфраструктура для интеграции с панелями управления.
Огромное спасибо всем, кто тестировал первые сборки, присылал баг‑репорты в Issues и помогал выжимать максимум из сетевого стека в PR! Если гоняете клиент на роутерах, NAS или нестандартных дистрибутивах — пишите в комментарии или в Issues, будем допиливать вместе.
Комментарии
0Обсудите материал с другими читателями.
Загружаем комментарии…