耗时两天!我在 SIP 协议的「Via 头域」里踩了个「NAT 陷阱」(附人类能看懂的避坑指南)

一、bug 现场:摄像头在内网「贴贴」,到公网却「装死」

先讲个魔幻现实的故事:

我用若依框架 + WVPPRO+ZLM 搭了个国标视频平台,在内网测试时,摄像头像乖巧的小学生,注册 - 鉴权 - 心跳一气呵成。结果部署到京东云服务器后,自家摄像头突然「失忆」:

  • 平台日志显示:设备发了注册请求,平台回了 401 鉴权(相当于「请出示密码」),但设备从此消失,再也不发带密码的二次请求;

  • 海康摄像头却「反骨」:同样的公网地址,秒级注册成功,画面流畅得让我怀疑人生。

核心矛盾 :为啥同样的公网环境,有的设备能通,有的不行?答案藏在 SIP 协议的一个小字段里 ------Via 头域

二、Wireshark 抓包:发现「神秘 IP」篡改了我的信令

祭出抓包神器tcpdump(命令记好:sudo tcpdump -i eth0 -w capture.pcap -C 10,自动分割 10MB 小文件,防卡死!),发现诡异现象:

  1. 平台回复的 401 消息里,Via 头域的 IP 是100.64.38.134,但这既不是设备公网 IP(113.132.64.61),也不是设备内网 IP(192.168.1.142),而是个陌生的内网 IP!

  2. 模拟设备改 UDP 通讯后报错:

    plaintext

    csharp 复制代码
    [ERROR] sip_check_response_via:Via头的IP(100.64.38.134)和本地IP对不上!

    翻译:设备收到平台的 401 后,检查「导航日志」(Via 头)发现地址不对,直接拒绝「交密码」,导致注册流程卡死。

三、科普时间:SIP 协议的「导航日志」------Via 头域

1. Via 头域是啥?相当于快递的「物流轨迹」

SIP 协议(GB28181 的信令核心)靠 Via 头域记录消息经过的每一跳地址,确保响应能按原路返回。比如你点外卖:

  • 请求消息(设备→平台):Via 头记「小区门卫(NAT)→外卖站(平台)」;
  • 响应消息(平台→设备):Via 头记「外卖站→小区门卫」,门卫再转给你。

关键参数(划重点!):

plaintext

css 复制代码
Via: SIP/2.0/UDP [当前节点IP:端口];received=[真实公网IP];rport=[真实公网端口]
  • 当前节点 IP :理论上是设备 IP,但可能被 NAT 篡改(如变成100.64.38.134);
  • received/rport:NAT 告诉设备「我真实的公网地址是这个」,但有些设备「死脑筋」,只信当前节点 IP。

2. 注册流程中的「鉴权卡死」

正常流程应该是:

  1. 设备发注册请求(带 Via 头:设备内网 IP:5060)→经 NAT 转成公网 IP:6272→平台;
  2. 平台回 401(Via 头应写平台公网 IP:5060)→但被神秘力量改成100.64.38.134:61300
  3. 设备收到后检查 Via 头 IP,发现和自己记录的平台公网 IP 对不上,直接摆烂,不发带密码的二次请求。

四、罪魁祸首:NAT 网关的「热心肠」搞砸了一切

1. NAT 是啥?内网设备的「公网伪装者」

你家路由器就是个 NAT 设备,作用是让多个内网设备(192.168.1.x)共享一个公网 IP。比如你手机发消息给服务器:

  • 发送时:NAT 把你的内网 IP: 端口(192.168.1.100:5060)换成自己的公网 IP: 随机端口(113.132.64.61:6272);
  • 接收时:NAT 看端口 6272 对应你手机,再转发给你。

2. 为什么 Via 头会被篡改?因为 NAT 的「过度热心」!

很多 NAT 网关自带 ALG(应用层网关),会主动解析 SIP 协议的消息内容,试图「修正」地址。但它搞错了一件事:

  • 当平台用5060 端口(SIP 默认端口)回复 401 时,ALG 认为「这个端口是 SIP 专用,得帮设备改回内网地址」,于是把 Via 头的 IP 改成自己的内网地址(100.64.38.134),不管服务器实际公网 IP 是多少;
  • 而用5061 端口时,ALG 觉得「这不是标准 SIP 端口,别瞎改」,Via 头保留正确的公网 IP,设备校验通过。

3. 四种 NAT 类型,哪种最坑?

NAT 类型 特点 对 SIP 的友好度
对称 NAT 每次访问不同目标,NAT 都会分配新的公网端口,还可能篡改 Via 头(坑王!) ★☆☆☆☆
完全圆锥 NAT 固定公网 IP: 端口,最友好 ★★★★★
受限圆锥 NAT 允许指定 IP 回连,中等友好 ★★★☆☆
端口受限圆锥 NAT 允许指定 IP: 端口回连,较友好 ★★★☆☆

