BLE Mesh蓝牙组网技术详细解析之Network Layer网络层(三)

目录

一、什么是BLE Mesh Network Layer网络层?

二、网络层介绍

2.1 节点地址

2.2 Network PDU

2.2.1 IVI

2.2.2 NID

2.2.3 CTL

2.2.4 TTL

2.2.5 SEQ

2.2.6 SRC

2.2.7 DST

2.2.8 TransportPDU

2.2.9 NetMIC

三、网络层收发数据流程

四、资料获取


一、什么是BLE Mesh Network Layer网络层?

  • 定义了数据的寻址和转发。Network Layer网络层使用一种称为Mesh Address的地址类型,来标识不同的节点和消息。每个节点都有一个唯一的Mesh Address,用于在网络中定位和通信。每条消息也有一个Mesh Address,用于在网络中传递和转发。
  • 定义了网络层的格式。Network Layer网络层使用一种称为PDU(Protocol Data Unit)的数据结构,来封装和传输消息。PDU包含了消息的头部和尾部,以及一些可选的字段。头部包含了消息的类型、长度、序列号等信息,用于标识和处理消息。尾部包含了消息的校验码、加密密钥等信息,用于验证和保护消息。
  • 定义了一些输入输出过滤器(Filter)。Network Layer网络层使用过滤器来决定哪些消息需要转发、处理还是拒绝。过滤器可以根据不同的条件来选择性地接收或发送消息,例如节点地址、Mesh Address、TTL(Time to Live)值等。

二、网络层介绍

2.1 节点地址

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)生成,可以创建更多的地址,但需要更多存储空间和处理时间

2.2 Network PDU

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是否被更改

2.2.1 IVI

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更新过程中的时间要求和状态转换条件,具体如下

  • ivi更新过程有两种操作状态:正常操作(Normal Operation)和更新进行中(IV Update in Progress)。正常操作状态的ivi更新标志(IV Update Flag)为0,更新进行中状态的ivi更新标志为1。
  • 节点可以在任何时候从正常操作状态切换到更新进行中状态,但必须在更新进行中状态运行至少96小时后才能切换回正常操作状态。
  • 当节点从正常操作状态切换到更新进行中状态时,ivi递增1,序列号不变。当节点从更新进行中状态切换回正常操作状态时,ivi不变,序列号重置为0。
  • 在更新进行中状态,节点应使用旧的ivi传输数据,使用旧的ivi和新的ivi接收数据。在正常操作状态,节点应使用新的ivi传输和接收数据。
  • 如果节点在网状网络中缺失一段时间,它可以扫描安全网络信标或使用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);
	}
}

2.2.2 NID

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只占用很少的空间,可以节省网络的带宽和资源,提高网络的速度和稳定性

2.2.3 CTL

CTL(Control)是一个1位的值,用于区分网络层PDU(Protocol Data Unit)是访问消息(Access Message)还是控制消息(Control Message)。访问消息是用于传输应用层数据的,控制消息是用于实现网络层的管理功能的,比如创建和维护友谊(Friendship)和心跳(Heartbeat)。CTL的值也会影响网络层PDU的大小和NetMIC(Network Message Integrity Check)的长度。具体来说:

  • 如果CTL为0,表示网络层PDU是访问消息,NetMIC是32位的,传输PDU(Transport PDU)的长度限制在16字节以内
  • 如果CTL为1,表示网络层PDU是控制消息,NetMIC是64位的,传输PDU的长度限制在12字节以内

2.2.4 TTL

TTL(Time To Live)是一个7位的值,用于限制网络层PDU(Protocol Data Unit)的最大中继次数。

TTL的作用是防止消息在网络中无限循环,同时也可以控制消息的传播范围和效率。TTL的值由消息的发布者设定,每个消息都有一个初始的TTL值,范围是0-127。当消息被中继节点转发时,TTL的值会减1,当TTL的值为0时,消息不会再被转发

TTL的值也会影响消息的优先级,TTL越高的消息越优先被转发。

2.2.5 SEQ

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;
}

