2. 数据包结构:MAC层帧格式、帧头(FHDR)与端口(FPort)详解

好,咱们接着聊。上一章我们把LoRaWAN的整体架构捋了一遍,这一章我带你钻进数据包内部,看看MAC层到底长什么样。

说实话,我刚接触LoRaWAN那会儿,最头疼的就是看帧结构。一堆字段,什么MHDR、FHDR、FPort,看着就晕。后来在项目里调了几个月,才真正搞明白每个bit的用途。嗯,今天我就把这些经验掰开揉碎讲给你听。

2.1 MAC层帧格式概览

先看整体结构。LoRaWAN的MAC层帧,说白了就是一层套一层的盒子。最外层是物理层,里面包着MAC层,MAC层里面又包着应用层。咱们今天重点拆MAC层。

一个完整的MAC帧长这样:

| 前导码 | PHDR | PHDR_CRC | PHYPayload | CRC |

其中PHYPayload才是我们关心的MAC层内容:

| MHDR(1字节) | MACPayload(可变) | MIC(4字节) |

你看,MAC层就三个部分:头部、载荷、校验。简单吧?但别急,真正的门道在MACPayload里面。

核心要点:MIC是消息完整性校验码,不是CRC。它用AES-CMAC算法计算,能防篡改。我在项目里见过有人把MIC当CRC用,结果数据被篡改了都不知道——这坑我踩过。

2.2 MHDR:帧头里的秘密

MHDR只有1个字节,但信息量不小。它分成三个部分:

位域 长度 说明
MType 3 bit 消息类型
RFU 3 bit 保留位,必须为0
Major 2 bit LoRaWAN版本号,目前是0b00

MType定义了6种消息类型,我列个表给你看:

MType值 消息类型 方向
000 Join-Request 终端 → 服务器
001 Join-Accept 服务器 → 终端
010 Unconfirmed Data Up 终端 → 服务器
011 Unconfirmed Data Down 服务器 → 终端
100 Confirmed Data Up 终端 → 服务器
101 Confirmed Data Down 服务器 → 终端
110 RFU 保留
111 Proprietary 厂商自定义

我个人习惯,上行数据用Confirmed,下行用Unconfirmed。为什么?因为终端上行数据丢了可以重传,但服务器下行数据丢了,终端可以通过超时机制发现。你想想看,如果服务器也用Confirmed,那ACK风暴能把网络打爆。

2.3 MACPayload:真正的数据载体

MACPayload是帧的核心,结构如下:

| FHDR(7~22字节) | FPort(1字节,可选) | FRMPayload(可变) |

这里有个细节:FPort是可选的。如果FRMPayload为空,FPort就不存在。我刚开始做开发时,以为FPort必须带,结果发空包时服务器解析报错——嗯,后来查了规范才明白。

2.3.1 FHDR:帧头详解

FHDR固定7字节,加上可选的选项,最多22字节:

字段 长度 说明
DevAddr 4字节 终端设备地址
FCtrl 1字节 帧控制字节
FCnt 2字节 帧计数器
FOpts 0~15字节 MAC命令选项

DevAddr是终端的网络标识,32位。注意,这个地址不是全球唯一的,是网络服务器分配的。同一个网络内唯一就行。

FCtrl这个字节很有意思,它里面塞了很多控制信息:

名称 说明
7 ADR 自适应数据速率使能
6 ADRACKReq ADR确认请求
5 ACK 确认位,收到Confirmed帧时置1
4 FPending 服务器还有更多数据待下发
3~0 FOptsLen FOpts字段的长度,单位字节

这里我要特别说一下FPending位。这个位只有下行帧才有用。服务器告诉你:「我还有数据没发完,你赶紧再开个接收窗口。」我在做智能水表项目时,就靠这个位实现了大数据量的分包下发。

FCnt是帧计数器,16位。每次上行或下行,计数器加1。服务器用这个来检测重放攻击。但有个坑:FCnt溢出后怎么办?规范说可以重置,但实际项目中,我建议用32位扩展计数器,否则一年发65536个包,有些场景真不够用。

FOpts是MAC命令的快速通道。它和FRMPayload里的MAC命令不同,FOpts不加密,但长度限制在15字节以内。我一般把链路检查、设备状态这类不敏感的命令放FOpts里,省得加解密折腾。

经验之谈:FOpts和FPort不能同时存在。如果FOptsLen > 0,FPort必须为0。这个规则很多人不知道,导致设备入网后无法通信——我排查过好几次这种问题。

2.3.2 FPort:端口号的意义

FPort只有1字节,取值范围0~255。它的作用类似于TCP/UDP的端口号,用来区分上层应用:

FPort值 用途
0 MAC命令传输(仅限FRMPayload)
1~223 应用数据
224~255 保留给未来标准化应用

为什么要有FPort?说白了,一个终端可能跑多个应用。比如我的一个项目,终端同时采集温湿度和电量,我就用FPort=1传温湿度,FPort=2传电量。服务器收到后,根据FPort分发到不同的处理模块。

这里要注意:FPort=0时,FRMPayload里装的是MAC命令,而且是加密的。这和FOpts里的MAC命令不同,FOpts不加密但长度受限,FPort=0的MAC命令加密但长度不受限。怎么选?看你的安全需求。

2.4 FRMPayload:应用数据的最终归宿

FRMPayload是应用层数据,最大长度取决于数据速率(DR)。以最常见的SF12/125kHz为例,FRMPayload最多51字节。如果开了ADR,速率高了,长度还能更大。

加密方式是这样的:FRMPayload用AES-128进行加密,密钥是AppSKey(应用会话密钥)。注意,MAC命令如果走FPort=0,用的是NwkSKey(网络会话密钥)加密。这两个密钥不能混用,否则解密失败。

重要提醒:FRMPayload的加密是端到端的。也就是说,网络服务器只负责转发,它看不到应用数据的内容。这在物联网安全中非常重要——我曾经帮一个客户做安全审计,发现他们把敏感数据明文放在FRMPayload里,吓得我赶紧让他们改了。

2.5 实际抓包分析

光说不练假把式。我给你看一个实际的上行数据包:

MHDR: 0x40 (MType=Unconfirmed Data Up, Major=0)
FHDR:
  DevAddr: 0x26011F4D
  FCtrl: 0x80 (ADR=1, ACK=0, FOptsLen=0)
  FCnt: 0x0001
FPort: 0x01
FRMPayload: 0x12345678 (加密后的数据)
MIC: 0xAABBCCDD

这个包什么意思?终端地址是0x26011F4D,开启了ADR,帧计数是1,用FPort=1传应用数据。MIC校验通过后,服务器用AppSKey解密FRMPayload,得到原始数据。

我在调试时,经常用这个结构来排查问题。比如MIC校验失败,先看FCnt对不对;解密失败,先看FPort和密钥是否匹配。90%的问题都能定位。

2.6 本章小结

好了,这一章的内容就到这儿。我们拆解了MAC层的帧结构,从MHDR到FRMPayload,每个字段的作用和注意事项都讲了一遍。下一章,我们聊聊重传机制——什么时候重传?怎么重传?重传多少次?这些都是实战中必须搞明白的问题。

最后送你一句话:理解帧结构,是调试LoRaWAN网络的第一步。把每个bit的含义刻在脑子里,遇到问题你就能快速定位。