Привет, Хабр! На днях AdGuard выложил в открытый доступ свой VPN-протокол, который назвал TrustTunnel. В статье разберемся, чем он отличается от того же VLESS и как устроен на самом деле.

Проблема любых VPN-протоколов
AdGuard в своем анонсе заявляют, что любой VPN-протокол - это компромисс. Он либо быстрый, но легко детектится (WireGuard, OpenVPN, etc.), либо маскируется под обычный трафик, но тормозит.
Главная проблема тормозов в классическом подходе «устойчивых» протоколов - они обертывают VPN-данные в TCP-соединение и мимикрируют под HTTPS. Выглядит это в итоге как обычный веб-трафик, но TCP добавляет проблем со своим обязательным подтверждением доставки пакетов. При таком подходе, при потере одного пакета встаёт вся очередь пакетов, что приводит к классическому head-of-line blocking, жутко замедляя соединение.
Оповещаем о действиях РКН и новых блокировках в нашем телеграм
Как это работает в VLESS
VLESS (V2Ray/Xray) - популярный выбор для обхода блокировок. Протокол лёгкий, имеет минимумом фич, которые бы его замедляли и умеет маскироваться под HTTPS через TLS.
Но есть у него и нюанс - VLESS работает поверх TCP. Конечно можно использовать WebSocket, gRPC или QUIC как транспорт, но всё же для упрощения возьмем базовую схему - это TCP-обёртка со всеми вытекающими.
Плюс VLESS сам по себе не шифрует, а полагается на внешний TLS. Это гибко, но добавляет слой абстракции, а пользователи не всегда заморачиваются с этим.
Как это хочет решить TrustTunnel?
На официальном сайте компании написано:
"Unlike traditional VPNs that operate on packets, TrustTunnel operates on data streams. This design enables packet buffering — multiple packets can be combined before transmission, dramatically reducing confirmation overhead"
Звучит так, будто они объединяют несколько VPN-пакетов в один блок и так экономят на ACK'ах (подтверждениях доставки). Грубо говоря так и есть, но с небольшими оговорками.
Как реализовали
Смотрим PROTOCOL.md:
Для TCP-соединения каждое туннелируемое TCP-соединение получает отдельный HTTP stream через стандартный метод CONNECT:
CONNECT example.com:443 HTTP/2
:method: CONNECT
:authority: example.com:443
proxy-authorization: Basic <credentials>
После ответа 200 OK протокол начинает общение по HTTP/2 стримингу двунаправленным байтовым потоком. Данные просто текут туда-сюда, не дожидаясь ACKов.
В UDP-трафике все поинтереснее: вся UDP-дата мультиплексируется в один HTTP stream (_udp2). Каждый пакет оборачивается в структуру:
[Length 4B][Src IP 16B][Src Port 2B][Dst IP 16B][Dst Port 2B][App Name Len 1B][App Name][Payload]
Аналогично для ICMP - один stream _icmp для всех пингов.
Таким образом, трафик, проходящий через туннель, смешивается с обычным легитимным, ведь по сути им и является. И, опять же, в отличии от обычных «оберток», по умолчанию оборачивает все в streamы.
А где буферизация-то?
В спецификации протокола нет собственного механизма буферизации. TrustTunnel не оверинжинирит, а использует буферизацию, в которую уже умеют HTTP streamы.
┌─────────────────────────────────────────────────────────┐
│ TrustTunnel │
│ (любая дата, передаваемая через туннель) │
└────────────────────────┬────────────────────────────────┘
│ write()
▼
┌─────────────────────────────────────────────────────────┐
│ HTTP/2 (h2) / HTTP/3 (quinn) стрим │
│ flow control window в 131072 байт │
│ Собирается фрейм с max_frame_size до 16384 байт │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ TLS / QUIC │
│ + Записывают несколько запросов в один │
│ + Свои буферы отправки │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ TCP / UDP │
└─────────────────────────────────────────────────────────┘
Конфигурационные параметры
В CONFIGURATION.md описаны параметры, влияющие на агрегацию:
Параметр | Значение по умолчанию | Описание |
|---|---|---|
| 16384 байт | Максимальный размер HTTP/2 фрейма |
| 1350 байт | Размер UDP-датаграммы для QUIC |
| 1350 байт | Размер исходящих UDP-пакетов |
| 65536 байт | Таблица сжатия HPACK |
| 131072 байт | Окно flow control |
И сколько пакетов влезает в один фрейм?
Окей, возьмем значения по умолчанию.
Раз max_frame_size = 16384 байт, можно посчитать:
HTTP/2 со стандартными пакетами (~1400 байт MTU):
16384 / 1400 ≈ 11 пакетов на фрейм
HTTP/2 с мелкими пакетами (VoIP, ~200 байт):
16384 / 200 ≈ 81 пакет теоретически
(Но на практике VoIP-пакеты не буферизируются так агрессивно из-за требований к latency - отправляются по 1-3 штуки)
QUIC с крупными пакетами:
1350 / 1400 < 1 — нужна фрагментация
QUIC с мелкими пакетами:
1350 / 200 ≈ 6-7 пакетов
Динамический батчинг
Тут важно понимать: такой расчет типа «сколько пакетов отправится за раз» - это то, сколько может быть отправлено их максимально в одном фрейме. На деле же это динамическая величина.
TrustTunnel написан на Rust с асинхронным Tokio. По сути, всю софтварную задержку этим убрали:
Нет блокирующего потока, который «сидит и ждёт» N пакетов
Все данные сразу копируются в буфер библиотеки h2/quinn
Сама библиотека решает, когда делать flush и отправлять данные в фрейме
При медленной же сети TCP-буфер заполняется, а пакеты накапливаются в RAM. Когда сеть освобождается, TrustTunnel может отправить сразу серию фреймов
В конфиге max_frame_size (что очевидно из названия, такто!) задаёт верхний предел одного фрейма данных, а не фиксированный размер батча.
Выводы и сравнения
Просуммируем весь кайф протокола, который уменьшает его задержку еще раз:
Мультиплексирует много TCP-соединений в одну TLS-сессию. Не надо делать отдельный TLS handshake на каждое соединение.
Поддержка QUIC (HTTP/3) при использовании QUIC получаем отсутствие head-of-line blocking. Потеря пакета в одном stream не блокирует другие.
То, за что изначально боролась компания - энергоэффективность на мобильных устройствах - агрегация в HTTP стримы позволяет LTE-модему передавать данные одним разом и быстрее уйти в режим пониженного энергопотребления, вместо того чтобы посылать по одному пакету и не давать продыху.
И при всем этом он идеально маскируется под HTTPS: ведь буквально весь трафик выглядит как обычные CONNECT-запросы. DPI видит стандартный HTTP/2 over TLS.
И всё это теперь открыто под лицензией Apache 2.0 - можно форкать и использовать. Красота.
Сравнение с VLESS
VLESS + TCP/TLS | TrustTunnel | |
|---|---|---|
Маскировка | Похож на HTTPS | Стандартный HTTP/2 CONNECT |
Транспорт | TCP (+ WS/gRPC/QUIC) | HTTP/2 или QUIC |
TCP-туннели | Своя реализация | HTTP CONNECT per connection |
UDP | Своя реализация (XUDP) | Мультиплекс через один stream |
Head-of-line blocking | Есть (на TCP) | Нет (при QUIC) |
Шифрование | Внешнее TLS | TLS 1.2+ обязателен |
Макс. размер батча | Зависит от транспорта | 16384 байт (HTTP/2) / 1350 байт (QUIC) |
Буферизация | Своя или транспорта | Полностью на уровне HTTP/2/QUIC |
Таймауты по умолчанию
Connection timeout: 30 сек
Health check: 7 сек
TCP idle: 2 часа
UDP idle: 120 сек
TrustTunnel - это HTTP/2 CONNECT прокси с поддержкой UDP/ICMP через мультиплексированные streams. Главное преимущество - не какая-то революционная архитектура, а то что протокол использует стандартные HTTP-методы (CONNECT), что делает трафик максимально похожим на обычное проксирование, а сам трафик идет через стримы.
Полезные ссылки: Спецификация протокола, Конфигурация, TrustTunnel на GitHub, Сайт проекта, Обсуждение на HN
Оповещаем о действиях РКН и новых блокировках в нашем телеграм