SIP理论篇12:NAT 环境下的典型故障与解决思路

SIP 在实验环境里通常不难跑通,但一旦进入真实网络,尤其是终端位于家庭宽带、企业内网、云上私网、运营商 NAT 或多层代理之后,问题就会明显增多。很多人第一次碰到 SIP 故障,看到的现象都很像:

  • 能注册但打不通
  • 能振铃但接通后无声
  • 单通
  • 呼叫过几秒自动断开
  • 对端收不到 BYE 或 re-INVITE

这类问题如果只从“是不是服务器坏了”去看,往往很难定位。根本原因通常不在 SIP 本身,而在于:SIP 是一个会在消息体、头域和媒体描述里携带地址信息的协议,而 NAT 恰恰会改变地址与端口映射关系。

所以,SIP 遇到 NAT 难,不是因为它天生不稳定,而是因为它的设计理念与 NAT 的工作方式天然存在冲突。

一、为什么 SIP 在 NAT 环境下容易出问题

先看 NAT 的基本行为。终端在内网时,通常使用私网地址,例如:

  • 192.168.x.x
  • 10.x.x.x
  • 172.16.x.x – 172.31.x.x

这些地址不能直接在公网路由。于是终端发包时,出口设备会把:

  • 源 IP 改成公网地址
  • 源端口改成映射后的公网端口

对一般 TCP/UDP 应用来说,这件事通常是透明的。但 SIP 不一样,它不仅有“网络层里的源地址”,还有很多“应用层显式携带的地址信息”,例如:

  • Via
  • Contact
  • Record-Route / Route
  • SDP 里的 c= 和 m= 信息

于是就出现一个经典矛盾:

网络层已经被 NAT 改成公网可达地址,但 SIP 报文内部仍然可能写着私网地址。

如果对端按照这些“写在 SIP 里的地址”来回消息或发媒体流,就很容易失败。

二、SIP 在 NAT 下为什么比很多协议更脆弱

SIP 的脆弱点主要体现在两个层面:

1. 信令层地址问题

信令报文的后续返回路径,可能依赖:

  • 源 IP / 源端口
  • Via
  • Contact
  • Route 集合

如果这些字段中混入了私网地址,而对端或代理又直接照单全收,就可能出现:

  • 注册成功但后续请求发不回终端
  • INVITE 到达不了实际终端
  • BYE / ACK / re-INVITE 路径错误

2. 媒体层地址问题

媒体通常用 RTP/RTCP,地址信息写在 SDP 里。如果 SDP 中声明的是内网地址,例如:

c=IN IP4 192.168.1.10
m=audio 4000 RTP/AVP 0 8 96

而对端又真的向 192.168.1.10:4000 发 RTP,那么公网对端当然无法把媒体送达内网终端。结果就是:

  • 无声
  • 单通
  • 双向都无声

这也是为什么很多人第一次做 SIP 测试会发现:信令看上去没问题,真正坏的是媒体路径。

三、先区分两类问题:信令问题与媒体问题

排障时第一步不要急着改配置,先区分问题出在哪一层。

1. 信令问题的典型表现

  • REGISTER 失败
  • 注册成功但来电送不到终端
  • INVITE 发出后收不到响应
  • 200 OK 发出后对端 ACK 回不来
  • 通话建立后 BYE 收不到

2. 媒体问题的典型表现

  • 能振铃、能接通,但没声音
  • 一方有声,一方无声
  • 前几秒正常,随后断流
  • 早期媒体失败,例如 183 场景听不到回铃音

这一步很关键,因为很多时候 SIP 消息本身交互完整,真正失败的是 RTP 路径;如果误把媒体问题当成纯信令问题,就会一直在错误方向排查。

四、NAT 下最常见的几个典型问题

1. Contact 写成私网地址,导致后续请求送不回去

终端注册时可能发送:

Contact: <sip:1001@192.168.1.10:5060>

如果服务端直接把这个 Contact 存下来,后面来电时就可能尝试把 INVITE 发到 192.168.1.10:5060。对公网侧或远端服务来说,这个地址显然不可达。

结果:

  • 注册看似成功
  • 但被叫无法收到来电

2. Via 中的返回地址不可达

