目录
一、什么是BLE Mesh?Network Layer网络层?
BLE mesh使用了四种类型的地址,地址长度为16位,分别是:
未分配的地址(Unassigned Address):一种特殊的地址类型,值为 0x0000。它的使用表明未经配置的元素或未被指定地址的元素。未分配的地址不能用于网络数据的发送。
单播地址(Unicast Address):单播地址的范围是从0x0001到0x7FFF,也就是说最多可以有32767个单播地址。用于标识网络中的单个元素,由provisioner分配,不能重复。在“启动配置”(provisioning)期间,启动配置设备(provisioner)会在网络节点的生命周期内为节点中的每个节点元素 (一个节点Node可以有多个节点元素Element, 例如一个多孔插座作为一个节点,插座上的每一个插孔都是一个独立的节点元素)分配一个单播地址。
组播地址(Group Address):用于标识网络中的一组元素,可以由任何节点创建,必须唯一。组播地址的范围是从0xC000到0xFFFB,其中0xFF00到0xFFFB是保留地址,不能使用。0xFFFC到0xFFFF是特殊地址,用于表示不同类型的节点。
0xFFFC 代指所有的代理节点(Proxy Node)
0xFFFD 代指所有的朋友节点(Friend Node)
0xFFFE 代指所有的转发节点(Relay Node)
0xFFFF 代指所有的节点(All Nodes)
虚拟地址(Virtual Address):用于标识网络中的一组元素,由128位的标签(Label UUID)生成,可以创建更多的地址,但需要更多的存储空间和处理时间。
Filed | Size(bits) | Notes |
IVI | 1 | 包含了IV Index的最低有效位 |
NID | 7 | 一个7位的网络标识符,用于查找验证和加密此网络层PDU的密钥 |
CTL | 1 | 用于确定消息是控制消息的一部分还是访问消息的一部分 |
TTL | 7 | 指定消息的传输生存时间 |
SEQ | 24 | 一个24位的整数,与IV Index结合使用,应该是唯一的值 |
SRC | 16 | 标识发起这个网络层PDU的元素地址 |
DST | 16 | 标识网络层PDU所指向的一个或多个元素地址 |
TransportPDU | 8-128 | 数据的字节序列 |
NetMIC | 32或64 | 用于验证DST和TransportPDU是否被更改 |
IVI(Initialization Vector Index)是一个32位的值,用于保证网络层的数据加密和混淆的安全性和随机性。
IVI与序列号(SEQ)一起组成了nonce,nonce是用于AES-CCM算法的输入参数,用于对网络层PDU进行加密和认证。
IVI可以防止nonce的重复使用,从而提高网络层的安全性。
IVI是一个共享的网络资源,网络中的所有节点必须保持IVI的一致性,否则会导致通信失败。IVI通过安全网络信标(Secure Network Beacon)进行共享和更新。当节点的序列号接近耗尽时,或者检测到其他节点的IVI更新时,节点会启动IV更新过程,将IVI递增1,并通知网络中的其他节点。IV更新过程有一定的时间限制和规则,以保证网络的稳定性和兼容性。
IVI更新时间和规则是指ivi更新过程中的时间要求和状态转换条件,具体如下:
节点需支持IV索引恢复,因长时间离线节点可能错过IV更新,导致无法通信。为恢复IV index,节点需监听含网络ID和当前IV的安全信标。当接收到验证后其IV高于当前的主子网信标时,节点应设置其当前IV及其更新状态。此过程不考虑96小时限制。
/*BLE Mesh网络层更新IVI子函数*/
/*源自开源协议栈NimBLE*/
static void ivu_refresh(struct ble_npl_event *work)
{
bt_mesh.ivu_duration += BT_MESH_IVU_HOURS;
BT_DBG("%s for %u hour%s",
atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ?
"IVU in Progress" : "IVU Normal mode",
bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1 ? "" : "s");
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_iv(true);
}
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
return;
}
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
bt_mesh_beacon_ivu_initiator(true);
bt_mesh_net_iv_update(bt_mesh.iv_index, false);
} else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_iv(true);
}
}
NID(Network Identifier)是一个7位的值,用于标识用于保护网络层PDU的加密密钥和隐私密钥。
NID是从NetKey派生出来的,每个NetKey对应一个唯一的NID。
当节点收到网络层PDU时,会根据NID字段查找是否有匹配的NetKey,如果没有,则忽略该PDU。如果有,则使用NetKey派生的EncryptionKey和PrivacyKey对PDU进行解密和去混淆,得到消息的内容和元数据。
NID可以防止节点接收和处理不属于同一个网络的PDU,从而提高网络的安全性和效率。
/*BLE Mesh网络层生成NID字段*/
/*源自开源协议栈NimBLE*/
err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy);
if (err) {
BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
return err;
}
为什么不直接用netkey去匹配,而要加个NID字段?
因为netkey是一个128位的密钥,直接用它来匹配会占用太多的空间和时间,影响网络的效率和性能。NID(Network Identifier)是一个7位的值,是从netkey派生出来的,每个netkey对应一个唯一的NID。NID可以提供一种快速的方法来确定使用哪个netkey对消息加密和解密,同时也保证了消息的安全性和隐私性。NID只占用很少的空间,可以节省网络的带宽和资源,提高网络的速度和稳定性。
CTL(Control)是一个1位的值,用于区分网络层PDU(Protocol Data Unit)是访问消息(Access Message)还是控制消息(Control Message)。访问消息是用于传输应用层数据的,控制消息是用于实现网络层的管理功能的,比如创建和维护友谊(Friendship)和心跳(Heartbeat)。CTL的值也会影响网络层PDU的大小和NetMIC(Network Message Integrity Check)的长度。具体来说:
TTL(Time To Live)是一个7位的值,用于限制网络层PDU(Protocol Data Unit)的最大中继次数。
TTL的作用是防止消息在网络中无限循环,同时也可以控制消息的传播范围和效率。TTL的值由消息的发布者设定,每个消息都有一个初始的TTL值,范围是0-127。当消息被中继节点转发时,TTL的值会减1,当TTL的值为0时,消息不会再被转发。
TTL的值也会影响消息的优先级,TTL越高的消息越优先被转发。
SEQ(Sequence Number)是一个24位的值,用于防止重放攻击。
重放攻击是一种网络安全威胁,攻击者拦截并重复发送消息,以欺骗接收者或执行未授权的操作。
SEQ的作用是保证每个消息的唯一性和时效性,避免消息被重复处理或滥用。
每个元素在发送消息时都会增加SEQ的值,从0开始到0xFFFFFF为止。每个节点在接收消息时都会检查SEQ的值,如果小于或等于之前收到的同一元素的消息的SEQ值,就会丢弃该消息,认为它是过期或重复的。当SEQ的值接近最大值时,元素应该更新IVI(Initialization Vector Index),以防止SEQ的值溢出或重复。
/*BLE Mesh网络层生成SEQ字段*/
/*源自开源协议栈NimBLE*/
u32_t bt_mesh_next_seq(void)
{
u32_t seq = bt_mesh.seq++;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_seq();
}
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) &&
bt_mesh.seq > IV_UPDATE_SEQ_LIMIT &&
bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY)) {
bt_mesh_beacon_ivu_initiator(true);
bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
bt_mesh_net_sec_update(NULL);
}
return seq;
}
SRC(Source Address)是一个16位的值,用于标识网络层PDU(Protocol Data Unit)的发送者。
SRC的作用是让接收者知道消息的来源,以便进行回复或处理。
SRC的值必须是一个单播地址或一个虚拟地址,不能是一个组播地址或一个未分配的地址。
SRC的值也会影响消息的安全性和隐私性,因为它会与IVI(Initialization Vector Index)和SEQ(Sequence Number)一起组成nonce,nonce是用于AES-CCM算法的输入参数,用于对网络层PDU进行加密和认证。
DST(Destination Address)是一个16位的值,用于标识网络层PDU(Protocol Data Unit)的接收者。DST的作用是让发送者指定消息的目的地,以便进行广播或单播。DST的值可以是一个单播地址、一个组播地址或一个虚拟地址,不能是一个未分配的地址。
DST的值也会影响消息的安全性和隐私性,因为它会与IVI(Initialization Vector Index)和SEQ(Sequence Number)一起组成nonce,nonce是用于AES-CCM算法的输入参数,用于对网络层PDU进行加密和认证。
TransportPDU(Transport Protocol Data Unit)是网络层PDU(Protocol Data Unit)的一部分,用于承载上层传输层的数据或控制信息。TransportPDU的作用是实现网络层和上层传输层之间的数据交换,以及对数据进行分段和重组。
TransportPDU的长度和内容取决于CTL(Control)字段的值,CTL字段是网络层PDU的一部分,用于区分网络层PDU是访问消息(Access Message)还是控制消息(Control Message)。具体来说:
NetMIC(Network Message Integrity Check)是网络层PDU(Protocol Data Unit)的一部分,用于保证网络层数据的完整性和认证性。NetMIC的作用是防止消息被篡改或伪造,从而提高网络的安全性和可靠性。
NetMIC是使用NetKey派生的EncryptionKey和nonce(由IVI,SEQ,SRC,DST组成)对网络层PDU进行AES-CCM算法的输出结果。NetMIC的长度取决于CTL(Control)字段的值,如果CTL为0,表示网络层PDU是访问消息,NetMIC是32位的;如果CTL为1,表示网络层PDU是控制消息,NetMIC是64位的。
BLE Mesh 网络层发送数据的流程大致如下:
/*BLE Mesh网络层发送数据子函数*/
/*源自开源协议栈NimBLE*/
int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
const struct bt_mesh_send_cb *cb, void *cb_data)
{
int err;
BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu",
tx->src, tx->ctx->addr, buf->om_len, net_buf_headroom(buf),
net_buf_tailroom(buf));
BT_DBG("Payload len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
BT_DBG("Seq 0x%06x", bt_mesh.seq);
if (tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) {
tx->ctx->send_ttl = bt_mesh_default_ttl_get();
}
err = bt_mesh_net_encode(tx, buf, false);
if (err) {
goto done;
}
BT_DBG("encoded %u bytes: %s", buf->om_len,
bt_hex(buf->om_data, buf->om_len));
/* Deliver to GATT Proxy Clients if necessary. Mesh spec 3.4.5.2:
* "The output filter of the interface connected to advertising or
* GATT bearers shall drop all messages with TTL value set to 1."
*/
if (MYNEWT_VAL(BLE_MESH_GATT_PROXY) &&
tx->ctx->send_ttl != 1) {
if (bt_mesh_proxy_relay(buf, tx->ctx->addr) &&
BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) {
/* Notify completion if this only went
* through the Mesh Proxy.
*/
send_cb_finalize(cb, cb_data);
err = 0;
goto done;
}
}
/* Deliver to local network interface if necessary */
if (bt_mesh_fixed_group_match(tx->ctx->addr) ||
bt_mesh_elem_find(tx->ctx->addr)) {
if (cb && cb->start) {
cb->start(0, 0, cb_data);
}
net_buf_slist_put(&bt_mesh.local_queue, net_buf_ref(buf));
if (cb && cb->end) {
cb->end(0, cb_data);
}
k_work_submit(&bt_mesh.local_work);
} else if (tx->ctx->send_ttl != 1) {
/* Deliver to to the advertising network interface. Mesh spec
* 3.4.5.2: "The output filter of the interface connected to
* advertising or GATT bearers shall drop all messages with
* TTL value set to 1."
*/
bt_mesh_adv_send(buf, cb, cb_data);
}
done:
net_buf_unref(buf);
return err;
}
BLE Mesh 网络层接收数据的流程大致如下:?
输入过滤器的功能:
? ? ? ? 1、防止不必要的消息传递:输入过滤器可以排除不关心的消息,减少网络层的处理负担。
? ? ? ? 2、提高网络效率:只有符合特定条件的消息才会被传递到上层,从而提高网络的效率。
? ? ? ? 3、增强网络安全性:输入过滤器可以阻止不合法或恶意的消息进入网络。
具体的流程:
/*BLE Mesh网络层接收数据子函数*/
/*源自开源协议栈NimBLE*/
void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi,
enum bt_mesh_net_if net_if)
{
struct os_mbuf *buf = NET_BUF_SIMPLE(29);
struct bt_mesh_net_rx rx = { .ctx.recv_rssi = rssi };
struct net_buf_simple_state state;
BT_DBG("rssi %d net_if %u", rssi, net_if);
if (!bt_mesh_is_provisioned()) {
BT_ERR("Not provisioned; dropping packet");
goto done;
}
if (bt_mesh_net_decode(data, net_if, &rx, buf)) {
goto done;
}
/* Save the state so the buffer can later be relayed */
net_buf_simple_save(buf, &state);
rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) ||
bt_mesh_elem_find(rx.ctx.recv_dst));
if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY)) &&
net_if == BT_MESH_NET_IF_PROXY) {
bt_mesh_proxy_addr_add(data, rx.ctx.addr);
if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_DISABLED &&
!rx.local_match) {
BT_INFO("Proxy is disabled; ignoring message");
goto done;
}
}
/* The transport layer has indicated that it has rejected the message,
* but would like to see it again if it is received in the future.
* This can happen if a message is received when the device is in
* Low Power mode, but the message was not encrypted with the friend
* credentials. Remove it from the message cache so that we accept
* it again in the future.
*/
if (bt_mesh_trans_recv(buf, &rx) == -EAGAIN) {
BT_WARN("Removing rejected message from Network Message Cache");
msg_cache[rx.msg_cache_idx] = 0ULL;
/* Rewind the next index now that we're not using this entry */
msg_cache_next = rx.msg_cache_idx;
}
/* Relay if this was a group/virtual address, or if the destination
* was neither a local element nor an LPN we're Friends for.
*/
if (!BT_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) ||
(!rx.local_match && !rx.friend_match)) {
net_buf_simple_restore(buf, &state);
bt_mesh_net_relay(buf, &rx);
}
done:
os_mbuf_free_chain(buf);
}
通过点击以下链接,您可以获取BLE Mesh模块原理图、源代码以及开发资料。链接地址将为您提供详细的文件资料,以供您进行参考和使用。
如果您在使用过程中遇到任何问题或疑虑,欢迎加我QQ ,一起探讨技术问题,我的QQ号是986571840,加的时候请注明CSDN。
BLE Mesh蓝牙组网模块 - 硬创社 (jlc.com)https://x.jlc.com/platform/detail/001d23cba7b64b0d9df5b9b69720fadb
感谢各位用户点赞、分享、在看,这些行为让知识得以更加广泛地传播,从而让更多人受益。
请在转载作品时注明出处,严禁抄袭行为。