stun协议:穿越NAT障碍的基础

在现代互联网通信中,尤其是涉及到点对点(P2P)连接的应用,如语音通话、视频会议、在线游戏等,一个常见的挑战是网络地址转换(NAT)。大多数家庭和企业网络都使用NAT,允许多个设备共享一个公共IP地址上网。然而,这使得外部设备难以直接连接到NAT后面的内部设备,因为外部设备只知道公共IP地址,而不知道如何将数据包路由到内部的特定设备和端口。STUN(Session Traversal Utilities for NAT)协议正是为解决这一问题而设计的工具之一。

什么是STUN协议?它解决了什么核心问题?

STUN协议,全称为Session Traversal Utilities for NAT,是一种客户端-服务器协议。它的主要目的是允许位于NAT后面的客户端发现自己的公共IP地址和端口号,以及NAT的具体类型。简单来说,STUN客户端通过向公网上的STUN服务器发送请求,由服务器反馈客户端数据包到达时所显示的源IP地址和端口号。这个反馈的地址和端口就是该客户端在NAT设备上映射的公共地址和端口。

它解决的核心问题是:帮助NAT后的设备了解自己在公共互联网上的“身份”(公共IP:端口)。这个信息对于建立P2P连接至关重要,因为一个外部设备需要知道这个公共地址才能尝试连接到NAT后面的内部设备。

需要明确的是,STUN本身并不能“穿透”所有类型的NAT,也不能直接建立或维持连接。它仅仅是提供NAT映射信息的“侦察”工具。

为什么需要STUN来发现公网地址?

原因在于NAT的工作方式。当一个内部设备(例如,本地IP是192.168.1.100,端口是5000)向外部服务器发送一个UDP或TCP数据包时,NAT设备会截获这个数据包。NAT会将数据包的源IP和源端口替换为自己的公共IP地址(例如,公共IP是203.0.113.5)以及一个临时分配的公共端口号(例如,端口是60000)。然后,NAT会维护一个映射表,记录“内部IP:内部端口 -> 公共IP:公共端口”的对应关系(例如,192.168.1.100:5000 -> 203.0.113.5:60000)。当有外部响应数据包到达NAT的203.0.113.5:60000时,NAT会查找映射表,将目标地址和端口改回192.168.1.100:5000,然后转发给内部设备。

问题来了:内部设备本身并不知道NAT给自己分配的那个公共IP和端口(203.0.113.5:60000)。它只知道自己的内部IP和端口(192.168.1.100:5000)。如果它想告诉另一个外部设备“请连接我的公共地址以便P2P通信”,它不能提供192.168.1.100:5000,因为这个地址在公共互联网上不可路由。它需要知道203.0.113.5:60000。STUN的作用就是让内部设备能够通过外部的STUN服务器,间接地“看到”自己经过NAT转换后的公共IP和端口。

STUN的工作原理:客户端是如何获取公共地址的?

STUN的工作流程相对简单,通常基于UDP,但也可以使用TCP:

基本流程步骤(UDP示例):

  1. 客户端向STUN服务器发送绑定请求(Binding Request):

    位于NAT后的客户端(例如,内部IP:内部端口 = 192.168.1.100:5000)向公网上的STUN服务器发送一个特殊的STUN绑定请求消息。这个请求会包含客户端的内部地址信息(但服务器看到的是经过NAT转换后的外部地址)。

  2. 请求经过NAT:

    当请求数据包通过客户端的NAT设备时,NAT会执行地址和端口转换,将源地址/端口修改为NAT的公共IP和NAT分配的一个临时公共端口(例如,公共IP:公共端口 = 203.0.113.5:60000)。这个被转换后的数据包继续发往STUN服务器。

  3. STUN服务器接收请求:

    STUN服务器接收到来自客户端的绑定请求数据包。服务器看到数据包的源地址是NAT的公共IP和端口(203.0.113.5:60000)。

  4. STUN服务器发送绑定响应(Binding Response):

    STUN服务器创建一个绑定响应消息,其中包含一个特殊的属性:XOR-MAPPED-ADDRESS(或旧版本中的MAPPED-ADDRESS)。这个属性的值就是服务器接收到请求数据包的源地址和端口,即客户端在公共互联网上的地址和端口映射(203.0.113.5:60000)。响应消息的目标地址是客户端的公共地址(203.0.113.5:60000)。

  5. 响应经过NAT返回:

    STUN服务器的响应数据包以203.0.113.5:60000为目标地址发回。当它到达客户端的NAT设备时,NAT会根据其内部映射表(192.168.1.100:5000 -> 203.0.113.5:60000),将数据包的目标地址和端口改回192.168.1.100:5000,然后转发给客户端。

  6. 客户端接收响应并提取公共地址:

    客户端接收到STUN服务器的绑定响应。它解析响应消息,提取出XOR-MAPPED-ADDRESS属性的值(203.0.113.5:60000)。客户端现在就知道了自己的公共IP和端口映射,可以将这个信息提供给其他对等方,以便尝试建立P2P连接。

