<!--
我相信读过小编前面章节内容的读者,或多或少已经对整个BLE Mesh的入网过程,Mesh Packet的各个层的作用以及在Mesh网络中数据是如何交互的有一定的了解;那么,接下来我们继续讲解Mesh中的基础模型 (Foundation Model)之一的Configuration Model。然而,Configuration Model又分两种类型:
然而,需要注意的是:不管是Server还是Client,在节点中都是强制要求必须有的一个模型。
在上面的前言中,小编也说了这个配置服务是强制要求的,是不可以省略的一个模式;那么该模型为什么这么重要,以至于每个节点都必须要有?其主要有以下几个特性:
那么,该模型又存放了哪些 “宝贝” 呢,如下表所示:
Configuration Server States | Bound States | ||||
---|---|---|---|---|---|
State | Instance | Model | State | Instance | |
Secure Network Beacon | Primary | - | - | - | |
Composition Data | Primary | - | - | - | |
Default TTL | Primary | - | - | - | |
GATT Proxy | Primary | Configuration Server | Node Identity | Primary | |
Friend | Primary | - | - | - | |
Relay | Primary | - | - | - | |
Model Publication | Primary | - | - | - | |
Subscription List | Primary | - | - | - | |
NetKey List | Primary | - | - | - | |
AppKey List | Primary | - | - | - | |
Model to AppKey List | Primary | - | - | - | |
Node Identity | Primary | - | - | - | |
Key Refresh Phase | Primary | - | - | - | |
Heartbeat Publish | Primary | - | - | - | |
Heartbeat Subscription | Primary | - | - | - | |
Network Transmit | Primary | - | - | - | |
Relay Retransmit | Primary | - | - | - |
从上表可知,洋洋洒洒共17项内容;可能有读者会发现有一个“腰间突出”的,即GATT Proxy这项内容;对于这个不知道大家还有没有印象:
mesh provisioning server
当设备还没有入网前,mesh gatt server就叫mesh provisioning server;其主要是让provisioning client配置provisioning server,让其加入mesh网络
mesh proxy server
当设备已经入网了,则为mesh proxy server;其主要作用是接收或者发送proxy pdus从或者到client
而这个GATT Proxy与Node Identity的关系就是,如果proxy feature被支持且mesh proxy server被公开。在入网完成之后,就会将在广播包中携带有Node Identity这个域值。
既然Configuration Server Model存放的是节点的配置内容,那用户应该如何去获取这个内容呢?具体如下所示:
Element | SIG Model ID | States | Messages | Rx | Tx |
---|---|---|---|---|---|
Primary | 0x0000 |
Secure Network Beacon |
Config Beacon Get | M | |
Config Beacon Set |
M | ||||
Config Beacon Status | - | M | |||
Composition Data | Config Composition Data Get | M | |||
Config Composition Data Status | M | ||||
Default TTL | Config Default TTL Get | M | |||
Config Default TTL Set | M | ||||
Config Default TTL Status | - | M | |||
GATT Proxy | Config GATT Proxy Get | M | |||
Config GATT Proxy Set | M | ||||
Config GATT Proxy Status | - | M | |||
Friend | Config Friend Get | M | |||
Config Friend Set | M | ||||
Config Friend Status | - | M | |||
Relay and Relay Retransmit | Config Relay Get | M | |||
Config Relay Set | M | ||||
Config Relay Status | - | M | |||
Model Publication | Config Model Publication Get | M | |||
Config Model Publication Set | M | ||||
Config Model Publication Virtual Address Set | M | ||||
Config Model Publication Status | M | ||||
Subscription List | Config Model Subscription Add | M | |||
Config Model Subscription Virtual Address Add | M | ||||
Config Model Subscription Delete | M | ||||
Config Model Subscription Virtual Address Delete |
M | ||||
Config Model Subscription Virtual Address Overwrite |
M | ||||
Config Model Subscription Overwrite |
M | ||||
Config Model Subscription Delete All |
M | ||||
Config Model Subscription Status | M | ||||
Config SIG Model Subscription Get | M | ||||
Config SIG Model Subscription List | M | ||||
Config Vendor Model Subscription Get | M | ||||
Config Vendor Model Subscription List | M | ||||
NetKey List | Config NetKey Add | M | |||
Config NetKey Update | M | ||||
Config NetKey Delete | M | ||||
Config NetKey Status | M | ||||
Config NetKey Get | M | ||||
Config NetKey List | M | ||||
AppKey List | Config AppKey Add | M | |||
Config AppKey Update | M | ||||
Config AppKey Delete | M | ||||
Config AppKey Status | M | ||||
Config AppKey Get | M | ||||
Config AppKey List | M | ||||
Model to AppKey List | Config Model App Bind | M | |||
Config Model App Unbind | M | ||||
Config Model App Status | M | ||||
Config SIG Model App Get | M | ||||
Config SIG Model App List | M | ||||
Config Vendor Model App Get | M | ||||
Config Vendor Model App List | M | ||||
Node Identity | Config Node Identity Get | M | |||
Config Node Identity Set | M | ||||
Config Node Identity Status | M | ||||
N/A | Config Node Reset | M | |||
Config Node Reset Status | M | ||||
Key Refresh Phase | Config Key Refresh Phase Get | M | |||
Config Key Refresh Phase Set | M | ||||
Config Key Refresh Phase Status | M | ||||
Heartbeat Publication | Config Heartbeat Publication Get | M | |||
Config Heartbeat Publication Set | M | ||||
Config Heartbeat Publication Status | M | ||||
Heartbeat Subscription | Config Heartbeat Subscription Get | M | |||
Config Heartbeat Subscription Set | M | ||||
Config Heartbeat Subscription Status | M | ||||
Network Transmit | Config Network Transmit Get | M | |||
Config Network Transmit Set | M | ||||
Config Network Transmit Status | M |
从上表可以看到,一个State对应多个Message;那么,State与Message的关系就类似于 “手机现在都支持BLE,那么就有打开BLE、关闭BLE以及查看当前BLE的状态的动作”;其中,手机支持BLE就等同于State,而那些动作就是Message。所以,如果你想要获取或者配置相关的内容,就可以通过Message去实现。对于,RX和TX是相对于节点本身为参考体。这里举个例子 “就Network Transmit状态而言,Config Network Transmit Get/Set表示该节点可以接收这些Messages;而节点在接收到这些消息之后,就会返回Config Network Transmit Status即发送回给源地址的设备”;那么,紧接着我们继续解析每一个State以及其对应的Message的作用。
该状态用于表示节点是否在周期性广播Secure Network Beacon,这个比较简单好理解;其具体的状态如下表所示:
Values | Description |
---|---|
0x00 | The node is not broadcasting a Secure Network beacon |
0x01 | The node is broadcasting a Secure Network beacon |
0x02–0xFF | Prohibited |
该消息用于获取当前安全网络信标的状态,当节点的configuration server model接收到了该消息后,会用Config Beacon Status去响应该消息。
跟上述的Config Beacon Get相似,只不过该消息用于设置当前的安全网格信标;当节点的configuration server model接收到了该消息后,将设置的新值通过Config Beacon Status去响应该消息;其中该消息的帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Beacon | 1 | New Secure Network Beacon state |
对于Config Beacon Get/Set这两个Message就是为了设置或者获取节点上述所描述的状态;不管是Get还是Set的Message,收到之后都会给其响应该消息。
Field | Size(octets) | Notes |
---|---|---|
Beacon | 1 | Secure Network Beacon state |
这个状态包含了节点的信息,如其元素以及模型的信息。这些信息可以由多个信息页组成;但是目前SIG只规定了Page0的内容,其他信息页内容是可选的。那些,SIG规定的Composition Data Page0有哪些内容呢?我想信很多一直看我们PB-GATT入网过程教程的读者应该对这些内容比较熟悉。没错!这些就是节点的元素、模型以及其所支持的特性等信息,入网成功之后Provisioner向节点获取得到的内容;
Field | Size(octets) | Notes |
---|---|---|
CID | 2 | Contains a 16-bit company identifier assigned by the Bluetooth SIG |
PID | 2 | Contains a 16-bit vendor-assigned product identifierVID2 Contains a 16-bit vendor-assigned product version identifier |
VID | 2 | Contains a 16-bit vendor-assigned product version identifier |
CRPL | 2 | Contains a 16-bit value representing the minimum number of replay protection list entries in a device |
Features | 2 | Contains a bit field indicating the device features |
Elements | variable | Contains a sequence of element descriptions |
CID
该域值包含的内容就是公司识别码(Company Identifier),如Config Composition Data Status所示,小编通过捉包工具抓取得到的该域值为0x0059,其值表示的是Nordic公司名;
如果有读者的域值跟小编不一样的话,可以在蓝牙联盟的Company Identifiers中查看是哪家公司的识别号
PID
同理,PID的全称为Product Identifier;但是,SIG没有对其进行规范,这个是由用户或者供应商自定义的一个域
VID
同样的情况,VID的全称为Version Identifer;也是一个由用户或者供应商自定义的一个域
CRPL
这个域值表明用于重播攻击保护 (replay attack protection) 的Relay cache List的大小,即最多可以存放多少个relay cache
Features
一看这个域名就知道什么意思了,其用于表示当前这个节点都支持哪些特性:
Bit | Feature | Notes |
---|---|---|
0 | Relay | Relay feature support: 0 = False, 1 = True |
1 | Proxy | Relay feature support: 0 = False, 1 = True |
2 | Friend | Relay feature support: 0 = False, 1 = True |
3 | Low Power | Low Power feature support: 0 = False, 1 = True |
4-15 | RFU | Reserved for Future Use |
关于这些特性,我们已经在BLE Mesh各层帧包格式详解中提及到,节点支持以上的一个或者多个特性,只是表明它有这个功能;但是,它们有没有打开这个是由用户去决定的;这个就好比 “笔记本电脑具备有路由的功能,至于你要不要打开这个功能,则是由用户层去决定”
Elements
该域表示当前这个元素具备有多少个SIG Model和Vendor Model、具体是哪几个模型;该域有如下几个字段:
Loc
该域为位置描述符的意思,主要起到加固上下文描述的作用;如 “我们有一个采用了BLE Mesh技术的排插,并且该排插有3个插座分别对应着三个Element,那么这个时候该域就可以配置为是哪个插座,例如:1、2、3” 具体可以为哪些值,请参考SIG的GATT Namespace Descriptors
NumS
这个域就相对比较好理解了,S = SIG Model;表示该元素下有多少个SIG Model
NumV
同理,V = Vendor;表示该元素下有多少个Vendor Model
SIG Models/Vendor Models
这个就更加好理解了,表明当前这个元素下SIG/Vendor Models的ID号是什么
该消息一般都是Provisioner发出,用于获取入网之后节点设备的一些设备参数,具体的帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Page | 1 | Page number of the Composition Data |
以下是小编通过ellisys抓取得到的Mesh数据包:
这个可能会有读者有疑问 “上述的帧格式不是表明Page域的内容,就是想要读的Composition Data的页数吗?而且目前Specification也只规定了Page0的内容,为什么抓包图显示出来却是读的255页的内容呢?”
有这样疑问的读者,都是有认真思考过的同学,而不是被我的文章带着走。这个问题问得很好,没有错这个的确是读第255页的composition data;所以这里就分两种情况:
- 所有255页的内容都是有效,那么就会从255递减直至0页的数据都返回给该消息;
- 如果是恶意读不存在有效数据的一页,那么节点收到之后首先就会返回它自身所支持的最大页数的数据,直到0页为止。如上述的抓包图就是这种情况:当节点接收到的是255这个页数的命令,而自身最大只支持第0页,所以就只响应了第0页的composition data给provisioner,如Composition Data Status中所示;
这个时候我们再来看响应的Composition Data Status内容就轻松多了,如下所示:
但是,有一点我们需要注意的是Composition Data的大小不能大于Access Payload的大小,即下述表格所示:
顾名思义,该State表示当message发送时所使用的默认TTL值是多少;当然,你可以手工更改该值而不用这个默认的TTL值也是没有什么问题的。关于TTL的详情,可以参考《BLE Mesh各层帧包格式详解》;其具体的数据帧格式如下所示:
Value | Description |
---|---|
0x00,0x02-0x7F | The Default TTL state |
0x01,0x80-0xFF | Prohibited |
该命令用于获取当前节点默认的TTL值,当对端设备接收到该Message之后就会将其默认的TTL值通过Config Default TTL Status返回。
毫无疑问,该命令用于设置当前节点默认的TTL值,其帧格式如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
TTL | 1 | New Default TTL value |
当节点收到这条message之后,然后就会将新的默认TTL值通过Config Default TTL Status返回。
主要用于响应上述Config Default TTL Get/Set的命令,其帧格式如下所示:
Parameters | Size(octets) | Notes |
---|---|---|
TTL | 1 | Default TTL |
该状态的作用为:表示当前的节点是否支持代理特性(Proxy feature),如果是支持的话还可以控制是打开还是关闭该特性。其具体值的含义如下表所示:
Value | Description |
---|---|
0x00 | The Proxy feature is supported and disabled |
0x01 | The Proxy feature is supported and enabled |
0x02 | The Proxy feature is not supported |
0x03–0xFF | Prohibited |
这里有一点小编觉得还是有必要说明一下,虽然我在之前的篇章中《BLE Mesh各层帧包格式详解》讲过这个问题。节点支持代理特性只是表示节点有这个功能;但是如果没有打开这个功能,就想把相关的数据通过GATT传到Mesh网络中是不可能的,你还必须把代理特性使能了才行。换一句话说:支持proxy特性只是表示你跟普通的BLE一样,只有使能了才能通过GATT跟Mesh网络中的其他设备通讯。因为这个时候Proxy Server已经生效了,你可以通过它跟其他节点愉快地进行数据交互了。为了巩固这个理解,小编举一个生活中的例子 “我们都知道现在的智能手机都支持BLE功能,但是你想要跟你手环通讯是不是要把蓝牙打开才行,如果我把蓝牙关了你不能说我的手机不支持BLE啊”。
获取当前节点的GATT Proxy的状态,具体值的含义如GATT Proxy所示;如果对端设备收到该消息的话,就会响应Config GATT Proxy Status。
与Get的操作相反,该消息用于设置GATT Proxy的状态,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
GATT Proxy | 1 | New GATT Proxy state |
这条命令发送出去,也是有响应数据的;如果设置成功,就会收到更新过后的Config GATT Proxy Status。
针对上述抓包图,如果将Proxy feature除能了,这个时候节点在没有断开连接之前,它与gatt client还是可以通过gatt通道进行数据交互的。在这个时候,一旦断开那么节点就会将proxy service除能,同时节点此时不再发出可连接的广播。这里可能会有读者会问了 “那么是不是就不能将proxy feature使能回去了?” 显然,通过GATT通道将其配置为原状是不可能了,但是你可以利用其他的proxy节点 (同时proxy feature enabled) 将其proxy feature再次使能 (通过广播通道接收使能的命令)。
向对端设备报告当前GATT Proxy的状态,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
GATT Proxy | 1 | GATT Proxy state |
该状态基本上跟GATT Proxy是类似的,表示当前的节点是否支持朋友特性,如果支持的话则可以配置朋友特性是使能还是除能;其中Friend的状态值含义如下表所示:
Value | Description |
---|---|
0x00 | The node supports Friend feature that is disabled |
0x01 | The node supports Friend feature that is enabled |
0x02 | The Friend feature is not supported |
0x03–0xFF | Prohibited |
注意: 如果节点之前支持friend特性且该特性是使能的,且与几个低功耗节点(LPN)建立了朋友关系;但是,之后Friend特性被除能了。那么朋友节点将解除所有与LPN的关系以及清除所有的为LPN保留的缓存。
获取节点当前的friend状态,其具体的状态值如Friend中所示;如果对端设备收到该消息的话,就会响应Config Friend Status。
与Get的操作相反,该消息用于设置Friend的状态,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Friend | 1 | New Friend state |
这条命令发送出去,也是有响应数据的;如果设置成功,就会收到更新过后的Config Friend Status。
向对端设备报告当前Friend的状态,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Friend | 1 | Friend state |
Relay
表示当前的节点是否支持中继特性,如果支持的话则可以配置中继特性是使能还是除能;其中中继的状态值含义如下表所示:
Value | Description |
---|---|
0x00 | The node support Relay feature that is disabled |
0x01 | The node supports Relay feature that is enabled |
0x02 | The Relay feature is not supported |
0x03–0xFF | Prohibited |
Relay Retransmit
该状态决定被节点中继的Network PDU的重传相关参数,分别为:
Relay Retransmit Count(3bits)
控制着被节点中继的Network PDU的重传次数,最终的次数为Relay Retransmit Count+1;例如:当该值为0时,则只有一次传输;如果该值为7 (最大值) 时,则表示有1次单次传输+7次重传,总共传8次;换句话说:就是被中继的Network PDU要发送多少次;显然,如果只有一次的话,有可能其他节点会有一定概率没有收到,最终的值视情况而定;
Relay Retransmit Interval Steps(5bits)
控制着重传的时间间隔,最终的间隔时间为 retransmission interval = (Relay Retransmit Interval Steps + 1) * 10 ms;但是需要注意的就是:“为了增强鲁棒性,会插入0-10ms的随机时间,最终的间隔时间=(retransmission interval + 0~10)ms ”;
这里我们通过真实的案例来加固你的理解,这也是为什么大家会选择我们红旭教程的原因:图文并茂加讲解;
废话不多说,我们直接上干货:
首先,我们先获取节点F的Relay参数是多少;从上图我们可以知道:
我们由Relay Retransmit Count和Relay Retransmit Interval Steps可以知道,这个参数所表示的意思就是说总共重传2次,重传的时间间隔为30ms;
通过同样的方式,获取节点D的默认TTL值;具体该值是多少我们不用关心,我们要关心的是当节点D通过广播通道返回该状态值的时候,节点F会中继该消息;由图我们可以知道,节点F总共发送了6个数据;由于数据是通过37、38、39三个广播通道发送,所以6除以3就等于2次,该值完成跟理论符合即Relay Retransmit Count+1;
这里我们可以更加清楚地知道,中继数据的时间间隔为30ms。由于我在上面已经说过,会增加0-10ms的随机时间来增强鲁棒性 (Specification规定),所以上述捉包图显示的是31多毫秒;
之所以将上述的两个状态放在一起,就是因为该消息一旦发出就会收到带有上述两个状态的信息 (Config Relay Status) 回复,即当前节点的中继和中继重复的状态信息。
同理,这个消息与上述的Get消息相反;其用于设置Relay和Relay Retransmit的状态,其帧格式如下所示:
Field | Size(bits) | Notes |
---|---|---|
Relay | 8 | Relay |
RelayRetransmitCount | 3 | Number of retransmissions on advertising bearer for each Network PDU relayed by the node |
RelayRetransmitIntervalSteps | 5 | Number of 10-millisecond steps between retransmissions |
当设置完成之后,如果一切正常的话,那么就会收到带有设置后的值的 (Config Relay Status)。
该消息是被动响应的,只有收到Set/Get消息之后,该消息才会被发送出去。该条消息的帧格式跟Config Relay Set是一模一样的。
该状态所包含的内容就比较多了,基本上覆盖了Model发布消息的方方面面:
Publish Address
这个比较好理解,就是模型发布时的目标地址;因此,它可以是单播地址、虚拟地址、组地址以及未分配的地址;但是当目标地址是未分配的地址的话,那么这也意味着该模型不能发送任何未经请求的消息 (如果要停止这个周期性的Publication,只需要将Publish地址设置为未分配的地址即可);只能发送响应消息,前提是其能接收到带有应答的消息;
Publish Period
该状态用于决定当模型发布Status消息时的时间间隔,其由Number of Steps和Step Resolution两部分组成:
Field | Size(bits) | Description |
---|---|---|
Number of Steps | 6 | The number of steps |
Step Resolution | 2 | The resolution of the Number of Steps field |
其中,Step Resolution又分以下几种情况:
Value | Description |
---|---|
0b00 | The Step Resolution is 100 milliseconds |
0b01 | The Step Resolution is 1 second |
0b10 | The Step Resolution is 10 seconds |
0b11 | The Step Resolution is 10 minutes |
而Number of Steps的取值也是有要求的,即:
Value | Description |
---|---|
0x00 | Publish Period is disabled |
0x01–0x3F | The number of steps |
其中,Step Resolution是最高2个bit,剩下的为Number of Steps;所以,最终Publish Period = Step Resolution * Number of Steps;如果Number of Steps为0时,那么周期发布功能除能,即此时不周期发布Network PDU;
Publish AppKey Index
该状态表示Model发送的Message所用到的AppKey的AppKey索引,我们在BLE Mesh各层帧包格式详解中提到Network PDU的长度是有限的,不可能将AppKey、DevKey以及其他的Key都塞进网络数据包中,所以需要一个索引与其相对应。因此,只要有索引值就能找到对应的Key;
Publish Friendship Credential Flag
这个状态也很好理解,主要用于控制模型发布消息时用的哪种安全素材。至于什么是安全素材,其作用于各种Packets的加解密。这里我不深究,只要知道其功能即可。其具体的取值情况如下所示:
Value | Description |
---|---|
0 | Master security material is used for Publishing |
1 | Friendship security material is used for Publishing |
如果该标志位被设置为1,但是相对应的友谊的安全素材(Friendship security material)无效。那么,最终还是会用主安全素材(Master security material);
Publish TTL
这个State定义了模型周期发布Status消息时的TTL值,如果Publish TTL域的值被设置为0xFF,则表示当模型发布周期的状态消息时使用默认的TTL值,也就是正常发送网络包 (Network PDU) 时默认的TTL是多少就是多少;其取值范围如下表示所示:
Value | Description |
---|---|
0x00–0x7F | The Publish TTL value, represented as a 1-octet integer |
0x80–0xFE | Prohibited |
0xFF | Use Default TTL |
Publish Retransmission Count
该状态以及下面的Publish Retransmit Interval Steps均跟上述的Relay Retransmit很相似,但是又完全不一样;至于,哪里不一定随小编慢慢道来;此处的State仅仅是指模型在Publish Period内发布状态消息时的次数,这也就是说如果Push Period中的Number of Steps为0或者Step Resolution为0,那么该域值仍然是有效的,只不过此时不再周期发布Network PDU,但是仍然会重传Network PDU;
从上述两幅图可以看出,即使Number of Steps为0,但是当发布消息时,仍然会重传两次;
Publish Retransmit Interval Steps
同理,该状态表示的模型在Publish Period内发布状态消息时的时间间隔;如果Publish Retransmission Count是1的话,那么发布完Network PDU之后,隔Retransmit Interval毫秒就重传一次此Network PDU就不再重传了;最终的retransmission interval = (Publish Retransmit Interval Steps + 1) * 50 ms
对于整个Model Publication的State消息,其主要用于配置指定某个模型周期性发布Status消息;但是,我估计有很多读着会对Publish Retransmit Interval Steps、Publish Retransmission Count以及Publish Period它们之间的关系很感兴趣,但是又有很多疑问。废话不多说,先直接上图:
从上图我们可以清晰地看到,Publish Retransmission相关的动作均是在Publish Period期间中进行的。那么就有如下几种可能:
跟上述的所有Get消息一样,该状态为了获得相对应State的相关参数;以下是其获取得到的数据:
Field | Size(octets) | Notes |
---|---|---|
Element Address | 2 | Address of the element |
Model Identifier | 2 or 4 | SIG Model ID or Vendor Model ID |
其中元素地址一定是单播地址,其他类型的地址无效。
同样的“配方”,设置相对应State的参数,其对应的帧格式如下所示:
Field | Size(bits) | Notes |
---|---|---|
Element Address | 16 | Address of the element |
Publish Address | 16 | Value of the publish address |
AppKey Index | 12 | Index of the application key |
CredentialFlag | 1 | Value of the Friendship Credential Flag |
RFU | 3 | Reserved for Future Use |
Publish TTL | 8 | IDefault TTL value for the outgoing messages |
Publish Period | 8 | Period for periodic status publishing |
Publish Period | 8 | Period for periodic status publishing |
Publish Retransmit Count | 3 | Number of retransmissions for each published message |
Publish Retransmit IntervalSteps | 5 | Number of 50-millisecond steps between retransmissions |
Model Identifier | 16 or 32 | SIG Model ID or Vendor Model ID |
上表所述的各个域值所代表的具体含义,请参考Model Publication;其中Publish Address在该命令中不能配置为虚拟地址,因为它有专门的命令来设置。
这条消息的内容基本上跟Config Model Publication Set是完全一样的,唯一不同的就是Publish Address这里只能为虚拟地址且其长度为128Bits。
这个State是配合上述的Get、Set使用的,但凡收到它们的消息就会返回该Status消息,但应答的内容有些许不同。同样的,其具体的内容跟Config Model Publication Set是一模一样的,至于Publish Address是16bits还是128bits,则根据发送过来的Set命令的情况而定。
首先,订阅列表只是组地址或者虚拟地址的集合;当节点收到Access Messages时,就会去匹配目标地址;如果存在于订阅列表中,就会做出所对应的Opcode的动作;每个模型都一个自己的订阅列表,但是每个元素中的所有扩展模型共用一个订阅列表。对于模型与元素的相关概念,请参考红旭之前写的教程《什么是Element和Model》。
该Message用于新增目标地址至订阅列表,其具体的帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Element Address | 2 | Address of the element |
Address | 2 | Value of the address |
Model Identifier | 2 or 4 | SIG Model ID or Vendor Model ID |
其中元素地址只能为单播地址,新增的地址也是只能为组地址(包括SIG规定的组地址),如果想要新增虚拟地址至订阅列表则需要用Config Model Subscription Virtual Address Add消息;下图为当前该消息的实际捉包图:
该Message是带应答的命令,其应答消息所对应的具体内容如Config Model Subscription Status所示。
这个操作跟上述的Config Model Subscription Add是相反的,此消息用于从订阅列表(Subscription List)中删除订阅地址。同样的,要删除的订阅地址只能是组地址(包括SIG规定的组地址),具体的帧格式内容跟Get是完全一样的,也是一个带应答的消息 (应答消息所对应的具体内容如Config Model Subscription Status所示)。
其实小编认为该命令称为 “Config Model Subscription Update” 更为贴切,因为OverWrite的意思就是说将订阅列表中的之前的组地址丢弃,并替换为一个新的地址,本质上跟Update的意思没啥区别;同样的,该消息的格式以及域值的定义跟Config Model Subscription Add如出一辙。
这是SIG专门为虚拟地址增加至订阅列表中所设的一条命令。其帧格式基本上跟Config Model Subscription Add是一样,唯一不同的地方就是此消息新增至Subscription List中的地址为16字节,详情如下所示:
Field | Size(octets) | Notes |
---|---|---|
Element Address | 2 | Address of the element |
Label | 16 | Value of the Label UUID |
Model Identifier | 2 or 4 | SIG Model ID or Vendor Model ID |
同样的,元素地址只能为单播地址,而Label为16字节的Label UUID。
同Config Model Subscription Delete。
同Config Model Subscription Overwrite。
该命令用于删除指定元素下的指定模型的所有订阅列表的内容,帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Element Address | 2 | Address of the element |
Model Identifier | 2 or 4 | SIG Model ID or Vendor Model ID |
同样,它也是一个带应答的消息 (应答消息所对应的具体内容如Config Model Subscription Status所示)。
该命令用于获取指定元素地址,且在该元素下的指定SIG模型的订阅列表信息,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Element Address | 2 | Address of the element |
Model Identifier | 2 | SIG Model ID |
这也是一个带应答的消息,其应答消息如Config SIG Model Subscription List所示。
专门用于应答Config SIG Model Subscription Get消息,其内容主要为指定元素的指定SIG模型的订阅列表内容,其帧格式内容如下表所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
Element Address | 2 | Address of the element |
Model Identifier | 2 | SIG Model ID |
Addresses | variable | A block of all addresses from the Subscription List |
其中,Addresses域的值表示的是该指定元素下所指定的模型所订阅的全部地址:
组地址/虚拟地址
如果订阅列表是空的,那么Addresses域的内容也是空的,也就是说返回的消息中没有该字段的内容
然而,该消息的具体内容又分成如下几种情况:
如果发送方是Config SIG Model Subscription Get
同Config SIG Model Subscription Get一样,只是该消息是用于获取指定元素下的指定的Vendor模型的订阅列表信息,帧格式如下表所示:
Field | Size(octets) | Notes |
---|---|---|
Element Address | 2 | Address of the element |
Model Identifier | 4 | Vendor Model ID |
专门用于应答Config Vendor Model Subscription Get消息,其内容主要为指定元素的指定Vendor模型的订阅列表内容,其帧格式内容如下表所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
Element Address | 2 | Address of the element |
Model Identifier | 4 | Vendor Model ID |
Addresses | variable | A block of all addresses from the Subscription List |
其中,Addresses域的值表示的是该指定元素下所指定的模型所订阅的全部地址:
然而,该消息的具体内容又分成如下几种情况:
如果发送方是Config Vendor Model Subscription Get
该状态消息用于应答Config Model Subscription Add、Config Model Subscription Delete、Config Model Subscription Overwrite、Config Model Subscription Virtual Address Add、Config Model Subscription Virtual Address Delete、Config Model Subscription Virtual Address Overwrite、Config Model Subscription Delete All等消息,其帧格式内容如下所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
| Element Address | 2 |Address of the element | Address | 2 |Value of the address | | Model Identifier | 2 or 4 |Vendor Model ID or SIG Model ID|
针对上述的表格,可能读者会有这样一个疑问:“如果我新增的地址是Label UUID,那么为什么返回的应答消息是2字节而不是16个字节?”,其实这跟下述的NetKey/AppKey List是类似的;如果将Label UUID放入网络封包的话就太占资源了,所以通过算法将Label UUID压缩为2个字节的虚拟地址;具体的计算方法如下所示:
SALT = s1 (“vtad”)
hash = AES-CMACSALT (Label UUID) mod 214
其中虚拟地址的第15个bit为1,第14个bit为0,其余的13~0 bit则是上述的hash值。然而,该消息的具体内容又分成如下几种情况:
如果发送方是Config Model Subscription Add或者Config Model Subscription Virtual Address Add
如果发送方是Config Model Subscription Delete或者Config Model Subscription Virtual Address Delete
如果发送方是Config Model Subscription Overwrite或者Config Model Subscription Virtual Address Overwrite
如果发送方是Config Model Subscription Delete All
相关出错的原因,如下表所示:
Error Condition | Status Code Name |
---|---|
The model defined by ElementAddress and ModelIdentifier does not support subscription mechanism | Not a Subscribe Model |
The device cannot store new address due to insufficient resources on device | Insufficient Resources |
The unicast address provided in ElementAddress is not known to the node | Invalid Address |
The model identified by SIG Model ID or Vendor Model ID is not found in a given element | Invalid Model |
而Address域的内容是组地址还是虚拟地址,则是由发送方携带的订阅订表中的地址域所决定。
该状态表示网络密钥的索引列表,每个入口最多只能容纳两个NetKey,一个是旧的密钥,另外一个是新的密钥。小编一直都在强调,由于NetKey的长度一般都很长,将其塞入网络消息中不太实现;然而,使用一个索引值就可以找到相对应的NetKey的方式无疑是最佳的。一些消息可能有一个或者多个索引值,比如说:节点使用“Config NetKey Add”添加了多个NetKey,现在通过“Config NetKey Get”获取当前的NetKey索引值,这个时候“Config NetKey List”就会携带多个NetKey索引值了。然而,每个索引值的长度均为12Bits,不是8的倍数;这个时候就需要根据索引值个数的奇偶来重新组成这个索引值了,具体的实现方式如下所示:
顾名思义,该消息是新增网络密钥至节点的NetKey列表,其帧格式如下表所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | NetKey Index |
NetKey | 16 | NetKey |
跟上面以往的Add一样,这是一个带应答的消息,具体应答的内容请参考Config NetKey Status。其中NetKeyIndex与NetKey是对应关系,“我就是它,它就是我”。
该消息用于更新节点的NetKey,其帧格式及其含义跟上述的Config NetKey Add一样。
该消息用于从节点的NetKey列表中删除NetKey,其帧格式如下表所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | NetKey Index |
同样这也是一个带应答的消息,具体应答的内容请参考Config NetKey Status;其中NetKey Index为12Bits,但是会填充至16Bits再发送出去。
该消息用于应答上述的Config NetKey Add、Config NetKey Update、Config NetKey Delete的命令,帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
NetKeyIndex | 2 | Index of the NetKey |
但是有如下几例特殊的情况也是返回成功的状态:
除此之外,该消息的内容根据不同的情况不同而不同,如果发送方是Config NetKey Add或者Config NetKey Update或者Config NetKey Delete
主要用于获取当前节点所有已知的NetKey,该消息是一个带应答的消息,而应答的内容如Config NetKey List所示。
专门用于应答Config NetKey Get消息,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndexes | variable | A list of NetKey Indexes known to the node |
我们可以看看相关的实际捉包图是怎么样的:
相关出错的原因,如下表所示:
Error Condition | Status Code Name |
---|---|
The NetKey identified by NetKeyIndex is already stored in the node and the new NetKey value is different | Key Index Already Stored |
The key identified by NetKeyIndex is not valid for this device for Config NetKey Update message | Invalid NetKey Index |
The node cannot store the new key due to insufficient resources | Insufficient Resources |
The requested delete operation cannot be performed due to general constraints | Cannot Remove |
The requested update operation cannot be performed due to general constraints | Cannot Update |
该State跟上述的NetKey List的描述基本上是一致的,只不过这个是AppKey的索引值列表;同样,每个AppKey列表的条目都只包括一个索引值以及最大两个AppKey值,其中一个是旧的AppKey,另外一个是新的AppKey。其中AppKey Index与NetKey Index是一样的,它们都是12Bits。
还是一样的配方,该消息用于新添AppKey至AppKey列表。然而,跟Config NetKey Add不一样的是,除了AppKey索引值以及AppKey之外,还需要绑定一个NetKey (也就是NetKey Index,因为你通过索引也能找到相应的NetKey值),其具体的帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndexAndAppKeyIndex | 3 | Index of the NetKey and index of the AppKey |
AppKey | 16 | AppKey value |
其中NetKey Index和AppKey Index总共24bits (NetKey Index是第一个Key Index,AppKey Index是第二个Key Index),组成了一个3字节的字段;如果这个时候有多个NetKey,你可以设置想要的NetKey Index (NetKey) 与AppKey进行绑定。之所以要这样,我在SIG MESH协议各个层的作用中就已经提过,NetWork PDU是由 NetKey加密而Upper Transport Layer由DevKey或者AppKey加密,所以如果将AppKey与NetKey绑定在一样时,只要我收到NetWork PDU那么我就知道接下来的Upper Transport PDU用哪个AppKey加解密。这个时候可能又有读者会问:“为什么不绑定DevKey,这是因为自始自终它就只有一个,不允许在一个节点中存在多个DevKey。同时,它只会在Configuration Model Server 与 Configuration Model Client之间使用,因此不需要绑定”;这也是一个带应答的消息,具体的应答消息内容如Config AppKey Status所示。
该消息用于更新节点上的AppKey列表上已有的AppKey,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndexAndAppKeyIndex | 3 | Index of the NetKey and index of the AppKey |
AppKey | 16 | New AppKey value |
具体的应答消息内容如Config AppKey Status所示。
该消息是从节点上的AppKey列表中删除AppKey,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndexAndAppKeyIndex | 3 | Index of the NetKey and index of the AppKey |
如果某个Model绑定了某个AppKey用于周期发布Status信息,但是该AppKey被这条消息删除了,那么Model周期Status信息的动作则无效;具体的应答消息内容如Config AppKey Status所示。
该消息用于应答Config AppKey Add、Config AppKey Update、Config AppKey Delete,但是有如下几例特殊的情况也是返回成功的状态:
除此之外,该消息的内容根据不同的情况不同而不同,如果发送方是Config AppKey Add或者Config AppKey Update或者Config AppKey Delete
该消息用于获取指定绑定的NetKey所对应的AppKey,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | Index of the NetKey |
由于NetKeyIndex是12bits,所以会填充至16bits,具体的应答消息内容如Config AppKey List所示。
该消息主要用于应答Config AppKey Get,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
NetKeyIndex | 2 | NetKey Index of the NetKey that the AppKeys are bound to |
AppKeyIndexes | variable | A list of AppKey indexes that are bound to the NetKey identified by NetKeyIndex |
除此之外,该消息的内容根据不同的情况不同而不同,如果发送方是Config AppKey Get
相关出错的原因,如下表所示:
Error Condition | Status Code Name |
---|---|
The AppKey identified by AppKeyIndex is already stored in the node and the new AppKey is different | Key Index Already Stored |
The node cannot store the new key due to insufficient resources | Insufficient Resources |
The key identified by AppKeyIndex is not valid for this device | Invalid AppKey Index |
The key identified by NetKeyIndex is not valid for this device | Invalid NetKey Index |
The requested update operation cannot be performed due to general constraints | Cannot Update |
The NetKeyIndexAndAppKeyIndex combination is not valid for a Config AppKey Update message | Invalid Binding |
该状态用于阐述模型与AppKey的关系,也就是说模型绑定了哪些AppKey,怎么解绑,怎么获取绑定的内容等等。
该消息用于在指定元素的情况下,将AppKey绑定至指定的模型;其具体的帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
ElementAddress | 2 | Address of the element |
AppKeyIndex | 2 | Index of the AppKey |
ModelIdentifier | 2 or 4 | SIG Model ID or Vendor Model ID |
还有一点需要注意的是一个模型可以绑定多个AppKey,该消息的应答内容如Config Model App Status所示。
跟Config Model App Bind相反的操作,该消息是想要移除指定元素下的指定模型所绑定的AppKey,其具体的帧格式跟上述的Bind消息是一模一样的。
专门应答Config Model App Bind和Config Model App Unbind消息:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
ElementAddress | 2 | Address of the element |
AppKeyIndex | 2 | Index of the AppKey |
ModelIdentifier | 2 or 4 | SIG Model ID or Vendor Model ID |
返回的内容相比较于上述的消息,就是多了一个状态值用于指示Bind和Unbind的消息执行的结果,其中Status值除了0x00表示成功处理之外,还有如下几种情况:
Error Condition | Status Code Name |
---|---|
The model identified by SIG Model ID or Vendor Model ID is not found for a given element | Invalid Model |
The unicast address provided in ElementAddress is not used by the node | Invalid Address |
The key identified by AppKeyIndex is not stored in the node | Invalid AppKey Index |
The node cannot store new binding due to insufficient resources | Insufficient Resources |
The requested bind operation cannot be performed due to general constraints | Cannot Bind |
该消息用于获取指定元素以及指定的SIG Model下,绑定了多少个AppKey;其应答消息如Config SIG Model App List所示,具体的帧格式如下表所述:
Field | Size(octets) | Notes |
---|---|---|
ElementAddress | 2 | Address of the element |
ModelIdentifier | 2 | SIG Model ID |
用于应答Config SIG Model App Get消息,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
ElementAddress | 2 | Address of the element |
ModelIdentifier | 2 | SIG Model ID |
AppKeyIndexes | variable | All AppKey indexes bound to the Model |
跟Config SIG Model App Get消息基本一样,只不过该消息获取的是Vendor Model下绑定的AppKey,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
ElementAddress | 2 | Address of the element |
ModelIdentifier | 4 | Vendor Model ID |
同样,该消息也是带应答的,具体的应答内容如Config Vendor Model App List所述。
该消息只用于应答Config Vendor Model App Get,其帧格式的内容如下所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
ElementAddress | 2 | Address of the element |
ModelIdentifier | 4 | Vendor Model ID |
AppKeyIndexes | variable | All AppKey indexes bound to the Model |
该状态的作用是配置是否使用Node Identity来广播 (可连接的非定向广播),其主要目的是当proxy node的Proxy Service在广播包中被暴露出来,可以根据proxy节点的首要元素的单播地址和该节点所属子网的网络密钥,来快速锁定想要连接的哪个proxy节点,尤其是周边有大量的节点的时候;其中,Node Identity有如下几种不同类型的值:
Value | Description |
---|---|
0x00 | Advertising with Node Identity for a subnet is stopped |
0x01 | Advertising with Node Identity for a subnet is running |
0x02 | Advertising with Node Identity is not supported |
0x03–0xFF | Prohibited |
其中使用的比较多的一个实例就是:“如果Proxy Service在广播包中被暴露出来,那么当Provisioning Complete之后,节点会先主动断开,然后使用Node Identity来广播。这样provisioner就可以快速找到相对应的节点,从而可以更快速地完成配置”;这个时候,肯定会有不服的人站出来说:“为什么要搞这么复杂,我通过Mac address或者Device name也行啊”;
因为这个在Spec已经强制规定好了,虽然Mac address和Device Name的确能起到一定的识别作用,但是貌似官方的这个规则更加精确定位且安全。接下来,让我们看看,Proxy Service暴露且使用Node Identity的广播包长什么样:
Field | Size(octets) | Notes |
---|---|---|
Identification Type | 1 | 0x01 (Node Identity type) |
Hash | 8 | Function of the included random number and identity information |
Random | 8 | 64-bit random number |
而上述的Hash值的计算公式如下所示:
Hash = e(IdentityKey, Padding || Random || Address) mod 2 64
其中:
而IdentityKey则是根据相对应的NetKey由下述的公式生成:
xsalt = s1(“nkik”)
P = “id128” || 0x01
IdentityKey = k1 (NetKey, salt, P)
注意:如果节点存在多个NetKey,则不同的NetKey所对应的IdentityKey可能就各不相同。
除了上述之外,这里还存在两种情况:
如果没有使用Node Identity,但是Proxy Service又暴露了而且这个时候节点又存在多个NetKey;那么,这个时候就要将交替发送不同NetKey加密过后的广播包(比如有3个NetKey);
如果使用Node Identity且Proxy Service又暴露了,那么这个时候就只需要发出该节点所指定的那个子网所属的NetKey,而不用交替发该节点所拥有的所有Netkey的广播包;
但是,不管是哪种情况,这两种情况的广播的持续时长均被限制在60杪;至于这两种类型的广播的广播间隔是多少则根据各大芯片原厂的不同而不同,Nordic Mesh SDK该值最小为200ms。
显然,该消息用于获取节点当前子网的Node Identity状态,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | Index of the NetKey |
其中,NetKeyIndex填充的是当前子网所使用的NetKey的索引值。这是一个带应答的消息,具体的应答内容如Config NetKey Status。
该消息用于设置节点当前子网的Node Identity状态,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | Index of the NetKey |
Identity | 1 | New Node Identity state |
NetKeyIndex填充的值跟上述的Get是一样的,Identity填充的值则如Node Identity所述。
主要用于应答Config Node Identity Get以及Config Node Identity Set的消息,其帧格式如下所示:
Field | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
NetKeyIndex | 2 | Index of the NetKey |
Identity | 1 | Node Identity state |
对于Get消息而言,Identity域的值就是当前所对应子网的NetKey的节点的值,而Set消息则是设置后的值 (前提是该消息要被成功处理,否则没有该域的值)。其中Status除了success值为0之外,其他的值如下表所示:
Error Condition | Status Code Name |
---|---|
The key identified by the NetKeyIndex is not valid for this device | Invalid NetKey Index |
The node cannot start advertising with Node Identity since the maximum number of parallel advertising is reached | Temporarily Unable to Change State |
该消息就是将当前节点从Mesh网络中移除出去,其应答的消息为Config Node Reset Status。该消息没有携带任何参数,所以除了Opcode也就没什么其他的帧格式了。复位之后,Provisioner可以重复使用该节点的所有单播地址,但是还要重复使用Sequence Numbers则需要等IV Index更新之后 (比删除前的IV Index值大就行) 才能使用这些单播地址。这是为什么呢?
小编不知道看过BLE Mesh各层帧包格式详解的小伙伴们,对那个Replay Attack List还有没有印象;如果你重用这些地址和Sequence Numbers,如果IV Index又小于或者等于当前的IV Index的话,那么就很有可能被误认为这是一个Replay攻击而直接被节点丢弃。
用于应答Config Node Reset该消息,同样除了OpCode之外,不携带任何的参数。当Configuration Server用该消息应答之后,就会将自身保存的Mesh相关的数据从Flash中清除,然后断开连接并转换为Unprovisioned Device。
显然,该状态用于节点的NetKey列表中的NetKey以及其相对应绑定的AppKey的刷新,其中Config NetKey Update和Config AppKey Update就在密钥刷阶段过程中才会使用,该状态有如下几种取值:
Value | Description |
---|---|
0x00 | Normal operation; Key Refresh procedure is not active |
0x01 | First phase of Key Refresh procedure |
0x02 | Second phase of Key Refresh procedure |
0x03–0xFF | Prohibited |
除了上述的取值之外,该状态还可以控制密钥刷新阶段的所有可能的转换,具体如下表所示:
Old State | Transition | New State | Description |
---|---|---|---|
0x00 | 0x03 | 0x00 | Transition 3 from Key Refresh Phase 0x00 does not cause any state change |
0x01 | 0x02 | 0x02 | Transition 2 from Key Refresh Phase 0x01 moves to Key Refresh Phase 0x02 |
0x01 | 0x03 | 0x00 | Transition 3 from Key Refresh Phase 0x01 invokes Key Refresh Phase 3 and then moves to Key Refresh Phase 0x00 |
0x02 | 0x02 | 0x02 | Transition 2 from Key Refresh Phase 0x02 does not cause any state change |
0x02 | 0x03 | 0x00 | Transition 3 from Key Refresh Phase 0x02 invokes Key Refresh Phase 3 and then moves to Key Refresh Phase 0x00 |
除了上述表格所述的阶段转换,其他的转换均由协议栈内部处理。到这里,小编估计读者应该有无数个疑问,什么是阶段1?什么是阶段2?密钥刷新又是什么鬼?etc....
在谈及以上问题时,我们先来说一说为什么要发起密钥刷新这一动作:
在继续培析密钥刷新之前,还有几处关键点需要读者们注意:
接下来,让我们正式开始讲解密钥刷新做了什么事情,又有哪几个步骤:
阶段0表示当前节点处于正常的状态,密钥刷新程序没有被激活。
Configuration Client
对于Configuration Client而言,该阶段主要是派送新的密钥给需要进行密钥更新的节点;但是有一点需要注意的就是:configuration client同一时刻只能派发新的密钥给一个节点,如果有n个节点则需要发送n次;当然,这个时候肯定又会有读者表示不服:“为什么不将目标地址设为0xFFFF?这样网络中的所有节点就会收到新的密钥,为什么不将想要更新的节点组成一个Group,然后将密钥发给这个Group?这样就不需要一个一个发了”;
首先,密钥更新命令Config NetKey Update和Config AppKey Update是一个带应答的消息,所以不管目标地址是0xFFFF还是订阅了一个组地址(暂且不管一个个节点订阅组地址麻不麻烦),一旦这样子做的话,那么整个Mesh网络就会充斥着大量的应答包,很容易引起 “网络风暴”,这是大家都不愿意看到的;因此,目前只能给节点一个一个派送新的密钥,但是这样也有一个好处:如果所有的节点都应答了密钥更新的消息,那么configuration client就知道所有的节点都收到了新的密钥,那么密钥刷新阶段1完成,准备进入第二阶段;
Configuration Server
对于节点而言,一旦收到configuration Client的密钥更新消息时,就意味着其已经进入密钥刷新第一阶段了(密钥刷新阶段标志位此时为1),但是此时节点的Key Refresh Flag标志位还未置1;下图分别表示普通节点与低功耗节点的区别:
普通节点
低功耗节点
节点收到新的密钥,以及当前的密钥刷新阶段1的标志位也一并存储至Flash中;其中LPN可能会相对费事一点,因为configuration client不知道此时LPN的状况,只能等LPN节点从Friend节点轮询信息时,才会获取得到新的密钥;又因为新的密钥的内容一包NetWork PDU发送不完,必须要分两包才能发送完毕,所以这个时候就必须是由Friend节点给configuration client进行segmented acknowledgment;为了让新的密钥更快速有效地传达到LPN节点上,当configuration client收到分段应答消息时,就会向Friend节点执行Polltimeout List Procedure获取得到当前的Polltimeout值,然后再根据该值重新安排发送给LPN新的密钥。
注意:当configuration client收到所有节点正确的应答,则认为阶段1已经完成了,所有的节点已经收到新的密钥了
Configuration Client
当configuration client认为所有的节点都收到了新的密钥,那么开始进行密钥刷新的第二阶段;但是,其又分两种方式:
Secure Network Beacon
使用安全网络信标向整个Mesh网络宣布进入密钥刷新的第二阶段,这个时候Key Refresh Flag就要置为1,且使用新的NetKey加密之后才传送出去;
Config Key Refresh Phase Set
分别向各个处于阶段1的节点,传送该命令消息(0x02)用于告诉这些节点现在进入密钥刷新的第二阶段了;
Configuration Server
同样的,因为Client有两种方式传播信息,那么Server同样对应着两种方式接收,下图分别表示普通节点与低功耗节点的区别:
普通节点
低功耗节点
Secure Network Beacon
当节点收到Key Refresh Flag为1的安全网络信标时,其就会将密钥刷新从阶段1设置为阶段2并保存至Flash中;由于没有应答,Client并不清楚节点是否收到其信标,因为Beacon并不会被Relay节点转发,所以一般都是Client->Neighbors->Neighbors;这样的话,Client就不知道是否要进入阶段3,而且也有可能进入阶段3后,导致有些没有收到信标的节点间接被T出Mesh网络了;加上Spec并没有强制规定怎么判断所有的节点都已经收到安全网络信标,所以对于这种情况,需要应用层自己去处理,比如:接下来要讲的 “心跳包” 就可以很好地解决这个问题;但是,好处就是不用对着节点一个一个地发命令;
Config Key Refresh Phase Set
当节点收到携带的参数为0x02的设置消息时,同样其会将密钥刷新从阶段1设置为阶段2并保存至Flash中,同时还会应答该消息;而采用Config Key Refresh Phase Set命令,则可以根据应答消息判断节点是否能收到其消息,从而判断当前密钥刷新阶段2是否已结束,从而可以进入阶段3;但是,缺点就是要对着节点一个一个地发送该命令;
针对上述的两种方式,孰优孰劣?在小编看来,后者会相对更好一点(因为信息传输相对可靠一点);
接下来然后就开始使用新的密钥加密并发送数据 (如果发送的是安全网络信标,那么这个时候Key Refresh Flag就要置1了),但是这个时候仍然可以接收新旧密钥加密的消息 (对于Secure Network Beacon而言,只接收使用新的密钥加密后的安全网络信标)
Configuration Client
当configuration client认为所有的节点进入了阶段2,那么开始进行密钥刷新的第三阶段;但是,其又分两种方式:
Secure Network Beacon
使用安全网络信标向整个Mesh网络宣布进入密钥刷新的第三阶段,这个时候Key Refresh Flag重新置为0,且使用新的NetKey加密之后才传送出去;
Config Key Refresh Phase Set
分别向各个处于阶段2的节点,传送该命令消息(0x03)用于告诉这些节点现在进入密钥刷新的第三阶段了;
Configuration Server
同样的,因为Client有两种方式传播信息,那么Server同样对应着两种方式接收;其实这个动作跟Phase2(切换到新的密钥)中的Configuration Server是一样的;只不过是Secure Network Beacon中携带的Key Refresh Flag为0,而Config Key Refresh Phase Set中携带的参数是0x03;
不管采用哪种方式,只要此时节点收到进入密钥刷新阶段3的消息,那么就摒弃旧的密钥;接下来就开始全面使用新的密钥进行数据的收发,同时将当前的Key Refresh Flag以及Phase重置为0;此时,就算使用新的密钥但是Key Refresh Flag仍然为1的安全网络信标以及Friend Update消息均将被忽略;
至此,整个密钥刷新过程就彻底完成了;但是,在这个过程中还是有几点是需要注意的:
这个时候,我们再回过头来看看上述的状态转换表格以及流程图是不是又多了几分了解甚至全解。
有了上面的长篇大论铺垫,那么该消息显然就是为了获取指定NetKey的当前节点处于密钥刷新过程的哪个阶段,应答消息的内容如Config Key Refresh Phase Status所示;其帧格式如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | Index of the NetKey |
该消息设置指定NetKey的密钥刷新进程,其应答消息的内容同样如Config Key Refresh Phase Status所示;其帧格式如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
NetKeyIndex | 2 | Index of the NetKey |
Transition | 1 | New Key Refresh Phase Transition |
对于Transition域的内容,需要根据状态转换表格所示的来设置相对应的密钥刷新进程,由表格中可以看出,只能设置0x02、0x03而其他值均无效。
该消息是应答上述的Config Key Refresh Phase Set和Config Key Refresh Phase Get,其帧格式如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
NetKeyIndex | 2 | Index of the NetKey |
Transition | 1 | Key Refresh Phase State |
这里需要注意的还是Transition域的内容,根据状态转换表格所示只能为0x00或者0x02;其中Status除了Success为0x00,其他的状态值如下所示:
Error Condition | Status Code Name |
---|---|
The key identified by the NetKeyIndex is not valid for this device | Invalid NetKey Index |
该状态跟上述的Model Publication有些类似,只是该状态用于控制心跳消息周期地发送以及发送相关内容的配置;既然讲到心跳消息,那么我们先看看心跳消息长得啥样:
Field | Size(bits) | Notes |
---|---|---|
RFU | 1 | Reserved for Future Use |
InitTTL | 7 | Initial TTL used when sending the message |
Features | 16 | Bit field of currently active features of the node |
InitTTL
顾名思义,该域表示当发送心跳消息时初始的TTL值是多少,取值范围为0x00-0x7F
Features
该域表示当前节点所支持的特性,具体的内容如下表所示:
Bit | Feature | Notes |
---|---|---|
0 | Relay | Relay feature in use: 0 = False, 1 = True |
1 | Proxy | Proxy feature in use: 0 = False, 1 = True |
2 | Friend | Friend feature in use: 0 = False, 1 = True |
3 | Low Power | Low Power feature in use: 0 = False, 1 = True |
4–15 | RFU | Reserved for Future Use |
根据上述的内容,我们可以很明显地知道心跳消息有如下几个作用:
介绍完心跳包内容,那么继续接下来看看配置心跳包的相关参数:
Heartbeat Publication Destination
很明显该状态值用于设置心跳包发往的目标地址,该值可以是未分配的地址、单播地址以及是组地址,其他类型的地址均不允许;需要注意的是:当目标地址为未分配的地址时,则心跳包停止发送;
Heartbeat Publication Count
该状态用于表示被周期发送的心跳包的次数,是一个16Bits的值;其中有两个特殊的值:
0x0000
当值为该值时,心跳包不能被发送
0xFFFF
当值为该值时,心跳包可以被永久发送,不受次数的影响
剩下的0x0001-0xFFFE值,每发一次心跳包则该值减去1;但是为了压缩这个值,Spec又引出了一个Heartbeat Publication Count Log的概念,其取值范围为0x01~0x11;同时,也伴随引出了一个计算公式:
2 (n-1),其中n就表示Heartbeat Publication Count Log
那这是什么意思呢?这个就是说真正的心跳包次数直接跟这个公式挂钩,也就是如果你想要设置心跳包的次数,不能直接配置Heartbeat Publication Count,而是通过Heartbeat Publication Count Log来配置;那这个时候问题就来了,通过上述的公式得出来的心跳包次数除了1之外都是偶数,那奇数咋搞?
这个时候,Spec又做出了一个规定:“2 (n-1)换算出来的值要大于或者等于Heartbeat Publication Count且n是计算出来的最小的整数,即n = log2(Heartbeat Publication Count)+1”;那我们这里举个例子 :
如果Heartbeat Publication Count想要设置为0x1234,那么n=log(0x1234)/log(2)+1=13.186......,按理这个时候n应该为13;但是你将n=13代入公式得到的值是小于0x1234的,这个时候就跟规定冲突;因此,最终的n应该为14,即0x0E;
但是,当Heartbeat Publication Count Log为0x00和0xFF时,其跟Heartbeat Publication Count的0x0000和0xFFFF表示的意思是一样的;其具体的取值范围如下所示:
Value | Description |
---|---|
0x00 | Heartbeat messages are not being sent periodically |
0x01–0x11 | Number of Heartbeat messages, 2(n-1), that remain to be sent |
0x12-0xFE | Prohibited |
0xFF | Heartbeat messages are being sent indefinitely |
Heartbeat Publication Period Log
该状态表示心跳包发送的时间间隔,该值的取值内容如下表所示:
Value | Description |
---|---|
0x00 | Heartbeat messages are not being sent periodically |
0x01–0x11 | The period of heartbeat messages (Spec原文的描述有误,这里小编已经改为正确的描述) |
0x12-0xFF | Prohibited |
计算公式如下:
Heartbeat Publication Period = 2(n-1)秒,其中n表示Heartbeat Publication Period Log;如果n=3,那么就是4秒,以此类推;
Heartbeat Publication TTL
该状态表示心跳包发送时的初始TTL值,该值的主要作用就是:当对端设备收到心跳包时,它就知道发送心跳包的节点离它有多远 (心跳包的TTL值减去NetWork PDU的TTL值再加1就可以得到两者之间的距离);具体的取值内容如下所示:
Value | Description |
---|---|
0x00-0x7F | The Heartbeat Publication TTL state |
0x80-0xFF | Prohibited |
Heartbeat Publication Features
该状态表示当相对应的Mesh节点特性被置1时,这些特性状态当发生变化时,周期心跳包就会按照设置好的参数再次被触发,例如:“中继特性从使能变成除能,反之亦然”;具体的内容如下表所示:
Bit | Feature | Notes |
---|---|---|
0 | Relay | Relay feature change triggers a Heartbeat message: 0 = False, 1 = True |
1 | Proxy | Proxy feature change triggers a Heartbeat message: 0 = False, 1 = True |
2 | Friend | Friend feature change triggers a Heartbeat message: 0 = False, 1 = True |
3 | Low Power | Low Power feature change triggers a Heartbeat message: 0 = False, 1 = True |
4–15 | RFU | Reserved for Future Use |
Heartbeat Publication NetKey Index
该状态就比较简单了,其表示发送出去的心跳包用哪个NetKey加密。
讲完上述的心跳包格式以及其配置,那么我们看看捉包得到的心跳包是怎么样的,如下图所示:
其中,多出来了一个Directed Forwardeding域,在Specv1.0.1是没有明确指出,其有可能是v1.1的内容(v1.1应该会在2020年下半年发布)。
该消息就是为了获取当前节点的心跳包的配置参数,其应答的内容如Config Heartbeat Publication Status所述。
跟Get的操作相反,设置心跳包的配置参数,如下表所示:
Parameter | Size(octets) | Notes |
---|---|---|
Destination | 2 | Destination address for Heartbeat messages |
CountLog | 1 | Number of Heartbeat messages to be sent |
PeriodLog | 1 | Period for sending Heartbeat messages |
TTL | 1 | TTL to be used when sending Heartbeat messages |
Features | 2 | Bit field indicating features that trigger Heartbeat messages when changed |
NetKeyIndex | 2 | NetKey Index |
以上各域的具体含义,我在Heartbeat Publication中已经细述;但是这里我还是需要再一次强调Features的作用:
类似于一个开关,如果相对应的特性被置1了;那么,当这个被置1的特性的状态发生变化时就会触发心跳包的周期发送;这个跟节点本身是否支不支持该特性没有关系,因为如果某特性节点本身就不支持,那么就不可能存在特性变化的可能;但是,如果某特性节点本身是支持的,那么该域中所对应的其位域,用户可以根据实际情况选择是否要置1;
由上述的捉包图可知,该心跳包总共发4次且时间间隔为4秒:
CountLog
PeriodLog
该消息是响应上述的Get和Set消息,具体的帧格式内容如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
Destination | 2 | Destination address for Heartbeat messages |
CountLog | 1 | Number of Heartbeat messages to be sent |
PeriodLog | 1 | Period for sending Heartbeat messages |
TTL | 1 | TTL to be used when sending Heartbeat messages |
Features | 2 | Bit field indicating features that trigger Heartbeat messages when changed |
NetKeyIndex | 2 | NetKey Index |
其中Status除了Success是0x00之外,其余的取值如下所示:
Error Condition | Status Code Name |
---|---|
The key identified by the NetKeyIndex is not valid for this device | Invalid NetKey Index |
下图是实际的捉包图:
该状态跟上一章节的Heartbeat Publication是相反的动作;Heartbeat Publication用于节点周期发送心跳包,而Heartbeat Subscription用于接收节点周期发送的心跳包。同时,该状态有如下几个参数:
Heartbeat Subscription Source
该参数表示发送心跳包的节点的源地址,其可以是单播地址也可以是未分配的地址;如果是未分配的地址,那么该心跳包无效。
Heartbeat Subscription Destination
该参数表示心跳包要发往的目标地址,该地址只能是未分配的地址、目标节点的首要元素的地址以及组地址;如果是未分配的地址,那么该心跳包无效。
Heartbeat Subscription Count
该值表示接收到的心跳包的数量总共是多少且其是一个16Bits的参数:
Value | Description |
---|---|
0x0000–0xFFFE | Number of Heartbeat messages received |
0xFFFF | More than 0xFFFE messages have been received |
但是Spec使用公式2(n-1) 压缩成8bits,其中n表示Heartbeat Subscription Count Log,与Heartbeat Subscription Count的对应关系如下所示:
Log Field Value(Heartbeat Subscription Count Log) | 2-octet Value(Heartbeat Subscription Count) |
---|---|
0x01 | 0x0001 |
0x02 | 0x0002 through 0x0003 |
0x03 | 0x0004 through 0x0007 |
0x04 | 0x0008 through 0x000F |
0x05 | 0x0010 through 0x001F |
0x06 | 0x0020 through 0x003F |
0x07 | 0x0040 through 0x007F |
0x08 | 0x0080 through 0x00FF |
0x09 | 0x0100 through 0x01FF |
0x0A | 0x0200 through 0x03FF |
0x0B | 0x0400 through 0x07FF |
0x0C | 0x0800 through 0x0FFF |
0x0D | 0x1000 through 0x1FFF |
0x0E | 0x2000 through 0x3FFF |
0x0F | 0x4000 through 0x7FFF |
0x10 | 0x8000 through 0xFFFF |
这也就是在说Heartbeat Subscription Count的范围内,对应的Heartbeat Subscription Count Log可能会是一样的;
Heartbeat Subscription Period Log
跟上述的Heartbeat Publication Period Log是基本一致的,只不过上述的指的发送的时间间隔,而该参数则是表示用于处理周期心跳包的时间还剩下多少,它会每秒都会自动减去1直至为0;如果该为0的话,那么该心跳包被丢弃不进行处理。
Value | Description |
---|---|
0x00 | Heartbeat messages are not being processed |
0x01–0x11 | Remaining period in 2(n-1) seconds for processing periodical Heartbeat messages |
0x12-0xFF | Prohibited |
Heartbeat Subscription Min Hops
该参数表示接受到心跳包的最小跳数,由上述可知跳数的计算公式如下:
跳数 = 心跳包的初始TTL值 - 接受到NetWork PDU(心跳包)的TTL + 1,即hops = InitTTL - RxTTL +1
所以,如果通过上述公式计算出来的跳数小于最小的跳数,那么最小的跳数就重新被赋值为计算出来的跳数。至于最小的跳数默认是多少,请参考Config Heartbeat Subscription Set消息。其中,最少跳数的取值范围如下表所示:
Value | Description |
---|---|
0x00 | No Heartbeat messages have been received |
0x01–0x7F | The Heartbeat Subscription Min Hops state |
0x80-0xFF | Prohibited |
Heartbeat Subscription Max Hops
同理,该参数表示接受到心跳包的最大跳数;同样的,如果通过上述公式计算出来的跳数大于最大的跳数,那么最大的跳数就重新被赋值为计算出来的跳数。至于最大的跳数默认是多少,请参考Config Heartbeat Subscription Set消息。其中,最少跳数的取值范围如下表所示:
Value | Description |
---|---|
0x00 | No Heartbeat messages have been received |
0x01–0x7F | The Heartbeat Subscription Max Hops state |
0x80-0xFF | Prohibited |
该消息是获取节点心跳订阅配置的参数,具体的应答内容如Config Heartbeat Subscription Status所示。
该消息是设置节点心跳订阅的内容,具体的帧格式如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
Source | 2 | Source address for Heartbeat messages |
Destination | 2 | Destination address for Heartbeat messages |
PeriodLog | 1 | Period for receiving Heartbeat messages |
各域的含义参考上述的Heartbeat Subscription所示;但是,这里有一点需要特别注意的是:当接收到这条Set消息时,伴随会将上述的Heartbeat Subscription Count设置为0x00,Heartbeat Subscription Max Hops设置为0x00,Heartbeat Subscription Min Hops设置为0x7F。
该消息只是用于应答上述的Set和Get消息,具体的内容如下所示:
Parameter | Size(octets) | Notes |
---|---|---|
Status | 1 | Status Code for the requesting message |
Source | 2 | Source address for Heartbeat messages |
Destination | 2 | Destination address for Heartbeat messages |
PeriodLog | 1 | Remaining Period for processing Heartbeat messages |
CountLog | 1 | Number of Heartbeat messages received |
MinHops | 1 | Minimum hops when receiving Heartbeat messages |
MaxHops | 1 | Maximum hops when receiving Heartbeat messages |
其中Status的取值如下表所示:
Status Code | Status Code Name |
---|---|
0x00 | Success |
0x01 | Invalid Address |
0x02 | Invalid Model |
0x03 | Invalid AppKey Index |
0x04 | Invalid NetKey Index |
0x05 | Insufficient Resources |
0x06 | Key Index Already Stored |
0x07 | Invalid Publish Parameters |
0x08 | Not a Subscribe Model |
0x09 | Storage Failure |
0x0A | Feature Not Supported |
0x0B | Cannot Update |
0x0C | Cannot Remove |
0x0D | Cannot Bind |
0x0E | Temporarily Unable to Change State |
0x0F | Cannot Set |
0x10 | Unspecified Error |
0x11 | Invalid Binding |
0x12-0xFF | RFU |
该状态与上述的Relay Retransmit极其相似,只不过该状态用于配置节点发送NetWork PDU时的次数以及相对应的时间间隔,分别对应的参数如下所示:
Network Transmit Count
该参数只有3bits,用于设置节点发送NetWork PDU的次数,最终Network PDU的发送次数等于Network Transmit Count + 1,如果该参数值为0,则表示只传输一次,那么下面的Network Transmit Interval Steps就无效了;
Network Transmit Interval Steps
该参数只有5bits,表示节点发送NetWork PDU的时间间隔,其最终的计算公式如下所示:
transmission interval = (Network Retransmit Interval Steps + 1) * 10
但是需要注意的就是:“为了增强鲁棒性,会插入0-10ms的随机时间,最终的间隔时间=(transmission interval + 0~10)ms ”;
其中高3bit为Network Transmit Count,低5bit是Network Transmit Interval Steps;为了更好的阐述这个问题,让我们看看Network Transmit实际的情况是怎么样的:
小编将上图做了3个标记:
通过上述的实际捉包图与理论结合是不是已经完全ojbk了😄。
该消息用于获取当前节点的Network Transmit的配置参数,其应答内容如Config Network Transmit Status所示。
该消息是设置节点的Network Transmit的配置参数,具体的帧格式如下所示:
Field | Size(bits) | Notes |
---|---|---|
NetworkTransmitCount | 3 | Number of transmissions for each Network PDU originating from the node |
NetworkTransmitIntervalSteps | 5 | Number of 10-millisecond steps between transmissions |
该消息帧数据各域含义请参考Network Transmit中所述。
该消息只用于应答上述的Set和Get消息,其帧格式跟Config Network Transmit Set是一模一样的。
该模型跟Configuration Server Model是一样的:
至于,该模型对应的状态和消息;小编在Configuration Server Model中已经详述,无非就是Client扮演的是配置Server的角色,而Server扮演的是接收Client的配置命令的角色;同样的消息在client端则是发往server,在server端则变成接收client的设置命令;基本上拥有配置Client模型的都是Provisioner,而Server模型的则是节点。它们两者的数据交互拓扑图如下所示:
注意:同时支持Client模型和Server模型的叫控制端模型
至此,Configuration Model相关的内容就讲解完毕;以上篇幅内容耗费小编近三周的时间,差点吐了一身老血。