一些响应路径会参考 Via 信息。如果 Via 没有正确反映终端真实可回达地址,或者代理没有启用对称响应处理,响应可能被发往错误位置。

结果:

  • 请求已到达服务端
  • 但响应回不到客户端
  • 抓包会看到客户端一直重传请求

3. SDP 中 c= 地址是私网地址,导致无声或单通

这是最常见的媒体问题。如果终端把内网地址写进 SDP,对端就会向错误地址发 RTP。

结果:

  • 呼叫建立正常
  • 媒体完全不通或单通

4. NAT 映射超时,导致注册掉线或通话中断

NAT 设备不会永久保存映射。对于 UDP,映射老化往往更快。如果终端长时间没有发包,NAT 表项被清理:

  • 后续 SIP 消息找不到原端口映射
  • 服务端发来的请求可能进不来
  • 媒体流也可能在空闲后断掉

结果:

  • 注册一开始正常,过一会儿失效
  • 通话中某些方向突然静音
  • 需要重新注册才能恢复

5. 多层 NAT 导致路径更加复杂

如果终端位于家庭路由器之后,而家庭路由器上游又是运营商 CGNAT,或者云上容器 / 虚拟机又嵌套 NAT,那么问题会更加棘手:

  • 公网地址并不真正属于终端出口设备
  • 端口映射不可预测
  • 外部打回来的路径更难稳定建立

此时单纯依赖手工端口映射往往效果有限。

五、最典型的故障现象应该怎么理解

1. 能注册,但打电话打不通

这说明至少有一部分信令是通的。常见根因包括:

  • 注册时服务端记录了错误的 Contact
  • 后续 INVITE 的目标地址不可达
  • NAT 表项超时,导致服务端回推请求失败

2. 能振铃,接通后无声

优先怀疑 SDP 地址、RTP 端口映射或媒体回程路径问题。信令通常已经跑通,问题多半出在媒体层。

3. 单通

单通往往比双向无声更有排障价值,因为它说明至少有一条 RTP 路是通的。通常应考虑:

  • 一侧 SDP 地址错误
  • 一侧 NAT 出口映射没建立成功
  • 一侧防火墙只放行了单方向或部分端口

4. 过几秒自动挂断

这可能和 ACK、re-INVITE、Session Timer、RTP 不通后的业务判定有关,也可能与 NAT 映射很快失效有关。不要一上来就只看“谁发了 BYE”,还要看之前是否有消息没有成功往返。

六、为什么 ALG 经常“越帮越忙”

很多路由器、防火墙会提供所谓的 SIP ALG(Application Layer Gateway),号称可以自动识别 SIP,并帮你修改报文里的地址和端口。

理论上它是为了帮助 NAT 环境下的 SIP 通过,但现实中它经常带来更多问题,原因包括:

  • 对 SIP 语法解析不完整
  • 对某些厂商私有头域兼容差
  • 对 TCP/TLS、SRTP 等场景支持不好
  • 错误改写 Contact、Via、SDP
  • 对多代理、Record-Route、分叉场景处理混乱

所以在很多工程实践中,一个很常见的经验反而是:

如果网络设备启用了 SIP ALG,且出现了莫名其妙的 SIP/NAT 问题,优先考虑先把 ALG 关掉再看。

七、SIP 处理 NAT 的几种常见思路

SIP 生态并不是没有应对 NAT 的办法,真正的关键是:不同办法解决的是不同层面的问题

1. 对称响应(rport / received)

服务端不要盲目信任报文里声明的返回地址,而是根据实际收到请求的源 IP/源端口来发响应。这可以缓解 Via 不可达导致的响应回不去问题。

它主要解决的是:信令返回路径

2. 注册保活 / keepalive

通过定期 REGISTER、CRLF keepalive、OPTIONS、双向 ping 等方式维持 NAT 映射不失效。

它主要解决的是:NAT 表项老化

3. 公网地址发现(STUN)

终端通过 STUN 发现自己在公网侧看到的地址和端口,再把这些信息用于 SIP/SDP 生成。

它适合一定范围内的 NAT 场景,但不是所有 NAT 类型都能完全搞定。特别是复杂或对称型 NAT 下,单靠 STUN 往往不够。

