STUN简介
STUN(Simple Traversal of User Datagram Protocol through Network Address Translators (NATs),NAT的UDP简单穿越)是一种网络协议,它允许位于NAT(或多重NAT)后的客户 端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于 NAT路由器之后的主机之间建立UDP通信。(摘抄自百度百科)
概括来说STUN就是一种用于穿越NAT的基于UDP的网络协议。其目的是在两个同时处于NAT之后的主机建立通信。
STUN协议由RFC3489定义,现在已经被RFC5389所取代。在RFC5389中STUN协议的全称是Session Traversal Utilities for NAT即解决NAT转换环境下的会话工具。STUN协议(RFC5389)本身并不是NAT穿越的解决方案,而是用来解决NAT穿越的工具。相比之前的RFC3489来说,这是个很重要的改变。因为RFC3489本身是NAT穿越的完整解决方案。
以下分析基于RFC5389
STUN报文结构
下图是stun请求的抓包
下图是STUN响应的抓包
图的左上部分是用WireShark抓包的16进制数据。右边是用Wire Shark的抓包截图。
左下部分是展开的16进制数据和含义的对应。
STUN Header
STUN协议首先包括20字节的Header部分。
Header中包含2个字节的STUN Message Type, 2个字节的Message Length,
4个字节的Magic Cookie,12个字节的Transaction ID。
以下是Header的格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0| STUN Message Type | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transaction ID (96 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Message Type:
0 1
2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+-+-+-+-+-+-+-+-+-+-+-+-+
|M |M |M|M|M|C|M|M|M|C|M|M|M|M|
|11|10|9|8|7|1|6|5|4|0|3|2|1|0|
+--+--+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 3: Format of STUN Message Type Field
在MessgeType部分中,前两位必须是00。当STUN和其他协议在同一端口复用时,来区分STUN的数据包。
然后其中两位的C0和C1组合表示的请求类型
0b00表示一个请求
0b01表示是一个指示
0b10表示是成功的响应
0b11表示是失败的响应
12位的M0到M11用于定义请求和指示
Message Length
Message Length表示STUN协议内容的总长度,不包含20字节的Header长度。
以之前的一个完整的响应报文为例子
可以看到Header中的Message Length为68,其中4个Attribute中的Message Length 为 8+8+8+26(1a) = 50, 其中每个Attribute带2个字节的Attribute Type和2个字节的Attribute Length, 即4x4 = 16, 再加上最后的两个字节的pandding。所以Header中的Message Length 为68。
Message Cookie
该字段为固定值2112A442。这么做一方面是向前兼容RFC3489,另一方面是为了通过这一属性可以判断客户端是否可以识别某些属性
Message Transaction ID
该字段为12个字节,目的是标识一次request对应的response。stun server收到request后,会以同样的transaction id回复。例如之前的图中的请求和响应的transaction id是相同的。
STUN Attributes
STUN Header后面有0至多个Attributes,每个Attribute都是TLV(type length Value)格式。2个字节(16bit)的type, 2个字节的length,而Value的长度并不是固定的。每个STUN协议的Attribute必须四个字节(32bit)的整数倍。如果不够,则用pandding填充对齐。
下图是Attribute的格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value (variable) ....
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 4: Format of STUN Attributes
下面以MAPPED-ADDRESS为例分析Attribute的格式
MAPPED-ADDRESS
MAPPED-ADDRESS属性表示客户端经过NAT后的映射地址。它由一个8位的address family和16位的端口号组成。接下来是固定长度的IP地址。如果adress family是IPv4类型的,那么adress是32位长度。如果adress family是IPv6类型的,则adress为128位长度。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0| Family | Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Address (32 bits or 128 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 5: Format of MAPPED-ADDRESS Attribute
MAPPED-ADDRESS中的起始8位必须设为0,并且这些0直接忽略即可。设为0的目的是为了凑够32位对齐数据,没有其他实际意义。
参考资料
https://datatracker.ietf.org/doc/rfc5389/?include_text=1
https://www.cnblogs.com/pannengzhi/p/5041546.html