STUN消息的关键组成(属性 Attributes)

STUN消息除了头部外,包含一系列属性来承载信息。一些重要的属性包括:

  • MAPPED-ADDRESS: (较旧的STUN版本)表示STUN服务器从客户端请求数据包中看到的源IP地址和端口。
  • XOR-MAPPED-ADDRESS: (较新的STUN/RFC 5389)功能与MAPPED-ADDRESS类似,但其值是经过XOR操作混淆过的,以规避某些NAT可能修改已知值(如0.0.0.0)的问题,并提供了额外的数据完整性检查。这是推荐使用的属性。
  • SOURCE-ADDRESS: (Binding Response中)表示STUN服务器发送响应时使用的源地址(通常就是STUN服务器的地址)。
  • CHANGED-ADDRESS: (Binding Response中)表示STUN服务器另一个备用地址。客户端可以向这个备用地址发送请求,以测试NAT的行为(例如,是否会为不同的目标地址分配不同的公共源端口)。
  • ERROR-CODE: 如果请求失败,包含错误码和原因短语。
  • MESSAGE-INTEGRITY: 包含消息的HMAC-SHA1哈希值,用于验证消息的完整性和真实性。
  • FINGERPRINT: 包含消息的CRC-32校验和,提供额外的消息完整性检查。

STUN在哪些应用和场景下使用?

STUN最常见的应用场景是那些需要建立直接P2P媒体流的应用:

  • WebRTC: 这是现代浏览器实现实时通信(音视频通话、数据共享)的标准。WebRTC广泛使用STUN作为其连接建立过程(即ICE框架的一部分)的第一步,以发现端点的公共地址。
  • VoIP电话(如SIP软电话): 许多基于SIP或其他协议的网络电话应用使用STUN来帮助用户在NAT后注册并接收来电。
  • 在线游戏: 部分P2P架构的多人在线游戏可能使用STUN来帮助玩家之间建立直接连接。
  • 文件共享应用: 一些P2P文件共享客户端使用STUN来提高连接能力,允许其他用户更容易连接到自己。

在这些应用中,STUN通常不是独立使用的,而是作为ICE(Interactive Connectivity Establishment)框架的一部分。ICE是一个更高级的协议,它结合了STUN、TURN(Traversal Using Relays around NAT)以及本地地址发现等多种技术,尝试找到最佳的连接路径,首先尝试STUN发现的P2P路径,如果失败,则可能回退到使用TURN服务器进行中继。

STUN能识别哪些类型的NAT?如何帮助NAT穿透?

STUN协议通过向STUN服务器发送不同类型的请求(绑定请求,以及通过CHANGED-ADDRESS发送到备用地址的请求),并观察服务器的响应以及响应数据包到达时客户端看到的源地址,可以帮助客户端识别其NAT属于以下哪种类型:

  • 全锥形NAT (Full Cone NAT): 一旦内部地址 (IP:端口) 映射到外部地址 (IP’:端口’),任何外部主机都可以向 IP’:端口’ 发送数据包,并且这些数据包都会被转发到内部地址。
  • 受限锥形NAT (Restricted Cone NAT): 一旦内部地址映射建立,只有先前内部地址已经向其发送过数据包的外部IP地址,才能向该外部映射地址发送数据包并被转发。端口号是无关的。
  • 端口受限锥形NAT (Port Restricted Cone NAT): 比受限锥形NAT更严格。只有先前内部地址已经向其发送过数据包的外部IP地址和外部端口号,才能向该外部映射地址发送数据包并被转发。IP地址和端口号都相关。

对于前三种锥形NAT,STUN发现的公共IP:端口映射是相对稳定的,并且外部设备知道这个映射后,通常可以直接向其发送数据包(前提是符合NAT的过滤规则)。STUN提供这个映射信息,使得P2P连接成为可能。

