3. MAC层帧结构:帧头(FHDR)、帧端口(FPort)、帧负载(FRMPayload)、MIC校验

好,咱们今天来啃一块硬骨头——MAC层的帧结构。说实话,很多刚接触LoRaWAN的工程师,看协议栈文档时最容易在这里卡住。我自己当年也是,对着那几张帧格式图看了半天,总觉得每个字节都认识,但连起来就不知道它在干嘛。

其实没那么复杂。你想想看,LoRaWAN的MAC层帧,本质上就是一个精心设计的信封。信封外面写着地址和校验码,里面装着你要发的数据。咱们一层层拆开看。

3.1 帧头(FHDR)——信封的封面信息

FHDR是帧头的缩写,它位于整个MAC帧的最前面。我习惯把它理解为快递单上的寄件人和收件人信息。FHDR里主要包含四个字段:DevAddr、FCtrl、FCnt、FOpts。

字段 长度(字节) 说明
DevAddr 4 设备地址,32位,网络内唯一标识一个终端
FCtrl 1 帧控制字节,包含确认位、ADR位等标志
FCnt 2 帧计数器,用于防重放攻击和消息排序
FOpts 0~15 可选字段,携带MAC命令,长度由FCtrl中的FOptsLen决定

这里我要特别提一下FCnt。我在项目中遇到过一个问题:设备重启后FCnt清零了,但服务器端还记着之前的计数值。结果服务器认为这是重放攻击,直接把消息丢了。嗯,这就是典型的FCnt不同步问题。后来我养成了一个习惯——设备端每次上电都从服务器同步一次FCnt,或者用ABP模式时手动记录FCnt到非易失存储。

关键点:FCnt是单调递增的,不能回退。一旦回退,服务器会认为消息无效。这是LoRaWAN安全机制的基础之一。

3.2 帧端口(FPort)——数据该走哪个门

FPort只有1个字节,取值范围是0~255。它的作用很简单——告诉接收方,FRMPayload里的数据应该交给哪个应用层处理。

我个人习惯把FPort想象成大楼里的门牌号。0号端口是留给MAC命令的专用通道,1~223是应用数据端口,224~255是保留给将来扩展用的。

  • FPort = 0:FRMPayload里装的是MAC命令,不是应用数据。这时候应用层别去碰它。
  • FPort = 1~223:应用数据。具体用哪个端口,由你的应用层协议决定。我一般用1~10做传感器数据,11~20做配置命令。
  • FPort = 224~255:保留,目前别用。我曾经手贱试过用224发数据,结果网关直接丢弃了。

小技巧:如果你的设备同时上报多种类型的数据(比如温湿度、电量、信号强度),建议用不同的FPort来区分。这样服务器端解析起来会清晰很多,也方便后续扩展。

3.3 帧负载(FRMPayload)——真正要传的东西

FRMPayload就是你要发送的实际数据。但注意,它不一定是明文。LoRaWAN支持两种模式:不加密和加密。

对于上行消息(终端→服务器),FRMPayload是加密的。加密密钥由AppSKey派生。对于下行消息(服务器→终端),同样加密。但有个例外——如果FPort=0,FRMPayload里是MAC命令,这时候用的是NwkSKey加密,而不是AppSKey。

我记得有一次调试,同事发现设备上报的数据服务器解出来全是乱码。查了半天,原来是上行用了AppSKey加密,但服务器端配置的密钥是NwkSKey。说白了就是钥匙拿错了。从那以后,我每次配置密钥都会在文档里明确标注:上行用哪个Key,下行用哪个Key,MAC命令用哪个Key。

避坑指南:FRMPayload的最大长度受限于数据速率(DR)和区域规定。比如EU868频段,SF12时最多只能带51个字节,SF7时可以带222个字节。我曾经在SF12下硬塞了100个字节的数据,结果网关直接不响应。后来学乖了,每次组包前先查一下当前DR下的最大负载。

3.4 MIC校验——信封上的火漆封印

MIC全称是Message Integrity Code,消息完整性校验码。它占4个字节,放在整个MAC帧的最后。它的作用有两个:一是确保消息没有被篡改,二是验证消息确实来自合法的设备。

MIC的计算涉及多个参数:MHDR、FHDR、FPort、FRMPayload,以及两个密钥——NwkSKey和AppSKey。具体算法是AES-CMAC。嗯,这里我不展开讲算法细节,你只要知道:MIC是防篡改的,不是防窃听的。防窃听靠的是FRMPayload的加密。

我见过一个典型的错误:有人自己写LoRaWAN协议栈,MIC计算时漏掉了FPort字段。结果设备发出去的消息,服务器端MIC校验总是不通过。排查了三天才发现是这个小问题。所以,如果你也在自己实现协议栈,务必对照协议文档逐字节核对MIC的输入数据。

一句话总结:MIC是LoRaWAN安全体系的最后一道防线。没有它,你的消息在空气中就是裸奔的——任何人都可以伪造。

3.5 完整的MAC帧长什么样?

咱们把上面这些拼起来,一个完整的MAC帧就是这样的结构:

+------+------+------+------+------+------+
| MHDR | FHDR | FPort| FRMPayload | MIC  |
| 1B   | 7~22B| 0/1B | 0~N B      | 4B   |
+------+------+------+------+------+------+

注意,FPort是可选的。如果FRMPayload为空,FPort也可以省略。但MIC是必须的,每个MAC帧都得带。

举个例子,假设你要发送一个温度值25.6℃:

  • MHDR:0x40(表示这是一个未确认的上行数据帧)
  • FHDR:包含DevAddr、FCtrl、FCnt,假设共12字节
  • FPort:0x01(表示应用数据)
  • FRMPayload:加密后的温度数据,假设4字节
  • MIC:4字节校验码

整个帧加起来大概21个字节。嗯,这就是LoRaWAN的MAC层帧结构。说白了,它不复杂,但每个字段都有它的使命。你只要记住:FHDR是地址和控制信息,FPort是数据分类标签,FRMPayload是真正的数据,MIC是安全锁。

我的建议:刚开始做LoRaWAN开发时,可以用Wireshark抓包看看实际的MAC帧。亲眼看到这些字段在空口中是怎么排列的,比看十遍文档都管用。