蓝牙mesh network management
1.mesh网络创建步骤
要创建一个mesh网络,必须要创建一个配网器。配网器应该生成一个网络密钥,提供IV Index(也叫IV 索引)
,并分配单播地址。
网络密钥应使用符合要求的随机数生成器生成。
IV 索引应设置为 0x00000000。
单播地址的设置应由 配网器分配完成。
使用上述信息创建mesh网络,配网器的主元素应分配单播地址。配网器的其他元素应分配在单播地址之后的顺序地址。
配网器可以通过使用主动或被动扫描扫描未配置设备信标来找到未配置设备。然后,配网器可以将这些设备配置为mesh网络中的节点。一旦这些设备被配置成功,配置客户端就可以通过向它们提供应用程序密钥并设置发布和订阅地址来配置节点,以便节点可以相互通信。
注意:Provisioner 的设备密钥仅在一个 Provisioner 直接与另一个 Provisioner 通信并且此设备密钥已通过 OOB 通信时使用。 Provisioner 的设备密钥应该在多个 Provisioner 之间进行协调。
2.临时访客访问(Temporary guest access)
可以为节点提供对mesh网络的临时访客访问。 这是通过创建单独的客人子网,向客人提供单独的网络密钥,来访问客人需要访问的节点。
向客人提供单独的应用程序密钥,以限制客人在访问层可以访问的模型。
访客永远不会获得被排除在访客访问之外的节点和模型使用的应用程序密钥或网络密钥。 只有属于来宾子网的节点才会与来宾节点通信; 在这些节点中,客人只能使用绑定到客人应用程序密钥的模型。 这允许对特定节点和功能的访客访问进行非常精细的控制。
访客无法在主子网上启动 IV 索引更新。 这可以保护作为网络共享资源的 IV 索引免受潜在恶意行为的影响。
访客访问由配置客户端使用由设备密钥保护的配置服务器模型(Configuration Server model)
配置。 可以为多个来宾提供来宾访问权限,每个来宾都在他们自己的来宾子网和模型域中。通过密钥刷新程序刷新应用程序和网络密钥,来撤销访客访问。
配网器在程序中的本质
- 配网器的deviceKey是创建配网器时,生辰改的128位随机数。
- 配网器的NetworkKey是在创建配网器时,将网络中所有的networkKey的index赋值给配网器的NetKey属性。
蓝牙mesh中网络信息需要导入导出,SIG确定了数据格式是Json。配网器是导出数据中的一部分。配网器在程序中是一个数据模型,配网器类必须支持从JSon数据构建模型和将数据模型归档为Json数据。在iOS Swift中,Provisioner继承自Codable,在安卓Java中,Provisioner继承自Parcelable。
配网器有哪些成员属性和成员方法
如果没有特别指出,说明iOS和Android命名相同。
provisionerUuid:128-bit Device UUID,128位的设备UUID。
provisionerName:UTF-8 string, which should be a human readable name of the Provisioner. UTF8字符串,一个具有可阅读性的名称。
allocatedUnicastRange:An array of unicast range objects.可供分配的单播地址范围对象的数组。
allocatedGroupRange:An array of group range objects.可供分配的组播地址范围对象的数组
allocatedSceneRange:An array of scene range objects.可供分配的虚拟地址范围对象的数组。
安卓端:globalTtl,iOS端放在MeshNetworkManager.swift:defaultTtl :The Default TTL will be used for sending messages, if the value has not been set in the Provisioner's Node. By default it is set to 5, which is a reasonable value. The TTL shall be in range 2...127.用于设置配网器传递消息中可以转发的次数,默认为5,取值范围最好在2到127之间。
安卓端:sequenceNumber:配网器的序列号。
安卓端:provisionerAddress:给配网器分配的单播地址。
安卓端:lastSelected:是否是最后操作的配网器。
iOS端构造方法
有三种
public init(name: String,
uuid: UUID,
allocatedUnicastRange: [AddressRange],
allocatedGroupRange: [AddressRange],
allocatedSceneRange: [SceneRange]) {
self.provisionerName = name
self.provisionerUuid = MeshUUID(uuid)
self.allocatedUnicastRange = allocatedUnicastRange.merged()
self.allocatedGroupRange = allocatedGroupRange.merged()
self.allocatedSceneRange = allocatedSceneRange.merged()
}
public convenience init(name: String,
allocatedUnicastRange: [AddressRange],
allocatedGroupRange: [AddressRange],
allocatedSceneRange: [SceneRange]) {
self.init(name: name,
uuid: UUID(),
allocatedUnicastRange: allocatedUnicastRange,
allocatedGroupRange: allocatedGroupRange,
allocatedSceneRange: allocatedSceneRange
)
}
public convenience init(name: String) {
self.init(name: name,
uuid: UUID(),
allocatedUnicastRange: [AddressRange.allUnicastAddresses],
allocatedGroupRange: [AddressRange.allGroupAddresses],
allocatedSceneRange: [SceneRange.allScenes]
)
}
配网器就是一个典型的智能手机或者其他移动计算设备。尽管在一个网络中只允许使用一个配网器,但是可能在配网设备上存在多个配网器。分享缓存数据和合并多个配网器的数据是必须实现的功能。
To provision a device, the provisioning bearer must be established between a Provisioner and a device. A device can be identified to a Provisioner by its Device UUID and other supplementary information that may also be provided.
After the provisioning bearer is established, the Provisioner establishes a shared secret with the device using an Elliptic Curve Diffie-Hellman (ECDH) protocol. It then authenticates the device using OOB information that is specific to that device. Such OOB information may include a public key of the device, a long secret, the requirement to input a value into the device, or the requirement to output a value on that device. Such OOB information also enables the authentication of that device. Once the device has been authenticated, the provisioning data is transmitted to the device encrypted with a key derived from that shared secret. The device key is derived from the ECDHSecret and ProvisioningSalt.
在安卓端的构造方法
/**
* Constructs {@link Provisioner}
*/
public Provisioner(@NonNull final String provisionerUuid,
@NonNull final List<AllocatedUnicastRange> allocatedUnicastRanges,
@NonNull final List<AllocatedGroupRange> allocatedGroupRanges,
@NonNull final List<AllocatedSceneRange> allocatedSceneRanges,
@NonNull final String meshUuid) {
this.provisionerUuid = provisionerUuid.toUpperCase(Locale.US);
this.allocatedUnicastRanges = allocatedUnicastRanges;
this.allocatedGroupRanges = allocatedGroupRanges;
this.allocatedSceneRanges = allocatedSceneRanges;
this.meshUuid = meshUuid;
}
protected Provisioner(Parcel in) {
meshUuid = in.readString();
provisionerUuid = in.readString();
provisionerName = in.readString();
in.readTypedList(allocatedUnicastRanges, AllocatedUnicastRange.CREATOR);
in.readTypedList(allocatedGroupRanges, AllocatedGroupRange.CREATOR);
in.readTypedList(allocatedSceneRanges, AllocatedSceneRange.CREATOR);
sequenceNumber = in.readInt();
provisionerAddress = in.readInt();
globalTtl = in.readInt();
lastSelected = in.readByte() != 0;
}
public static final Creator<Provisioner> CREATOR = new Creator<Provisioner>() {
@Override
public Provisioner createFromParcel(Parcel in) {
return new Provisioner(in);
}
@Override
public Provisioner[] newArray(int size) {
return new Provisioner[size];
}
};