blob: 77ca15b2d614b443c903569d8164f38a071e0c00 [file] [log] [blame]
/*
* 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;
}
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;
}
}
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;
}
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);
err = bt_mesh_adv_gatt_send();
if (err && (err != -ENOTSUP)) {
LOG_WRN("GATT send failed (err %d)", err);
return err;
}
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_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();
} else {
bt_mesh_scan_enable();
}
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;
}