zephyr/subsys/bluetooth/mesh/main.c
Kyra Lengfeld b65f53dd76 Bluetooth: Mesh: enable scanner before enabling proxy gatt
Once proxy gatt is enabled, due to the nature of gatt advertisements
being continuous, the controller will reject starting the scanner after
enabling proxy gatt. As such we should start the scanner first.

Currently the order works as we are using k_work to postpone the proxy
gatt service registration, because services can't be registered between
initializing BT and loading the settings.
Once we start using the new settings priority feature, we are sure to
envoke enabling proxy gatt after loading the settings, so the delayed
enabling can and should be removed.

This preventetive fix has been tested by running relevant bsim tests
after rearranging in which order the settings of the subsystems are
loaded via `h_commit`.

Signed-off-by: Kyra Lengfeld <kyra.lengfeld@nordicsemi.no>
2024-12-19 12:37:54 +01:00

673 lines
14 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/net_buf.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/logging/log.h>
#include <common/bt_str.h>
#include "test.h"
#include "prov.h"
#include "provisioner.h"
#include "net.h"
#include "subnet.h"
#include "app_keys.h"
#include "rpl.h"
#include "cfg.h"
#include "beacon.h"
#include "lpn.h"
#include "friend.h"
#include "transport.h"
#include "heartbeat.h"
#include "access.h"
#include "foundation.h"
#include "proxy.h"
#include "pb_gatt_srv.h"
#include "settings.h"
#include "mesh.h"
#include "solicitation.h"
#include "gatt_cli.h"
#include "crypto.h"
LOG_MODULE_REGISTER(bt_mesh_main, CONFIG_BT_MESH_LOG_LEVEL);
int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
uint8_t flags, uint32_t iv_index, uint16_t addr,
const uint8_t dev_key[16])
{
struct bt_mesh_key mesh_dev_key;
struct bt_mesh_key mesh_net_key;
bool is_net_key_valid = false;
bool is_dev_key_valid = false;
int err = 0;
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_INIT)) {
return -ENODEV;
}
struct bt_mesh_cdb_subnet *subnet = NULL;
struct bt_mesh_cdb_node *node = NULL;
LOG_INF("Primary Element: 0x%04x", addr);
LOG_DBG("net_idx 0x%04x flags 0x%02x iv_index 0x%04x", net_idx, flags, iv_index);
if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EALREADY;
}
if (IS_ENABLED(CONFIG_BT_MESH_CDB) &&
atomic_test_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID)) {
const struct bt_mesh_comp *comp;
const struct bt_mesh_prov *prov;
comp = bt_mesh_comp_get();
if (comp == NULL) {
LOG_ERR("Failed to get node composition");
atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
return -EINVAL;
}
subnet = bt_mesh_cdb_subnet_get(net_idx);
if (!subnet) {
LOG_ERR("No subnet with idx %d", net_idx);
atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
return -ENOENT;
}
prov = bt_mesh_prov_get();
node = bt_mesh_cdb_node_alloc(prov->uuid, addr,
comp->elem_count, net_idx);
if (node == NULL) {
LOG_ERR("Failed to allocate database node");
atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
return -ENOMEM;
}
if (BT_MESH_KEY_REFRESH(flags)) {
subnet->kr_phase = BT_MESH_KR_PHASE_2;
} else {
subnet->kr_phase = BT_MESH_KR_NORMAL;
}
/* The primary network key has been imported during cdb creation.
* Importing here leaves it 'as is' if the key is the same.
* Otherwise, cdb replaces the old one with the new one.
*/
err = bt_mesh_cdb_subnet_key_import(subnet, BT_MESH_KEY_REFRESH(flags) ? 1 : 0,
net_key);
if (err) {
LOG_ERR("Failed to import cdb network key");
goto end;
}
bt_mesh_cdb_subnet_store(subnet);
addr = node->addr;
bt_mesh_cdb_iv_update(iv_index, BT_MESH_IV_UPDATE(flags));
err = bt_mesh_cdb_node_key_import(node, dev_key);
if (err) {
LOG_ERR("Failed to import cdb device key");
goto end;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_cdb_node_store(node);
}
}
err = bt_mesh_key_import(BT_MESH_KEY_TYPE_DEV, dev_key, &mesh_dev_key);
if (err) {
LOG_ERR("Failed to import device key");
goto end;
}
is_dev_key_valid = true;
err = bt_mesh_key_import(BT_MESH_KEY_TYPE_NET, net_key, &mesh_net_key);
if (err) {
LOG_ERR("Failed to import network key");
goto end;
}
is_net_key_valid = true;
err = bt_mesh_net_create(net_idx, flags, &mesh_net_key, iv_index);
if (err) {
atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
goto end;
}
bt_mesh_net_settings_commit();
bt_mesh.seq = 0U;
bt_mesh_comp_provision(addr);
memcpy(&bt_mesh.dev_key, &mesh_dev_key, sizeof(struct bt_mesh_key));
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
IS_ENABLED(CONFIG_BT_MESH_LPN_SUB_ALL_NODES_ADDR)) {
bt_mesh_lpn_group_add(BT_MESH_ADDR_ALL_NODES);
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_net_store();
}
bt_mesh_start();
end:
if (err && node != NULL && IS_ENABLED(CONFIG_BT_MESH_CDB)) {
bt_mesh_cdb_node_del(node, true);
}
if (err && is_dev_key_valid) {
bt_mesh_key_destroy(&mesh_dev_key);
}
if (err && is_net_key_valid) {
bt_mesh_key_destroy(&mesh_net_key);
}
return err;
}
#if defined(CONFIG_BT_MESH_RPR_SRV)
void bt_mesh_reprovision(uint16_t addr)
{
LOG_DBG("0x%04x devkey: %s", addr,
bt_hex(&bt_mesh.dev_key_cand, sizeof(struct bt_mesh_key)));
if (addr != bt_mesh_primary_addr()) {
bt_mesh.seq = 0U;
bt_mesh_comp_provision(addr);
bt_mesh_trans_reset();
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friends_clear();
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_friendship_end();
}
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
LOG_DBG("Storing network information persistently");
bt_mesh_net_store();
bt_mesh_net_seq_store(true);
bt_mesh_comp_data_clear();
}
}
void bt_mesh_dev_key_cand(const uint8_t *key)
{
int err;
LOG_DBG("%s", bt_hex(key, 16));
err = bt_mesh_key_import(BT_MESH_KEY_TYPE_DEV, key, &bt_mesh.dev_key_cand);
if (err) {
LOG_ERR("Failed to import device key candidate");
return;
}
atomic_set_bit(bt_mesh.flags, BT_MESH_DEVKEY_CAND);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_net_dev_key_cand_store();
}
}
void bt_mesh_dev_key_cand_remove(void)
{
if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_DEVKEY_CAND)) {
return;
}
LOG_DBG("");
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_net_dev_key_cand_store();
}
}
void bt_mesh_dev_key_cand_activate(void)
{
if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_DEVKEY_CAND)) {
return;
}
bt_mesh_key_destroy(&bt_mesh.dev_key);
memcpy(&bt_mesh.dev_key, &bt_mesh.dev_key_cand, sizeof(struct bt_mesh_key));
memset(&bt_mesh.dev_key_cand, 0, sizeof(struct bt_mesh_key));
LOG_DBG("");
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_net_pending_net_store();
bt_mesh_net_dev_key_cand_store();
}
}
#endif
int bt_mesh_provision_adv(const uint8_t uuid[16], uint16_t net_idx,
uint16_t addr, uint8_t attention_duration)
{
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EINVAL;
}
if (bt_mesh_subnet_get(net_idx) == NULL) {
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) {
return bt_mesh_pb_adv_open(uuid, net_idx, addr,
attention_duration);
}
return -ENOTSUP;
}
int bt_mesh_provision_gatt(const uint8_t uuid[16], uint16_t net_idx, uint16_t addr,
uint8_t attention_duration)
{
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EINVAL;
}
if (bt_mesh_subnet_get(net_idx) == NULL) {
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT_CLIENT)) {
return bt_mesh_pb_gatt_open(uuid, net_idx, addr,
attention_duration);
}
return -ENOTSUP;
}
int bt_mesh_provision_remote(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
const uint8_t uuid[16], uint16_t net_idx,
uint16_t addr)
{
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EINVAL;
}
if (bt_mesh_subnet_get(net_idx) == NULL) {
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
IS_ENABLED(CONFIG_BT_MESH_RPR_CLI)) {
return bt_mesh_pb_remote_open(cli, srv, uuid, net_idx, addr);
}
return -ENOTSUP;
}
int bt_mesh_reprovision_remote(struct bt_mesh_rpr_cli *cli,
struct bt_mesh_rpr_node *srv,
uint16_t addr, bool comp_change)
{
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
IS_ENABLED(CONFIG_BT_MESH_RPR_CLI)) {
return bt_mesh_pb_remote_open_node(cli, srv, addr, comp_change);
}
return -ENOTSUP;
}
void bt_mesh_reset(void)
{
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID) ||
!atomic_test_bit(bt_mesh.flags, BT_MESH_INIT)) {
return;
}
bt_mesh.iv_index = 0U;
bt_mesh.ivu_duration = 0;
bt_mesh.seq = 0U;
memset(bt_mesh.flags, 0, sizeof(bt_mesh.flags));
atomic_set_bit(bt_mesh.flags, BT_MESH_INIT);
bt_mesh_scan_disable();
/* If this fails, the work handler will return early on the next
* execution, as the device is not provisioned. If the device is
* reprovisioned, the timer is always restarted.
*/
(void)k_work_cancel_delayable(&bt_mesh.ivu_timer);
bt_mesh_access_reset();
bt_mesh_model_reset();
bt_mesh_cfg_default_set();
bt_mesh_trans_reset();
bt_mesh_app_keys_reset();
bt_mesh_net_keys_reset();
bt_mesh_net_loopback_clear(BT_MESH_KEY_ANY);
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
if (IS_ENABLED(CONFIG_BT_MESH_LPN_SUB_ALL_NODES_ADDR)) {
uint16_t group = BT_MESH_ADDR_ALL_NODES;
bt_mesh_lpn_group_del(&group, 1);
}
bt_mesh_lpn_disable(true);
}
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friends_clear();
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
(void)bt_mesh_proxy_gatt_disable();
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_CLIENT)) {
bt_mesh_gatt_client_deinit();
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_net_clear();
}
bt_mesh_key_destroy(&bt_mesh.dev_key);
memset(&bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));
bt_mesh_beacon_disable();
bt_mesh_comp_unprovision();
if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION)) {
bt_mesh_sol_reset();
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_settings_store_pending();
}
if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
bt_mesh_prov_reset();
}
}
bool bt_mesh_is_provisioned(void)
{
return atomic_test_bit(bt_mesh.flags, BT_MESH_VALID);
}
static void model_suspend(const struct bt_mesh_model *mod, const struct bt_mesh_elem *elem,
bool vnd, bool primary, void *user_data)
{
if (mod->pub && mod->pub->update) {
mod->pub->count = 0U;
/* If this fails, the work handler will check the suspend call
* and exit without transmitting.
*/
(void)k_work_cancel_delayable(&mod->pub->timer);
}
}
int bt_mesh_suspend(void)
{
int err;
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EINVAL;
}
if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
return -EALREADY;
}
err = bt_mesh_scan_disable();
if (err) {
atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
LOG_WRN("Disabling scanning failed (err %d)", err);
return err;
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_CLIENT)) {
bt_mesh_proxy_disconnect(BT_MESH_KEY_ANY);
}
bt_mesh_hb_suspend();
bt_mesh_beacon_disable();
bt_mesh_model_foreach(model_suspend, NULL);
bt_mesh_access_suspend();
if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
err = bt_mesh_pb_gatt_srv_disable();
if (err && err != -EALREADY) {
LOG_WRN("Disabling PB-GATT failed (err %d)", err);
return err;
}
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
err = bt_mesh_proxy_gatt_disable();
if (err && err != -EALREADY) {
LOG_WRN("Disabling GATT proxy failed (err %d)", err);
return err;
}
}
err = bt_mesh_adv_disable();
if (err) {
atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
LOG_WRN("Disabling advertisers failed (err %d)", err);
return err;
}
return 0;
}
static void model_resume(const struct bt_mesh_model *mod, const struct bt_mesh_elem *elem,
bool vnd, bool primary, void *user_data)
{
if (mod->pub && mod->pub->update) {
int32_t period_ms = bt_mesh_model_pub_period_get(mod);
if (period_ms) {
k_work_reschedule(&mod->pub->timer,
K_MSEC(period_ms));
}
}
}
int bt_mesh_resume(void)
{
int err;
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
return -EINVAL;
}
if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
return -EALREADY;
}
if (!IS_ENABLED(CONFIG_BT_EXT_ADV)) {
bt_mesh_adv_init();
}
err = bt_mesh_adv_enable();
if (err) {
atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
LOG_WRN("Re-enabling advertisers failed (err %d)", err);
return err;
}
err = bt_mesh_scan_enable();
if (err) {
LOG_WRN("Re-enabling scanning failed (err %d)", err);
atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
return err;
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && bt_mesh_is_provisioned()) {
err = bt_mesh_proxy_gatt_enable();
if (err) {
LOG_WRN("Re-enabling GATT proxy failed (err %d)", err);
return err;
}
}
if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && !bt_mesh_is_provisioned()) {
err = bt_mesh_pb_gatt_srv_enable();
if (err) {
LOG_WRN("Re-enabling PB-GATT failed (err %d)", err);
return err;
}
}
bt_mesh_hb_resume();
if (bt_mesh_beacon_enabled() ||
bt_mesh_priv_beacon_get() == BT_MESH_PRIV_BEACON_ENABLED) {
bt_mesh_beacon_enable();
}
bt_mesh_model_foreach(model_resume, NULL);
bt_mesh_adv_gatt_update();
return 0;
}
int bt_mesh_init(const struct bt_mesh_prov *prov,
const struct bt_mesh_comp *comp)
{
int err;
if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_INIT)) {
return -EALREADY;
}
err = bt_mesh_test();
if (err) {
return err;
}
err = bt_mesh_crypto_init();
if (err) {
return err;
}
err = bt_mesh_comp_register(comp);
if (err) {
return err;
}
if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
err = bt_mesh_prov_init(prov);
if (err) {
return err;
}
}
bt_mesh_cfg_default_set();
bt_mesh_net_init();
bt_mesh_trans_init();
bt_mesh_access_init();
bt_mesh_hb_init();
bt_mesh_beacon_init();
bt_mesh_adv_init();
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_settings_init();
}
return 0;
}
static void model_start(const struct bt_mesh_model *mod, const struct bt_mesh_elem *elem,
bool vnd, bool primary, void *user_data)
{
if (mod->cb && mod->cb->start) {
mod->cb->start(mod);
}
}
int bt_mesh_start(void)
{
int err;
err = bt_mesh_adv_enable();
if (err) {
LOG_ERR("Failed enabling advertiser");
return err;
}
if (bt_mesh_beacon_enabled() ||
bt_mesh_priv_beacon_get() == BT_MESH_PRIV_BEACON_ENABLED) {
bt_mesh_beacon_enable();
}
if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_scan_enable();
}
if (!IS_ENABLED(CONFIG_BT_MESH_PROV) || !bt_mesh_prov_active() ||
bt_mesh_prov_link.bearer->type == BT_MESH_PROV_ADV) {
if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
(void)bt_mesh_pb_gatt_srv_disable();
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
(void)bt_mesh_proxy_gatt_enable();
}
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_CLIENT)) {
bt_mesh_gatt_client_init();
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_init();
}
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friend_init();
}
if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
struct bt_mesh_subnet *sub = bt_mesh_subnet_next(NULL);
uint16_t addr = bt_mesh_primary_addr();
bt_mesh_prov_complete(sub->net_idx, addr);
}
bt_mesh_hb_start();
bt_mesh_model_foreach(model_start, NULL);
return 0;
}