STUN的局限性: STUN通常无法独立穿透对称型NAT (Symmetric NAT)。对称型NAT为每个新的外部目标地址或端口对内部会话分配一个不同的公共源端口。这意味着客户端与STUN服务器通信时获得的公共端口,与客户端试图与另一个对等方通信时NAT为其分配的公共端口是不同的。STUN只能告诉客户端与STUN服务器通信时用的那个端口,这个信息对于连接其他对等方没有用。在这种情况下,通常需要TURN协议进行中继。

STUN的局限性是什么?与TURN协议有什么关系?

正如上面提到的,STUN的主要局限在于它无法有效地应对对称型NAT,因为它只能发现到特定STUN服务器的映射地址,而对称型NAT对不同的目标会使用不同的端口映射。此外,STUN本身不提供数据中继功能。如果两个对等方都位于严格的NAT(如对称型NAT)之后,或者由于防火墙规则等原因无法建立直接连接,仅仅知道对方的公共地址是不够的,数据包仍然无法到达。

这就是TURN(Traversal Using Relays around NAT)协议发挥作用的地方。TURN服务器是一个中继服务器。当STUN无法帮助建立直接P2P连接时,客户端可以向TURN服务器请求分配一个公共中继地址和端口。然后,两个对等方都可以向TURN服务器的这个中继地址发送数据,TURN服务器负责在它们之间转发数据包。这样,即使无法直接连接,它们也能通过TURN服务器进行通信。

在ICE框架中,STUN和TURN是互补的。ICE客户端会:

  • 首先,收集所有可能的本地地址。
  • 然后,使用STUN服务器发现其公共映射地址。
  • 最后,如果STUN不足以建立连接(例如,遇到对称型NAT或防火墙阻止),则会向TURN服务器请求分配中继地址。

ICE会尝试所有这些“候选地址”,并选择最有效的路径进行通信,通常优先选择P2P路径(STUN辅助发现的路径),因为它延迟最低且不消耗中继带宽。

如何获取和使用STUN服务器?

STUN服务器可以是由服务提供商(如WebRTC服务)、开源项目或个人部署的。许多公共STUN服务器可以免费使用,例如由Google、Mozilla、Twilio等提供的。在应用中集成STUN功能通常是通过网络通信库或框架来完成的,例如WebRTC栈本身就包含了STUN客户端功能。

对于开发者而言,使用STUN通常只需要在应用的网络配置中指定一个或多个STUN服务器的地址和端口(标准的STUN端口是UDP/TCP 3478,TLS over TCP 5349)。应用底层的ICE实现会自动与这些服务器交互,执行地址发现过程。

部署自己的STUN服务器也是可能的,有多种开源实现可供选择,例如rfc5766-server项目中的STUN/TURN服务器。部署后,需要在防火墙中开放相应的端口(通常是UDP/TCP 3478)。

关于STUN的一些技术细节或误解

  • 轻量级: STUN请求和响应消息非常小,对带宽消耗微乎其微。其主要开销是引入了额外的网络往返延迟来获取地址信息。
  • 主要使用UDP: 虽然STUN协议也定义了基于TCP的传输方式,但在实践中,由于P2P媒体流(如WebRTC的RTP)主要使用UDP以减少延迟和开销,STUN也更常通过UDP传输。TCP STUN有时用于穿越更严格的防火墙。
  • 非安全协议本身: STUN协议本身不提供加密或强大的认证,但STUN消息可以通过MESSAGE-INTEGRITY和FINGERPRINT属性进行验证,防止篡改。在ICE中使用STUN时,整个通信会话的安全性(如使用DTLS和SRTP)由更上层的协议提供。STUN服务器无需存储任何用户数据,通常只处理请求并返回映射信息。
  • 不是万能钥匙: STUN不能解决所有NAT穿透问题,尤其是面对对称型NAT和严格的企业防火墙时。它仅仅是ICE工具箱中的一个重要工具。

总而言之,STUN协议是实时通信和P2P应用中一个基础且重要的组件。它通过与公共STUN服务器交互,巧妙地利用NAT的行为来帮助客户端发现自己在公共互联网上的地址映射,为后续尝试建立直接的P2P连接奠定基础。理解STUN的工作原理及其在ICE框架中的位置,对于构建或调试需要穿越NAT的应用至关重要。

stun协议