/* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include "test.h" #include "adv.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_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_SETTINGS)) { bt_mesh_settings_store_pending(); } if (IS_ENABLED(CONFIG_BT_MESH_PROV)) { bt_mesh_prov_reset(); } if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION)) { bt_mesh_sol_reset(); } } bool bt_mesh_is_provisioned(void) { return atomic_test_bit(bt_mesh.flags, BT_MESH_VALID); } static void model_suspend(struct bt_mesh_model *mod, 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); return 0; } static void model_resume(struct bt_mesh_model *mod, 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; } 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); return err; } 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_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(struct bt_mesh_model *mod, 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; }