前两年做一款无线传感器节点组网的项目,现场部署了大概两百多个采集节点,网关和节点之间通过低功耗无线做配对绑定。出厂前在实验室里配对测试都挺顺,结果到了现场,有将近三成的节点反复配对失败。现象很邪门:网关端日志显示配对请求收到了,也回了配对响应,但节点侧就是停在“等待密钥”的状态,超时后重试,重试几次就彻底放弃。
客户那边催得紧,现场施工人员拿着手持配置器一个个手动复位,两百多个节点折腾了整整两天。更让人难受的是,有些节点复位后能配对成功,但过几个小时又掉线,重新配对又失败。当时第一反应是无线信号干扰,换了几个信道,问题依旧。后来抓了空口数据,才发现问题根本不在信号强度上。
这个坑让我重新梳理了一遍配对流程里密钥分发这个环节的定位思路。下面把当时踩过的坑和后来沉淀下来的判断方法写出来。
先搞清楚配对和密钥分发是两件事
很多刚接触无线组网的人会把“配对成功”和“密钥分发成功”混为一谈。实际上这是两个阶段:
配对阶段解决的是“我要和你建立关系”,双方交换身份标识、能力信息,协商出一个会话基础参数。这个阶段通常用明文或者默认密钥保护,因为此时双方还没有共享密钥。
密钥分发阶段解决的是“我们之间的通信怎么加密”,由一方生成或者协商出会话密钥,然后安全地传给另一方。这个阶段一旦出问题,表现往往就是配对流程卡住、超时、重试,但抓包看配对请求和响应都正常。
现场排查时第一个要确认的就是:故障到底卡在配对阶段还是密钥分发阶段。判断方法很简单,在节点侧加一个状态机打点,看它停在哪个状态。如果停在“已发送配对请求”之后、“已收到配对响应”之前,那是配对阶段的问题;如果停在“已收到配对响应”之后、“已安装密钥”之前,那就是密钥分发阶段的问题。
当时我们的节点固件里没有加这个打点,现场只能靠猜,这是第一个教训。
密钥分发失败的几种典型表现
密钥分发失败不是只有“完全收不到”这一种。根据现场观察,大概可以分成几类:
第一类,节点侧完全收不到密钥分发消息。现象是节点在等待密钥状态超时,网关侧日志显示密钥分发消息已经发出,甚至可能显示发送成功。这种最常见的原因是密钥分发消息用的目标地址或者连接句柄不对,消息发出去了但节点根本不认。
第二类,节点侧收到了密钥分发消息,但校验失败。现象是节点收到消息后直接丢弃,继续等待,超时后重试。这种通常是密钥分发消息的完整性保护出了问题,比如消息认证码计算不一致、序列号窗口不对、加密模式不匹配。
第三类,节点侧收到了也校验通过了,但安装密钥时失败。现象是节点短暂进入“已安装密钥”状态,但很快又回到等待状态。这种往往是密钥存储区域写失败、密钥长度和算法不匹配、或者密钥槽位已经被占用。
第四类,节点侧安装成功了,但网关侧没有收到确认。现象是节点认为配对完成,开始用新密钥发数据,但网关侧还在用旧密钥或者默认密钥,导致数据解密失败。这种最隐蔽,因为双方状态不一致,但各自都觉得自己是对的。
定位时先看空口抓包,但别只看空口
空口抓包是定位密钥分发问题的第一手资料,但很多人抓了包不知道怎么读。当时我们抓包后发现,网关确实发出了密钥分发消息,节点也回了确认,但确认消息里的状态码是“密钥校验失败”。
这里有个细节:密钥分发消息本身可能是加密的,抓包工具不一定能解出来。但消息的头部、长度、序列号、目标地址这些元信息通常是可见的。先看这些元信息对不对,再看节点回的确认消息里的状态码。
如果空口抓包显示密钥分发消息发出去了,节点也回了确认,但确认是失败,那问题就在节点侧的校验逻辑。这时候要对比网关侧和节点侧的几个参数:密钥分发消息的序列号是否在节点接收窗口内、消息认证码的计算输入是否一致、加密算法的初始化向量是否同步。
当时我们遇到的一个坑是:网关侧在密钥分发时用的序列号是从配对阶段延续下来的,但节点侧在配对完成后重置了序列号计数器。结果节点收到密钥分发消息时,发现序列号比预期大很多,直接判定为重放攻击丢弃了。这个细节在文档里根本没提,是抓包对比了两边的计数器才发现的。
密钥分发通道本身可能就有问题
密钥分发消息通常走的是配对阶段建立的临时通道。这个临时通道可能用的是默认密钥、或者配对阶段协商出来的临时密钥。如果临时通道本身就不安全或者不稳定,密钥分发自然容易失败。
当时现场有个现象:节点离网关近的时候配对成功率高,离得远就经常失败。一开始以为是信号强度问题,后来发现不是。近距离时临时通道的误码率低,密钥分发消息一次就能传完;远距离时误码率高,密钥分发消息可能需要分片重传,而分片重传的逻辑有bug,重传时序列号没有正确递增,导致节点侧认为收到了重复消息而丢弃。
这个问题的定位方法是:在密钥分发阶段统计消息重传次数。如果重传次数明显高于配对阶段,那就要怀疑分片重传逻辑。当时我们在网关侧加了一个计数器,发现失败的那些配对,密钥分发消息的平均重传次数是成功配对的五倍以上。
节点侧的资源状态也要看
密钥分发失败有时候不是协议问题,而是节点侧资源不够了。比如密钥存储区域写满、内存不够导致消息队列溢出、或者任务调度优先级太低导致密钥分发消息被其他任务挤掉。
当时有一个批次的节点,配对失败率特别高。后来发现这些节点在配对前刚执行过一次固件升级,升级后没有清理临时存储区,导致密钥槽位被旧的临时密钥占满了。新密钥分发过来时,找不到空闲槽位,直接返回失败。
这种问题的定位方法是:在节点侧加资源状态打点,看密钥分发失败时密钥槽位的使用情况、内存剩余情况、任务队列深度。如果发现资源紧张,那就先解决资源问题,而不是死磕协议。
网关侧的状态机也要对齐
密钥分发是双方状态机协同的过程。网关侧的状态机如果和节点侧不一致,就会出现一方认为在等待确认、另一方认为已经完成的情况。
当时有个隐蔽的bug:网关侧在发出密钥分发消息后,如果在一定时间内没有收到确认,会重发。但重发时没有检查节点侧是否已经切换到了新密钥。结果节点侧已经安装新密钥并开始用新密钥通信,网关侧还在用旧密钥重发密钥分发消息,节点侧收到后用新密钥解密失败,直接丢弃。
这种问题的定位方法是:在网关侧记录每次密钥分发消息的发送时间、重发次数、以及收到确认的时间。如果发现重发次数大于零且最终失败,就要检查重发逻辑是否考虑了节点侧的状态迁移。
一个实用的排查顺序
综合当时的经验,现场排查密钥分发失败可以按这个顺序来:
第一步,确认故障阶段。在节点侧加状态打点,看是卡在配对阶段还是密钥分发阶段。
第二步,看空口抓包。确认密钥分发消息是否发出、节点是否收到、节点回的确认状态码是什么。
第三步,对比双方参数。重点看序列号、密钥长度、加密算法、消息认证码计算输入。
第四步,检查通道质量。统计密钥分发阶段的重传次数,和配对阶段对比。
第五步,检查节点资源。看密钥槽位、内存、任务队列。
第六步,对齐双方状态机。检查网关侧的重发逻辑是否考虑了节点侧的状态迁移。
这个顺序不是死的,但核心思路是先定位阶段,再定位方向,最后定位具体参数。最怕的是一上来就怀疑信号强度,换信道、加功率,折腾半天发现是序列号不匹配。
一些取舍
密钥分发失败定位最耗时的地方在于复现。现场环境复杂,实验室很难完全模拟。当时我们的做法是在实验室里搭了一个可调衰减的射频环境,把信号衰减调到临界值,人为制造误码和重传,这样就能稳定复现现场的问题。
另一个取舍是日志粒度。日志打得太细,固件体积和运行开销受不了;打得太粗,现场又定位不了。当时的做法是分级日志,默认只打关键状态迁移,出问题时可以通过手持配置器打开详细日志,复现一次就能抓到足够的信息。
还有一个取舍是密钥分发的可靠性机制。加确认重传能提高成功率,但会增加配对时间。当时现场有两百多个节点,配对时间从平均三秒增加到八秒,施工人员等得着急。后来折中方案是只在密钥分发阶段加确认重传,配对阶段不加,因为配对阶段失败可以快速重试,密钥分发阶段失败重试代价更高。
这些取舍没有标准答案,取决于具体项目的部署规模、施工条件、节点资源。但定位思路是通用的:先分阶段,再看抓包,对比参数,检查资源,对齐状态机。
关键词
核心关键词:配对、重传、抓包、超时、信道、绑定、低功耗、功耗、固件升级
精准长尾关键词:配对过程中密钥分发失败的定位方法、密钥分发失败的几种典型表现、定位时先看空口抓包,但别只看空口、节点侧的资源状态也要看、网关侧的状态机也要对齐、蓝牙BLE开发实战、BLE协议栈详解、低功耗蓝牙调试、BLE连接参数优化、蓝牙抓包分析、BLE广播数据解析、蓝牙连接稳定性优化