抓包、解码、一致性与版本迁移¶
协议验证的目标不是“工具能显示”,而是让第三方能够从原始证据重现:线上发送了什么、解析器为何接受或拒绝、应用最终采取了什么动作。

证据链¶
仅保存解码文本会失去原始帧边界和错误;仅保存原始字节又无法证明字段语义和应用行为。
测试对象清单¶
每次捕获建立记录:
测试编号:
日期、时间和时区:
协议与规范版本:
发送端硬件、固件、提交和配置:
接收端硬件、固件、提交和配置:
消息定义、方言或数据结构描述语言(DSDL,Data Structure Description Language)提交:
接口、电压、极性、速率、字符格式和单双工:
物理连接和探测点:
捕获设备、固件、采样率和解码器版本:
测试环境:离线 / SITL / HITL / 无功率级波形负载 / 独立CAN测试总线 / 拆桨隔离台架 / 不适用
安全隔离:
预期帧和预期应用行为:
原始文件:
摘要:
结论与限制:
不能在记录中保存密钥、口令或未脱敏的敏感位置。
三类捕获¶
电气波形¶
示波器或逻辑分析仪用于证明:
- 电平;
- 极性;
- 位周期;
- 边沿;
- UART字符格式;
- 半双工换向;
- 总线争用;
- 物理错误。
采样率、探头带宽和参考地必须足够。只看自动解码结果不能证明电气质量。
原始字节或CAN帧¶
保存:
- 原始时间戳;
- 方向或接口;
- 原始字节;
- CAN ID、扩展标志、数据长度码(DLC,Data Length Code)和数据;
- 丢包或捕获溢出;
- 捕获起止时间。
文本十六进制转储适合人工核对,但长期证据应保留机器可读原件和摘要。
应用日志¶
保存:
- 解析成功和错误计数;
- 消息更新时间;
- 队列深度和丢弃;
- 状态转换;
- 命令接受、拒绝和原因;
- 重连;
- 资源负载。
应用日志是结果证据,不能替代线上原始捕获。
时钟对齐¶
比较飞控、逻辑分析仪、网络抓包和地面站日志前,要记录:
- 每个时钟的来源;
- 时区;
- 单调时钟还是墙上时钟;
- 分辨率;
- 启动后的零点;
- 同步误差;
- 是否发生跳变。
可以使用同一可观察事件建立对齐点,例如:
- 一次物理开关变化;
- 一条唯一测试消息;
- 电源上升沿;
- GPIO标记;
- 同时写入多个日志的标记。
对齐误差必须进入时延结论。毫秒级日志不能支持微秒级时序声明。
独立解码¶
至少使用两条独立路径中的两种:
- 项目自身解析器;
- 官方生成库;
- 独立脚本;
- Wireshark或其他分析器;
- 手工按规范重算长度和校验。
两种工具显示相同结果仍可能共享同一个错误定义,因此还要核对固定规范和至少一个已知测试向量。
黄金样本¶
建立最小语料库:
- 最短合法帧;
- 最大合法帧;
- 每个主要消息一个正常样本;
- 载荷全零与尾零;
- 边界枚举和最大数值;
- 单帧与多帧;
- 请求、成功响应和错误响应;
- 带签名与不带签名;
- 协议重启后的第一帧;
- 稳态高负载片段。
每个样本附:
- 原始字节;
- 来源版本;
- 预期解码;
- 预期校验;
- 允许或拒绝原因;
- 是否包含敏感数据。
黄金样本必须固定,不能在测试运行时由待测解析器同时生成预期结果。
负面语料¶
至少包含:
- 每个字节位置截断;
- 声明长度不足和超长;
- 单比特翻转;
- 错误CRC或校验和;
- 未知消息;
- 未知标志;
- 非法枚举;
- 重复、乱序和过期;
- 帧头嵌入载荷;
- 连续噪声;
- 内外层长度或校验不一致;
- 多帧丢中间帧、错误Toggle或错误Transfer-ID;
- 签名错误和重放;
- 资源上限附近的合法输入。
通过标准不仅是“不崩溃”,还包括:
- 不越界;
- 不死循环;
- 不分配无界内存;
- 不执行部分命令;
- 错误计数正确;
- 能在后续合法帧处恢复;
- 不把错误日志放大为拒绝服务。
解析器单元边界¶
解析层只应输出:
- 完整已验证帧;
- 明确错误类别;
- 消耗字节数;
- 是否需要更多输入;
- 重同步状态。
业务层再处理:
- 消息版本;
- 字段范围;
- 来源和权限;
- 当前系统状态;
- 动作和响应。
将命令副作用直接放在逐字节状态机中,会使截断帧、重复帧和回滚难以测试。
模糊测试¶
模糊测试(Fuzz Testing)适合发现:
- 长度计算溢出;
- 越界访问;
- 状态机死循环;
- 错误重同步;
- 大量分配;
- 未初始化字段;
- 罕见字段组合。
合理流程:
- 从合法黄金样本建立种子;
- 给解析器设置明确输入长度和资源上限;
- 禁用真实设备副作用;
- 加入内存和未定义行为检测;
- 保存最小崩溃样本;
- 修复后加入永久回归;
- 分别覆盖流式分片和完整帧入口。
模糊测试不能替代协议语义和状态机测试。
一致性矩阵¶
| 维度 | 发送端 | 接收端 | 工具 | 证据 |
|---|---|---|---|---|
| 协议版本 | 固定 | 固定 | 固定 | 版本输出 |
| 消息定义 | 提交 | 提交 | 提交 | 文件摘要 |
| 帧格式 | 编码器 | 解析器 | 解码器 | 黄金样本 |
| 字段语义 | 单位/比例 | 单位/比例 | 展示 | 对照值 |
| 错误处理 | 响应 | 拒绝 | 展示 | 负面样本 |
| 安全 | 签名/权限 | 验证/门禁 | 配置 | 反例 |
| 性能 | 调度 | 队列 | 捕获 | 时延和负载 |
任一列未知时,该维度不能标记通过。
版本迁移¶
兼容变化¶
可能包括:
- 在允许位置追加可选扩展;
- 新增接收端可忽略的消息;
- 新增不改变旧值的枚举;
- 提高实现容量但保持旧边界。
仍需验证旧接收端实际会忽略,而不是越界或错误解释。
不兼容变化¶
包括:
- 改变已有字段单位、比例或含义;
- 改变字段宽度或端序;
- 重用旧消息ID;
- 改变校验覆盖;
- 改变默认权限;
- 改变失联或超时语义;
- 从一个线协议切换到另一个不兼容协议。
不兼容变化需要新版本、新消息、显式协商或网关,不能只修改说明文档。
升级流程¶
- 固定旧发送端、旧接收端和旧工具;
- 保存黄金样本与配置;
- 建立新旧能力矩阵;
- 测试新发送端到旧接收端;
- 测试旧发送端到新接收端;
- 测试新旧工具交叉组合;
- 验证未知扩展、降级和拒绝;
- 验证升级中断和回滚;
- 比较带宽、时延和资源;
- 记录最后兼容版本和停止支持条件。
只测试“全部升级后正常”不能证明滚动升级安全。
网关¶
网关不是透明电线。它可能改变:
- 帧边界;
- 地址;
- 优先级;
- 时间戳;
- 认证状态;
- 错误语义;
- 数据精度;
- 更新率;
- 失联判断。
网关记录至少包含:
- 两侧协议版本;
- 完整字段映射;
- 无法映射的字段;
- 单位和坐标转换;
- 队列和限速;
- 来源身份如何保留;
- 安全边界终止在哪里;
- 一侧故障如何传播。
不能把未经认证一侧的消息在网关后标记为“已认证”。
结果分级¶
| 状态 | 含义 |
|---|---|
| 未测试 | 没有执行对应场景 |
| 受限通过 | 指定正常样本通过,但边界或负载未完成 |
| 一致通过 | 固定版本的正常、边界和恢复满足契约 |
| 安全通过 | 在一致通过基础上,权限、重放和资源攻击边界通过 |
| 不通过 | 存在可复现偏差 |
| 不适用 | 有明确的范围依据 |
“未复现问题”不等于“一致通过”。
最终归档¶
归档包至少包括:
- 测试卡;
- 版本和配置;
- 协议定义摘要;
- 原始捕获及散列;
- 解码结果;
- 自动化测试报告;
- 性能统计;
- 异常样本;
- 修复差异;
- 回归结果;
- 已知限制;
- 审核人和日期。
对外共享前删除密钥、精确位置、设备唯一标识和其他敏感信息,并保留原件访问控制。
检查理解¶
- 为什么应用日志不能替代原始线上捕获?
- 黄金样本为什么不能由待测解析器同时生成预期值?
- 模糊测试通过为什么仍不能证明协议语义正确?
- 滚动升级为什么要测试四种新旧组合?
- 网关为什么可能终止原有安全边界?