BLE 蓝牙 MAC 地址相关说明

复制代码
 讲解一下 BLE 蓝牙 MAC 地址分类及应用     ...... 矜辰所致

前言

最近在 CH585 使用过程中,遇到要动态修改蓝牙 MAC 地址的应用,于是了解了一下 BLE MAC 地址有关的定义规则,发现还是有必要写一篇博客来记录说明一下的。

所以本文的内容就是介绍一下 BLE 蓝牙 MAC 地址规则以及在应用中如何规范合理的定义 MAC 地址。

相关博文:
BLE 蓝牙空中报文格式与解析(广播包)

沁恒微 RISC-V 蓝牙 入门教程目录:
【导航】沁恒微 RISC-V 蓝牙 入门教程目录 【快速跳转】

.

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • [一、BLE MAC 地址分类](#一、BLE MAC 地址分类)
  • 二、格式详解
    • [2.1 公共地址](#2.1 公共地址)
    • [2.2 随机地址](#2.2 随机地址)
      • [2.2.1 静态设备地址](#2.2.1 静态设备地址)
      • [2.2.2 不可解析私有地址](#2.2.2 不可解析私有地址)
      • [2.2.3 可解析私有地址](#2.2.3 可解析私有地址)
      • [2.2.4 关于 IRK](#2.2.4 关于 IRK)
  • [三、 如何区分地址类型](#三、 如何区分地址类型)
  • [四、 实际应用中的修改设置](#四、 实际应用中的修改设置)
    • [4.1 上电默认地址](#4.1 上电默认地址)
    • [4.2 动态修改流程](#4.2 动态修改流程)
    • [4.3 静态地址](#4.3 静态地址)
    • [4.4 不可解析私有地址](#4.4 不可解析私有地址)
    • [4.5 可解析私有地址](#4.5 可解析私有地址)
    • [4.6 公共地址](#4.6 公共地址)
  • 结语

一、BLE MAC 地址分类

BLE 设备地址主要分为公共设备地址(Public Device Address)和随机设备地址(Random Device Address),随机地址又可细分为 静态地址 和 可解析与不可解析的私有地址,框架如下图:

最终对于我们应用来说,就等于分成了四类:

  • 公共地址 Public Address
  • 私有静态地址 Static Private Address
  • 私有不可解析地址 Non-Resolvable Private Address
  • 私有可解析地址 Resolvable Private Address

二、格式详解

2.1 公共地址

对于公共设备地址,格式解析如下:

格式:6 字节(48-bit),符合 IEEE EUI-48

Company ID: 24-bit\] + \[Company Assigned: 24-bit

↑ MSB 高位

示例:

DC:32:62:12:56:78

└─┬──┘ └─┬──┘

IEEE OUI 厂商分配

Comp ID Comp Assigned

(需购买) (内部管理)

.

这是最常见的情况,属于 MA-L 尺寸的地址块,另外两种是 MA-M 和 MA-S 类型,也是 48-bit 的 MAC 地址,格式和上面一致,区别就是 IEEE OUI 需要多占用一些位置,那么留给厂商自动管理的 Comp Assigned 部分就变少了,MA-M Comp Assigned 部分 20bit ,MA-L Comp Assigned 部分只有 12 bit 。

适用场景:

设备需要永久固定身份,一些工业设备,高端旗舰产品,比如网关、工业传感器、 beacon 基站等等。

对于我们实际手机扫描到公共地址的设备,如下图所示:

上面 DC:32:62:D0:BE:68 前面 24 bit DC:32:62 是南京沁恒微的公司 ID,这个是可以查询的(查询的网站大家自己网上找一下):

在用户界面层面,一般来说蓝牙 MAC 地址排列按照 MSB -> LSB(从左到右,高位在左边) 顺序。蓝牙手机 APP 扫描到的 MAC 地址都是高位在左。

2.2 随机地址

对于随机设备地址,我们上面介绍过,其下面有3个子类,其最高 2 bit 决定子类型(bit 47:46,即最高字节的 bit7:6),表格如下:

bit7 (MSB) bit6 子类型
0 0 Non-resolvable Private
0 1 Resolvable Private (RPA)
1 0 Reserved(预留)
1 1 Static Device Address

我们一个一个来格式说明。

2.2.1 静态设备地址

Static Device Address(静态设备地址),格式:

解析如下:

格式要求:

bit 47:46 = 11(最高字节在 0xC0 到 0xFF 之间)

剩余 46 bit 就是随机的数据,可以自定义,但是至少有一个 0 和一个 1(不能全 0 或全 1)

示例:C0:11:22:33:44:55

1100 0000 → bit7=1, bit6=1 ✓

手机实际扫描图如下:

适用场景:

一台设备模拟多个设备、动态切换地址,智能硬件、DIY 项目、测试设备。

2.2.2 不可解析私有地址

Non-resolvable Private Address(不可解析私有地址),格式:

解析如下:

格式:

bit47:46 = 00(最高字节在 0x00 到 0x3F 之间)

剩余 46 bit 完全随机,可以全0,也可以全1。

示例:

3F:FF:FF:FF:FF:FF

0011 1111 → bit7=0, bit6=0 ✓

手机实际扫描图如下:

适用场景:

纯广播 Beacon,一次性设备,不想被任何设备记住身份完全匿名场景,不能用于需要配对的设备。

2.2.3 可解析私有地址

Resolvable Private Address (RPA)(可解析私有地址),格式:

解析如下:

格式:

bit 47:46 = 01(最高字节在 0x40 到 0x7F 之间)

整体 48bit 格式:

0b01 + prand (22-bit) + hash (24-bit)

↑ MSB 高位

其中:

prand(22 bit):随机数,必须至少有一个 0 和一个 1

hash(24 bit): 用 IRK + prand 加密生成的哈希值 hash = AES(IRK, prand)

示例:42:AC:0B:12:6A:00

0100 0010 → bit7=0, bit6=1 ✓

手机实际扫描图如下:

适用场景:

手机、耳机、手环等消费类电子,设备需要频繁换地址,但又要和已配对手机保持连接,苹果 / 谷歌强制隐私要求的场景,必须保护用户隐私的场景。

2.2.4 关于 IRK

上面可解析私有地址里面,讲到了 IRK ,我们额外介绍一下什么是 IRK。

IRK = Identity Resolution Key(身份解析密钥),身份解析密钥(IRK) 用于构造可解析私有地址(RPA),每个设备都会有自己的 IRK。

设备用自己的 IRK + 随机数(prand),通过加密算法算出一个哈希值(hash),再拼上 prand,就得到了一个临时的 RPA 地址。

两个设备第一次配对(Bonding)时,当双方需要互相解析对方的私有地址时,会互相交换并保存对方的 IRK(如果一方是固定公共地址、不使用隐私地址,只需保存对方 IRK,不需要交换自己的 IRK) 。

配对成功后,IRK 会被存在设备的绑定列表里(保存在 Flash 中掉电不丢失),后续连接用 IRK 解析 RPA 认出对方。

IRK 是用于配对后的设备识别,那他是什么时候产生的呢?

在查阅了很多资料,最后找到蓝牙官方有文档说明:

文档地址:https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-60/out/en/host/security-manager-specification.html

IRK 可以由设备在生产阶段被分配,或随机生成,也可以使用其他方式。至于我们 CH585 的 IRK ,如果需要使用可解析私有地址,需要随机生成一下,下面会有对应说明。

三、 如何区分地址类型

只通过查看设备的 MAC 地址,是无法确定地址类型的。

因为我们不能保证地址的高位几个字节是厂商的 ID,还是用户自定义的,有可能是自定义的数据正好匹配上了厂商 ID 位置。

唯一可靠的确定 MAC 地址类型的方式是 查看设备广播/数据包中的 TxAdd 位 (此位为0: 公共地址,此位为1:随机地址)。

在我之前博文 《BLE 蓝牙空中报文格式与解析(广播包)》 有广播包的格式解析:

确定了随机地址,再通过查看 MAC 地址的最高 2 bit 决定子类型(bit 47:46,即最高字节的 bit7:6)。

通过蓝牙协议分析仪可以很直观的查看(使用的是沁恒微蓝牙协议分析仪) :

总结一下上面的基础理论介绍,可以用下面的一张图片概括:

上面的理论介绍和类型区分是基于 MAC 地址规范使用的情况,有时候在产品开发 / 调试 的时候,可能使用一些不规范的定义,比如仅供测试的假的公共地址。

四、 实际应用中的修改设置

完成了理论的介绍,下面来说明一下实际应用,如何实现不同的 MAC 地址。

4.1 上电默认地址

芯片上电以后就要配置自己的 MAC 地址, 根据 BLE_MAC 宏定义是否存在来决定使用哪种 MAC 地址,但是在初始化时候配置的 MAC 地址,都属于公共地址,如下图:

如果不需要中途修改 MAC 地址,直接修改上面自定义数组可以改变设备的 MAC 地址(公共地址)。

4.2 动态修改流程

如果在应用途中需要修改 MAC 地址,可以使用下面函数:

c 复制代码
bStatus_t GAP_ConfigDeviceAddr( uint8_t addrType, uint8_t *pStaticAddr );
//第一个参数可选如下宏定义
// GAP_ADDR_TYPE_DEFINES GAP Address Types
#define ADDRTYPE_PUBLIC                         0x00  //!< Use the BD_ADDR
#define ADDRTYPE_STATIC                         0x01  //!< Static address
#define ADDRTYPE_PRIVATE_NONRESOLVE             0x02  //!< Generate Non-Resolvable Private Address
#define ADDRTYPE_PRIVATE_RESOLVE                0x03  //!< Generate Resolvable Private Address

要使得修改的 MAC 地址生效,需要先关闭广播,修改MAC 地址,再打开广播流程操作,可参考如下流程:

此为参考示例,在参数修改处使用 GAP_ConfigDeviceAddr 函数配置新的 MAC 地址。只要满足 关闭广播 -> 修改配置 -> 确保广播关闭后,再开启广播 ,代码逻辑可以自行调整。

测试方式:大家可以在设备开启任务里加一个任务,比如 10s 后启动这个修改配置的任务,那么上电时候我们扫描可以看到设备原本的 MAC 地址, 10s 后再扫描就能发现设备的 MAC 地址发生了变化:

c 复制代码
if(events & SBP_START_DEVICE_EVT)
{
    // Start the Device
    GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);
    tmos_start_task(Peripheral_TaskID, MY_ChangePower_EVT, 16000);
    return (events ^ SBP_START_DEVICE_EVT);
}

4.3 静态地址

静态地址设置示例如下:

c 复制代码
if(events & MY_ChangePower_EVT)
 {
     uint8_t myAddr[6] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x00};
     PRINT("Set Set...\r\n");
     uint8_t  initial_advertising_enable = FALSE;
     GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
     // set set
     // GAP_SetParamValue(TGAP_ADV_TX_POWER,0x3B);
     GAP_ConfigDeviceAddr(ADDRTYPE_STATIC, myAddr);
     return (events ^ MY_ChangePower_EVT);
 }

说明,使用此函数设置静态地址,协议栈会自动把最高2位 bit 47:46 设置为 1。

然后,这个 MAC 地址的顺序和初始化时候设置的数组那个顺序结果是反的,仔细看也能知道初始化的时候赋值时那个 for 循环是反过来的,这里就提一下。

结果图就是上文中格式解析部分,手机实际扫描图对应部分。

4.4 不可解析私有地址

不可解析私有地址设置示例如下:

c 复制代码
if(events & MY_ChangePower_EVT)
{
    uint8_t ownAddr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F};
    PRINT("Set Set...\r\n");
    uint8_t  initial_advertising_enable = FALSE;
    GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
    // set set
    // GAP_SetParamValue(TGAP_ADV_TX_POWER,0x3B);
    GAP_ConfigDeviceAddr(ADDRTYPE_PRIVATE_NONRESOLVE, ownAddr);
    return (events ^ MY_ChangePower_EVT);
}

说明,使用此函数设置不可解析私有地址,要注意最高位 bit 47:46 必须为 0 才能设置成功。 也就是自定义数组最后一个字节的 bit7 和 bit6 必须为 0 。

结果图也在上文中格式解析部分 。

4.5 可解析私有地址

可解析私有地址要准备 IRK ,把自己的 IRK 告诉协议栈,用来做配对准备,然后设置自己为可解析私有地址。

示例如下:

c 复制代码
if(events & MY_ChangePower_EVT)
{
    PRINT("Set Set...r\n");
    //关闭广播
    uint8_t  initial_advertising_enable = FALSE;
    GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
    //读取 IRK ,如果失败,就生成随机 IRK
    tmos_snv_init();
    PRINT("tmos %x\n",tmos_snv_read(BLE_NVID_IRK,KEYLEN,BLE_IRK));
    PRINT("IRK %x %x %x %x %x %x\n",BLE_IRK[5],BLE_IRK[4],BLE_IRK[3],BLE_IRK[2],BLE_IRK[1],BLE_IRK[0]);
    if(tmos_snv_read(BLE_NVID_IRK,KEYLEN,BLE_IRK))
    {
        uint32_t rand;
        uint8_t j;
        for(j=0; j<4; j++)
        {
            rand = tmos_rand();
            BLE_IRK[0+4*j] = BREAK_UINT32(rand, 0);
            BLE_IRK[1+4*j] = BREAK_UINT32(rand, 1);
            BLE_IRK[2+4*j] = BREAK_UINT32(rand, 2);
            BLE_IRK[3+4*j] = BREAK_UINT32(rand, 3);
        }
        PRINT("gets here\n");
        tmos_snv_write(BLE_NVID_IRK,KEYLEN,BLE_IRK);
        tmos_snv_notify(1);
    }
    GAPRole_SetParameter(GAPROLE_IRK, KEYLEN, BLE_IRK);
    // uint8_t myAddr[6] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
    uint8_t myAddr[6] = {0};
    GAP_ConfigDeviceAddr(ADDRTYPE_PRIVATE_RESOLVE, myAddr);

    return (events ^ MY_ChangePower_EVT);
}

说明,使用此函数设置可解析私有地址,用来定义 MAC 地址的数组 myAddr[6] 数据是没有意义的,协议栈会自动生成随机地址。

除了最高位 bit 47:46 = 0b 01,每一次生成 MAC 地址都是随机的,这个大家可以自己测试一下。

4.6 公共地址

最后再来看下,如果动态修改地址里面设置为公共地址会出现什么情况,根据前面我们可以推测出来,使用如下代码进行配置:

c 复制代码
uint8_t myAddr[6] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
GAP_ConfigDeviceAddr(ADDRTYPE_PUBLIC, myAddr);

使用此函数设置公共地址,这里的用来定义 MAC 地址的数组 myAddr[6] 数据是没有意义的,设备地址会变成上电时候初始化的地址。

说明,使用此函数设置公共地址,最终都会变成初始化的时候 CH58x_BLEInit 函数中 cfg.MacAddr[] 被赋予的地址。

结语

本文我们详细介绍了 BLE 蓝牙 MAC 地址分类和格式,也针对实际的应用讲解了在使用 CH58x 芯片时候如何设置修改 MAC 地址。希望能够解决大家应用中与 MAC 地址有关的问题 。

好了,本文就到这里。谢谢大家!

相关推荐
嵌入式小企鹅5 天前
蓝牙学习系列(二):BLE协议栈解析
学习·蓝牙·ble·蓝牙协议栈·协议栈
矜辰所致6 天前
CH58x 蓝牙主机获取从设备服务特征值句柄
蓝牙主机·ble 蓝牙·ch58x·蓝牙获取服务句柄·蓝牙获取特征值句柄
Leung_ManWah8 天前
RTL8762C学习笔记(1)——搭建环境、编译烧写
iot·ble·rtl8762c
babytiger22 天前
ble扫描相关的问题,蓝牙 MAC 是否可以确定厂商?
蓝牙·ble
dozenyaoyida1 个月前
BLE传输WiFi列表的问题分析
网络·经验分享·物联网·wifi·中文乱码·json解析·ble
Darkershadow1 个月前
蓝牙学习之发送 Mesh Provisioning Service advertising
学习·蓝牙·ble·mesh
Darkershadow2 个月前
蓝牙学习之Time Set
python·学习·蓝牙·ble·mesh
wotaifuzao2 个月前
Matter-PICS梳理(ble-thread)
认证·thread·ble·matter·pics
飞易通2 个月前
蓝牙WIFI模块推动两轮车仪表与智能头盔智能化发展
蓝牙模块·ble·wifi模块·低功耗蓝牙模块·wifi蓝牙组合模块