进阶必读:代理协议 UDP 全方位透彻解析 #237
Replies: 15 comments 7 replies
-
深度好文,赞一下。 |
Beta Was this translation helpful? Give feedback.
-
第四步中的N是否为M之误? |
Beta Was this translation helpful? Give feedback.
-
TPROXY返回的时候建议使用raw socket,需要root权限,是的。 |
Beta Was this translation helpful? Give feedback.
-
感谢!写的很详细,特别是解释UDP那段,比那些ccnp老师解释接地气多了。 |
Beta Was this translation helpful? Give feedback.
-
看完想问如果以打P2P游戏为目的,目前是不是用Trojan效果较佳?(网络环境封UDP的情况下) |
Beta Was this translation helpful? Give feedback.
-
v2ray v5 新增了他们的 fullcone,能否讲一下? |
Beta Was this translation helpful? Give feedback.
-
感谢大佬写出这么详细的文章, 请教一个问题, 以socks入站和Freedom出站为例, Xray-core/tree/main/proxy/socks 哪些个struct里面字段对应上文里的 载荷与目标 这两个字段? |
Beta Was this translation helpful? Give feedback.
-
您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
|
Beta Was this translation helpful? Give feedback.
-
您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
|
Beta Was this translation helpful? Give feedback.
-
As a newbie I have been experiencing a lot of troubles setting up my servers firewall to allow EndpointIndependent filtering behavior with ubuntu 24.04 and ufw with iptables. |
Beta Was this translation helpful? Give feedback.
-
Особенности UDP в протоколах проксиБольшинство пользователей не имеют представления о роли UDP в протоколах прокси. В настоящее время даже опытные пользователи и разработчики сталкиваются с трудностями при работе с UDP, что приводит к тому, что многие проблемы, связанные с UDP, остаются нерешенными. Учитывая возрастающую важность UDP и отсутствие подробных статей о UDP в протоколах прокси, была написана данная работа. Цель этой статьи - изменить текущую ситуацию и предоставить полное понимание UDP, что позволит более эффективно использовать Xray-core и другое программное обеспечение для проксирования. Основные концепции: IP-пакет, TCP-соединение, пятиэлементные наборы, порты, User Datagram ProtocolЭто фундаментальные концепции, которые необходимо освоить. На самом деле они достаточно просты. IP-пакетIP-пакет представляет собой отдельную единицу данных, соответствующую протоколу IP. Характеристики IP-пакетов включают:
IP-пакет не является самым низкоуровневым элементом, и здесь нет необходимости углубляться в детали. Важно понимать его основные характеристики. IP-пакеты не могут обеспечить надежную передачу данных, что неудобно для приложений. Поэтому был разработан протокол TCP, ориентированный на установление соединения и основанный на IP-пакетах, но реализующий механизм надежной передачи. Большинство других протоколов могут быть реализованы поверх TCP. TCP-соединениеTCP-соединение определяется "пятиэлементным набором":
После установления 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-пакетов:
UDP можно рассматривать как протокол, который просто добавляет механизм портов и проверку к протоколу IP. В UDP отсутствует механизм "соединения", характерный для TCP. После получения локального UDP-порта можно напрямую отправлять прикладные данные на любой UDP-порт любого IP-адреса без необходимости установления соединения. При этом нет необходимости заботиться о том, получила ли другая сторона данные, и другая сторона также не подтверждает получение данных. (Примечание: существует промежуточное состояние "connected UDP", которое просто определяет адрес назначения перед отправкой данных, без реального установления соединения) Эти характеристики 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, но рассмотрим наиболее показательные случаи. Представим следующие сценарии:
Если из-за различия целевых двухэлементных наборов маршрутизатор отображает 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 реализована для:
Поддержка только Symmetric NAT реализована для:
Известно, что поддержка UDP в v2ray оставляет желать лучшего. Поэтому Xray-core был перестроен с учетом связанных архитектур и кода для входящих и исходящих соединений. После многократных тестов и устранения множества проблем была достигнута полная поддержка FullCone NAT (за исключением случаев, когда протокол не поддерживает его, для которых предусмотрен адекватный Symmetric NAT; у VMess в Clash есть проблемы с реализацией). В заметках к релизу Xray-core содержится много полезной информации:
Дополнительно отметим:
Проблемы, существующие в v2ray-core, здесь разъясняются, чтобы сделать UDP менее загадочным:
Относительно третьего пункта можно сказать следующее:
Такое поведение не ожидается, и в сочетании с некоторыми нестандартными тестовыми серверами это может привести к тому, что v2ray и VMess показывают FullCone NAT, который на самом деле не работает. В конце июля прошлого года этот вопрос обсуждался в группе разработчиков (касательно нестандартных тестовых серверов, таких как серверы Google, и запутанного поведения UDP в v2ray). После этого обновление NatTypeTester оставило только пять стандартных тестовых серверов и специально проверило исходящие адреса пакетов, что привело к отображению результатов для v2ray как UnsupportedServer. Кроме того, некоторые приложения Google сначала самостоятельно тестируют тип NAT текущей сети, и если обнаруживается ложный FullCone NAT, это может привести к различным странным проблемам. Действительно, поведение NAT не ограничивается только FullCone и Symmetric NAT, хотя это два крайних случая. Фактически, поведение NAT определяется двумя аспектами: "маппингом при отправке пакетов" и "фильтрацией при получении пакетов". FullCone NAT является самым открытым в обоих аспектах, в то время как Symmetric NAT является самым строгим. RFC 3489 определяет четыре классических типа поведения NAT, и v2ray на самом деле не подпадает ни под один из них напрямую. Его поведение наиболее близко к Address and Port-Dependent Mapping с Endpoint-Independent Filtering, что соответствует NAT Type 7 по RFC 5780. Однако v2ray может неправильно обрабатывать источник возвращаемых пакетов. Эти четыре классических типа включают:
Как Xray-core реализует протокол прокси с FullCone NATПринцип реализации FullCone NAT в Xray-core довольно прост: он отображает один из локальных UDP-портов на UDP-порт VPS, обеспечивая им одинаковое поведение. Рассмотрим самый простой сценарий: Socks на входе + Freedom на выходе.
Разумеется, для достижения FullCone NAT вызывающей стороне необходимо правильно использовать протокол прокси Socks, и обычно реализации tun2socks работают без проблем. Теперь добавим в сценарий Shadowsocks на входе и выходе:
Протокол Trojan для UDP работает по аналогичному принципу, но каждый исходный двухэлементный набор соответствует одному TCP-соединению, по которому передается "полезная нагрузка и цель" UDP. Почему же VMess, который также использует UoT, не может реализовать FullCone NAT? Основная причина заключается в том, что структура протокола UoT VMess позволяет передать "цель" только в самом начале, а последующие пакеты могут передавать только "полезную нагрузку" без "цели". Сервер будет отправлять все последующие пакеты на исходную "цель". Сервер отправляет пакеты обратно клиенту по той же логике: структура протокола содержит только "полезную нагрузку", и клиент будет считать, что все возвращенные пакеты исходят из первоначальной "цели". Это продолжение проблемы дизайна v2ray, и, конечно, встроенный Mux работает таким же образом. (VLESS также затрагивается, но уже находится в процессе изменений). Таким образом, для VMess, Mux и VLESS Xray-core в настоящее время использует маршрутизацию по принципу Symmetric NAT. В противном случае, если использовать режим FullCone NAT, последующие пакеты будут отправляться на адрес первого пакета, что является проблемой Clash с VMess, но это сложно исправить. Кроме того, когда такая проблема существует, VMess может быть ошибочно идентифицирован как FullCone NAT, что неверно. Принцип прозрачного проксирования UDP через TPROXY заключается в следующем: чтобы использовать Xray-core для реализации FullCone NAT для игровых консолей, обычно требуется Linux-устройство для настройки прозрачного прокси, например, Raspberry Pi. Почему для прозрачного проксирования UDP предпочтительнее использовать TPROXY, а не REDIRECT?
Когда необходимо отправить пакет обратно, программное обеспечение-прокси сначала создает локальный сокет, имитирующий "исходный двухэлементный набор источника возвращаемого UDP-пакета", и использует этот сокет для отправки пакета обратно. Это и есть принцип, из-за которого может возникнуть ошибка "слишком много открытых файлов". По сравнению с другим программным обеспечением, Xray-core имеет специальные оптимизации для этой ситуации, обеспечивая более элегантное и производительное решение. Если вы тестируете NAT прозрачного прокси в Windows, обязательно установите текущую сеть как "Частную сеть". Это распространенная ошибка, на которую многие уже наступили, включая автора. Отдельно стоит упомянуть о QUIC: когда активирован XTLS в Xray-core, трафик, идущий на UDP-порт 443, по умолчанию блокируется (блокировка QUIC). Это делается для того, чтобы приложения не использовали QUIC, а использовали TLS, благодаря чему XTLS будет действительно работать. На самом деле, QUIC сам по себе не подходит для проксирования, так как он уже включает в себя функции TCP, и использование UoT в этом случае означало бы двойное использование TCP. |
Beta Was this translation helpful? Give feedback.
-
绝大多数人对代理协议中的 UDP 部分完全没概念,目前很多经验丰富的使用者甚至是开发者一遇到 UDP 就变成小白,导致大多数关于 UDP 的问题悬而不决。鉴于 UDP 正扮演着越来越重要的角色,却没有一篇文章讲代理协议中的 UDP,我干脆写了这篇文章。
这篇文章的目的就是扭转现状,让大家完全参透 UDP,以便更游刃有余地使用 Xray-core 或其它代理类软件。 —— RPRX
简单理解 IP Packet、TCP Connection、五元组、端口、User Datagram Protocol
这些是必须掌握的基本概念,实际上非常简单。
IP Packet:一个个符合 IP 协议的数据包,允许丢包,允许乱序(即接收时的顺序不同于发送时的顺序),属于非可靠传输。
它不是最底层的形式,这里无需深究,重点是知道它的特性。IP 数据包无法直接提供可靠传输,这对应用来说当然是很不方便的,于是就有了面向连接的 TCP 协议,它基于 IP 数据包,但实现了一套连接和可靠传输机制,大多数其它协议直接放 TCP 上面即可。
确定一个 TCP 连接的是“五元组”:
TCP 连接建立后,双方便可从自己的端口向对方的端口发送应用数据,这是全双工的,即双方都可以一边发送数据、一边接收数据。
“端口”这个概念各位都不陌生,它常和 IP 一起出现,但实际上它不属于 IP 协议(没想到吧.jpg),它属于更上层的 TCP、UDP 协议。TCP、UDP 是分别实现了“端口”这一标识方式,所以这两个协议的“端口”不会互相影响。
ping
用到的协议是 ICMP,它也是基于 IP 数据包,与 TCP、UDP 是类似的,但 ICMP 就没有“端口”这个概念。顺便提一句,常见的代理协议只能代理 TCP 或加个 UDP,不能代理 ICMP,所以就无法ping
,有此需求请用传统的 VPN。接下来终于轮到我们的主角登场了:UDP(User Datagram Protocol)
虽然 UDP 和 TCP 一样是基于 IP 数据包的,但它异常简单,直接完全继承了 IP 数据包的特性:
你可以这样简单理解:UDP 协议就只是在 IP 协议的基础上加了一个端口机制和校验而已。
对于 UDP 而言,TCP 的“连接”机制也是不存在的,即你申请到一个本地 UDP 端口后,不需要握手/建立连接即可直接向任意 IP 的任意 UDP 端口发送应用数据。 不需要关心对方有没有收到数据,对方也不会告诉你有没有收到数据。(需要指出:存在一种 connected UDP 的中间状态,这只是在发送数据前确定一下目标地址,并没有真正去握手)
UDP 的这些特性催生了三种应用方式:
当然还有另一种对 UDP 的应用方式:基于 UDP 造新的通用可靠传输协议,比如 KCP、QUIC。为什么这些新协议不直接基于 IP 协议而要基于 UDP 协议?因为前者往往需要各级运营商进行设备、系统改造来支持,这显然不太现实,所以 UDP 成了更合适的选择。
那么 FullCone、Symmetric 又是什么?
这两个指的都是 NAT 行为,NAT 的全称为
Network Address Translation
,就是你家路由器、各级运营商做的事情:地址转换。NAT 的广泛存在是因为 IPv4 地址不足,另一方面它还可以保护局域网中的设备。对于 TCP 而言,NAT 行为是什么并不重要,因为 TCP 是双向的流,本机每发起一个 TCP 连接往往会使用一个新的临时端口,从而对应一个新的五元组。
但对于 UDP,NAT 行为可太重要了,因为 UDP 是方向不定的包,使用同一个本地 UDP 端口向不同的目标二元组发包十分常见。
那么这种情况下 UDP 数据包到达路由器后,路由器要怎样转发它呢?这就取决于路由器的 NAT 行为了。
其实 NAT 行为多种多样,这里先举例介绍最具代表性的情况。首先有以下情景:
如果由于目标二元组不同,路由器把 A->M、A->N 分别映射成了自己的 A1->M、A2->N(一般为分别使用两个不同的端口发包),且严格限制回包来源,就属于 Symmetric。不难发现,这时候实际上变成了类似 TCP “连接”的通信模式,也是大多数运营商的做法。
而若路由器只看来源二元组 A,始终映射成自己的 A1 向 M、N 发包,就属于 Cone NAT;更进一步,如果 A1 收到了回包,路由器不管来源,直接把这个包发回给 A,就属于 FullCone,也是代理类软件能实现的最佳 NAT 等级、P2P 游戏必备神器(GTA NAT 开放)
上面是简单举例,现实中运营商大概率不会主动分配给你一个公网 IP,也就是说还需要经过层层 NAT,最终得到 Symmetric 很正常。所以为什么你用了代理协议,比如 Xray-core 的 Shadowsocks、Trojan 就可以获得 FullCone?
很简单,因为此时用到的是你的 VPS 的公网 IP,和你本地的 NAT 环境没有任何关系。
这就是为什么要特殊设置 VPS 的防火墙:它默认会过滤返回的包的来源,导致你只能得到某种 Restricted Cone 而不是 FullCone。
对于简单的 UDP 需求比如 DNS 查询,Symmetric 也不是不能用。但对于复杂的 UDP 需求,比如各类 P2P 场景,实现 FullCone 就非常重要了,因为应用程序需要对外使用一个固定的端口,通过这个端口不受限制地往任何目标发包、从任何目标收包(至少别测错了当前的 NAT 类型,后文会说明)。如果你主要是为了打游戏,可以让 UDP 走 SS 协议,因为它拥有原生 UDP 的特性。
这里提一下,Xray-core 正计划着推出更适合打游戏的协议。
Xray-core 和一些代理协议中的 UDP 细节讲解
前面都是铺垫,终于到主菜了。
Xray-core 同时支持 FullCone 和 Symmetric 两种模式,且对协议的支持也非常全面,是很理想的例子。
完美支持 FullCone 的有:
仅支持 Symmetric 的有:
众所周知 v2ray 对 UDP 的支持一言难尽,所以 Xray-core 是重构了相关架构和各个出入站的代码,外加反复测试和对很多细节问题的定位、修复,才实现了全面 FullCone 化(除非协议不支持,这种情况会为它准备没问题的 Symmetric,Clash 的 VMess 存在问题)
Xray-core 的 release note 都很有营养,再摘抄一段:
补充:Socks、SS 是原生 UDP,套 TLS/WSS 后它们的 UDP 并没有被特殊处理,除非开了 Mux。SS 的 SIP003 插件也不管 UDP。
UDP over TCP 简称 UoT,特别注意,即使你用 mKCP、QUIC 作为底层传输方式,UoT 的也并不会表现出原生 UDP 的那些特性。
v2ray-core 存在的问题
这里主要是解惑,让 UDP 不再玄学。
对于第 3 点,简单来说是这样:
这种行为是预期之外的,再加上一些不标准的测试服务器,就会导致能给 v2ray、VMess 测出 FullCone,实际上却完全不起作用。
去年七月底我在某个开发者群内说过这个问题(不标准的测试服务器比如 Google 的那些,以及 v2ray UDP 的迷惑行为),随后 NatTypeTester 的更新只保留了五个标准的测试服务器,并特意验证了返回的包的源地址,测 v2ray 会显示 UnsupportedServer。
此外,Google 的一些应用会先自己测一下当前网络的 NAT 类型,若测出了假的 FullCone,就会导致奇奇怪怪的问题。
NAT 行为进一步探究
相信你已经发现了,NAT 行为并不只有 FullCone、Symmetric 这两种(但这是最极端的两种),实际上 NAT 行为由“发包时映射”和“收包时过滤”这两个行为来共同确定,FullCone 就是两者都最开放,Symmetric 就是两者都最严格,引用一张图:
可以看到,RFC 3489 定义了四种经典的 NAT 行为,v2ray 实际上不属于其中的任何一种,但它最接近 RFC 5780 的
Address and Port-Dependent Mapping
加Endpoint-Independent Filtering
,即图中的 NAT Type 7,只是可能会把返回的包的来源搞错。Xray-core 的代理协议如何实现 FullCone
这是本文的核心内容,其实原理很简单:把你本地的一个 UDP 端口映射为 VPS 的一个 UDP 端口,并使它们具有相同的效果。
拿一个最简单的场景举例:Socks 入站 + Freedom 出站
当然,FullCone 还需要调用方按常理使用 Socks 代理协议,诸如各种 tun2socks 实现一般是没问题的。
下面插一个 Shadowsocks 出入站进来:
Trojan 协议的 UDP 也是类似的原理,不同之处是每个来源二元组都会对应一条 TCP 连接,在 TCP 上传输 UDP 的“载荷与目标”。
那么为什么同样是 UoT 的 VMess 却无法实现 FullCone?
根本原因是 VMess 的 UoT 协议结构只能在最开始时传一个“目标”,后面的多个数据包只能传“载荷”而不能带“目标”,服务端会把后续的数据包都发往最开始的“目标”。服务端向客户端返回数据包是同理的,协议结构只有“载荷”,客户端会认为返回的数据包都来自于最开始的“目标”,这和 v2ray 设计上的问题倒是一脉相承的,自带的 Mux 当然也是这样。(VLESS 也没有幸免,不过在改了)
所以对于 VMess、Mux、VLESS,Xray-core 目前是按 Symmetric 来路由的。否则如果是 FullCone 模式,后面的包都会被发到第一个包的目标地址,这就是 Clash 的 VMess 存在的问题,但并不好解决。此外,存在此问题时 VMess 又会被测出 FullCone,假的。
透明代理 TPROXY UDP 的原理
为了让游戏机用上 Xray-core 实现 FullCone,通常需要一个 Linux 设备来透明代理,一个树莓派就可以搞定。
为什么透明代理 UDP 只能 TRPOXY 而不推荐 REDIRECT?
等需要往回发包时,代理软件会先在本地伪造出“返回的 UDP 包的来源二元组”的 socket,用这个 socket 把包发回去(这个原理就是可能会遇到
too many open files
的原因)。相比于其它软件,Xray-core 对这里有专门的优化,更优雅且有更好的性能。若你在用 Windows 测透明代理的 NAT,一定注意要把当前网络设为 专用网络,这是很多人踩过的大坑,我也踩过。
提一下 QUIC:启用了 Xray-core 的 XTLS 时,通往 UDP 443 端口的流量默认会被拦截(拦截 QUIC),这样应用就不会使用 QUIC 而会使用 TLS,XTLS 才会真正生效。实际上,QUIC 本身也不适合被代理,因为 QUIC 自带了 TCP 的功能,UoT 就相当于两层 TCP 了。
最后,仓库右上角点个 Star,谢谢!
Beta Was this translation helpful? Give feedback.
All reactions