4. 中继转发(TURN / 媒体中继)

如果双方都很难建立稳定直连,就让媒体先走中继。这样终端不必直接暴露可达媒体地址,而是统一和中继服务器交互。

它主要解决的是:RTP 媒体不可达

5. SBC / Session Border Controller

SBC 是工程上非常常见的一种方案。它不只是做 NAT 穿越,还会承担:

  • 信令拓扑隐藏
  • 报文归一化
  • 地址改写
  • 媒体锚定
  • 安全策略
  • 互通兼容

如果是运营级、企业级或多网络复杂互通场景,SBC 往往比简单的“端口映射 + 参数修补”更可靠。

6. 让 SIP 走 TCP / TLS

这不能从根本上解决所有 NAT 问题,但对信令层通常更友好,因为:

  • 连接状态明确
  • 传输可靠
  • 某些 NAT 设备对 TCP 映射维持更稳定

但要注意:信令走 TCP/TLS,并不自动等于 RTP 媒体也解决了。

八、排障时最重要的思路:先看地址,再看方向

面对 SIP NAT 问题,最怕一上来就改一堆配置。更有效的方法是按下面顺序排查:

第一步:看信令里写了什么地址

  • Via
  • Contact
  • Record-Route
  • Route
  • SDP 的 c= / m=

只要看到私网地址出现在一个本应对公网可达的方向上,就要高度警惕。

第二步:看对端实际上往哪里发

有时报文里写的是私网地址,但中间设备做了修正;有时报文看似正常,但对端还是按旧地址发。一定要确认“对端真正发包的目标地址与端口”。

第三步:区分是回不来,还是根本没发出去

例如:

  • 客户端一直重传 REGISTER:可能是响应回不来
  • 200 OK 重复出现:可能是 ACK 回不去
  • 接通后单通:可能是某一方向 RTP 根本没到达

第四步:确认 NAT 映射是否保持住了

如果刚开始正常,过一会儿出问题,就要重点看 keepalive、注册周期、媒体保活和 NAT 老化时间。

九、原理层面可以记住的几个判断口诀

如果不想每次都从头分析,可以记住这几个非常实用的判断逻辑:

  • 注册成功不代表来电可达 —— Contact 可能仍然错
  • 振铃成功不代表媒体正常 —— SDP / RTP 可能有问题
  • 单通通常比双向无声更容易定位 —— 至少说明有一侧 RTP 路径是通的
  • ACK / BYE / re-INVITE 丢失,也可能是 NAT 问题 —— 不只是初始 INVITE 会受影响
  • 看到私网地址,不一定必然失败;但在公网互通场景下,必须高度怀疑

十、工程上更稳的解决方向

如果从纯原理走向工程实践,一般更稳定的方向通常是:

  1. 尽量让服务端具备 NAT 感知能力
  2. 优先启用对称响应和保活机制
  3. 谨慎使用 SIP ALG,必要时直接关闭
  4. 复杂网络优先考虑媒体中继或 SBC
  5. 不要只修信令,不处理媒体路径

很多部署失败的根源就在于:只解决了 REGISTER,没解决 INVITE;只解决了 INVITE,没解决 RTP;只解决了初始呼叫,没解决通话中的后续请求。

十一、总结

SIP 在 NAT 环境下的问题之所以常见,不是因为协议本身“脆弱”,而是因为它天然依赖地址信息,而 NAT 又天然会修改地址映射。两者叠加后,问题会在信令层和媒体层同时出现。

真正理解这类问题,可以抓住下面几条主线:

  • 信令问题关注 Via、Contact、响应路径、后续请求可达性
  • 媒体问题关注 SDP 地址、RTP 端口、单通/无声现象
  • 时序问题关注 NAT 映射老化、保活机制、长时间空闲后的可达性
  • 工程方案关注 rport、keepalive、STUN、TURN、SBC、媒体锚定等机制

只要把“报文里写的地址”和“网络里真实可达的地址”分开看,SIP NAT 问题就会清晰很多。

如果继续往下深入,最适合接着看的主题通常是:

  • SIP 重传机制与 Timer 全解析
  • 一次完整 SIP 呼叫流程抓包分析
  • SIP 常见无声、单通问题的排障思路

上一篇