摘要

  • RFC 3326 让后续 SIP 请求携带触发它的协议原因,因此相同的 CANCEL 或 BYE 动作可以对应不同的未接来电记录、业务选择和诊断结论。
  • Reason 可以被忽略,也不改变 SIP 状态机;协议命名空间、数值原因、创建或复制节点、映射规则与完整性证据必须分别保存,字段本身并不认证历史。

数字只有放回命名空间才有意义

Reason 最容易被错误理解为一个“错误码”字段,但它从一开始就不是单一编号表。每个值先写明协议,再附带数值 cause、可读 text 和扩展参数。SIP 原因使用 SIP 状态码,Q.850 原因属于电话网的 ISUP 语义。相同数字进入不同命名空间,不能假定含义相同。

这一区分不是数据库设计的细枝末节。若日志平台只保存 cause 整数而丢掉 protocol,后来的分析就无法知道它读到的是 SIP 响应、Q.850 释放原因,还是未来登记的另一套协议。把所有数字压进一张“错误码”表,等于在采集时永久删除解释它们的词典。

RFC 3326 要解决的原始问题更接近因果丢失。一个代理把 INVITE 分叉到多台设备,其中一条分支返回 200 OK 后,代理向仍在振铃的分支发送 CANCEL。另一种情况下,主叫在无人接听前放弃,代理也发送 CANCEL。两台终端看到的动作完全相同:振铃停止。

用户界面的结论却不相同。第一种情况不应该轻易显示“未接来电”,因为用户可能已在另一台设备接通;第二种情况通常值得留下未接记录。CANCEL 说明应该做什么,无法独自说明是什么事件触发了它。代理可以在第一种 CANCEL 中携带 SIP cause 200 与“在别处完成”的文本,让终端保留这个差异。

Reason 因而没有为每一种动机创造新方法。它保持 CANCEL 仍是 CANCEL,同时把原因作为独立对象随请求传递。它还可以出现在对话内的任意请求、任意 CANCEL,以及明确允许它的响应中。一个第三方呼叫控制器在另一段对话看到 486 Busy Here 后,可以用带 486 原因的 BYE 结束当前对话;网关也能把外部 ISUP 释放原因带入 SIP。

原因影响后果,却不参与核心处理

规范明确允许客户端和服务器忽略 Reason,并说明它不影响协议处理。即使接收方不认识原因,CANCEL 仍然取消,BYE 仍然终止。Reason 可以改变来电历史、业务逻辑或诊断说明,却不是请求生效的第二个开关。

这使它成为一种受限证据。抓包能够证明某一跳收到了带有某个值的字段,但不能单独证明另一分支真的接通、最终发送者亲眼观察到事件、中间节点没有修改内容,或终端确实按照它更新界面。每一个更强的结论都需要额外收据。

text 参数也不能取代结构化原因。人类可读短语可能被翻译、缩写或错误填写;机器语义仍由 protocol 与 cause 的组合决定。一个可靠的事件库应分别保存协议、数值、原始文本和扩展,而不是只保留最容易展示的字符串。

最初的 RFC 规定,一条消息可以有多个 Reason 值,但每个值必须使用不同协议,例如一项 SIP、一项 Q.850。RFC 9366 在 2023 年更新了规则:只有当某个已登记协议定义了同协议多值的含义时,才可以重复该协议;否则仍然每协议一个值。解析器不能仅凭逗号列表就自行发明“取第一个”“取最严重”或“全部成立”的规则。

IANA 的 SIP 参数注册表因此也是证据链的一部分。它说明哪些 Reason 协议和参数拥有公开语义。采集系统不但要留住字段,还要记录解析时使用的注册表或实现版本,避免后来新增的重复语义被旧解析器静默丢弃。

网关保存了原因,也改变了观察者

RFC 3398 规定了 ISUP 到 SIP 的映射。电话网一侧发来 REL 释放消息时,网关可以生成 SIP CANCEL,并把接收到的 Q.850 原因写入 Reason。SIP 终端不再只能从“被取消”猜测电话网发生了什么。

RFC 6432 后来允许除 100 Trying 之外的 SIP 响应携带 Q.850 原因。RFC 8606 又增加 location 扩展,在信息可用时说明呼叫在哪一类 ISUP 网络位置被释放。它表达的是本地、转接、远端等粗粒度网络位置,不是主叫或被叫的物理地址,更不是人的身份。

跨协议携带扩大了可见性,也引入新的不确定性。终端看到的 Q.850 值可能是网关从原始消息直接复制的,也可能经过映射、规范化或本地生成。最后一跳的发送者未必是最初观察者。若要把值用于高风险判断,就应保存原始外部消息、映射表版本、网关身份、生成时间、复制路径和完整性结果。

RFC 7044 的 History-Info 与 RFC 5806 的 Diversion 处理相邻问题:请求曾被重新定向到哪里、为何发生转移。它们不能替代 Reason。路径历史可以列出目标,却未必给出释放原因;一个释放原因也不能重建完整路径。不同证据层应被关联,而不是相互覆盖。

复制让因果存活,也让错误传播

代理收到带 Reason 的 CANCEL,再向下一跳生成 CANCEL 时,规范建议复制这个字段。否则取消动作会继续传播,原因却在中途消失,最终设备又只能从沉默猜测历史。

复制同时改变来源属性。终端看到的发送代理可能只是转述更早节点的陈述。若中间件删除字段、改写 text、用另一套表映射 cause,终端抓包无法复原最初观察。因此日志必须区分“直接观察”“本地生成”“协议翻译”和“原样转发”。

HERFP 场景进一步说明了完整性的价值。分叉 INVITE 中,一条分支的最终错误可能因为其他分支仍在进行而不能直接送达客户端。规范曾设想把最终状态封装进临时响应。攻击者若伪造或移除 Reason,客户端可能无法正确更新先前请求,甚至无法建立会话,所以 RFC 3326 建议使用适当的完整性保护。

完整性保护仍有边界。验证成功只能说明被保护的字节在确定端点之间没有改变,不能证明原始事件真实、最初陈述诚实或跨协议映射正确。缺少保护也不意味着原因毫无价值,而是意味着自动化需要更保守,并寻找分支响应、网关日志或终端状态等旁证。

RFC 3326 在互联网史上的价值,是让已经发生的状态转换不再独占叙事。动作告诉系统接下来做什么,Reason 让系统记住为什么走到这里。只有继续保存命名空间、观察者与变换过程,这份原因才不会在传递中被洗白成确定事实。

来源