SIP UDP 分片与 Kamailio------精简 SIP 消息头
作为一家基于 Kamailio 搭建 SIP 代理呼叫处理平台的服务商,大型 UDP SIP 消息分片导致呼叫失败是我们日常技术支持中最常见的问题之一。
事实上,随着 SIP 协议日趋复杂,新增了大量以消息字段承载的扩展能力,同时 Opus、G.722 等新型语音编解码不断普及,这类问题正变得愈发普遍。但反观多数 SIP 终端设备,其对 UDP 分片重组的处理能力在过去几年中并未显著提升。即便部分终端支持 UDP 重组,劣质路由器与 NAT 网关仍会直接丢弃 UDP 分片包,设置 IP 头的不分片(DF)标志位也无济于事。可以肯定地说,UDP 分片消息在公网环境中无法被可靠传输。
sip_fragmentation
Kamailio 自身可以正常处理入站的 UDP 分片消息,但这并不意味着我们能将完整消息转发给对端------消息会在对端再次分片,最终大概率丢失。你可能已经想到,这对我们构成了独特挑战:Kamailio 是 CSRP 呼叫处理核心的关键组件,并非传统 SBC 或软交换中常见的背靠背用户代理(B2BUA)。
内置式 B2BUA 可在 A 侧宽松接收冗余的 SIP 消息,同时在 B 侧精简发送消息头,轻松规避分片问题。而 Kamailio 这类 SIP 代理仅属于单一逻辑呼叫链路 的一部分,且在多数场景下受协议标准约束,必须原样转发接收到的 SIP 消息,不得篡改。这就形成了"入脏出脏"的互通难题。客户选择我们的平台,正是看重其轻量化、高吞吐的特性,这也是他们需要接受的性能取舍。
即便如此,我们仍频繁被问及:能否借助 Kamailio 剔除臃肿 INVITE 消息中的部分 SIP 头,将消息体积压回通用 1500 字节 MTU 对应的约 1480 字节分片阈值以下? 如果可以,哪些头字段可以安全移除?这样做又会带来哪些影响?
首先,RFC 3261 已明确给出该问题的标准解决方案:
若请求消息大小与路径 MTU 差值小于 200 字节,或消息大于 1300 字节且路径 MTU 未知,必须 采用符合 RFC 2914 的拥塞控制传输协议(如 TCP)发送。若传输协议与最顶层 Via 头标识不一致,必须 修改该 Via 字段值。此举可避免 UDP 消息分片,并为大型消息提供拥塞控制。但实现方案必须支持处理最大数据报包大小的消息,对 UDP 而言,该上限为 65535 字节(含 IP 与 UDP 头)。
换言之,UDP 消息超过 1300 字节或接近 MTU 200 字节以内时,应改用 TCP 传输。
RFC 3261 强制要求所有兼容终端支持 TCP,但实际场景中,大量终端未启用、不支持或无法连通 TCP。尽管受硬件、内存、带宽升级及 WebRTC 等技术推动,SIP over TCP 正逐步普及且实用性提升,但它尚未实现全覆盖与全启用,再加上 NAT 等中间网络因素,对多数运营商而言并非可行方案。
在排除 TCP 方案后,业界通常会考虑移除非必要的 SIP 头字段,例如常见的:
Date: 17 Dec 2015 12:20:17 GMT
Allow: ACK, BYE, CANCEL, INFO, INVITE, MESSAGE, NOTIFY, OPTIONS, PRACK, REFER, UPDATE
User-Agent: SIPpety SIP BigSIP Rev v0.12 r2015092pl14
以及冗长的 SDP 媒体描述:
a=rtpmap:0 PCMU/8000
a=rtpmap:9 G722/16000
a=rtpmap:8 PCMA/8000
a=rtpmap:18 G729/8000
a=fmtp:18 annexb=no
a=rtpmap:101 telephone-event/8000
"我们能不能直接删掉这些内容?"
答案取决于你是否愿意突破标准规范的约束。
从标准层面来讲,代理服务器不应修改传输中的任何消息内容。RFC 3261 第 16.6 节(请求转发)明确规定:
代理需基于接收请求的副本进行处理,该副本初始必须包含原请求的所有头字段 ,未在后续处理流程中明确说明的字段禁止移除 ;应尽量保留原头字段顺序,不得重排序同名字段值 (见 7.3.1 节);不得新增、修改或删除消息体。
因此,标准层面不允许以任何方式篡改消息头。
但对于愿意突破规范的开发者,Kamailio 提供了大量超出标准代理权限的技术能力。我必须强烈提醒:能做不代表应该做。这一原则常被侧重销售的管理者忽视,但最终要为技术后果兜底的,却是一线工程师。
需要明确的是,除明显未标准化的 X- 自定义头外,所有 SIP 头字段均已标准化且具备特定用途。只不过部分用途优先级更高,在传统语音呼叫建立流程中,并非所有头字段的作用都不可或缺。代理对消息的适度篡改,可能不会对网络、主叫用户代理(UAC)或被叫用户代理(UAS)产生实质影响。
即便如此,我仍建议切勿轻率或主观地删减字段 。在穷尽所有可减小终端消息体积的配置方案前,不应将代理删减 SIP 消息作为首选手段,可优先尝试:
- 禁用未使用或非必要编解码,避免其出现在 SDP 中;
- 尽可能启用紧凑头格式;
- 切换至 TCP 传输;
- 若在可完全管控 MTU 的局域网内传输信令,可提升 MTU 值。
若你必须采取"非常规方案",请注意:绝大多数头字段不应移除,部分字段按 IETF RFC 规范绝对禁止移除。结合笔者经验,以下字段可相对安全地移除:
- User-Agent / Server:纯信息描述字段,移除后无功能影响;
- SDP 中的冗余编解码 :只要两端仍能协商出通用编解码,删减未使用编解码可显著减小负载。必须确保所有终端至少保留一种兼容编解码,且协商结果能满足全场景需求;
- Allow :在传统语音呼叫的 INVITE 流程中移除该字段,未观察到不良影响。RFC 3261 第 20.5 节指出,该字段在非 OPTIONS 请求中存在一定冗余性: Allow 头字段列出消息发送终端支持的请求方法;若存在该字段,终端支持的所有方法(含 ACK、CANCEL)均需列入。缺失该字段不应被解读为终端不支持任何方法,仅表示终端未声明支持能力。在非 OPTIONS 响应中携带 Allow 头,仅能减少消息交互次数。
- Date:终端时间标识无实际业务意义,可安全移除;
- Timestamp:除非业务环境依赖该字段做往返时延估算,否则可移除。
强烈不建议移除 Supported 字段 ,你无法确定一端支持的能力是否为另一端必需;同理,也不应修改 RFC 4028 定义的会话定时器相关头字段。其他常用标准头字段在任何情况下都不得修改。
上述方案仅为临时缓解手段,无法解决分片问题的根本矛盾。该场景下的最佳实践方案依旧是:
- 通过终端配置减小消息体积;
- 采用支持可靠重组的传输层协议(即 TCP);
- 部署内置式 B2BUA(CSRP 也已将其作为备选方案提供)。