Bluetooth: Mesh: Private Beacons

Adds support for private beacon sending and receiving.

Co-authored-by: Krzysztof Kopyściński <krzysztof.kopyscinski@codecoup.pl>
Co-authored-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2020-06-02 12:04:53 +02:00 committed by Carles Cufí
commit f9b19010ed
26 changed files with 1711 additions and 125 deletions

View file

@ -13,6 +13,7 @@
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/sys/util.h>
#include <zephyr/bluetooth/hci.h>
@ -35,6 +36,12 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_gatt);
/* Interval to update random value in (10 minutes).
*
* Defined in the Bluetooth Mesh Specification v1.1, Section 7.2.2.2.4.
*/
#define PROXY_RANDOM_UPDATE_INTERVAL (10 * 60 * MSEC_PER_SEC)
#if defined(CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME)
#define ADV_OPT_USE_NAME BT_LE_ADV_OPT_USE_NAME
#else
@ -312,10 +319,15 @@ static void proxy_msg_recv(struct bt_mesh_proxy_role *role)
static int beacon_send(struct bt_mesh_proxy_client *client,
struct bt_mesh_subnet *sub)
{
NET_BUF_SIMPLE_DEFINE(buf, 23);
int err;
NET_BUF_SIMPLE_DEFINE(buf, 28);
net_buf_simple_reserve(&buf, 1);
bt_mesh_beacon_create(sub, &buf);
err = bt_mesh_beacon_create(sub, &buf);
if (err) {
return err;
}
return bt_mesh_proxy_msg_send(client->cli->conn, BT_MESH_PROXY_BEACON,
&buf, NULL, NULL);
@ -354,7 +366,7 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
}
}
static void node_id_start(struct bt_mesh_subnet *sub)
static void identity_enabled(struct bt_mesh_subnet *sub)
{
sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
sub->node_id_start = k_uptime_get_32();
@ -366,9 +378,31 @@ static void node_id_start(struct bt_mesh_subnet *sub)
}
}
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
static void node_id_start(struct bt_mesh_subnet *sub)
{
node_id_start(sub);
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
sub->priv_beacon.node_id = false;
#endif
identity_enabled(sub);
}
static void private_node_id_start(struct bt_mesh_subnet *sub)
{
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
sub->priv_beacon.node_id = true;
#endif
identity_enabled(sub);
}
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub, bool private)
{
if (private) {
private_node_id_start(sub);
} else {
node_id_start(sub);
}
/* Prioritize the recently enabled subnet */
beacon_sub = sub;
@ -401,20 +435,39 @@ int bt_mesh_proxy_identity_enable(void)
return 0;
}
#define NODE_ID_LEN 19
int bt_mesh_proxy_private_identity_enable(void)
{
LOG_DBG("");
if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return -ENOTSUP;
}
if (!bt_mesh_is_provisioned()) {
return -EAGAIN;
}
if (bt_mesh_subnet_foreach(private_node_id_start)) {
bt_mesh_adv_gatt_update();
}
return 0;
}
#define ENC_ID_LEN 19
#define NET_ID_LEN 11
#define NODE_ID_TIMEOUT (CONFIG_BT_MESH_NODE_ID_TIMEOUT * MSEC_PER_SEC)
static uint8_t proxy_svc_data[NODE_ID_LEN] = {
static uint8_t proxy_svc_data[ENC_ID_LEN] = {
BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL),
};
static const struct bt_data node_id_ad[] = {
static const struct bt_data enc_id_ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)),
BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN),
BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, ENC_ID_LEN),
};
static const struct bt_data net_id_ad[] = {
@ -424,46 +477,113 @@ static const struct bt_data net_id_ad[] = {
BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN),
};
static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
static int randomize_bt_addr(void)
{
/* TODO: There appears to be no way to force an RPA/NRPA refresh. */
return 0;
}
static int enc_id_adv(struct bt_mesh_subnet *sub, uint8_t type,
uint8_t hash[16], int32_t duration)
{
struct bt_le_adv_param slow_adv_param = {
.options = ADV_OPT_PROXY,
ADV_SLOW_INT,
};
struct bt_le_adv_param fast_adv_param = {
.options = ADV_OPT_PROXY,
ADV_FAST_INT,
};
uint8_t tmp[16];
int err;
LOG_DBG("");
proxy_svc_data[2] = BT_MESH_ID_TYPE_NODE;
err = bt_rand(proxy_svc_data + 11, 8);
err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, hash, hash);
if (err) {
return err;
}
(void)memset(tmp, 0, 6);
memcpy(tmp + 6, proxy_svc_data + 11, 8);
sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp,
tmp);
/* Section 7.2.2.2.4: The AdvA field shall be regenerated whenever the Random field is
* regenerated.
*/
err = randomize_bt_addr();
if (err) {
LOG_ERR("AdvA refresh failed: %d", err);
return err;
}
memcpy(proxy_svc_data + 3, tmp + 8, 8);
proxy_svc_data[2] = type;
memcpy(&proxy_svc_data[3], &hash[8], 8);
err = bt_mesh_adv_gatt_start(&fast_adv_param, duration, node_id_ad,
ARRAY_SIZE(node_id_ad), NULL, 0);
err = bt_mesh_adv_gatt_start(
type == BT_MESH_ID_TYPE_PRIV_NET ? &slow_adv_param : &fast_adv_param,
duration, enc_id_ad, ARRAY_SIZE(enc_id_ad), NULL, 0);
if (err) {
LOG_WRN("Failed to advertise using Node ID (err %d)", err);
LOG_WRN("Failed to advertise using type 0x%02x (err %d)", type, err);
return err;
}
return 0;
}
static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
uint8_t *random = &proxy_svc_data[11];
uint8_t tmp[16];
int err;
LOG_DBG("0x%03x", sub->net_idx);
err = bt_rand(random, 8);
if (err) {
return err;
}
memset(&tmp[0], 0x00, 6);
memcpy(&tmp[6], random, 8);
sys_put_be16(bt_mesh_primary_addr(), &tmp[14]);
return enc_id_adv(sub, BT_MESH_ID_TYPE_NODE, tmp, duration);
}
static int priv_node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
uint8_t *random = &proxy_svc_data[11];
uint8_t tmp[16];
int err;
LOG_DBG("0x%03x", sub->net_idx);
err = bt_rand(random, 8);
if (err) {
return err;
}
memset(&tmp[0], 0x00, 5);
tmp[5] = 0x03;
memcpy(&tmp[6], random, 8);
sys_put_be16(bt_mesh_primary_addr(), &tmp[14]);
return enc_id_adv(sub, BT_MESH_ID_TYPE_PRIV_NODE, tmp, duration);
}
static int priv_net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
uint8_t *random = &proxy_svc_data[11];
uint8_t tmp[16];
int err;
LOG_DBG("0x%03x", sub->net_idx);
err = bt_rand(random, 8);
if (err) {
return err;
}
memcpy(&tmp[0], sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8);
memcpy(&tmp[8], random, 8);
return enc_id_adv(sub, BT_MESH_ID_TYPE_PRIV_NET, tmp, duration);
}
static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
struct bt_le_adv_param slow_adv_param = {
@ -472,8 +592,6 @@ static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
};
int err;
LOG_DBG("");
proxy_svc_data[2] = BT_MESH_ID_TYPE_NET;
LOG_DBG("Advertising with NetId %s", bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8));
@ -497,7 +615,8 @@ static bool advertise_subnet(struct bt_mesh_subnet *sub)
}
return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING ||
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED ||
bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
}
static struct bt_mesh_subnet *next_sub(void)
@ -591,12 +710,20 @@ static int gatt_proxy_advertise(struct bt_mesh_subnet *sub)
if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
uint32_t active = k_uptime_get_32() - sub->node_id_start;
bool priv_node_id = false;
if (active < NODE_ID_TIMEOUT) {
remaining = MIN(remaining, NODE_ID_TIMEOUT - active);
LOG_DBG("Node ID active for %u ms, %d ms remaining", active,
remaining);
err = node_id_adv(sub, remaining);
LOG_DBG("Node ID active for %u ms, %d ms remaining",
active, remaining);
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
priv_node_id = sub->priv_beacon.node_id;
#endif
if (priv_node_id) {
err = priv_node_id_adv(sub, remaining);
} else {
err = node_id_adv(sub, remaining);
}
planned = true;
} else {
bt_mesh_proxy_identity_stop(sub);
@ -608,10 +735,25 @@ static int gatt_proxy_advertise(struct bt_mesh_subnet *sub)
* A node that does not support the Proxy feature or
* has the GATT Proxy state disabled shall not advertise with Network ID.
*/
if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED &&
bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
err = net_id_adv(sub, remaining);
planned = true;
if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) {
if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) &&
(bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED)) {
/* Bluetooth mesh specification v1.1, section 7.2.2.2.4: The Random
* field should be updated every 10 minutes. Limit advertising to
* 10 minutes to ensure regeneration of a new random value at least
* that often.
*/
if (remaining == SYS_FOREVER_MS ||
remaining > PROXY_RANDOM_UPDATE_INTERVAL) {
remaining = PROXY_RANDOM_UPDATE_INTERVAL;
}
err = priv_net_id_adv(sub, remaining);
planned = true;
} else if (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
err = net_id_adv(sub, remaining);
planned = true;
}
}
beacon_sub = bt_mesh_subnet_next(sub);