sip 与 nat 穿越

参考:

1. 概述

nat 穿越是一个复杂困难的话题,在较新的 ice 协议中,完整实现了各种 nat 场景下通信两端的 nat 穿越,webrtc 对 ice 协议有良好支持。
但是在 sip 协议中,nat 穿越是一系列较为复杂的 sip 扩展特性(且依赖于应用层的各种实现),sip 协议实际很难应对所有 nat 场景。
这篇文章主要记录自己对 sip 协议下 nat 穿越的理解。

2. sip agent 的网络类型

这里将 sip agent 分为 uac 端和 uas 端。根据 sip agent 在不同 nat 下的情况,实际上有如下几种场景:

  • uac 和 uas 在同一 nat 下,信令和媒体都能正常通信
  • uac 和 uas 在不同 nat 下,这时如果不进行
  • uac 在 nat 下,uas 在公网下
  • uas 在 nat 下,uac 在公网下

在 sip server 的部署中,一般都会将 server 部署在能让 sip client 直接能访问到的网络上,比如与 sip client 处于同一 nat 下,或者 server 部署在公网上。即使 server 部署在另一 nat 下,也会对 nat 进行静态地址映射。
所以为了方便分析,以下假设有一个 sip agent 客户端,其在 nat 下;一个 sip agent 服务端,其在公网下。sip client 向 sip server 进行注册。传输协议优先 udp。

2.1 nat 类型

nat 有如下类型:

  • Full Cone NAT(全锥型NAT),被 nat 网关映射到外网的 ip:port 地址对,任何一个外部主机/端口发送到此 ip:port 地址对的报文都会顺利通过
  • Restricted Cone NAT(限制锥型NAT),被 nat 网关映射到外网的 ip:port 地址对,只有与映射时目标外部主机相同 ip 的报文,才能顺利通过,即限制了 ip 地址
  • Port Restricted Cone NAT(端口限制锥型),被 nat 网关映射到外网的 ip:port 地址对,只有与映射时目标外部主机相同 ip:port 的报文,才能顺利通过,即限制了 ip 地址和 port 端口
  • Symmetric NAT(对称型NAT),前面几种 nat 类型,只要内网源 ip:port 地址对相同,nat 映射后的地址也相同。但是对称型 nat 只要目标地址不同,同一个内网源 ip:port 地址 nat 映射后的地址也不同

注意,这里只是简单描述几种 nat 类型,具体请参看其它文档。

3. sip 响应消息的穿越

sip client 发送过来的消息,sip server 能够得到 nat 映射后的 ip:port 地址对。

3.1 rport

sip client 发送请求给 sip server 时,via 头可以携带空的 rport 参数,sip server 进行响应的时候,会将 rport 设置为 nat 映射后的端口。

  • 请求:
  • 响应:

    via 中携带 rport 意味着 client 期待 server 将响应消息发送给 rport 的端口,而不是 sent-by 中的端口。
    sip server 端也可以根据应用层策略,强制将响应发送给 nat 映射后的端口。

3.2 received

在 sip 协议中,如果 sent-by 中的 host 是一个域名或者与 server 看到的 nat 映射后的地址不一样,server sip 协议栈会添加 received 字段表明 nat 映射后的 ip。
sip server 端也可以根据应用层策略,强制将响应发送给 nat 映射后的 ip。

3.3 tcp 的情况

如果 sip client 是通过 tcp 发送的请求,sip 协议规定优先使用原来的连接来发送响应。如果 tcp 已经断开,sip server 端也可以根据应用层策略,强制将响应通过 udp 发送给 nat 映射后的 ip:port 对。

3.4 总结

响应消息的 nat 穿越实际比较简单,即将响应发送给 nat 映射后的 ip:port 对即可。

4. sip contact

sip contact 是 sip client 的联系地址。sip server 需要将 client 的 contact 保存起来,用于 server 主动发送请求时确定目的地址。

4.1 注册时 contact

sip client 注册的时候,会携带一个或多个 contact 地址。当 sip client 处于 nat 下,contact 地址有两种情况:

  • ip:port 地址对是私网地址,sip server 无法主动发送请求给 sip client
  • ip:port 地址对是 nat 映射后的地址,sip server 可以主动发送请求给 sip client

对于 contact 是 nat 映射后的地址,为了保持 nat 地址映射,需要进行保活处理:

  • sip client 可以每隔不大于 30s 的时间发起刷新注册
  • sip server 可以每隔不大于 30s 的时间发起 option 请求

4.2 invite 时的 contact

