4. LoRaWAN消息类型与帧结构:MAC层帧格式
好,咱们今天聊点硬核的——LoRaWAN的MAC层帧结构。说实话,我刚入行那会儿,看协议栈文档看得头大,一堆字段、标志位,感觉像在读天书。后来做认证测试,踩了不少坑,才慢慢摸清楚门道。今天我就把这些经验掰开揉碎,跟你讲讲。
4.1 帧结构总览:从MHDR到FRMPayload
一个完整的LoRaWAN MAC帧,说白了就是一段精心打包的数据。它长这样:
| MHDR (1字节) | MACPayload (可变) | MIC (4字节) |
嗯,就这么简单?别急,咱们一层层剥开看。
MHDR(MAC Header)——帧头,1个字节。它告诉接收方:这条消息是什么类型,用的是哪个版本协议。我习惯把它看成快递单上的“加急”或“普通”标签。
MACPayload——这才是真正的“货”。它里面又套了一层:
| FHDR (7~22字节) | FPort (1字节,可选) | FRMPayload (可变) |
MIC(Message Integrity Code)——4字节的消息完整性校验码。说白了就是防篡改的。我在项目里见过有人把MIC算错,结果服务器死活不认,折腾了一整天。
4.2 FHDR:帧头里的门道
FHDR是MACPayload的头部,结构如下:
| DevAddr (4字节) | FCtrl (1字节) | FCnt (2字节) | FOpts (0~15字节) |
DevAddr——设备地址。每个节点在入网后都会分配一个32位的地址。注意,这不是全球唯一的,只是网络内唯一。我遇到过有人把DevAddr和DevEUI搞混,结果测试一直不过。
FCtrl——控制字节。这个字段很有意思,它里面藏了好几个标志位:
- ADR(Adaptive Data Rate):自适应速率开关。开了之后网络可以帮你调速率,省电。我建议在固定场景下关掉它,不然调试时数据速率忽高忽低,你根本搞不清问题出在哪。
- ACK:确认标志。收到Confirmed消息后,这个位要置1。
- FPending:这个咱们后面细说,是个坑。
- FOptsLen:FOpts字段的长度,单位字节。
FCnt——帧计数器。每个方向(上行/下行)各有一个16位的计数器。从0开始,每发一帧加1。为什么要有这个?防重放攻击。你想想看,如果没有计数器,坏人截获了一条“开锁”指令,反复重放怎么办?
FOpts——可选字段,最多15字节。用来携带MAC命令。注意,如果FOptsLen不为0,那FPort就不能是0。这个规则我当年背了好久才记住。
4.3 FPort与FRMPayload:数据怎么装
FPort——端口号,1字节。取值范围:
- 0:表示FRMPayload里只有MAC命令,没有应用数据。
- 1~223:应用数据端口。具体用哪个,看你的应用层协议怎么定。
- 224~255:保留给将来扩展用。我建议你别碰,除非你知道自己在干什么。
FRMPayload——真正的数据载荷。如果FPort=0,这里就是MAC命令列表。如果FPort非0,这里就是加密后的应用数据。
加密用的是AES-128,密钥是网络会话密钥(NwkSKey)或应用会话密钥(AppSKey)。具体用哪个,取决于FPort的值。嗯,这里有个细节:上行数据用AppSKey加密,下行数据用NwkSKey加密。为什么不一样?为了安全隔离。网络层和应用层的密钥分开,就算网络被攻破,应用数据还是安全的。
4.4 Confirmed与Unconfirmed消息
这两种消息的区别,说白了就是一个要回执,一个不要。
Unconfirmed消息:发出去就不管了。适合传感器上报温度、湿度这种非关键数据。丢了就丢了,下次再报。优点是省电、省带宽。
Confirmed消息:发出去后,接收方必须回复一个ACK。如果没收到ACK,发送方会在下一个接收窗口重传。适合控制指令,比如“打开阀门”、“关闭电源”。
我在做智能路灯项目时,就吃过亏。当时用Unconfirmed消息发控制指令,结果路灯时亮时不亮。排查了半天,发现是丢包了。后来改成Confirmed消息,问题解决。但代价是电池消耗快了30%。所以,选哪种,得权衡。
4.5 FOpts与FPending机制
FOpts——这个字段我前面提过,用来捎带MAC命令。为什么要有它?因为LoRaWAN的带宽太珍贵了。你想想看,一个上行帧可能只有几十字节,如果专门发一条MAC命令,太浪费了。所以协议设计者搞了个“捎带”机制:在数据帧里顺带把MAC命令塞进去。
FOpts最多15字节,可以塞多个MAC命令。每个命令格式是:
| CID (1字节) | 参数 (可变) |
CID是命令标识符,比如LinkCheckReq的CID是0x02,LinkADRReq的CID是0x03。
FPending——这个标志位只在下行帧里出现。它告诉节点:服务器还有数据要发,你赶紧再开一个接收窗口。
为什么会这样?因为LoRaWAN的接收窗口是有限的。节点发完数据后,会打开两个接收窗口(RX1和RX2)。如果服务器数据太多,一个窗口发不完,就会把FPending置1。节点收到后,会继续打开额外的接收窗口,直到FPending被清零。
4.6 实战中的常见问题
说了这么多理论,咱们来点实际的。我在认证测试中遇到过几个典型问题:
- MIC计算错误:最常见的问题。原因往往是密钥搞错了,或者计算时包含了错误的字段。记住,MIC计算范围是MHDR+MACPayload,不包括MIC本身。
- FCnt回绕:16位计数器,最大65535。到了之后会回绕到0。但服务器端如果没处理好,会认为是重放攻击。解决办法是使用32位扩展计数器(FCnt32),或者服务器端做特殊处理。
- FOpts长度超限:FOpts最多15字节。如果你塞了16字节,接收方会直接丢弃。我见过有人把MAC命令和FRMPayload都塞满了,结果帧长度超了,发不出去。
- FPort=0时FRMPayload非空:FPort=0表示FRMPayload里只能有MAC命令。如果你放了应用数据,接收方会解析失败。
4.7 小结
好了,这一章的内容就这些。咱们回顾一下重点:
- LoRaWAN帧结构:MHDR + MACPayload + MIC
- FHDR里藏着DevAddr、FCtrl、FCnt、FOpts
- Confirmed消息要ACK,Unconfirmed不要
- FOpts用来捎带MAC命令,FPending告诉节点“还有数据”
下一章咱们会讲MAC命令的具体实现,包括LinkCheck、LinkADR这些常用的。到时候我会拿实际项目里的抓包数据给你看,保证比干讲有意思。
记住,协议这东西,光看文档是不够的。你得动手抓包、分析、调试,才能真正理解。我当年也是这么过来的,踩的坑多了,自然就熟了。