摘要

  • PPP 的帧校验序列只覆盖规定的帧字段,不包括首尾 Flag、异步启停位以及为了透明传输而插入的比特或字节;发送端先算 FCS 再填充,接收端先去填充再校验。
  • 接收 ACCM 只允许在 FCS 计算前移除被选中的、数值低于 0x20 的控制字符,因为中间通信设备可能插入它们。这是一项按方向协商的窄规则,并非丢弃任意变化的许可。
  • 字节填充与比特填充以不同表示解决同一边界问题。FCS 合格只能说明还原后的帧通过了差错检测,不能证明身份、认证或物理路径从未被改动。

多出来的字符不一定属于帧

串行线路上的“所见即所得”很容易误导。端点发出的 PPP 帧本来没有某个控制字符,沿途的调制解调器、终端驱动或其他数据通信设备却可能把它插进字节流。如果接收端把它纳入 FCS,原本正确的帧会被判错;如果接收端随意删掉陌生字节,又会让真正的载荷损坏逃过检查。

PPP 的答案是一张边界明确的清单,而不是两种极端。RFC 1662 规定:接收 ACCM 标记的字符在计算 FCS 之前移除。于是被检查的对象不是每个观察点看到的原始串行字节,而是依照共同规则还原出来的 PPP 帧。

这并不是校验和“忽略错误”。恰恰相反,只有先把不属于帧的路径材料排除,校验和才能保护双方共同理解的对象。这里所谓“规范化”只是对处理顺序的编辑性概括;RFC 并没有定义一个叫作 canonicalization 的字段。

先算校验,再给路径穿上外衣

基本的 HDLC-like 帧以 0x7e 的 Flag 开始和结束,中间依次容纳 Address、Control、Protocol、Information、可选 Padding 与 FCS。默认 FCS 为 16 位,也定义了 32 位形式。计算范围从 Address 到 Padding,不包括 Flag、FCS 自身、异步启停位以及为透明传输插入的字节或比特。

在字节填充链路上,0x7d 是 Control Escape。发送端至少必须转义 0x7e 与 0x7d,避免载荷里的相同值被错当成结构。顺序不可颠倒:先对帧计算 FCS,再扫描两个 Flag 之间的内容。需要保护的字节变成 0x7d 加上“原值 XOR 0x20”。因此 0x7e 在线路上写成 0x7d 0x5e,0x7d 写成 0x7d 0x5d。

接收端在校验之前做逆操作:删掉 Control Escape,再把后一个字节 XOR 0x20。若 Control Escape 后面紧跟 Flag,这不是普通数据,而是中止该帧。临时的线路表示之所以可以撤销,是因为变换可逆且规则封闭,不是因为接收端可以凭感觉解释字节。

ACCM 管的是一个方向的最低要求

异步路径最麻烦的是 ASCII 控制字符。软件流控设备可能截获 0x11 的 XON 或 0x13 的 XOFF,甚至不理会最高的奇偶位。PPP 可以通过转义让这些值安全通过,也可以在接收侧忽略依配置认定为沿途插入的低位控制字符。

每个异步端点各自维护接收与发送两张图。接收 ACCM 为 32 位,覆盖低于 0x20 的值;发送 ACCM 最多可达 256 位。两端合计是四张图,而不是全网统一的一张禁用表。

LCP 中的 ACCM 选项类型为 2、长度为 6,携带四字节位图。某一位为 1,表示对端朝请求方发送时必须继续映射对应控制字符;某一位为 0,只表示“不必映射”,并不禁止对端因本地路径限制而多转义一些值。

这套权限安排有意保持不对称。接收方声明进入自己方向所需的最低保护,发送方保留更谨慎的空间。Configure-Nak 应建议双方需求集合的并集,让可能被路径无故插入的字符在接收时能够被识别。局部经验不会自动升级为其他链路的普遍政策。

默认值也体现了这种思路。异步链路初始接收图选择全部低位控制字符,协商后往往可以缩小;其他类型链路默认无需映射。先以保守默认获得互通,再让运行路径证明哪些开销可以去掉。

同一个问题,在同步链路上变成比特问题

比特同步链路不能照搬 0x7d 算法。发送端计算 FCS 后,每遇到连续五个 1 就插入一个 0,包括 FCS 内的序列;接收端在计算 FCS 前删去这个 0。这样,帧内部不会偶然形成 Flag 模式。

两种机制共享同一不变量:透明传输材料在生成校验值之后加入,在验证校验值之前移除。但它们的线路表示不同。异步到同步转换器负责完成填充方式转换。RFC 1662 还要求同步实现为兼容转换器而确认 ACCM 选项,同时明确:确认不等于同步端点自己执行字节映射。

这是一条重要的证据边界。选项被接受,可能是为中间转换组件保留合同,并不能直接证明最终端点的哪段代码做了什么。

被丢弃不等于 FCS 失败

PPP 还把帧格式错误和 FCS 错误分开。帧短得不足以容纳必要字段、Control Escape 悬在关闭 Flag 之前,或异步停止位违反字节成帧规则,都会被静默丢弃,但不计为 FCS 错误。比特填充链路出现超过六个连续 1,也属于同类无效帧。

因此,只盯 FCS 计数器可能看不到真正的故障。驱动去除转义之后的抓包,与原始串行侧抓包不是同一个对象。分析器若使用错误的 ACCM、错误的链路模式或错误的 FCS 形式,可能制造端点从未看到的“坏校验和”。

最低限度的事件记录应包括方向、双方收发 ACCM、链路模式、FCS 形式、抓包位置、解码阶段、无效帧计数,以及原始与还原后的字节。只有这些边界固定后,“FCS 错误”才是一条可解释的证据。

差错检测结束之处,才是安全问题开始之处

RFC 1570 允许按方向协商 Null、16 位或 32 位 FCS,并限制其生效阶段;控制与认证帧仍有校验要求。RFC 1662 还指出 NRZI 会削弱 16 位 FCS 的差错检测性质,因此建议在该情形使用 32 位形式。这些都是差错检测选择,不是身份认证。

RFC 1662 的安全章节另行警告:链路层可能不知道底层交换连接已经改变,插入攻击或呼叫身份伪造可能逃过其他环节的假设。一个 FCS 完好的帧完全可能来自错误的物理延续,因为多项式回答的是比特是否与校验值一致,而不是发送者是否有权出现。

PPP 的历史贡献因而十分克制。它让端点对“要校验哪一个帧”达成一致,同时容纳串行路径所需的可逆包装。机制有效,正因为例外是可枚举、分方向而且很小。一旦实现把它扩张为删除不便字节的普遍权力,完整性边界也就随之消失。

Sources