1. Add an API to set Provisioner static oob value 2. Add an API to deinit BLE Mesh stack 3. Add an API to set Provisioner unicast address 4. Add an API to provision devices with fixed address 5. Add an API to store node composition data 6. Add an API to get node with device uuid 7. Add an API to get node with unicast address 8. Add an API to delete node with device uuid 9. Add an API to delete node with unicast address 10. Add an API for Provisioner to update local AppKey 11. Add an API for Provisioner to update local NetKey 12. Support Provisioner persistent functionality 13. Fix Provisioner entering IV Update procedure 14. Fix an issue which may cause client failing to send msg 15. Use bt_mesh.flags to indicate device role 16. Remove several useless macros 17. Callback RSSI of received mesh provisioning packets 18. Modify the Provisioner disable function 19. Change some log level from debug to info 20. Add parameters to Provisioner bind AppKey completion event 21. Fix node ignoring relay messages issue 22. Support using a specific partition for BLE Mesh 23. Fix compile warning when proxy related macros are disabled 24. Clean up BLE Mesh stack included header files 25. NULL can be input if client message needs no parameters 26. Fix compile warning when BT log is disabled 27. Initilize BLE Mesh stack local variables 28. Support using PSRAM for BLE Mesh mutex, queue and task 29. Add a menuconfig option to enable using memory from PSRAM 30. Clean up sdkconfig.defaults of BLE Mesh examples
475 lines
12 KiB
C
475 lines
12 KiB
C
/* Bluetooth Mesh */
|
|
|
|
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_BEACON)
|
|
|
|
#include "adv.h"
|
|
#include "mesh.h"
|
|
#include "prov.h"
|
|
#include "crypto.h"
|
|
#include "beacon.h"
|
|
#include "access.h"
|
|
#include "foundation.h"
|
|
#include "proxy_client.h"
|
|
#include "mesh_main.h"
|
|
#include "provisioner_prov.h"
|
|
#include "provisioner_main.h"
|
|
|
|
#if defined(CONFIG_BLE_MESH_FAST_PROV)
|
|
#define UNPROVISIONED_INTERVAL K_SECONDS(3)
|
|
#else
|
|
#define UNPROVISIONED_INTERVAL K_SECONDS(5)
|
|
#endif /* CONFIG_BLE_MESH_FAST_PROV */
|
|
#define PROVISIONED_INTERVAL K_SECONDS(10)
|
|
|
|
#define BEACON_TYPE_UNPROVISIONED 0x00
|
|
#define BEACON_TYPE_SECURE 0x01
|
|
|
|
/* 3 transmissions, 20ms interval */
|
|
#define UNPROV_XMIT BLE_MESH_TRANSMIT(2, 20)
|
|
|
|
/* 1 transmission, 20ms interval */
|
|
#define PROV_XMIT BLE_MESH_TRANSMIT(0, 20)
|
|
|
|
static struct k_delayed_work beacon_timer;
|
|
|
|
static struct bt_mesh_subnet *cache_check(u8_t data[21])
|
|
{
|
|
size_t subnet_size = 0U;
|
|
int i = 0;
|
|
|
|
subnet_size = bt_mesh_rx_netkey_size();
|
|
|
|
for (i = 0; i < subnet_size; i++) {
|
|
struct bt_mesh_subnet *sub = bt_mesh_rx_netkey_get(i);
|
|
|
|
if (sub == NULL || sub->net_idx == BLE_MESH_KEY_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
if (!memcmp(sub->beacon_cache, data, 21)) {
|
|
return sub;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void cache_add(u8_t data[21], struct bt_mesh_subnet *sub)
|
|
{
|
|
memcpy(sub->beacon_cache, data, 21);
|
|
}
|
|
|
|
static void beacon_complete(int err, void *user_data)
|
|
{
|
|
struct bt_mesh_subnet *sub = user_data;
|
|
|
|
BT_DBG("err %d", err);
|
|
|
|
sub->beacon_sent = k_uptime_get_32();
|
|
}
|
|
|
|
void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
u8_t flags = bt_mesh_net_flags(sub);
|
|
struct bt_mesh_subnet_keys *keys = NULL;
|
|
|
|
net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE);
|
|
|
|
if (sub->kr_flag) {
|
|
keys = &sub->keys[1];
|
|
} else {
|
|
keys = &sub->keys[0];
|
|
}
|
|
|
|
net_buf_simple_add_u8(buf, flags);
|
|
|
|
/* Network ID */
|
|
net_buf_simple_add_mem(buf, keys->net_id, 8);
|
|
|
|
/* IV Index */
|
|
net_buf_simple_add_be32(buf, bt_mesh.iv_index);
|
|
|
|
net_buf_simple_add_mem(buf, sub->auth, 8);
|
|
|
|
BT_INFO("net_idx 0x%04x flags 0x%02x NetID %s", sub->net_idx,
|
|
flags, bt_hex(keys->net_id, 8));
|
|
BT_INFO("IV Index 0x%08x Auth %s", bt_mesh.iv_index,
|
|
bt_hex(sub->auth, 8));
|
|
}
|
|
|
|
/* If the interval has passed or is within 5 seconds from now send a beacon */
|
|
#define BEACON_THRESHOLD(sub) (K_SECONDS(10 * ((sub)->beacons_last + 1)) - \
|
|
K_SECONDS(5))
|
|
|
|
static int secure_beacon_send(void)
|
|
{
|
|
static const struct bt_mesh_send_cb send_cb = {
|
|
.end = beacon_complete,
|
|
};
|
|
u32_t now = k_uptime_get_32();
|
|
size_t subnet_size = 0U;
|
|
int i = 0;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
subnet_size = bt_mesh_rx_netkey_size();
|
|
|
|
for (i = 0; i < subnet_size; i++) {
|
|
struct bt_mesh_subnet *sub = bt_mesh_rx_netkey_get(i);
|
|
struct net_buf *buf;
|
|
u32_t time_diff;
|
|
|
|
if (sub == NULL || sub->net_idx == BLE_MESH_KEY_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
time_diff = now - sub->beacon_sent;
|
|
if (time_diff < K_SECONDS(600) &&
|
|
time_diff < BEACON_THRESHOLD(sub)) {
|
|
continue;
|
|
}
|
|
|
|
/**
|
|
* If a node enables the Proxy Client functionality, and it
|
|
* succeeds to send Secure Network Beacon with GATT bearer,
|
|
* here we will continue to send Secure Network Beacon of
|
|
* other subnets.
|
|
*/
|
|
#if defined(CONFIG_BLE_MESH_GATT_PROXY_CLIENT)
|
|
if (bt_mesh_proxy_client_beacon_send(sub)) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
buf = bt_mesh_adv_create(BLE_MESH_ADV_BEACON, PROV_XMIT,
|
|
K_NO_WAIT);
|
|
if (!buf) {
|
|
BT_ERR("%s, Unable to allocate beacon buffer", __func__);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
bt_mesh_beacon_create(sub, &buf->b);
|
|
|
|
bt_mesh_adv_send(buf, &send_cb, sub);
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BLE_MESH_NODE)
|
|
static int unprovisioned_beacon_send(void)
|
|
{
|
|
#if defined(CONFIG_BLE_MESH_PB_ADV)
|
|
const struct bt_mesh_prov *prov = NULL;
|
|
u8_t uri_hash[16] = { 0 };
|
|
struct net_buf *buf = NULL;
|
|
u16_t oob_info = 0U;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
buf = bt_mesh_adv_create(BLE_MESH_ADV_BEACON, UNPROV_XMIT, K_NO_WAIT);
|
|
if (!buf) {
|
|
BT_ERR("%s, Unable to allocate beacon buffer", __func__);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
prov = bt_mesh_prov_get();
|
|
|
|
net_buf_add_u8(buf, BEACON_TYPE_UNPROVISIONED);
|
|
net_buf_add_mem(buf, prov->uuid, 16);
|
|
|
|
if (prov->uri && bt_mesh_s1(prov->uri, uri_hash) == 0) {
|
|
oob_info = prov->oob_info | BLE_MESH_PROV_OOB_URI;
|
|
} else {
|
|
oob_info = prov->oob_info;
|
|
}
|
|
|
|
net_buf_add_be16(buf, oob_info);
|
|
net_buf_add_mem(buf, uri_hash, 4);
|
|
|
|
bt_mesh_adv_send(buf, NULL, NULL);
|
|
net_buf_unref(buf);
|
|
|
|
if (prov->uri) {
|
|
size_t len;
|
|
|
|
buf = bt_mesh_adv_create(BLE_MESH_ADV_URI, UNPROV_XMIT,
|
|
K_NO_WAIT);
|
|
if (!buf) {
|
|
BT_ERR("Unable to allocate URI buffer");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
len = strlen(prov->uri);
|
|
if (net_buf_tailroom(buf) < len) {
|
|
BT_WARN("Too long URI to fit advertising data");
|
|
} else {
|
|
net_buf_add_mem(buf, prov->uri, len);
|
|
bt_mesh_adv_send(buf, NULL, NULL);
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
#endif /* CONFIG_BLE_MESH_PB_ADV */
|
|
return 0;
|
|
}
|
|
#else /* CONFIG_BLE_MESH_NODE */
|
|
static int unprovisioned_beacon_send(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_NODE */
|
|
|
|
static void update_beacon_observation(void)
|
|
{
|
|
static bool first_half;
|
|
size_t subnet_size = 0U;
|
|
int i = 0;
|
|
|
|
/* Observation period is 20 seconds, whereas the beacon timer
|
|
* runs every 10 seconds. We process what's happened during the
|
|
* window only after the second half.
|
|
*/
|
|
first_half = !first_half;
|
|
if (first_half) {
|
|
return;
|
|
}
|
|
|
|
subnet_size = bt_mesh_rx_netkey_size();
|
|
|
|
for (i = 0; i < subnet_size; i++) {
|
|
struct bt_mesh_subnet *sub = bt_mesh_rx_netkey_get(i);
|
|
|
|
if (sub == NULL || sub->net_idx == BLE_MESH_KEY_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
sub->beacons_last = sub->beacons_cur;
|
|
sub->beacons_cur = 0U;
|
|
}
|
|
}
|
|
|
|
static bool ready_to_send(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) {
|
|
return true;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER) && bt_mesh_is_provisioner_en()) {
|
|
if (bt_mesh_provisioner_get_all_node_count()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void beacon_send(struct k_work *work)
|
|
{
|
|
/* Don't send anything if we have an active provisioning link */
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_node() &&
|
|
IS_ENABLED(CONFIG_BLE_MESH_PROV) && bt_prov_active()) {
|
|
k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
if (ready_to_send()) {
|
|
update_beacon_observation();
|
|
secure_beacon_send();
|
|
|
|
/* Only resubmit if beaconing is still enabled */
|
|
if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED ||
|
|
bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR)) {
|
|
k_delayed_work_submit(&beacon_timer,
|
|
PROVISIONED_INTERVAL);
|
|
}
|
|
} else {
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_node()) {
|
|
unprovisioned_beacon_send();
|
|
k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void secure_beacon_recv(struct net_buf_simple *buf)
|
|
{
|
|
u8_t *data = NULL, *net_id = NULL, *auth = NULL;
|
|
struct bt_mesh_subnet *sub = NULL;
|
|
u32_t iv_index = 0U;
|
|
bool new_key = false, kr_change = false, iv_change = false;
|
|
u8_t flags = 0U;
|
|
|
|
if (buf->len < 21) {
|
|
BT_ERR("%s, Too short secure beacon (len %u)", __func__, buf->len);
|
|
return;
|
|
}
|
|
|
|
sub = cache_check(buf->data);
|
|
if (sub) {
|
|
/* We've seen this beacon before - just update the stats */
|
|
goto update_stats;
|
|
}
|
|
|
|
/* So we can add to the cache if auth matches */
|
|
data = buf->data;
|
|
|
|
flags = net_buf_simple_pull_u8(buf);
|
|
net_id = net_buf_simple_pull_mem(buf, 8);
|
|
iv_index = net_buf_simple_pull_be32(buf);
|
|
auth = buf->data;
|
|
|
|
BT_DBG("flags 0x%02x id %s iv_index 0x%08x",
|
|
flags, bt_hex(net_id, 8), iv_index);
|
|
|
|
sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key);
|
|
if (!sub) {
|
|
BT_DBG("No subnet that matched beacon");
|
|
return;
|
|
}
|
|
|
|
if (sub->kr_phase == BLE_MESH_KR_PHASE_2 && !new_key) {
|
|
BT_WARN("Ignoring Phase 2 KR Update secured using old key");
|
|
return;
|
|
}
|
|
|
|
cache_add(data, sub);
|
|
|
|
/* If we have NetKey0 accept initiation only from it */
|
|
if (bt_mesh_primary_subnet_exist() &&
|
|
sub->net_idx != BLE_MESH_KEY_PRIMARY) {
|
|
BT_WARN("Ignoring secure beacon on non-primary subnet");
|
|
goto update_stats;
|
|
}
|
|
|
|
BT_INFO("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x",
|
|
sub->net_idx, iv_index, bt_mesh.iv_index);
|
|
|
|
if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR) &&
|
|
(bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS) ==
|
|
BLE_MESH_IV_UPDATE(flags))) {
|
|
bt_mesh_beacon_ivu_initiator(false);
|
|
}
|
|
|
|
iv_change = bt_mesh_net_iv_update(iv_index, BLE_MESH_IV_UPDATE(flags));
|
|
|
|
kr_change = bt_mesh_kr_update(sub, BLE_MESH_KEY_REFRESH(flags), new_key);
|
|
if (kr_change) {
|
|
bt_mesh_net_beacon_update(sub);
|
|
}
|
|
|
|
if (iv_change) {
|
|
/* Update all subnets */
|
|
bt_mesh_net_sec_update(NULL);
|
|
} else if (kr_change) {
|
|
/* Key Refresh without IV Update only impacts one subnet */
|
|
bt_mesh_net_sec_update(sub);
|
|
}
|
|
|
|
update_stats:
|
|
if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED &&
|
|
sub->beacons_cur < 0xff) {
|
|
sub->beacons_cur++;
|
|
}
|
|
}
|
|
|
|
void bt_mesh_beacon_recv(struct net_buf_simple *buf, s8_t rssi)
|
|
{
|
|
u8_t type = 0U;
|
|
|
|
BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len));
|
|
|
|
if (buf->len < 1) {
|
|
BT_ERR("%s, Too short beacon", __func__);
|
|
return;
|
|
}
|
|
|
|
type = net_buf_simple_pull_u8(buf);
|
|
switch (type) {
|
|
case BEACON_TYPE_UNPROVISIONED:
|
|
BT_DBG("Unprovisioned device beacon received");
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER) &&
|
|
bt_mesh_is_provisioner_en()) {
|
|
bt_mesh_provisioner_unprov_beacon_recv(buf, rssi);
|
|
}
|
|
break;
|
|
case BEACON_TYPE_SECURE:
|
|
secure_beacon_recv(buf);
|
|
break;
|
|
default:
|
|
BT_DBG("Unknown beacon type 0x%02x", type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void bt_mesh_beacon_init(void)
|
|
{
|
|
k_delayed_work_init(&beacon_timer, beacon_send);
|
|
}
|
|
|
|
void bt_mesh_beacon_deinit(void)
|
|
{
|
|
k_delayed_work_free(&beacon_timer);
|
|
}
|
|
|
|
void bt_mesh_beacon_ivu_initiator(bool enable)
|
|
{
|
|
bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_INITIATOR, enable);
|
|
|
|
if (enable) {
|
|
k_work_submit(&beacon_timer.work);
|
|
} else if (bt_mesh_beacon_get() == BLE_MESH_BEACON_DISABLED) {
|
|
k_delayed_work_cancel(&beacon_timer);
|
|
}
|
|
}
|
|
|
|
void bt_mesh_beacon_enable(void)
|
|
{
|
|
size_t subnet_size = 0U;
|
|
int i = 0;
|
|
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_node() &&
|
|
!bt_mesh_is_provisioned()) {
|
|
k_work_submit(&beacon_timer.work);
|
|
return;
|
|
}
|
|
|
|
subnet_size = bt_mesh_rx_netkey_size();
|
|
|
|
for (i = 0; i < subnet_size; i++) {
|
|
struct bt_mesh_subnet *sub = bt_mesh_rx_netkey_get(i);
|
|
|
|
if (sub == NULL || sub->net_idx == BLE_MESH_KEY_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
sub->beacons_last = 0U;
|
|
sub->beacons_cur = 0U;
|
|
|
|
bt_mesh_net_beacon_update(sub);
|
|
}
|
|
|
|
k_work_submit(&beacon_timer.work);
|
|
}
|
|
|
|
void bt_mesh_beacon_disable(void)
|
|
{
|
|
if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR)) {
|
|
k_delayed_work_cancel(&beacon_timer);
|
|
}
|
|
}
|