June 12, 2023

Поговорим про UDP 

# Особенности UDP в протоколах прокси

Большинство пользователей не имеют представления о роли UDP в протоколах прокси. В настоящее время даже опытные пользователи и разработчики сталкиваются с трудностями при работе с UDP, что приводит к тому, что многие проблемы, связанные с UDP, остаются нерешенными. Учитывая возрастающую важность UDP и отсутствие подробных статей о UDP в протоколах прокси, была написана данная работа.

Цель этой статьи - изменить текущую ситуацию и предоставить полное понимание UDP, что позволит более эффективно использовать Xray-core и другое программное обеспечение для проксирования.

## Основные концепции: IP-пакет, TCP-соединение, пятиэлементные наборы, порты, User Datagram Protocol

Это фундаментальные концепции, которые необходимо освоить. На самом деле они достаточно просты.

### IP-пакет

IP-пакет представляет собой отдельную единицу данных, соответствующую протоколу IP. Характеристики IP-пакетов включают:

1. Допустимость потери пакетов
2. Возможность изменения порядка доставки (т.е. порядок приема может отличаться от порядка отправки)
3. Ненадежность передачи

IP-пакет не является самым низкоуровневым элементом, и здесь нет необходимости углубляться в детали. Важно понимать его основные характеристики. IP-пакеты не могут обеспечить надежную передачу данных, что неудобно для приложений. Поэтому был разработан протокол TCP, ориентированный на установление соединения и основанный на IP-пакетах, но реализующий механизм надежной передачи. Большинство других протоколов могут быть реализованы поверх TCP.

### TCP-соединение

TCP-соединение определяется "пятиэлементным набором":

1. Идентификатор протокола TCP
2. IP-адрес отправителя
3. Порт отправителя
4. IP-адрес получателя
5. Порт получателя

После установления TCP-соединения обе стороны могут отправлять прикладные данные со своих портов на порты другой стороны. Это полнодуплексная передача, то есть обе стороны могут одновременно отправлять и принимать данные.

Концепция "порта" широко известна и часто упоминается вместе с IP, но на самом деле она не относится к протоколу IP. Порты принадлежат к протоколам более высокого уровня, таким как TCP и UDP. TCP и UDP реализуют механизм "портов", поэтому порты этих двух протоколов не влияют друг на друга. Протокол, используемый командой ping, - ICMP, он также основан на IP-пакетах и похож на TCP и UDP, но у ICMP нет концепции "порта". Стоит отметить, что обычные протоколы прокси могут проксировать только TCP или добавлять поддержку UDP, но не могут проксировать ICMP, поэтому ping через них невозможен. Для этого необходимо использовать традиционный VPN.

### UDP (User Datagram Protocol)

UDP, как и TCP, основан на IP-пакетах, но является чрезвычайно простым протоколом, полностью наследующим характеристики IP-пакетов:

1. Допустимость потери пакетов
2. Возможность изменения порядка доставки
3. Ненадежность передачи

UDP можно рассматривать как протокол, который просто добавляет механизм портов и проверку к протоколу IP.

В UDP отсутствует механизм "соединения", характерный для TCP. После получения локального UDP-порта можно напрямую отправлять прикладные данные на любой UDP-порт любого IP-адреса без необходимости установления соединения. При этом нет необходимости заботиться о том, получила ли другая сторона данные, и другая сторона также не подтверждает получение данных. (Примечание: существует промежуточное состояние "connected UDP", которое просто определяет адрес назначения перед отправкой данных, без реального установления соединения)

Эти характеристики UDP определили три основных сценария его применения:

1. Повышение эффективности, например, для запросов DNS (не требуется предварительное установление соединения)
2. Обеспечение работы в реальном времени, например, для прямых трансляций и передачи голосовых данных (допускается потеря пакетов, не требуется ожидание повторной отправки)
3. Комбинация двух предыдущих аспектов + поддержка P2P, например, для некоторых онлайн-игр и передачи голосовых данных. Это полное использование вышеупомянутых особенностей UDP, что является ключевым моментом данной статьи.

Существует еще один способ использования UDP: создание новых универсальных надежных транспортных протоколов на его основе, таких как KCP и QUIC. Почему эти новые протоколы основаны на UDP, а не напрямую на IP? Потому что прямая реализация на уровне IP часто требует от операторов изменения оборудования и систем на всех уровнях для обеспечения поддержки, что не всегда реалистично. Поэтому UDP стал более подходящим выбором для создания новых протоколов.

## Что такое FullCone и Symmetric NAT?

Эти два термина относятся к поведению NAT (Network Address Translation), то есть преобразованию сетевых адресов, которое выполняется домашними маршрутизаторами и провайдерами на различных уровнях. Широкое распространение NAT обусловлено нехваткой адресов IPv4, а также тем, что он может обеспечивать защиту устройств в локальной сети.

Для TCP поведение NAT не так критично, поскольку TCP представляет собой двунаправленный поток, и при каждом установлении TCP-соединения обычно используется новый временный порт, что соответствует новому пятиэлементному набору.

Однако для UDP поведение NAT имеет решающее значение, так как UDP представляет собой пакеты с неопределенным направлением, и часто используется один и тот же локальный UDP-порт для отправки пакетов различным целевым двухэлементным наборам.

Двухэлементный набор состоит из IP-адреса и порта. Любое отличие в этих элементах рассматривается как различные двухэлементные наборы.

Как маршрутизатор должен перенаправлять UDP-пакеты при их получении? Это зависит от типа NAT, реализованного в маршрутизаторе.