invite 时的 contact 应该是来自于注册时多个 contacts 中的某一个,但是实际上也可以不一样。此 contact 地址也可能是私网地址,或者是 nat 映射后的地址。
对于 contact 是 nat 映射后的地址,为了保持 nat 地址映射,需要进行保活处理:

  • sip client 可以每隔不大于 30s 的时间发起会话内刷新 invite 请求,或者会话内 option 请求
  • sip server 可以每隔不大于 30s 的时间发起会话内 option 请求

5. sip 非会话内请求消息的穿越

实现 nat 穿越可以在 client 端主动实现,也可以由 server 端实现。

5.1 sip client 主动发现 nat

主动发现 nat 可以有如下方式(也可以称为 nat 地址映射/绑定):

  • 配置 stun 服务,sip client 通过 stun 服务实现 nat 地址映射
  • client 向 server 发送 option 请求,根据 rport 和 received 地址实现 nat 地址映射
  • client 第一次注册时,收到 401 响应后,根据 rport 和 received 地址实现 nat 地址映射

sip client 绑定 nat 地址映射后,将得到的映射后的 ip:port 地址对,修改 register 中 contact 地址,然后发起注册(register 401 后重新发起注册),这样 server 端就能得到 client nat 映射后的地址。

5.1.1 不同 nat 类型的讨论

sip client 主动发现 nat,考虑不同的 nat 类型下的有效性:

  • 通过 stun 服务发现的 nat 映射地址,如果 sip client 不是用映射地址发起的注册,那么 contact 地址只对 Full Cone NAT(全锥形 nat) 类型有效;否则,对所有 nat 类型都有效
  • 通过 option 请求后 rport 和 received 得到的 nat 映射地址,contact 地址对所有 nat 类型都有效,即使 sip client 不是用映射地址发起的注册
  • 通过 register 得到的 nat 映射地址,对所有 nat 类型都有效

5.2 sip server 发现 nat

如果 sip client 没有主动发现 nat 的动作,那么 server 端可以根据应用层策略,将 client 的 contact 地址强制替换为 nat 映射后的地址。并进行保活。

5.3 server 发送请求

非会话内请求直接查找 client 对应的 contact 地址,然后将请求发送过去(多个 contacts 的情况需要 fork)。
因为之前实现了 contact 地址 nat 映射的替换,且进行了保活,这里可以直接将请求发送过去。

5.4 tcp 下的考虑

tcp 下 server 主动发送请求与 udp 情况相同。

6. sip 会话内请求消息的穿越

  • 初始的 invite 如果是 sip client 发送的,那么 contact 地址(nat 映射后的,参见前文 contact 的内容)将被保存和保活。后面会话内请求从 sip server 发送给 sip client 的时候,直接使用 invite 接收的 contact 地址。
  • 初始的 invite 如果是 sip server 发送的,invite 穿越过程与 sip 非会话内请求消息的穿越 章节相同。后面会话内请求从 sip server 发送给 sip client 的时候,直接使用发送 invite 时的 contact 地址。

注意,invite 由 sip server 发送时,sip client 返回的 200ok 中携带的 contact 地址,sip server 可以根据本地策略是否保存下来作为后续会话内请求的目的地址。

7. rtp 媒体穿越

根据前文的假设,sip server 工作在 sip client 能直接访问到的网络中,所以从 sip client 发给 sip server 的媒体流能直接发送到。
但是 sip server 发送给 sip client 的媒体流,如果 sip client 端 sdp 中的媒体地址是私网地址,那么就无法发送过去。
sip client 也可以使用 stun 服务器主动发现 nat 映射后的媒体地址,但是此地址只对 Full Cone NAT(全锥形 nat) 类型有效,大部分情况下 nat 媒体流穿越将失败。
所以一种较好的做法是 sip server 先等待 sip client 发送媒体,得到 nat 映射后的地址后,sip server 再发送媒体流。
这里有一个问题是 sip client 或许不会发送任何媒体流给 server 端,这个时候就会出现信令走通但是媒体不通的情况。

7.1 tcp 下的考虑

由于 tcp 需要先建立连接,所以 sip server 可以修改 sdp 让 client 主动通过 tcp 连接上来,这样双方的媒体流就能打通。

8. 其它 nat 穿越的方式

一种典型方式就是 ALG(应用层网关),应用层网关能主动解析收发到的 sip 协议头和 sdp 内容,并进行地址替换。但是实际上许多路由器对于此功能的支持有限,有时还会影响正常 nat 穿越方法的实行。

posted @ 2022-03-05 15:16  小夕nike  阅读(1482)  评论(0编辑  收藏  举报