最近做一个服务器,在预先建立会话那块,协议提到了一种穿越NAT/防火墙的方案——ICE(Interactive Connectivity Establishment)交互式连接。ICE是通过综合运用STUN,TURN,RSIP等NAT穿透方式,使之能在最适合的情况下工作,以弥补单独使用其中任何一种所带来的固有缺陷。对于SIP来说,ICE只需要定义一些SDP(Sessionescription Protoc01)附加属性即可,对于别的多媒体信令协议也需要制定一些相应的机制来实现。
ICE的算法可以分为以下几个流程:
(1)收集本地传输地址
会话者从服务器上获得主机上一个物理(或虚拟)接口绑定一个端口的本地传输地址。
(2)启动STUN
与传统的STUN不同,ICE用户名和密码可以通过信令协议进行交换。
(3)确定传输地址的优先级
优先级反映了UA在该地址上接收媒体流的优先级别,取值范围0到1之间,按照被传输媒体流量来确定。
(4)构建初始化信息(Initiate Message)
初始化消息由一系列媒体流组成,每个媒体流的任意Peer之间实现最人连通可能性的传输地址是由公网L转发服务器(如TURN)提供的地址。
(5)响应处理
连通性检查和执行ICE算法中描述的地址收集过程。
(6)生成接受信息(Accept Message)
若接受则发送Accept消息,其构造过程与InitiateMessage类似。
(7)接受信息处理
接受过程需要发起者使用Send命令,由服务器转发至响应者。
(8)附加ICE过程
Initiate或Accept消息交换过程结束后,双方可能仍将继续收集传输地址。
简单的说,就是SIP终端在注册时,访问STUN服务器,获得NAT/防火墙的结构类型;当发起呼叫信息(INVITE)或接收到呼叫信息回应(200 OK)之前根据NAT/防火墙类型通过访问STUN服务器进行对RTP进行地址收集(任非对称性NAT/防火墙后需要收集NAT映射地址,在对称性NAT/防火墙后还需要收集TURN地址);然后在RTP的地址端口启动接收线程RSTUN服务程序;最后发送SIP消息,收集的地址放列SDP消息中的alt属性中。
STUN(Simple Traversal of UDP over NATs,NAT 的UDP简单穿越),首先在RFC3489中定义,作为一个完整的NAT穿透解决方案,英文全称是Simple Traversal of UDP Through NATs,即简单的用UDP穿透NAT。在新的RFC5389修订中把STUN协议定位于为穿透NAT提供工具,而不是一个完整的解决方案,英文全称是Session Traversal Utilities for NAT,即NAT会话穿透效用。RFC5389与RFC3489除了名称变化外,最大的区别是支持TCP穿透。
TURN,首先在RFC5766中定义,英文全称是Traversal Using Relays around NAT:Relay Extensions to Session Traversal Utilities for NAT,即使用中继穿透NAT:STUN的扩展。简单的说,TURN与STURN的共同点都是通过修改应用层中的私网地址达到NAT穿透的效果,异同点是TURN是通过两方通讯的“中间人”方式实现穿透。一般来说,实在P2P通信无法实现时,TURN就会派上用场。
STUN的报文结构为:
Ø 消息头
所有的STUN消息都包含20个字节的消息头,包括16位的消息类型,16位的消息长度和128位的事务ID。
字节
0 1 2 3
消息类型 |
消息长度 |
事务ID |
|
消息类型许可的值如下:
0x0001:捆绑请求
0x0101:捆绑响应
0x0111:捆绑错误响应
0x0002:共享私密请求
0x0102:共享私密响应
0x0112:共享私密错误响应
消息长度,是消息大小的字节数,但不包括20字节的头部。
事务ID,128位的标识符,用于随机请求和响应,请求与其相应的所有响应具有相同的标识符。
Ø 消息属性
消息头之后是0个或多个属性,每个属性进行TLV编码,包括16位的属性类型、16位的属性长度和变长属性值。
字节
0 1 2 3
属性类型 |
属性长度 |
属性值 |
|
…… |
属性类型定义如下:
MAPPED-ADDRESS
MAPPED-ADDRESS属性表示映射过的IP地址和端口。它包括8位的地址族,16位的端口号及长度固定的IP地址。
RESPONSE-ADDRESS
RESPONSE-ADDRESS属性表示响应的目的地址
CHASNGE-REQUEST
客户使用32位的CHANGE-REQUEST属性来请求服务器使用不同的地址或端口号来发送响应。
SOURCE-ADDRESS
SOURCE-ADDRESS属性出现在捆绑响应中,它表示服务器发送响应的源IP地址和端口。
CHANGED-ADDRESS
如果捆绑请求的CHANGE-REQUEST属性中的“改变IP”和“改变端口”标志设置了,则CHANGED-ADDRESS属性表示响应发出的IP地址和端口号。
USERNAME
USERNAME属性用于消息的完整性检查,用于消息完整性检查中标识共享私密。USERNAME通常出现在共享私密响应中,与PASSWORD一起。当使用消息完整性检查时,可有选择地出现在捆绑请求中。
PASSWORD
PASSWORD属性用在共享私密响应中,与USERNAME一起。PASSWORD的值是变长的,用作共享私密,它的长度必须是4字节的倍数,以保证属性与边界对齐。
MESSAGE-INTEGRITY
MESSAGE-INTEGRITY属性包含STUN消息的HMAC-SHA1,它可以出现在捆绑请求或捆绑响应中;MESSAGE-INTEGRITY属性必须是任何STUN消息的最后一个属性。它的内容决定了HMAC输入的Key值。
ERROR-CODE
ERROR-CODE属性出现在捆绑错误响应或共享私密错误响应中。它的响应号数值范围从100到699。
下面的响应号,与它们缺省的原因语句一起,目前定义如下:
400(错误请求):请求变形了。客户在修改先前的尝试前不应该重试该请求。
401(未授权):捆绑请求没有包含MESSAGE-INTERITY属性。
420(未知属性):服务器不认识请求中的强制属性。
430(过期资格):捆绑请求没有包含MESSAGE-INTEGRITY属性,但它使用过期
的共享私密。客户应该获得新的共享私密并再次重试。
431(完整性检查失败):捆绑请求包含MESSAGE-INTEGRITY属性,但HMAC验
证失败。这可能是潜在攻击的表现,或者客户端实现错误
432(丢失用户名):捆绑请求包含MESSAGE-INTEGRITY属性,但没有
USERNAME属性。完整性检查中两项都必须存在。
433(使用TLS):共享私密请求已经通过TLS(Transport Layer Security,即安全
传输层协议)发送,但没有在TLS上收到。
500(服务器错误):服务器遇到临时错误,客户应该再次尝试。
600(全局失败):服务器拒绝完成请求,客户不应该重试。
UNKNOWN-ATTRIBUTES
UNKNOWN-ATTRIBUTES属性只存在于其ERROR-CODE属性中的响应号为420的捆绑错误响应或共享私密错误响应中。
REFLECTED-FROM
REFLECTED-FROM属性只存在于其对应的捆绑请求包含RESPONSE-ADDRESS属性的捆绑响应中。属性包含请求发出的源IP地址,它的目的是提供跟踪能力,这样STUN就不能被用作DOS攻击的反射器。
属性空间分为可选部分与强制部分,值超过0x7fff的属性是可选的,即客户或服务器即使不认识该属性也能够处理该消息;值小于或等于0x7fff的属性是强制理解的,即除非理解该属性,否则客户或服务器就不能处理该消息。
现在在公网上有很多可以直接使用的STUN服务器,比如我在测试时使用的STUN服务器的IP为:77.72.169.211,端口为3478,以下为测试的其中事务ID部分直接填写为了全0,发送的数据如图所示,其中,发送的环境为windows 10环境,发送端口为50000端口:
测试抓包截图如图所示:
其中,在第三个服务器上同时抓包,目的就是验证STUN服务器发送给我的数据是否是正确的。
从本机发送到STUN服务器上的数据如图所示:
从STUN服务器返回到本机的数据如图所示:
在第三个服务器上抓取得到的数据包如图所示:
发送的源IP和源端口如图所示:
可见,本机的公网经过NAT映射后的IP和端口和STUN服务器返回的IP和端口数据保持一致。
当然,网上也有资料显示,如果一段时间经过NAT映射后的IP和端口没有被使用,那么NAT会回收相关的资源。所以这就要求在NAT后的终端定时和STUN服务器保持通信,防止NAT回收相关资源导致在后面的业务之中出现了错误。
结论:在一些种类的NAT下,STUN是一种穿透NAT的有效手段。