<!--
继前面几个章节的铺垫,我想读者或多或少对BLE MESH整个入网过程的了解,应该十成中起码占了有八成吧,剩下的2成通过反复阅读应该不是什么大问题;讲完了BLE MESH入网过程,那么接下来就是进行Model之间的数据交互了;这也是以后BLE MESH开发过程需要掌握的重要知识,试想一下这样一个场景:
我想没有人希望这样子吧?只有深刻地了解各个层的帧结构,当遇到这样或那样的问题时,我们通过抓包器分析对比就能很快锁定问题的源头,从而为我们快速解决问题打下坚实的基础,这也是本篇章节的主要目的。
小编在SIG MESH协议各个层的作用中已经讲过它们之间的区别:
GATT
那么GATT承载的作用到底是什么呢?就是让不支持mesh协议栈的设备与mesh网络的节点间接地进行通信;而其又是通过什么方式与网络的节点进行交流呢?就是使用“代理协议”,没错!就是Proxy节点,其是可以实现这些GATT特性的,并且同时支持GATT承载和ADV承载 (有的人可能会问为啥还要支持ADV承载?这不是废话吗,如果不支持的话怎么让不支持mesh协议的设备的数据送到mesh网络中去啊,就是因为正常mesh网络中的数据收发都是通过ADV承载实现的) 也就是说支持代理协议的节点是支持mesh proxy service 以及广播行为的,为了让大家更好的理解,我们可以看看下所示的拓扑图:
ADV
ADV承载利用BLE的GAP广播和扫描来传送和接收网络PDU,如下图所示:
然而,那只是从宏观的角度去说明它们之间的区别。而在该章节开始之前,小编觉得我们还应该从数据量化的角度去区分这两者在传输的数据格式和种类之间的不同。
GATT承载说得直白一点就是说,通过Proxy Data In和Proxy Data Out进行数据的收与发,换句话说就是基于连接的方式进行数据交互,这里强调的是传递数据的载体;但是,具体收发的数据类型还是与PB-ADV有所不同的,这点我们在之前的章节也有所提及,具体为:
虽然Proxy Data In和Proxy Data Out允许进行数据交互的长度取决于ATT_MTU,但是该类型PDU的的帧长度则是基于广播承载来设计的,也就是说就算ATT_MTU的值大于31;此时,PB-GATT的设备仍然只能传递最大29字节的Network PDU,多出来的数据则放在下一个Network PDU发送出去。这一点很重要!!!
Beacon类型的数据比较简单,帧格式相对比较固定。基本上一包数据可以一次性发送完成,不需要分包。
因为该PDU的帧格式跟Network PDU是一样的;同样的,即使ATT_MTU的值大于31,最大也只能传输29个字节的数据,多的那一部分则放在下一个Proxy Configuration PDU发送出去。而且仅具备Proxy特性的设备才支持该类型PDU。
该类型的PDU就不一样了,其完全可以挣脱开束缚。只要ATT_MTU的值够大,那么就可以发送多大的Provisioning PDU。例如:发送64字节的Public Key时,在ATT_MTU够大的情况下就可以一次性发送完成,而且不需要分包。而PB-ADV在处理这事的时候则必须分包。
实质就是Network PDU,只是换了一种叫法;该类型PDU最大的帧大小为29个字节,如果应用层的数据通过层层拼装到达Network层之后,其大小大于29字节则需要分包才能将所有的数据发送出去。
在帧格式这方面,基本上跟PB-GATT的是一样的。唯一不同的是PB-ADV在传递这些信息时,大部分情况下是分包才能完成整个数据包的传输,因为它走的是广播通道,最大只能传送31个字节。
希望读者不要被PB-GATT和PB-ADV迷糊了,它们都是用来进行数据收发的载体;前者走的是数据通道,有些数据包它可以根据ATT_MTU一次性传输完成,而后者走的是广播通道,一次性最大只能传递31字节的数据;只是PB-GATT更多的用于Proxy Client与Proxy Server之间基于连接的方式进行数据交互。
在前面几个篇章,小编就一直再强调目前所有的BLE MESH Packets都是通过不可连接非定向类型的广播PDU传递信息到MESH网络中;此时,有些读者可能会有以下几个疑问:
至于上面提及到的疑问,有一个概念我们还需要再重复一遍:
“PB-GATT只是让那些不支持BLE MESH的设备,如手机、电脑等间接地加入到BLE MESH网络中;其次,那么这些不支持BLE MESH的设备想要在MESH网络中设置或者获取网络中其他节点的数据,应该将数据通过BLE GATT传送给Proxy节点(类似于中转站),最后再由Proxy节点将数据通过BLE广播包 (不可连接非定向广播) 传递到MESH网络中去处理; 但是,也有一个例外:那就是手机通过PB-GATT跟Proxy节点已经建立连接,而手机此时想要获取或者设置Proxy节点 (Proxy client<-->Proxy server),则仍然是使用GATT的方式进行数据交互;同时,这也说明Proxy节点是必须同时支持PB-GATT和PB-ADV”
比如:“手机的Mesh APP跟某一个Proxy Node建立了连接,现在其还需要控制另外一个节点,就可以用上述办法去实现”;这个时候读者可能会有这个的一个疑问“为什么不直接跟其进行连接并进行控制呢?”---如果这个时候还有几十个或者上百个节点要控制,那么这种方式是绝对行不通的。 为了让读者有更加深刻的了解,我们可以参考如下的图示:
BLE Mesh Packet通过广播包的方式传递数据的最大长度就只有31字节,大于31字节的数据包就要进行分包处理了。那么,BLE MESH Specification是如何划分这31个字节的,这里小编不得再次将SIG MESH协议各个层的作用中提及到的MESH Packet示意图拿出来向广大读者展示。
那么我们如何来看上面的示意图呢?对于发送方,其只要将数据打包成Network PDU的帧格式就可以了,至于什么传输层、访问层的内容那是接收方接收到之后,通过层层解密最终上抛给应用层处理。
在开始分段式细述之前,我们先上一幅NetWork PDU的捉包图再来讲解:
我们可以看到,Network PDU的帧格式完全跟上面MESH Packet示意图所述的一模一样。其中,我们可以观察到除了IVI|NID和NetMIC,其他的字段 (CTL|TTL、SEQ、SRC) 要么被模糊化处理了,要么被NetKey加密 (DST、Transport PDU) 了。
该值相对来说比较好理解,其表示IV索引值的最低有效位。例如:IV索引值为0x00101847,则此时IVI为1;当0x00101846时,则此时IVI为0。至于其在整个Network PDU中也就起到这样的作用,除此之外并无他用。
该字段的全称是 “Network Identifier”,即网络标识符的意思;该值由Network Key派生出来,其主要起到标识的作用;
Main Network Messages
NID || EncryptionKey || PrivacyKey = k2(NetKey, 0x00) ,其中NID占7个Bit数据,其余的Key均占16字节
Private Network Messages(Friend<->LPN)
NID || EncryptionKey || PrivacyKey = k2(NetKey, 0x01 || LPNAddress || FriendAddress || LPNCounter || FriendCounter) ,同理也是占7个Bit数据,其余的Key均占16字节
当节点接收到Network PDU时,可以从中获取得到明文的NID域的值,紧接着通过这个NID在Flash中轮询查找是否有其对应的encryption key和privacy key;如果没有,则该Network PDU被丢弃。所以就算这个时候有其他Mesh网络的数据被接收到也会被丢弃的;更多的详情,请查阅《Mesh Profile》的3.8.6.3.1章节内容。
该字段只有一个Bit,0表示Access Message且NetMIC为32bits,1表示Control Message且NetMIC为64bits。那么此时不知道有没有读者好奇什么是Control Message、什么是Access Message?反正小编是挺好奇的,但是不在此处开展而是放到Lower Transport Layer中细述。
由于BLE Mesh当前采用的是泛洪的方式,而不是主流的路由机制。但是该泛洪是可以控制的,那么应如何控制呢?就是利用TTL域的值来控制转发数据的次数。当且仅当TTL>1时,才能中继接受到的数据。
说到TTL就不得不说一下中继(Relay)了,因为TTL域的值只在需要中继时方有效,否则可以忽略它。然则,能不能中继转发是有条件的,下述表示TTL与Relay特性的关联:
接收到广播承载传递的消息
接收到GATT承载传递的消息
该情况多用于代理节点通过GATT承载接收到目标地址不是它的Network PDU时,会帮忙将该帧数据通过ADV承载重传出去,而接收方可以是支持ADV或者GATT承载的设备
接收到广播承载传递的消息
该情况多用于代理节点通过ADV承载接收到目标地址不是它的Network PDU而是与其连接的另外一个支持GATT承载的节点时,就会将该帧数据通过GATT承载传递给它
该字段共24bits长度,表示Network PDU数据包的索引,每一包SEQ都是不同的且从0开始叠加,如果SEQ值要溢出了则就会发起IV更新请求;具体的IV Update Procedure详情,可以参考IV Index更新过程章节的内容
该字段共16bits的长度,顾名思义就是数据包发起方的源地址且该地址应为单播地址。
同SRC一样,该字段域同样为16bits长度,表示数据包要发送的目的地点,其地址可以为单播地址、组地址以及虚拟地址。
该字段的内容才是Network PDU真正想要传输的,至于能传输的最大长度根据前面提及的CTL值来决定,而相关的内容的细节则将其放在下面的几个章节细述。
同样的,该字段为32bits或者64bits取决于前面提及的CTL值;但是,需要注意的是它只是对DST和Transport PDU的内容进行校验。整个过程以下图所示:
由上述的算法公式可以知道,Network Layer中的Transport PDU和DST是由EncryptionKey加解密的,而EncryptionKey又是由Network Key派生出来的。这里提到了DST和Transport PDU的加解密,就不得不提同样被保护的CTL、TTL、SEQ、SRC等域了,它们的内容则是被模糊化的,而同样用的是由Network Key派生出来的Privacy Key加解密的。至于,EncryptionKey和Privacy Key如何派生出来的,则可以参数下面章节的AID所示;
至此,Network PDU的内容基本讲解完成了;那么,这个时候可能有读者会说:“小编虽然你把每个字段都剖析了,但是串起来还是有点模糊啊”。
别急,我们可以看看接收到Network PDU之后,它们是如何工作的,也许这个时候你 “似乎” 就懂了。具体如下图所示:
从上图 (如何看不清的话就放大看) 我们可以得知,Network PDU想要进来而不是件容易的事啊,经过了层层枷锁,最终才会上传到应用层给用户处理。那么,让我们看看进来之后,都经过了什么考验。
Is NID known?
我们在NID中已经提及到了,当Node接收到Network PDU时,会通过明文NID在flash中轮询是否有对应的encryption key和privacy key,如果存在则说明该Network PDU是有效的,因为接下来Node会利用获取得到的encryption key和privacy key分别来解密network key encryption和obfuscated的加密内容。反之,如果没有轮询一遍发现找不到相应的秘钥,则Network PDU就会被直接抛弃,不会再往上层传递了;
Is SRC valid?
这个会相对比较简单,它仅仅是判断源地址是不是单播地址,如果是的话就继续下面的验证,否则直接被抛弃;
Already in message cache
因为我们在IS_NID_KNOWN中就已经获取得到了encryption key和privacy key,所以CTL、TTL、SEQ、SRC这些域已经去混淆了,这一步用于判断接收到的Network PDU之前是否之前已经接收过,如果是则直接被丢弃;(注意:这里的cache是指的network cache)
An encryption key verifies MIC
上面小编也说了,到这一步的时候我们已经拿到encryption key了,利用这个秘钥计算出一个Calculated MIC再与接到的Network PDU中的MIC比较;如果是一致的话,那就验证通过并同时将DST、Transport PDU域的内容也解密了,如上图流程图所示;接下来,将数据上抛给上层应用继续处理;否则的话,直接抛弃该网络数据包;
Is DST valid?
因为步骤4中已经将DST、Transport PDU域的内容解密了,此时就继续判断目标地址是否有效,判断规则如下:
如果无效的话,同样被抛弃;
Already in replay cache
到了这一步的时候,除了Transport PDU没有被处理,基本上Network PDU其他域的内容已经被处理完了;该抛弃的抛弃,而留下的都是 “精英”;紧接着判断SEQ + IV INDEX的值 (Replay Attack就是在这里保护了):
Add to replay cache
经过上述的6个步骤,基本上这包网络包问题不大了;将当前新的Network PDU的sequence number、iv index、source address保存至replay cache中;
Add to message cache
同步骤7一样,将将当前新的Network PDU的sequence number、source address保存至message cache中;
Should replay
而中继的要求,如上述TTL所述;如果满足条件,则将接收到的Network PDU原封不动的重传出去;
至此,整个Network PDU处理流程就完成了;
Network PDU中的Transport PDU内容,接着由Lower Transport Layer继续进行分割,但是具体的内容则在Network PDU中就已经被安排地明明白白;换句话说就是:Lower Transport PDU传输的内容已经在Network PDU中就已经指明了;而决定的字段则是前面提及的CTL值,即0为Access Message,1为Control Message。
Control Message
控制消息为段确认消息和朋友以及心跳包相关的消息
Unsegmented
这个时候,眼尖的读者可能会发现 “这个不是未分段的控制信息吗?怎么有段确认消息啊?” 这里还是要澄清一下:
对于未分段的控制消息帧数据格式,由于比较简单这里就不再细述。
Segmented
显而易见,该类型的消息指的就是一包数据发不完,需要分包发送了;换句话说当是控制消息大于29字节时就要分包了;注意,控制消息还是访问消息都是Network PDU。
SEG
此时,该字段只能为为1
Opcode
只能是0x01~0x07中的一个值,具体表述的含义下面会有细述
RFU
保留以后使用
SeqZero
SeqAuth的最低的有效位13bit,而通过IV Index、SeqZero以及第一个分包数据的SEQ就可以计算得到SeqAuth。计算公式如下:
如果SEQ&8191小于SeqZero,则SeqAuth = (IV Index << 24) + (SEQ-(SEQ&8191-SeqZero)-8192);否则,SeqAuth = (IV Index << 24) + (SEQ-(SEQ&8191-SeqZero)); 例如:“SEQ = 0x647262,SeqZero = 0x1849,IV Index = 0x58437AF2,则SeqAuth = 0x58437AF264589”
而SeqAuth的作用主要是用来判断接收到的分段Upper Transport PDU之前是否已经被接收过还是处于正在接收的状态
SegO
表示当前分包的索引号,从0开始
SegN
表示最后一个分包的索引号
Segment m
表示当前分包的具体数据内容,如第0包、1包、2包......的数据内容
至于opcode的内容,之前我们已经在SIG MESH协议各个层的作用就已经提及到了,现在我们回过头看,体会是不是更深了😄?
值 | 操作码 | 含义 |
---|---|---|
0x00 | – | Reserved for lower transport layer |
0x01 | Friend Poll | Sent by a Low Power node to its Friend node to request any messages that it has stored for the Low Power node |
0x02 | Friend Update | Sent by a Friend node to a Low Power node to inform it about security updates |
0x03 | Friend Request | Sent by a Low Power node the all-friends fixed group address to start to find a friend |
0x04 | Friend Offer | Sent by a Friend node to a Low Power node to offer to become its friend |
0x05 | Friend Clear | Sent to a Friend node to inform a previous friend of a Low Power node about the removal of a friendship |
0x06 | Friend Clear Confirm | Sent from a previous friend to Friend node to confirm that a prior friend relationship has been removed |
0x07 | Friend Subscription List Add | Sent to a Friend node to add one or more addresses to the Friend Subscription List |
0x08 | Friend Subscription List Remove | Sent to a Friend node to remove one or more addresses from the Friend Subscription List |
0x09 | Friend Subscription List Confirm | Sent by a Friend node to confirm Friend Subscription List updates |
0x0A | Heartbeat | Sent by a node to let other nodes determine topology of a subnet |
0x0B~0x7F | RFU | Reserved for Future Use |
从上述表格中可以再次看出,控制消息基本上是为 “Friend & Heartbeat” 量身定制的。
Access Message
除了段确认消息和朋友以及心跳包相关的消息之外,其余的都是访问消息,平时我们Mesh通讯大部分情况,都是使用的访问清息;如“Mesh Generic OnOff Set/Get”等等
Unsegmented
我们可以看到Upper Transport Access PDU的大小再次跟MESH Packet示意图中示意的吻合,其最大为15字节即120bits
SEG
此时,该字段只能为为0
AKF
如果该标志位为1就表示当前使用了Application Key加密Upper Transport Access PDU,否则使用Device Key加密
AID
如果AKF为0时,则AID应该被设置为0b000000。否则设置为Application Key标识符,其跟Network Key标识符是相似的。前者是Application Key通过k4加密获取,而后者则是Network Key通过k2加密得到,如下所示:
之所以这样的原因是一方面出于安全考量,另一方面可以节省载荷包的开销。
Upper Transport Access PDU
上层传输层的数据内容
从上图中,我们可以明显看出实际的抓包图跟上述的内容是完全吻合的,即当使用Device Key时,所以AKF和AID均为0;同时,发送的Access Message也是使用了Device Key加密了的。
Segmented
从上述的表格,我们可以看出基本上跟unsegmented的帧格式是一样的,无非就是当传送比较大Size的Upper Transport Access PDU时,需要拆分成多少包从而引入了新的帧格式。至于该PDU最大有多大呢?不管是从MESH Packet示意图还是抓包图,我们都可以看到该值最大为15Bytes。
SEG
此时,该字段只能为为1
AKF
同上述的Unsegmented Access Message
AID
同上述的Unsegmented Access Message
SZMIC
如果该Bit为1时,则 Upper Transport Access PDU中的TransMIC长度为64bits,否则为32bits;这里又引出另外一个注意事项:Control Message是不包含TransMIC域内容的。同时,TransMIC域的内容只有最后一包Segmented Access Message会携带,其余的包是不会携带的。但是,Unsegmented Access Message包含的TransMIC域的内容是固定为32bits
SeqZero
同上述的Segmented Control Message
SegO
同上述的Segmented Control Message
SeqN
同上述的Segmented Control Message
Segment m
同上述的Segmented Control Message
Access Message的分包与重组如下示意图所示:
实际上上面的分包,其实指的就是Config AppKey Add
Segment Acknowledgment message
分段应答消息由Lower Transport Layer应答目前接受到了多少个分段消息,其具体的帧格式如下所示:
其中各域所对应的含义如下表所示:
Field | Size(bits) | Notes |
---|---|---|
SEG | 1 | 0 = Unsegmented Message |
Opcode | 7 | 0x00 = Segment Acknowledgment Message |
OBO | 1 | Friend on behalf of a Low Power node |
SeqZero | 13 | SeqZero of the Upper Transport PDU |
RFU | 2 | Reserved for Future Use |
BlockAck | 32 | Block acknowledgment for segments |
SEG
此时该域必须为0,表示该应答消息是未分段消息
Opcode
固定为0,表示为分段应答消息
OBO
如果是接收到的消息直接寻址的节点,那么该域值为0;如果是朋友节点接收到发送给与其建立了友谊关系节点的LPN,那么朋友节点会应答该分段消息,且该域值此时为1
SeqZero
该域表示被应答的Upper Transport Layer消息的SeqZero,计算方式同上述的Segmented Control Message
RFU
该域值保留为以后使用
BlockAck
该域共有32位,第0位表示segment 0,第n位就表示segment n;如果第n位为1,则表示segment n已经被应答,否则没有;同时,任何大于SegN的第n位均要设置为0且被忽略
注意
如果接收到的消息,其TTL域值为0;那么,相对应的分段应答消息的TTL域也要设置为0
经过上述的内容分析以及文章前面的Mesh_Packet图,我们可以知道Network PDU的Transport PDU又细分为Unsegmented/Segmented Access 或 Control Message,而Access Message的内容则根据AKF的值决定由Device Key还是Application Key加密,然而Control Message的内容则自始自终都是由Network Key加密的。还有一处关键点,小编觉得大家还是要有所了解的,即Access Message的内容可以一直传送到Access Payload层,而Control Message到Upper Transport PDU就停止了;但是,Segment Acknowledgment的Control Message则传至Lower Transport Layer就停止了,也就是说对分段消息的应答是在Lower Transport Layer中处理的!!!
不管是Access Message还是Control Message,都有可能涉及到分包重组的问题,只是前者会出现的比较多点;对于更多的分包与重组细节,我们会专门开篇讲解,这里仅简单讲解否则就会有点本末倒置了。
对于分包的Message,如果目标地址是单播地址,接收完所有的分段Message之后,则需要由对端设备发送应答Message。但是,如果目标地址是虚拟地址或者组地址时,此时则不需要发送应答Message; 我相信有很多读者会问 “对于分段消息的应答信号应该在多长时间内要应答给发送方?总不能“死等”吧?” 没错,肯定不可能“死等”的,而该时长的设置至少为150+50*TTL毫秒。同时,如果在规定的时长内没有收到有效的应答消息,则Lower Transport Layer将重发所有未应答的分段的数据包。这个时候又要读者可能会问“分段的消息不是发完之后,对端设备会发送应答消息吗?为什么会出现重发的情况?”这个其实很好理解,有可能你发送方啪啪啪地发完了所有的分段数据,但是对方不一定啪啪啪全都收到了,很有可能丢了其中的几包,然而上面所说的时长时间一到就必须要给发送方应答你收到了多少包数据,如果全部收到还好,要是收不全这个时候发送方就会重发没有应答的分段数据包了;针对上述的情况,Mesh Spec做出以下几种规定:
当Lower Transport Layer PDUs被传输完成时 (注意:是每个分段的消息,但不一定都能被对端设备收到),段传输计时器就应该启动,且其最小值应为200+50*TTL毫秒(Nordic的Mesh SDK默认为500+50xTTL),其中当目标地址是单播地址时,TTL的值就是Network PDU中携带的TTL值,否则TTL为0
如果段传输计时器时间到了,仍然没有收到有效的应答消息,则底层传输层将重传所有未应答的底层传输PDUs,段传输计时器会被复位并再次重启。如果一直没有收到有效的应答消息,是有重传限制的 (Nordic的Mesh SDK默认是4次)
这里小编根据红旭的自定义模型,给大家看看如果一直没有应答时的捉包图:
目标地址为单播地址
从上图可以看出当目标地址为单播地址时,TTL为4;共尝试了4次且段传输计时器为500ms+4*50ms = 700ms
目前地址为组地址
从上图可以看出当目标地址为组地址时,TTL为0;共尝试了4次且段传输计时器为500ms
如果有效的应答消息被接收到,但是只是传送了部分的底层传输PDUs,此时则段传输计时器的值被复位,同时将重传所有未应答的底层传输PDUs
如果有效的应答消息被接收到,且所有的底层传输PDUs都被传送完成,则底层传输层则停止段传输计时器
只要底层传输层接收到分段消息时,且SeqAuth值大于sequence authentication值,且目标地址是单播地址,则将开启应答计时器(第一次是这样,其他时刻看具体情况),其值最小为150+50*TTL毫秒。
该计时器跟应答计时器(acknowledgement timer)有点类似,只要底层传输层接收到分段消息,且SeqAuth值大于Sequence Authentication值,则开启未完成计时器(incomplete timer),其值最小为10秒。
对于上述Acknowledgment Timer和Incomplete Timer中提及到的SeqAuth和sequence authentication,前者为当前接收到的分段消息的SeqAuth值,后者为上一次接收到的分段消息的SeqAuth(Nordic MESH SDK默认保存最近收到的4条分段消息)(实质就是通过SeqZero换算成SeqAuth);同样还有一点也是要引起大家注意,Segment Transmission Timer中说的TTL指的是Network Packets中的TTL,而Acknowledgement Timer中说的TTL虽然也属于Network Layer PDU,但是它是专用于Segment Acknowledgment Message的。
同样的,Upper Transport Layer对Lower Transport Layer的数据进行下一步处理,其通过Device/Application Key解密之后上抛给Access Payload Layer或者将Access Payload Layer下抛的数据进行加密;但是,对于加密则有以下三种情况:
Application Key & DST == Virtual Address
EncAccessPayload, TransMIC = AES-CCM (AppKey, Application Nonce, AccessPayload, Label UUID)
Application Key & DST == Unicast Address or Group Address
EncAccessPayload, TransMIC = AES-CCM (AppKey, Application Nonce, AccessPayload)
Device Key & DST == Unicast Address
EncAccessPayload, TransMIC = AES-CCM (DevKey, Device Nonce, AccessPayload)
Access Message
对于Access Message而言,此时只是将Upper Transport Access PDU的内容进行加密并携带TransMIC数据内容。而用什么秘钥加密则由AKF来决定:
0
Device Key
1
Application Key
至于TransMIC,当是Unsegmented Packet时则固定为32bits。而为Segmented Packet则取决于SZMIC值,1表示64bits,0表示32bits;最后再上抛给Access Payload层判断最终的内容是什么;这里再次强调一下:如果是Segmented Packets时,TransMIC是随最后一包一起发送出去的,同时Access Payload层一次性向下抛数据的最大长度为380字节(不包括TransMIC)。
Control Message
由Mesh Packet剖析图可知,Upper Transport Layer只关注Unsegmented的Parameters以及Segmented的Segment m的内容,而具体对应的内容指的是什么,则是由Lower Transport Layer中的Opcode字段指明。至此,对于Control Message而言,其生命周期就结束了,不会再向上抛数据给Access Payload层了。同样的,在Upper Transport Layer能传输的最大长度的Control Message为256字节,具体如下所示:
Number of Packets | Transport Control PDU Payload Size |
---|---|
1 | 11 (Unsegmented) |
1 | 8 (Segmented) |
2 | 16 |
3 | 24 |
n | n*8 |
32 | 256 |
当数据闯关达到这一层时,基本上已经在最顶层了,这也是我们以后用的最多的一层。由于Control Message在Upper Transport Layer就已经停止了 (其中Segment Acknowledgment则在Lower Transport Layer就停止了);如Config Default TTL Get、Config Default TTL Status、Generic OnOff Get等等这些命令,在Access Payload层将统统被解析并展现给开发者,开发者则根据这些命令相应地做出处理。
最后,小编将通过真实的案例(Generic OnOff Set命令的抓包图),将上述的内容全部串连起来。
从上图我们可知,Network PDU的有效内容为Lower Transport PDU,而Lower Transport PDU有效的内容则是PDU即:
B2 34 1B 3C D2 69 23 69 B3 9C
而上述的数据则是由Upper Transport PDU + TransMIC组成,而TransMIC为 “23 69 B3 9C”(Unsegmented的Access Message固定为32bits),故Upper Transport PDU则是:
B2 34 1B 3C D2 69
对比解密后的Access Payload层数据可知,该数据显然是被Application Key加密过的(此时,AKF为1);使用Application Key解密之后,则就是我们最终想要的数据内容了,即Generic OnOff Set命令的相关内容。至此,小编相信现在大家对整个Mesh Packet的内容应该有更深刻地了解了吧,Mesh不同层通过层层加解密最终上抛或者下抛给应用层或到网络层。基于上述的抓包图,不管是从底层往上层还是上层向下层都是可以很好理解的。具体的处理流程如下图所示:
当通过了所有层的检验之后,来到我们顶层的Access Layer;这里继续对解密后的上层传输PDU进行判断,然后上抛到最后的应用层或者在Access Layer被抛弃;
判断接收到的Message的目标地址是不是单播地址
如果不是单播地址,紧接着判断是不是固定的组地址 (fixed group address)
Values | Fixed Group Address Name |
---|---|
0xFF00–0xFFFB | RFU |
0xFFFC | all- proxies |
0xFFFD | all- friends |
0xFFFE | all- relays |
0xFFFF | all- nodes |
还要注意的是:如果目标地址是固定的组地址,则只有节点的首要元素 (primary element) 的模型 (model) 能处理该消息
如果既不是单播地址,也不是固定的组地址,那么再判断是不是订阅地址,通常都是通用的组地址即0xC000~0xFEFF的地址或者虚拟地址;如果1.2.3都不是,那么直接丢弃
判断匹配的模型是否有匹配的绑定应用秘钥,如果有则交给这个模型的回调函数进行处理,否则直接丢弃
按理这个时候,该章节已经是接近尾声了;但是,我不知道有没有读者想过这样的问题?如果我的设备掉电复位了或者不小心复位了,下面的场景怎么办?不管读者有没有想过这个问题,马上耐心地跟随小编继续深究下去吧,小编不会再新增内容了 (发现该文章越写越长了,根本停不下来😢)。
场景1
如果此时此刻,又“刚好”接受到了之前已经处理的Message(可能是Relay Attacker发出的),会把该网络包丢弃吗?
场景2
这个时候,网络PDU的序列号是否是重新从0开始还是接上一次的继续累加吗?
针对这个场景,主要是message cache和replay cache去检验并拦截已经处理过的Message。而这两个cache均是存在RAM区域的,这也就意味着不管是什么原因导致复位了,那么必然这两个cache肯定是被清空了。而它们实质缓存的内容如下所示:
message cache
replay cache
很遗憾,这个时候这两个cache均未能起到保护的作用。但是,只要它们又接收到一个新的有效的message或者cache中已经有了上一次的内容 (就算这个内容是旧的),那么该保护又恢复了;而失效的情况,只有复位后又刚好第一次就收到了已经处理过的message;
这个也是很常见的一个情况,这里我就先告诉读者答案:“那就是这个时候,SEQ是不会从0重新开始,又不会继续上一次的值累加,而是比复位前的值大”,至于大多少?那就继续往下看吧。
由上图可知,只要复位之后发的第一包Network PDU的SEQ肯定会比复位前的SEQ大的;复位后的默认最大有效的SEQ值会比SEQ的起始值大8192。至于起始值与最大有效的SEQ值为多少,这里分两种情况:
情况1
如果复位前,当前累加的SEQ值小于最大有效的SEQ时,则复位之后起始值为上一次的最大有效SEQ,最大有效的SEQ为上一次最大有效SEQ+8192,如上图的2a所示;
情况2
如果复位前,当前累加的SEQ值小于最大有效的SEQ-64时,则主动更新最大有效值,在上一次的最大有效SEQ基础上加8192且将值存入flash中,如上图的2b-1所示。如果过段时间复位了,则复位之后起始值为上一次的最大有效SEQ,最大有效的SEQ为上一次最大有效SEQ+8192,如上图的2b-2所示;
注意:8192以及64仅仅是Nordic Mesh SDK默认的值,用户完全可以根据自身的情况做出相应的修改。至于其他厂商是什么策略,小编暂时未知,但是最终的结果都是一样的,即“SEQ是不会从0重新开始,也不会继续上一次的值累加,而是比复位前的值大”。
除此之外,假如遇到复位之后,当前网络中的IV Index值比之前的大,那么当这个复位后的设备接收到Secure Network Beacon,也会相对应地将IV Index同步为最新的IV Index值,更多的详情请参考后续的IV Index更新过程篇章