耗时两天!我在 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 坑~ 😉

相关推荐
我的golang之路果然有问题3 分钟前
案例速成GO+redis 个人笔记
经验分享·redis·笔记·后端·学习·golang·go
嘻嘻嘻嘻嘻嘻ys15 分钟前
《Vue 3.3响应式革新与TypeScript高效开发实战指南》
前端·后端
暮乘白帝过重山24 分钟前
路由逻辑由 Exchange 和 Binding(绑定) 决定” 的含义
开发语言·后端·中间件·路由流程
CHQIUU28 分钟前
告别手动映射:在 Spring Boot 3 中优雅集成 MapStruct
spring boot·后端·状态模式
广西千灵通网络科技有限公司38 分钟前
基于Django的个性化股票交易管理系统
后端·python·django
_揽1 小时前
前端开发本地配置 HTTPS 全面详细教程
网络协议·http·https
CodeFox1 小时前
动态线程池 v1.2.1 版本发布,告警规则重构,bytebuddy 替换 cglib,新增 jmh 基准测试等!
java·后端
tonydf1 小时前
0帧起手本地跑一下BitNet
后端·ai编程
S&Z34631 小时前
[FPGA Video IP] Video Processing Subsystem
网络协议·tcp/ip·fpga开发·video
zzmgc41 小时前
常用JVM配置参数
后端