我们遇到的大概率是「对称 NAT+ALG」,专门针对 5060 端口搞事情。

五、为什么海康设备能通?大厂的「NAT 防坑指南」

海康等大厂设备做了这些适配:

  1. 自动探测 NAT 类型:通过 STUN 协议获取真实公网地址,不依赖 Via 头;

  2. 宽松校验策略:优先信任 received/rport 参数,即使 Via 头被改也能继续交互;

  3. 支持端口自定义:允许手动设置公网 IP: 端口,绕过 NAT 的自动篡改。

而小众设备可能「轴」到只校验 Via 头的第一个 IP,导致一被篡改就罢工。

六、破案过程:从「玄学换端口」到协议层原理

1. 关键转折点:换端口为什么能解决?

  • 5060 是 SIP 的「知名端口」,容易被 NAT 的 ALG 识别并篡改 Via 头;
  • 换成 5061、5070 等「冷门端口」后,ALG 认为这不是 SIP 流量,不再篡改,Via 头恢复正确的公网 IP,设备校验通过。

2. 深挖 Via 头的「三层地址」

一个 SIP 消息可能包含三个地址,必须全部一致才能通:

  1. IP 层源地址:服务器回复 401 的真实源 IP(应是公网 IP);
  2. Via 头当前节点 IP:被 NAT 篡改后变成内网 IP(如 100.64.38.134);
  3. Contact 头地址 :设备注册时记录的平台地址(应包含公网 IP: 端口)。
    设备卡死的原因:只校验 Via 头的当前节点 IP,不管 IP 层源地址和 Contact 头。

七、终极避坑指南:从入门到入土的 NAT/SIP 知识

1. 新手必懂的 NAT 术语

  • NAT 地址转换:内网 IP→公网 IP,解决 IP 不够用的问题;
  • ALG 应用层网关:NAT 的「协议解析器」,可能好心办坏事;
  • STUN 协议:设备主动向服务器发送请求,获取自己的公网地址(防 NAT 篡改的利器)。

2. 抓包时必查的三个点

  1. 用 Wireshark 对比「IP 层源地址」和「Via 头当前节点 IP」是否一致;
  2. 查看 Contact 头是否包含正确的公网端口(如 5061 而非 5060);
  3. 检查设备日志是否有「Via 头校验失败」的报错(如sip_check_response_via)。

3. 进阶解决方案(换端口不管用怎么办?)

  • 关闭 NAT 的 ALG 功能:登录路由器后台,找到「SIP ALG」选项,手动关闭(部分设备支持);
  • 使用 STUN/TURN 协议:让设备主动获取公网地址,或通过中转服务器转发数据(适合复杂网络环境);
  • 手动配置端口映射:在路由器中固定映射服务器的 5060 端口到公网 IP(需管理员权限)。

八、总结:网络世界没有玄学,只有没吃透的规则

这个 bug 教会我们:

  1. SIP 协议的 Via 头很「轴」:部分设备严格校验 Via 头的第一个 IP,哪怕 received 参数是对的也没用;

  2. 知名端口是坑点:5060、80 等端口容易被网络设备特殊处理,换个端口可能「一键通关」;

  3. 抓包是救命稻草:所有玄学问题,最终都能通过对比协议字段找到答案(记得开 Wireshark!)。

最后想对设备厂商说:下次甩锅前,能不能先告诉我设备严格校验 Via 头?不过也好,现在我也算半个「NAT+SIP」专家了(叉腰)。

遇到类似问题的小伙伴,记住口诀:「Via 头不对别慌,先换端口再抓包,设备日志仔细瞧,NAT ALG 关掉!」 祝大家再也不踩 NAT 坑~ 😉

相关推荐
苏三的开发日记34 分钟前
linux搭建hadoop服务
后端
sir7611 小时前
Redisson分布式锁实现原理
后端
大学生资源网1 小时前
基于springboot的万亩助农网站的设计与实现源代码(源码+文档)
java·spring boot·后端·mysql·毕业设计·源码
苏三的开发日记1 小时前
linux端进行kafka集群服务的搭建
后端
苏三的开发日记2 小时前
windows系统搭建kafka环境
后端
爬山算法2 小时前
Netty(19)Netty的性能优化手段有哪些?
java·后端
Tony Bai2 小时前
Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域,AI 流量激增!
开发语言·人工智能·后端·golang
想用offer打牌2 小时前
虚拟内存与寻址方式解析(面试版)
java·后端·面试·系统架构
無量2 小时前
AQS抽象队列同步器原理与应用
后端
9号达人3 小时前
支付成功订单却没了?MyBatis连接池的坑我踩了
java·后端·面试