V2Ray UDP NAT 浅析
随着使用代理软件代理 P2P 流量需求的增加,在代理软件中实现 Full Cone NAT 的需求也越来越高。本文将会浅析 NAT 行为及 V2Ray 中的 UDP NAT 实现。
NAT 行为定义
目前 NAT 行为由 RFC 5780 定义。NAT 的行为分为两类,分别是 Mapping Behavior (映射行为) 和 Filtering Behavior (过滤行为)。
NAT Mapping Behavior (NAT 映射行为)
映射行为是决定内部向外发送数据时,NAT 创建和重用端口的行为。
Endpoint-Independent Mapping (对端独立映射)
:对于来自内部同一个地址和端口的数据包,都会被映射为同一个地址。Address-Dependent Mapping (地址依赖映射)
:对于来自内部同一个地址和端口的数据且目标地址相同的数据包,都会被映射为同一个地址。Address and Port-Dependent Mapping (地址和端口依赖映射)
:对于来自内部同一个地址和端口的数据且目标地址和端口相同的数据包,都会被映射为同一个地址。
NAT Filtering Behavior (NAT 过滤行为)
过滤行为是决定数据包是否被转发到内部的行为。
Endpoint-Independent Filtering (对端独立过滤)
:对于来自外部的所有数据包,都会被转发到内部。Address-Dependent Filtering (地址依赖过滤)
:对于来自同一个外部地址的数据包,会被转发到内部,其余的数据包将被丢弃。Address and Port-Dependent Filtering (地址和端口依赖过滤)
:对于来自同一个外部地址和端口的数据包,会被转发到内部,其余的数据包将被丢弃。
与 RFC 3489 对应
那么 Full Cone 又是什么呢?其实,包括 Full Cone 在内的四种 NAT 行为,是由 RFC 3489 中定义的,它们与 RFC 5780 中的 NAT 行为有如下关系。
NAT Mapping Behavior (RFC 5780) | NAT Filtering Behavior (RFC 5780) | NAT Variations (RFC 3489) |
---|---|---|
Endpoint-Independent | Endpoint-Independent | Full Cone |
Endpoint-Independent | Address-Dependent | Restricted Cone |
Endpoint-Independent | Address and Port-Dependent | Port Restricted Cone |
Address-Dependent | Endpoint-Independent | |
Address-Dependent | Address-Dependent | |
Address-Dependent | Address and Port-Dependent | |
Address and Port-Dependent | Endpoint-Independent | |
Address and Port-Dependent | Address-Dependent | |
Address and Port-Dependent | Address and Port-Dependent | Symmetric |
V2Ray 的 NAT 行为
由于代理软件需要将收到的数据转发到外部,因此代理软件拥有类似于 NAT 的行为。
Mapping Behavior
Address and Port-Dependent (Split)
Address and Port-Dependent Mapping
是 V2Ray 的默认 NAT 行为,V2Ray 会根据目标地址和端口进行分流,从而实现将不同目标的数据包发往不同的出站。
而这个设计也体现在 VMess 和 Mux.Cool 协议中,对于 VMess 和 Mux.Cool 协议而言,每条连接仅在头部传输一个目标地址和端口,之后仅传输发往该地址的载荷。
Endpoint-Independent (Packet Addr)
V2Ray 在 v5.0.2 中引入了一个名为 Packet Addr 的特性,该特性是 V2Ray 实现 Endpoint-Independent Mapping 的一个“魔法”。
由于 V2Ray 的 transport.Link
并不支持传递目标地址和端口,而 V2Ray 代码又较为抽象和复杂,所以在 V2Ray 中实现 Endpoint-Independent Mapping
并不容易。Packet Addr 通过将目标地址及端口编码并附加在原本用于存储 UDP 载荷的字节切片中,并将目标地址设置为 sp.packet-addr.v2fly.arpa
,从而以较少的修改实现了将不同目标的数据包发往了同一个出站连接。
对于 Socks 5 和 Shadowsocks 等协议而言,在入站时,V2Ray 会将标准的 UDP 格式解码并编码成 Packet Addr,在出站时,V2Ray 会将 Packet Addr 解码,并以标准形式编码并发送。
而对于 VMess 等协议而言,由于 VMess 并不能在单条连接上传输发往不同目标的数据包,所以 V2Ray 会直接将 Packet Addr 编码后的数据直接作为 UDP 载荷在出站发出,在入站时,也不会对其进行解码。
最终,Packet Addr 会在 freedom 出站被解码,freedom 出站会将 UDP 载荷发往目标。
出站在收到数据时,会以类似的方式进行编码并传输至入站。
Filtering Behavior
V2Ray 的 freedom 出站不会对数据包进行过滤,所以 V2Ray 的过滤行为为 Endpoint-Independent Filtering
。
Packet Addr 的使用方法
在 V2Ray 中使用 Packet Addr 需要使用版本为 v5.0.2 及以上的版本。如果使用 VMess 入站,那么我们无需对配置进行修改。对于 Socks 5 和 Shadowsocks 等协议的入站,我们则需要使用 JsonV5
配置文件格式并在入站配置中将 packetEncoding
设为 Packet
。以下是一个示例配置:
{
"inbounds": [
{
"protocol": "socks",
"listen": "127.0.0.1",
"settings": {
"udpEnabled": true,
"packetEncoding": "Packet"
},
"port": 1080
},
{
"protocol": "vmess",
"settings": {
"users": [
"00000000-0000-0000-0000-000000000000"
]
},
"port": 0
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"address": "v2fly.org",
"port": 0,
"uuid": "00000000-0000-0000-0000-000000000000"
}
}
]
}
本文使用 CC BY-NC-SA 4.0 授权
本文链接:https://blog.akinokae.de/archives/v2ray-udp-nat/