2.2.6 SRC

SRC(Source Address)是一个16位的值,用于标识网络层PDU(Protocol Data Unit)的发送者。

SRC的作用是让接收者知道消息的来源,以便进行回复或处理。

SRC的值必须是一个单播地址或一个虚拟地址,不能是一个组播地址或一个未分配的地址。

SRC的值也会影响消息的安全性和隐私性,因为它会与IVI(Initialization Vector Index)和SEQ(Sequence Number)一起组成nonce,nonce是用于AES-CCM算法的输入参数,用于对网络层PDU进行加密和认证

2.2.7 DST

DST(Destination Address)是一个16位的值,用于标识网络层PDU(Protocol Data Unit)的接收者。DST的作用是让发送者指定消息的目的地,以便进行广播或单播。DST的值可以是一个单播地址、一个组播地址或一个虚拟地址,不能是一个未分配的地址。

DST的值也会影响消息的安全性和隐私性,因为它会与IVI(Initialization Vector Index)和SEQ(Sequence Number)一起组成nonce,nonce是用于AES-CCM算法的输入参数,用于对网络层PDU进行加密和认证

2.2.8 TransportPDU

TransportPDU(Transport Protocol Data Unit)是网络层PDU(Protocol Data Unit)的一部分,用于承载上层传输层的数据或控制信息。TransportPDU的作用是实现网络层和上层传输层之间的数据交换,以及对数据进行分段和重组

TransportPDU的长度和内容取决于CTL(Control)字段的值,CTL字段是网络层PDU的一部分,用于区分网络层PDU是访问消息(Access Message)还是控制消息(Control Message)。具体来说:

  • 如果CTL为0,表示网络层PDU是访问消息,TransportPDU的最大长度为16字节,内容是上层传输层的访问PDU(Access PDU),用于传输应用层的数据
  • 如果CTL为1,表示网络层PDU是控制消息,TransportPDU的最大长度为12字节,内容是上层传输层的控制PDU(Control PDU),用于实现网络层的管理功能,比如创建和维护友谊(Friendship)和心跳(Heartbeat)。

2.2.9 NetMIC

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 网络层发送数据的流程大致如下:

  • 上层传输层将传输层PDU传递给网络层,网络层根据目的地址、TTL、SEQ等信息生成网络层PDU
  • 网络层对网络层PDU进行加密和混淆,使用NetKey派生的EncryptionKey和PrivacyKey,保证数据的安全性和隐私性
  • 网络层将网络层PDU传递给承载层,承载层选择合适的承载方式(广播承载或GATT承载)发送数据
  • 承载层在发送数据之前,会延迟一段随机时间,以避免与其他节点的数据冲突
  • 承载层将数据发送到BLE物理层和链路层,使用BLE广播或连接的方式传输数据
/*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、增强网络安全性:输入过滤器可以阻止不合法或恶意的消息进入网络。

具体的流程:

  • 网络层从承载层接收到网络层PDU,根据NID字段查找是否有匹配的NetKey,如果没有,则丢弃该PDU
  • 网络层使用NetKey派生的EncryptionKey和PrivacyKey对网络层PDU进行解密和去混淆,得到SRC、DST、TTL、SEQ等信息
  • 网络层检查该PDU是否已经在消息缓存中,如果是,则丢弃该PDU,避免重复处理
  • 网络层检查DST字段是否与本地的单播地址、组播地址或虚拟地址匹配,如果是,则将该PDU递交给底层传输层处理
  • 如果不匹配,网络层检查TTL字段是否大于1,如果是,则将TTL减1,重新加密和混淆该PDU,然后将该PDU传递给承载层进行中继转发
/*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)icon-default.png?t=N7T8https://x.jlc.com/platform/detail/001d23cba7b64b0d9df5b9b69720fadb

感谢各位用户点赞、分享、在看,这些行为让知识得以更加广泛地传播,从而让更多人受益。

请在转载作品时注明出处,严禁抄袭行为。

你可能感兴趣的:(嵌入式硬件,单片机,网络)