Существует множество видов поведения NAT, но рассмотрим наиболее показательные случаи. Представим следующие сценарии:

1. Локальный исходный двухэлементный набор A отправляет несколько пакетов удаленному целевому двухэлементному набору M.
2. Локальный исходный двухэлементный набор A отправляет несколько пакетов другому удаленному целевому двухэлементному набору N.

Если из-за различия целевых двухэлементных наборов маршрутизатор отображает A->M и A->N как A1->M и A2->N соответственно (обычно используя два разных порта для отправки пакетов) и строго ограничивает источник обратных пакетов, это относится к Symmetric NAT. Нетрудно заметить, что в этом случае коммуникация становится похожей на TCP-"соединение", что является практикой большинства провайдеров.

Если маршрутизатор рассматривает только исходный двухэлементный набор A и всегда отображает его как свой A1 для отправки пакетов к M и N, это относится к Cone NAT. Далее, если A1 получает обратный пакет, и маршрутизатор отправляет его обратно к A, не обращая внимания на источник, это относится к FullCone NAT. FullCone является наиболее благоприятным типом NAT для программного обеспечения прокси и необходимым атрибутом для P2P-игр (например, "NAT открыт" в GTA).

Приведенные выше примеры упрощены. На практике провайдеры редко предоставляют публичный IP-адрес напрямую, что означает, что прохождение через несколько уровней NAT и получение Symmetric NAT является нормой. Почему же использование протоколов прокси, таких как Shadowsocks или Trojan от Xray-core, позволяет получить FullCone NAT?

Это происходит потому, что в данном случае используется публичный IP-адрес вашего VPS, который не связан с вашей локальной средой NAT.

Вот почему важно специально настроить брандмауэр VPS: по умолчанию он будет фильтровать источник возвращаемых пакетов, что приведет к тому, что вы получите какой-то вид Restricted Cone NAT, а не FullCone NAT.

Для простых потребностей UDP, таких как запросы DNS, Symmetric NAT также может быть использован. Но для более сложных требований к UDP, таких как различные P2P-сценарии, реализация FullCone NAT становится критически важной. Приложению необходимо использовать фиксированный порт для отправки пакетов к любой цели и получения пакетов от любого источника без ограничений (по крайней мере, не должно быть ошибок в определении текущего типа NAT, что будет объяснено далее). Если ваша основная цель - игры, вы можете позволить UDP работать через протокол Shadowsocks, поскольку он обладает нативными характеристиками UDP.

Здесь стоит отметить, что Xray-core планирует внедрение протоколов, более подходящих для игр.

## Особенности UDP в Xray-core и некоторых прокси-протоколах

Xray-core поддерживает как FullCone, так и Symmetric режимы NAT, и предоставляет очень широкую поддержку протоколов, что делает его идеальным примером для рассмотрения.

Поддержка FullCone NAT реализована для:

- Shadowsocks (вход и выход)
- Trojan (вход и выход)
- Socks (вход и выход)
- Dokodemo-door TPROXY (вход, прозрачный прокси)
- Freedom (выход, с поддержкой разрешения доменных имен)

Поддержка только Symmetric NAT реализована для:

- VMess (структура протокола не поддерживает FullCone)
- Текущий Mux (также из-за структуры протокола)
- VLESS (FullCone в разработке)

Известно, что поддержка UDP в v2ray оставляет желать лучшего. Поэтому Xray-core был перестроен с учетом связанных архитектур и кода для входящих и исходящих соединений. После многократных тестов и устранения множества проблем была достигнута полная поддержка FullCone NAT (за исключением случаев, когда протокол не поддерживает его, для которых предусмотрен адекватный Symmetric NAT; у VMess в Clash есть проблемы с реализацией).

В заметках к релизу Xray-core содержится много полезной информации:

1. Socks5 и Shadowsocks используют нативный UDP, и их UDP-трафик не проходит через нижележащий транспорт.
2. VLESS, Trojan, VMess, Mux используют UDP over TCP и проходят через нижележащий транспорт.
3. HTTP на входе и выходе не поддерживает проксирование UDP, Socks до версии 5 также не поддерживает UDP.
4. FullCone в данном контексте относится к поведению NAT для UDP; при настройке особое внимание следует уделять конфигурации брандмауэра.
5. Для реализации FullCone NAT в цепочке прокси, как правило, все звенья должны поддерживать FullCone NAT.
6. Для реализации FullCone NAT в Docker сетевой режим связанных контейнеров должен быть установлен как Host.

Дополнительно отметим:

- Socks и Shadowsocks используют нативный UDP, и их UDP-трафик не подвергается специальной обработке при использовании TLS/WSS, если только не включен Mux. UDP-плагины SIP003 для Shadowsocks также не управляются.
- UDP over TCP (сокращенно UoT) важно отличать от нативного UDP. Даже если используются mKCP или QUIC в качестве нижележащего транспорта, UoT не будет демонстрировать те же характеристики, что и нативный UDP.

Проблемы, существующие в v2ray-core, здесь разъясняются, чтобы сделать UDP менее загадочным:

1. Архитектурно v2ray-core поддерживает только Symmetric NAT, поэтому любой протокол, используемый в v2ray-core, будет работать только в режиме Symmetric NAT.
2. Обработка UDP в v2ray-core похожа на логику TCP, и невозможно реализовать специфическое для UDP поведение FullCone NAT.​​​​​​​​​​​​​​​​