MAVLink 2帧、消息、签名与路由¶
微型飞行器链路协议(MAVLink,Micro Air Vehicle Link)是面向资源受限飞行系统的消息封装与定义体系。它可以运行在串口、UDP、TCP、无线数传和进程间连接上,但不规定这些承载本身的可靠性。

MAVLink 2帧¶
线上顺序为:
| 顺序 | 字段 | 长度 | 含义 |
|---|---|---|---|
| 1 | Magic | 1字节 | MAVLink 2为0xFD |
| 2 | Payload length | 1字节 | 线上载荷长度,最大255 |
| 3 | Incompatibility flags | 1字节 | 未理解的置位项要求丢弃帧 |
| 4 | Compatibility flags | 1字节 | 未理解的置位项可忽略 |
| 5 | Sequence | 1字节 | 发送端链路序号 |
| 6 | System ID | 1字节 | 发送系统标识 |
| 7 | Component ID | 1字节 | 发送组件标识 |
| 8 | Message ID | 3字节 | 24位消息标识,低字节先传 |
| 9 | Payload | 0至255字节 | 按消息定义序列化 |
| 10 | Checksum | 2字节 | CRC-16/MCRF4XX并加入消息CRC_EXTRA |
| 11 | Signature | 可选13字节 | 仅签名标志置位时存在 |
不含Magic时,MAVLink 2固定头为9字节;线上从Magic到Message ID共10字节。讨论“头长度”时必须说明是否包含Magic。
未签名帧总长度为12 + payload length,签名帧为25 + payload length;255字节载荷的签名帧最大280字节。
载荷序列化¶
消息定义来自方言XML并由代码生成器生成。基本字段按以下原则序列化:
- 非扩展字段会按类型大小重新排序,以减少对齐空洞;
- 同尺寸字段保持声明顺序;
- 多字节基本类型采用小端;
- 数组元素连续排列;
- 扩展字段位于基础字段之后;
- MAVLink 2可以截去载荷尾部连续的零字节,但不能截去载荷中间的零;全零载荷仍至少保留第一个载荷字节。
因此,不能只按XML文本顺序手工拼接全部字段。发送和接收应使用与固定方言匹配的生成库。
载荷长度短于当前定义的完整长度时,缺失尾部按零解释;超过接收端已知定义的扩展可能由生成库按兼容规则处理。基础字段发生不兼容修改时,必须使用新消息ID或其他明确版本策略。
校验¶
MAVLink使用CRC-16/MCRF4XX累加:
- 从
Payload length开始; - 覆盖不含Magic的头字段和实际线上载荷;
- 再累加该消息定义对应的
CRC_EXTRA; - 接收的两个CRC字节不参与自身计算。
CRC_EXTRA由消息名称和基础字段的类型、名称、数组长度及线上顺序生成,并作为额外一个字节参与运行时CRC计算,不作为独立字段传输。MAVLink 2扩展字段不参与旧消息的CRC_EXTRA,因此在尾部追加扩展不会改变旧定义的校验种子。它用于降低消息ID相同但定义不同被误解码的风险,不是密码学认证:
- 不隐藏载荷;
- 不证明发送者身份;
- 攻击者知道消息定义即可重新计算;
- 不能阻止重放。
只检查帧CRC不能代替命令授权。
序号和丢包¶
Sequence是每条发送链路上的8位递增序号,会回绕。接收端可用它估算丢帧,但要考虑:
- 发送端重启;
- 路由器合并多个来源;
- 多条独立链路;
- 过滤掉不关心的消息;
- 乱序承载;
- 序号回绕。
不能把任意一次跳号都等同于无线丢包,也不能把系统ID相同的不同物理来源合并统计。
System ID和Component ID¶
系统ID和组件ID用于寻址与路由,不是经过认证的身份。任意能够注入链路的节点都可能伪造这些字段,除非系统另有认证和访问控制。
常见路由规则会参考:
- 目标系统和目标组件字段;
- 消息是否广播;
- 最近从哪个接口看到某系统或组件;
- 避免把帧原路回送;
- 链路过滤和转发策略。
路由器必须防止环路、重复放大和未知高流量消息耗尽带宽。
心跳的边界¶
HEARTBEAT可用于发现系统、组件类型和基本状态,但一个心跳存在不能证明:
- 姿态、位置或电池消息持续更新;
- 命令通道可用;
- 参数协议兼容;
- 飞控已通过健康检查;
- 链路经过认证;
- 地面站显示值是新鲜数据。
应用应对所需消息分别设置更新时间和质量门禁。
命令与确认¶
MAVLink命令常通过COMMAND_LONG或COMMAND_INT发送,并由COMMAND_ACK报告结果。实现要区分:
- 已收到并接受;
- 暂时处理中;
- 完成;
- 拒绝;
- 不支持;
- 参数错误;
- 超时且最终状态未知。
重试前检查命令语义是否幂等。重启、存储、任务开始和执行器类动作可能不能安全重复。
目标系统/组件只是寻址。解锁、模式切换、任务写入、参数修改、FTP和远程命令仍需要飞控状态门禁、链路策略和权限控制。
参数和任务是会话协议¶
参数读取、任务上传和文件传输不是互不相关的单帧:
- 有请求和响应;
- 可能分页、分片或按序号传送;
- 需要超时和重试;
- 可能与其他客户端并发;
- 重连后需要重新确认状态;
- 工具和飞控可能支持不同子协议版本。
例如,任务上传中超时不能直接从头覆盖正在使用的任务。实现要确定当前事务所有者、预期序号和最终提交点。
MAVLink 2签名¶
签名帧把不兼容标志中的签名位设为1,并在CRC之后增加13字节:
| 字段 | 长度 | 含义 |
|---|---|---|
| Link ID | 1字节 | 本地链路标识 |
| Timestamp | 6字节 | 48位小端签名时间戳 |
| Signature | 6字节 | SHA-256结果的前6字节 |
签名计算输入包括32字节秘密密钥、从Magic开始的完整数据包直到CRC、Link ID和Timestamp。摘要截取为6字节。
签名提供什么¶
在密钥保密、接收策略和时间状态正确时,签名可以:
- 检测签名覆盖字段被修改;
- 证明发送方持有共享密钥;
- 配合时间戳和接收状态拒绝旧签名帧。
签名不加密载荷,旁路观察者仍能读取位置、状态和命令内容。
防重放¶
时间戳是从2015年1月1日00:00:00 UTC起、以10微秒为单位的48位无符号数。接收方需要按System ID + Component ID + Link ID维护最近接受的签名时间戳,而不是只按物理串口维护。
固定C库要求已建立状态的逻辑流严格递增,相同或更旧的时间戳会被拒绝。首次见到某逻辑流时,对过度落后于本地签名时钟的报文应用接受窗口;规范和固定实现没有给出“时间戳超前一分钟就拒绝”的对称规则。测试时应以所用库版本的精确比较和边界为准,不能凭直觉实现时间窗口。
48位时间戳在约2104年达到上限,规范没有定义可直接循环复用的普通序号语义。长期系统应在设计阶段处理寿命和迁移边界。
以下情况需要专项测试:
- 发送端重启;
- 接收端重启;
- 后备电源或持久化状态丢失;
- 两条链路误用相同Link ID;
- 时间戳回退;
- 合法帧延迟后到达;
- 密钥轮换;
- 签名与未签名链路混用。
如果接收策略仍允许高风险未签名命令,启用发送签名不代表系统已经建立强制认证。
签名策略矩阵¶
| 来源 | 帧状态 | 只读遥测 | 高风险命令 |
|---|---|---|---|
| 受信链路 | 签名有效且新鲜 | 可按策略接受 | 继续执行权限和飞行状态检查 |
| 受信链路 | 未签名 | 按明确策略处理 | 默认拒绝,除非有经评审的替代控制 |
| 任意链路 | 签名错误 | 丢弃并计数 | 丢弃并计数 |
| 任意链路 | 签名有效但重放 | 丢弃并计数 | 丢弃并告警 |
| 未知链路 | 签名有效但密钥域未知 | 不应因格式正确自动信任 | 拒绝 |
这是安全设计模板,不是所有平台的默认行为。必须读取固定版本实际配置和源码。
密钥配置消息¶
SETUP_SIGNING的完整解码载荷为42字节,字段顺序为:
| 偏移 | 字段 | 长度 | 处理 |
|---|---|---|---|
| 0 | initial_timestamp |
8字节 | 64位小端初始签名时间戳 |
| 8 | target_system |
1字节 | 目标系统 |
| 9 | target_component |
1字节 | 目标组件 |
| 10 | secret_key |
32字节 | 二进制秘密密钥 |
initial_timestamp用于建立后续签名时钟的起点,必须与10微秒签名时间基准和设备持久化状态一致,不能回退到已接受时间之前。全零密钥加零初始时间戳可触发禁用签名,属于高风险配置变更。
MAVLink 2仍会裁掉载荷末尾连续的零字节。因此,当32字节密钥末尾包含零,尤其发送全零密钥禁用签名时,线上Payload length可以小于42。接收生成库会把缺失的尾部扩展为零。测试应同时验证完整42字节逻辑布局和合法尾零裁剪,不能强制每个线上帧都携带42个载荷字节。
该消息只能在已经确认安全的直连链路上使用:
- 使用明确目标系统和目标组件逐台发送;
- 不得广播;
- 接收方和网关不得自动转发到其他MAVLink接口;
- 不得从无线、不受信局域网或共享遥测链路下发;
- 发送前确认当前接口不会被路由器复制;
- 发送后验证密钥已经保存,但不得回读或显示密钥值。
日志必须完全省略SETUP_SIGNING,或者把其中32字节秘密替换为固定脱敏值。共享调试日志、遥测日志、参数、崩溃转储和截图都不得泄露密钥。
线上签名帧写入普通遥测日志时,官方建议清除签名不兼容标志并移除签名块。由此产生的tlog不再是证明线上签名状态的原始证据;安全核验仍需受控访问的线上捕获和独立计数。
承载边界¶
串口¶
需要计算8N1位开销、双向消息、突发和队列。串口本身不重传。
UDP¶
可能丢包、重复、乱序和源地址伪造。一个UDP数据报可以包含一个或多个MAVLink帧,接收器仍需按字节流或数据报边界正确解析。
TCP¶
提供有序字节流,但没有MAVLink帧边界;仍可能半帧读取。阻塞和断线恢复也会增加时延。
路由器¶
路由器可能转发、过滤或合并多个接口。安全策略应位于所有可达路径,而不是只配置地面站一端。
固定版本核验¶
对Betaflight、INAV、ArduPilot和PX4分别保存:
- 固件版本和提交;
- 使用的MAVLink库或生成消息定义;
- 方言名称及提交;
- MAVLink 1/2输出设置;
- 系统ID和组件ID;
- 串口或网络承载;
- 消息率配置;
- 路由路径;
- 签名支持、密钥域和未签名接收策略;
- 关键命令的状态门禁。
平台支持“MAVLink 2”不能推导其支持全部方言消息、签名或同一参数微服务。
验证方法¶
执行错误签名、重放、路由或命令注入前,必须先完成协议审查与测试卡第0节,并按抓包、解码、一致性与版本迁移建立线上原始证据。前置不满足时只使用离线样本。
- 在隔离SITL网络中保存线上原始数据;
- 用两个独立解码器核对帧头、消息ID、载荷长度和CRC;
- 对照固定方言解释字段、单位和扩展;
- 检查序号、消息更新时间和链路统计;
- 分别测试未知兼容标志和未知不兼容标志;
- 测试载荷截断、最大载荷和错误
CRC_EXTRA; - 对签名链路测试正确签名、错误签名、旧时间戳和重启;
- 验证
SETUP_SIGNING不能广播、不能经网关自动转发,且不会把秘密写入日志; - 验证高风险命令即使格式和签名正确,仍会在错误飞行状态被拒绝;
- 恢复配置并删除测试密钥。
签名失败、伪造和重放测试禁止使用生产密钥或开放无线环境。
常见误判¶
- 把
0xFD后的固定头长度写成包含Magic的相同数字; - 忽略24位消息ID按低字节先传;
- 按XML声明顺序手工序列化字段;
- 认为CRC通过就代表发送者可信;
- 把系统ID当作认证身份;
- 认为签名会加密遥测;
- 只启用发送签名,不检查接收端未签名策略;
- 用地面站
tlog替代线上原始帧证明签名块; - 把心跳存在写成整条链路健康。
检查理解¶
- MAVLink 2的Magic字节是什么?
- 未知兼容标志和未知不兼容标志分别怎样处理?
CRC_EXTRA解决什么问题,为什么仍不是认证?- MAVLink 2签名为什么不能保护机密性?
- 系统ID为什么不能作为权限依据?