本节摘要:LoRaWAN 用 32 位帧计数器防重放:设备与网络服务器各维护一套,帧计数倒退或异常跳跃即丢弃。本节讲清计数器语义、回绕处理与网络侧的同步算法。位置:4.2 讲了加密与 MIC,本节补上"重放"这道防线——MIC 管完整性,帧计数管新鲜度。
LoRaWAN 1.0.x 维护两套 32 位计数器:FCntUp(设备侧递增,校验上行)与 FCntDown(网络服务器侧递增,校验下行)。1.1 起拆分更细(NFCntUp/Down、AFCntUp/Down),但原则一致:每个方向一个单调递增计数器,接收方拒绝"倒退"的帧。
帧里只携带 16 位(FCnt 字段),完整 32 位在接收端维护:
接收端算法(以上行为例): 1. 收到帧,取其 16 位 FCnt 与 DevAddr 2. 与本地记录的完整 FCntUp 比对:若 16 位值 + 高 16 位推断 ≈ 本地值 + 1,则正常 3. 若推断值 ≤ 本地值 → 疑似重放,丢弃;若推断值 > 本地值 + 允许的跳变窗口 → 计数跳跃异常 4. 正常则更新本地值,并处理窗口同步
"允许的跳变窗口"通常取 16384(2^14):设备侧计数器可能因断电回退或 ADR 重置发生小幅跳变,网络服务器在窗口内接受并重新同步,超出则要求设备重新入网。
回绕:32 位计数器到 0xFFFFFFFF 后回到 0。协议要求设备在接近回绕时(例如到 0xFFFFFFF0 附近)触发重新入网,网络服务器实现也要在检测到"接近回绕"时主动要求设备重入网——否则回绕后新旧帧计数无法区分。重置:设备断电重启后计数器从 Flash 恢复;若 Flash 未持久化导致计数回退,设备会发"倒退"的帧被网络服务器拒收,表现就是"设备重启后通信全部失败"——正确做法是计数器持久化到 NVM,且入网后由网络服务器把计数同步到设备。
攻击者截获一个上行帧(含有效 MIC),在网络里反复重放。没有帧计数检查时,每一条都会被处理;有帧计数检查时,只有第一条通过,后续重复帧因计数不递增被丢弃——网络服务器侧对"重复帧"有明确处置:不处理、不下发 ACK。下行同理:设备收到计数倒退的下行帧直接丢弃,防止攻击者回放历史下行指令(如"阀门关闭"指令)。
网络服务器同时服务大量设备,计数器同步是并发敏感逻辑。要点:
1. 每设备维护完整 FCntUp/FCntDown(32 位),存数据库 2. 收到上行:取 16 位 FCnt,与本地完整值比对 3. 窗口判定: - 16 位值 ≈ 本地低 16 位 + 1 → 正常,更新 - 16 位值 > 本地低 16 位但差值 < 窗口 → 视为跳变,接受并同步 - 16 位值 ≤ 本地低 16 位且窗口外 → 重放,丢弃并告警 4. 窗口通常取 16384,超窗要求设备重新入网
实现上两个坑:并发——同一设备的帧可能由多个网关同时上报,计数更新必须加锁或串行化,否则丢帧(已同步的计数被旧帧覆盖回退);持久化——NS 重启后计数丢失会拒绝所有存量设备的帧,计数要落库或允许按"设备上报的 16 位 + 假设"重建。
ADR 调整速率不会影响计数,但设备重启后计数从 NVM 恢复——若 NVM 丢失导致计数回到早期值,且 NS 的窗口判定通过(差值小于 16384),NS 会接受并把计数"拉高",此时旧帧重放窗口被意外打开。缓解:设备重启后主动重入网(计数清零重来),比靠窗口容忍更安全。这也是为什么很多运维规范要求"设备重启 = 重新入网"。
要点回顾:32 位计数器按方向各维护一套,16 位线上传输、32 位本地维护;回绕与重启回退要靠重入网与 NVM 持久化兜底;重放攻击被"计数不递增即丢弃"机制挡住。安全账结清,下一章进入软件架构——这些账怎么在嵌入式代码里落地。
帧计数只是重放防线的一层,成熟系统的纵深是:物理层(加密的调制信号本身难以伪造)→ MIC 层(篡改帧被完整性校验拦截)→ 帧计数层(重放旧帧被计数拦截)→ 业务层(关键指令带业务级流水号,重放的业务帧即使协议层通过也会在业务层被拒绝)。四层缺一不可:只靠帧计数,攻击者可以截获"计数最新的帧"反复重放;只靠业务流水号,攻击者仍然能制造大量无效流量消耗资源。
业务层的配合做法:关键指令(开关、解锁、参数下发)由应用侧生成单调递增的指令序号,设备侧校验"新指令序号必须大于已处理的最大序号",序号相同或更小的直接拒绝。这样即使攻击者拿到一条合法指令帧,也无法让它重复生效。
实际运维中"设备突然失联"排查到计数问题时的处置路径:第一步确认——在 NS 后台看该设备的最后帧计数与当前计数,若设备端计数远小于 NS 记录(重启回退),大概率是 NVM 未持久化;第二步恢复——让设备重新入网(OTAA 后计数清零重建),比手工改 NS 计数安全;第三步根治——更新固件,计数持久化到 NVM 并在重启后校验。不要手动修改 NS 侧计数:改错会打开重放窗口或误杀正常设备,宁可重入网。
小结与速查:32 位计数器按方向各一套,帧里只带 16 位;接收端按 (DevAddr, FCnt) 去重 + 窗口判定;回绕与重启回退靠重入网与 NVM 持久化;窗口取 16384 为社区常用值;双向都要校验;重放告警要聚合防淹没。