STUN 到底是个什么协议?为什么两个都躲在 NAT 后面的设备还能直连?哪些情况打不通?这篇解码真实的 STUN 请求和应答,用 RFC 5780 的方法测本机 NAT 的映射行为,并用一个小模拟推演打洞在四种 NAT 之间的成败。
请求(20 字节):00010000 2112a442 67292517 02fbd130 9a8cf24a
= 类型 0x0001(Binding Request)、长度 0、magic cookie 0x2112a442、事务编号 12 字节(随机)
服务器 2的应答 68 字节:类型 0x0101(Binding Success Response)、长度 48、事务编号与请求相同;属性:XOR-MAPPED-ADDRESS(8 字节);MAPPED-ADDRESS(8 字节);SOFTWARE(20 字节)
服务器 3的应答 68 字节:类型 0x0101(Binding Success Response)、长度 48、事务编号与请求相同;属性:MAPPED-ADDRESS(8 字节);RESPONSE-ORIGIN(8 字节);OTHER-ADDRESS(8 字节);XOR-MAPPED-ADDRESS(8 字节)
XOR-MAPPED-ADDRESS 的值 8 字节:保留 1 + 地址族 1(1 = IPv4)+ 端口 2 字节 + 地址 4 字节;线上的端口字段 0xe418,与 0x2112(cookie 的高 16 位)异或得 50442,我的本地端口是 50442,相同(NAT 没改端口);地址 4 个字节与 cookie 异或还原(公网地址不写出)
同一个应答里还有旧式的 MAPPED-ADDRESS(不异或):与 XOR 还原的地址 / 端口一致——旧版 STUN 直接写明文地址,会被某些改写载荷里 IP 地址的 NAT/ALG 破坏,异或就是为了避开它们
抓包:4 个 IP 包,请求 IP 总长 48 字节(20 IP + 8 UDP + 20 STUN),两个应答的 IP 总长 [48, 96] 字节;往返 RTT:服务器 2 207 ms、服务器 3 8 ms20 字节头加属性。应答里的 XOR-MAPPED-ADDRESS 把端口和地址与 magic cookie 异或,避免被改写载荷的 NAT 破坏;还原后得到 NAT 为这次连接分配的外部地址和端口。
服务器 1:不通告 OTHER-ADDRESS,没法做 RFC 5780 的映射测试 服务器 2:不通告 OTHER-ADDRESS,没法做 RFC 5780 的映射测试 服务器 3(本地端口 52182):主 IP:主端口 → 外部端口 52182;主 IP:另一个端口 → 外部端口 52182;另一个 IP:主端口 → 外部端口 52182;另一个 IP:另一个端口 → 外部端口 52182;公网地址是否一致:True;结论:与目的无关的映射(EIM) 服务器 4(本地端口 55093):主 IP:主端口 → 外部端口 55093;主 IP:另一个端口 → 外部端口 55093;另一个 IP:主端口 → 外部端口 55093;另一个 IP:另一个端口 → 外部端口 55093;公网地址是否一致:True;结论:与目的无关的映射(EIM)
用 OTHER-ADDRESS 向同一服务器的另一个 IP / 端口发请求,外部端口不变就是与目的无关的映射(EIM)。本机测出来是 EIM。
服务器 1:不通告 OTHER-ADDRESS,没法做过滤测试 服务器 2:不通告 OTHER-ADDRESS,没法做过滤测试 服务器 3:CHANGE-REQUEST 换 IP 和端口 → 没收到;只换端口 → 没收到;结论:两种都没收到——既可能是 NAT 的过滤,也可能是服务器根本没实现 CHANGE-REQUEST,这里无法区分 服务器 4:CHANGE-REQUEST 换 IP 和端口 → 没收到;只换端口 → 没收到;结论:两种都没收到——既可能是 NAT 的过滤,也可能是服务器根本没实现 CHANGE-REQUEST,这里无法区分 本机的 NAT:映射行为测出来是与目的无关(第二节);过滤行为没有测出来——公共 STUN 服务器大多不再实现 CHANGE-REQUEST(RFC 5780 已把这套测试标成可选),所以两种都没收到不能说明是端口受限。若收到了换 IP 的应答才能确定是全锥
要靠 CHANGE-REQUEST 让服务器换 IP / 端口回应。实测公共服务器没有回应,分不清是 NAT 过滤还是服务器没实现,所以本机的过滤行为没有确定。
重传时刻(计算,秒):[0, 0.5, 1.5, 3.5, 7.5, 15.5, 31.5];第 7 次发出后再等 16 × 500 ms = 8 秒才放弃,总共 39.5 秒 服务器 1:8 个请求收到 8 个应答,RTT 最小 203 / 中位 204 / 最大 206 ms 服务器 2:8 个请求收到 8 个应答,RTT 最小 185 / 中位 187 / 最大 233 ms 服务器 3:8 个请求收到 8 个应答,RTT 最小 5 / 中位 6 / 最大 13 ms 服务器 4:8 个请求收到 8 个应答,RTT 最小 94 / 中位 101 / 最大 103 ms
STUN 在 UDP 上用 500 ms 起翻倍的 RTO 重传,最多 7 次,总共 39.5 秒。
A \\ B 全锥 地址受限 端口受限 对称
全锥 通 通 通 通
地址受限 通 通 通 通
端口受限 通 通 通 不通
对称 通 通 不通 不通
16 种组合里 13 种能打通;打不通的是“对称 + 端口受限”和“对称 + 对称”(对称 NAT 给每个目的分配不同的外部端口,对方按 S 看到的端口去发,过滤规则不认)。这类情况只能走中继服务器(TURN)两边各向会合服务器报到,交换外部地址后同时发包。除了涉及对称 NAT 的几种组合外都能打通,打不通就要走 TURN 中继。