diff --git a/include/bluetooth/mesh/access.h b/include/bluetooth/mesh/access.h index 1ec3d8273e7..55a6e87751d 100644 --- a/include/bluetooth/mesh/access.h +++ b/include/bluetooth/mesh/access.h @@ -31,6 +31,12 @@ extern "C" { #define BT_MESH_KEY_UNUSED 0xffff #define BT_MESH_KEY_DEV 0xfffe +#define BT_MESH_KEY_DEV_LOCAL BT_MESH_KEY_DEV +#define BT_MESH_KEY_DEV_REMOTE 0xfffd +#define BT_MESH_KEY_DEV_ANY 0xfffc + +#define BT_MESH_IS_DEV_KEY(key) (key == BT_MESH_KEY_DEV_LOCAL || \ + key == BT_MESH_KEY_DEV_REMOTE) /** Helper to define a mesh element within an array. * diff --git a/include/bluetooth/mesh/main.h b/include/bluetooth/mesh/main.h index bbaa670fd05..f86313b4bcf 100644 --- a/include/bluetooth/mesh/main.h +++ b/include/bluetooth/mesh/main.h @@ -179,6 +179,18 @@ struct bt_mesh_prov { */ void (*complete)(u16_t net_idx, u16_t addr); + /** @brief A new node has been added to the provisioning database. + * + * This callback notifies the application that provisioning has + * been successfully completed, and that a node has been assigned + * the specified NetKeyIndex and primary element address. + * + * @param net_idx NetKeyIndex given during provisioning. + * @param addr Primary element address. + * @param num_elem Number of elements that this node has. + */ + void (*node_added)(u16_t net_idx, u16_t addr, u8_t num_elem); + /** @brief Node has been reset. * * This callback notifies the application that the local node diff --git a/subsys/bluetooth/mesh/CMakeLists.txt b/subsys/bluetooth/mesh/CMakeLists.txt index 72f5adcbdf2..3a286f7854b 100644 --- a/subsys/bluetooth/mesh/CMakeLists.txt +++ b/subsys/bluetooth/mesh/CMakeLists.txt @@ -32,3 +32,6 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_HEALTH_CLI health_cli.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_SELF_TEST test.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_SHELL shell.c) + +zephyr_library_sources_ifdef(CONFIG_BT_MESH_PROVISIONER nodes.c) + diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 4814a5704f5..59d59c490dd 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -32,6 +32,26 @@ config BT_MESH_PB_ADV Enable this option to allow the device to be provisioned over the advertising bearer. +config BT_MESH_PROVISIONER + bool "Provisioner support" + select BT_ECC + select BT_MESH_PROV + default n + help + Enable this option to have support for provisioning remote devices. + +if BT_MESH_PROVISIONER + +config BT_MESH_NODE_COUNT + int "Maximum number of saved nodes per network" + default 1 + range 1 4096 + help + This option specifies how many nodes each network can at most + save in the provisioning database. + +endif # BT_MESH_PROVISIONER + if BT_CONN # Virtual option enabled whenever any Proxy protocol is needed diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index c9f2bb963f8..30a923ec55f 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -414,7 +414,9 @@ static bool model_has_key(struct bt_mesh_model *mod, u16_t key) int i; for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { - if (mod->keys[i] == key) { + if (mod->keys[i] == key || + (mod->keys[i] == BT_MESH_KEY_DEV_ANY && + BT_MESH_IS_DEV_KEY(key))) { return true; } } diff --git a/subsys/bluetooth/mesh/cfg_cli.c b/subsys/bluetooth/mesh/cfg_cli.c index afbf231568f..1fb8c91ac3f 100644 --- a/subsys/bluetooth/mesh/cfg_cli.c +++ b/subsys/bluetooth/mesh/cfg_cli.c @@ -505,8 +505,11 @@ static int cfg_cli_init(struct bt_mesh_model *model) cli = model->user_data; cli->model = model; - /* Configuration Model security is device-key based */ - model->keys[0] = BT_MESH_KEY_DEV; + /* + * Configuration Model security is device-key based and both the local + * and remote keys are allowed to access this model. + */ + model->keys[0] = BT_MESH_KEY_DEV_ANY; k_sem_init(&cli->op_sync, 0, 1); @@ -558,7 +561,7 @@ int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page, BT_MESH_MODEL_BUF_DEFINE(msg, OP_DEV_COMP_DATA_GET, 1); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -592,7 +595,7 @@ static int get_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp, BT_MESH_MODEL_BUF_DEFINE(msg, DUMMY_2_BYTE_OP, 0); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -621,7 +624,7 @@ static int set_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp, BT_MESH_MODEL_BUF_DEFINE(msg, DUMMY_2_BYTE_OP, 1); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -700,7 +703,7 @@ int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status, BT_MESH_MODEL_BUF_DEFINE(msg, OP_RELAY_GET, 0); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -733,7 +736,7 @@ int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay, BT_MESH_MODEL_BUF_DEFINE(msg, OP_RELAY_SET, 2); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -768,7 +771,7 @@ int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_KEY_ADD, 18); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -809,7 +812,7 @@ int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_ADD, 19); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -851,7 +854,7 @@ static int mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_APP_BIND, 8); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -919,7 +922,7 @@ static int mod_sub(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr, BT_MESH_MODEL_BUF_DEFINE(msg, DUMMY_2_BYTE_OP, 8); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1026,7 +1029,7 @@ static int mod_sub_va(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr, BT_MESH_MODEL_BUF_DEFINE(msg, DUMMY_2_BYTE_OP, 22); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1142,7 +1145,7 @@ static int mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_PUB_GET, 6); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1211,7 +1214,7 @@ static int mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_PUB_SET, 13); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1284,7 +1287,7 @@ int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_SUB_SET, 5); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1325,7 +1328,7 @@ int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_SUB_GET, 0); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1363,7 +1366,7 @@ int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_PUB_SET, 9); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; @@ -1406,7 +1409,7 @@ int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr, BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_PUB_GET, 0); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, - .app_idx = BT_MESH_KEY_DEV, + .app_idx = BT_MESH_KEY_DEV_REMOTE, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 0b6e9f01965..9f4a54f9373 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -3269,8 +3269,11 @@ static int cfg_srv_init(struct bt_mesh_model *model) return -EINVAL; } - /* Configuration Model security is device-key based */ - model->keys[0] = BT_MESH_KEY_DEV; + /* + * Configuration Model security is device-key based and only the local + * device-key is allowed to access this model. + */ + model->keys[0] = BT_MESH_KEY_DEV_LOCAL; if (!IS_ENABLED(CONFIG_BT_MESH_RELAY)) { cfg->relay = BT_MESH_RELAY_NOT_SUPPORTED; diff --git a/subsys/bluetooth/mesh/crypto.c b/subsys/bluetooth/mesh/crypto.c index 671fad73783..412552fe211 100644 --- a/subsys/bluetooth/mesh/crypto.c +++ b/subsys/bluetooth/mesh/crypto.c @@ -840,6 +840,12 @@ int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], return bt_mesh_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8); } +int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25], u8_t out[25 + 8]) +{ + return bt_mesh_ccm_encrypt(key, nonce, data, 25, NULL, 0, out, 8); +} + int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, const u8_t net_id[8], u32_t iv_index, u8_t auth[8]) diff --git a/subsys/bluetooth/mesh/crypto.h b/subsys/bluetooth/mesh/crypto.h index 31642dd18df..414d34e78d2 100644 --- a/subsys/bluetooth/mesh/crypto.h +++ b/subsys/bluetooth/mesh/crypto.h @@ -152,3 +152,6 @@ int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], const u8_t data[25 + 8], u8_t out[25]); + +int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25], u8_t out[25 + 8]); diff --git a/subsys/bluetooth/mesh/friend.c b/subsys/bluetooth/mesh/friend.c index e3f87111812..8ea69da456f 100644 --- a/subsys/bluetooth/mesh/friend.c +++ b/subsys/bluetooth/mesh/friend.c @@ -337,10 +337,10 @@ static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd, int err; meta->subnet = bt_mesh_subnet_get(frnd->net_idx); - meta->is_dev_key = (app_idx == BT_MESH_KEY_DEV); + meta->is_dev_key = BT_MESH_IS_DEV_KEY(app_idx); bt_mesh_net_header_parse(&buf->b, &meta->net); - err = bt_mesh_app_key_get(meta->subnet, app_idx, &meta->key, - &meta->aid); + err = bt_mesh_app_key_get(meta->subnet, app_idx, meta->net.ctx.recv_dst, + &meta->key, &meta->aid); if (err) { return err; } diff --git a/subsys/bluetooth/mesh/net.c b/subsys/bluetooth/mesh/net.c index b634fe38448..2e1d5c1a533 100644 --- a/subsys/bluetooth/mesh/net.c +++ b/subsys/bluetooth/mesh/net.c @@ -84,6 +84,13 @@ struct bt_mesh_net bt_mesh = { .net_idx = BT_MESH_KEY_UNUSED, } }, +#if defined(CONFIG_BT_MESH_PROVISIONER) + .nodes = { + [0 ... (CONFIG_BT_MESH_NODE_COUNT - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, +#endif }; static u32_t dup_cache[4]; diff --git a/subsys/bluetooth/mesh/net.h b/subsys/bluetooth/mesh/net.h index 1481ec134a9..7d4e8503e3d 100644 --- a/subsys/bluetooth/mesh/net.h +++ b/subsys/bluetooth/mesh/net.h @@ -33,6 +33,13 @@ struct bt_mesh_app_key { } keys[2]; }; +struct bt_mesh_node { + u16_t addr; + u16_t net_idx; + u8_t dev_key[16]; + u8_t num_elem; +}; + struct bt_mesh_subnet { u32_t beacon_sent; /* Timestamp of last sent beacon */ u8_t beacons_last; /* Number of beacons during last @@ -216,6 +223,7 @@ enum { BT_MESH_CFG_PENDING, BT_MESH_MOD_PENDING, BT_MESH_VA_PENDING, + BT_MESH_NODES_PENDING, /* Don't touch - intentionally last */ BT_MESH_FLAG_COUNT, @@ -248,6 +256,10 @@ struct bt_mesh_net { u8_t dev_key[16]; +#if defined(CONFIG_BT_MESH_PROVISIONER) + struct bt_mesh_node nodes[CONFIG_BT_MESH_NODE_COUNT]; +#endif + struct bt_mesh_app_key app_keys[CONFIG_BT_MESH_APP_KEY_COUNT]; struct bt_mesh_subnet sub[CONFIG_BT_MESH_SUBNET_COUNT]; diff --git a/subsys/bluetooth/mesh/nodes.c b/subsys/bluetooth/mesh/nodes.c new file mode 100644 index 00000000000..4d6f3d639bf --- /dev/null +++ b/subsys/bluetooth/mesh/nodes.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_PROV) +#define LOG_MODULE_NAME bt_mesh_node +#include "common/log.h" + +#include "mesh.h" +#include "net.h" +#include "access.h" +#include "settings.h" + +/* + * Check if an address range from addr_start for addr_start + num_elem - 1 is + * free for use. When a conflict is found, next will be set to the next address + * available after the conflicting range and -EAGAIN will be returned. + */ +static int addr_is_free(u16_t addr_start, u8_t num_elem, u16_t *next) +{ + const struct bt_mesh_comp *comp = bt_mesh_comp_get(); + u16_t addr_end = addr_start + num_elem - 1; + u16_t other_start, other_end; + int i; + + if (comp == NULL) { + return -EINVAL; + } + + if (!BT_MESH_ADDR_IS_UNICAST(addr_start) || + !BT_MESH_ADDR_IS_UNICAST(addr_end) || + num_elem == 0 || next == NULL) { + return -EINVAL; + } + + other_start = bt_mesh_primary_addr(); + other_end = other_start + comp->elem_count - 1; + + /* Compare with local element addresses */ + if (!(addr_end < other_start || addr_start > other_end)) { + *next = other_end + 1; + return -EAGAIN; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) { + struct bt_mesh_node *node = &bt_mesh.nodes[i]; + + if (node->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + other_start = node->addr; + other_end = other_start + node->num_elem - 1; + + if (!(addr_end < other_start || addr_start > other_end)) { + *next = other_end + 1; + return -EAGAIN; + } + } + + return 0; +} + +/* + * Find the lowest possible starting address that can fit num_elem elements. If + * a free address range cannot be found, BT_MESH_ADDR_UNASSIGNED will be + * returned. Otherwise the first address in the range is returned. + * + * NOTE: This is quite an ineffective algorithm as it might need to look + * through the array of nodes N+2 times. A more effective algorithm + * could be used if the nodes were stored in a sorted list. + */ +static u16_t find_lowest_free_addr(u8_t num_elem) +{ + u16_t addr = 1, next; + int err, i; + + /* + * It takes a maximum of node count + 2 to find a free address if there + * is any. +1 for our own address and +1 for making sure that the + * address range is valid. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes) + 2; ++i) { + err = addr_is_free(addr, num_elem, &next); + if (err == 0) { + break; + } else if (err != -EAGAIN) { + addr = BT_MESH_ADDR_UNASSIGNED; + break; + } + + addr = next; + } + + return addr; +} + +struct bt_mesh_node *bt_mesh_node_find(u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) { + struct bt_mesh_node *node = &bt_mesh.nodes[i]; + + if (addr >= node->addr && + addr <= node->addr + node->num_elem - 1) { + return node; + } + } + + return NULL; +} + +struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem, + u16_t net_idx) +{ + int i; + + BT_DBG(""); + + if (addr == BT_MESH_ADDR_UNASSIGNED) { + addr = find_lowest_free_addr(num_elem); + if (addr == BT_MESH_ADDR_UNASSIGNED) { + return NULL; + } + } else if (!addr_is_free(addr, num_elem, NULL)) { + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) { + struct bt_mesh_node *node = &bt_mesh.nodes[i]; + + if (node->addr == BT_MESH_ADDR_UNASSIGNED) { + node->addr = addr; + node->num_elem = num_elem; + node->net_idx = net_idx; + return node; + } + } + + return NULL; +} + +void bt_mesh_node_del(struct bt_mesh_node *node, bool store) +{ + BT_DBG("Node addr 0x%04x store %u", node->addr, store); + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_clear_node(node); + } + + node->addr = BT_MESH_ADDR_UNASSIGNED; + (void)memset(node->dev_key, 0, sizeof(node->dev_key)); +} diff --git a/subsys/bluetooth/mesh/nodes.h b/subsys/bluetooth/mesh/nodes.h new file mode 100644 index 00000000000..c1b8c6c79d5 --- /dev/null +++ b/subsys/bluetooth/mesh/nodes.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +struct bt_mesh_node *bt_mesh_node_find(u16_t addr); +struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem, + u16_t net_idx); +void bt_mesh_node_del(struct bt_mesh_node *node, bool store); diff --git a/subsys/bluetooth/mesh/prov.c b/subsys/bluetooth/mesh/prov.c index a507e6172af..b9c1e9d06d2 100644 --- a/subsys/bluetooth/mesh/prov.c +++ b/subsys/bluetooth/mesh/prov.c @@ -33,6 +33,8 @@ #include "foundation.h" #include "proxy.h" #include "prov.h" +#include "settings.h" +#include "nodes.h" /* 3 transmissions, 20ms interval */ #define PROV_XMIT BT_MESH_TRANSMIT(2, 20) @@ -108,21 +110,40 @@ #define XACT_NVAL 0xff enum { - REMOTE_PUB_KEY, /* Remote key has been received */ + WAIT_PUB_KEY, /* Waiting for local PubKey to be generated */ LINK_ACTIVE, /* Link has been opened */ + LINK_ACK_RECVD, /* Ack for link has been received */ + LINK_CLOSING, /* Link is closing down */ + SEND_PUB_KEY, /* Waiting to send PubKey */ WAIT_NUMBER, /* Waiting for number input from user */ WAIT_STRING, /* Waiting for string input from user */ NOTIFY_INPUT_COMPLETE, /* Notify that input has been completed. */ LINK_INVALID, /* Error occurred during provisioning */ + PROVISIONER, /* The link was opened as provisioner */ NUM_FLAGS, }; +#if defined(CONFIG_BT_MESH_PROVISIONER) +#define PROVISIONER_LINK 1 +#else +#define PROVISIONER_LINK 0 +#endif + +struct provisioner_link { + struct bt_mesh_node *node; + u16_t addr; + u16_t net_idx; + u8_t attention_duration; +}; + struct prov_link { ATOMIC_DEFINE(flags, NUM_FLAGS); #if defined(CONFIG_BT_MESH_PB_GATT) struct bt_conn *conn; /* GATT connection */ #endif + struct provisioner_link provisioner[PROVISIONER_LINK]; + u8_t dhkey[32]; /* Calculated DHKey */ u8_t expect; /* Next expected PDU */ @@ -177,6 +198,7 @@ struct prov_rx { #define RETRANSMIT_TIMEOUT K_MSEC(500) #define BUF_TIMEOUT K_MSEC(400) +#define CLOSING_TIMEOUT K_SECONDS(3) #define TRANSACTION_TIMEOUT K_SECONDS(30) #define PROTOCOL_TIMEOUT K_SECONDS(60) @@ -210,6 +232,11 @@ static int reset_state(void) bt_mesh_attention(NULL, 0); } + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + link.provisioner->node != NULL) { + bt_mesh_node_del(link.provisioner->node, false); + } + #if defined(CONFIG_BT_MESH_PB_GATT) if (link.conn) { bt_conn_unref(link.conn); @@ -373,7 +400,7 @@ static void send_reliable(void) } } -static int bearer_ctl_send(u8_t op, void *data, u8_t data_len) +static int bearer_ctl_send(u8_t op, const void *data, u8_t data_len) { struct net_buf *buf; @@ -411,11 +438,20 @@ static u8_t last_seg(u8_t len) static inline u8_t next_transaction_id(void) { - if (link.tx.id != 0U && link.tx.id != 0xFF) { - return ++link.tx.id; + if (atomic_test_bit(link.flags, PROVISIONER)) { + if (link.tx.id != 0x7F) { + link.tx.id++; + } else { + link.tx.id = 0; + } + } else { + if (link.tx.id != 0U && link.tx.id != 0xFF) { + link.tx.id++; + } else { + link.tx.id = 0x80; + } } - link.tx.id = 0x80; return link.tx.id; } @@ -579,10 +615,58 @@ static void prov_invite(const u8_t *data) link.expect = PROV_START; } +#if defined(CONFIG_BT_MESH_PB_ADV) +static void send_invite(void) +{ + PROV_BUF(inv, 2); + + BT_DBG(""); + + prov_buf_init(&inv, PROV_INVITE); + net_buf_simple_add_u8(&inv, link.provisioner->attention_duration); + + link.conf_inputs[0] = link.provisioner->attention_duration; + + if (prov_send(&inv)) { + BT_ERR("Failed to send invite"); + return; + } + + link.expect = PROV_CAPABILITIES; +} +#endif + +static void send_start(void) +{ + PROV_BUF(start, 6); + + BT_DBG(""); + + prov_buf_init(&start, PROV_START); + + net_buf_simple_add_u8(&start, PROV_ALG_P256); + net_buf_simple_add_u8(&start, PUB_KEY_NO_OOB); + net_buf_simple_add_u8(&start, AUTH_METHOD_NO_OOB); + memset(link.auth, 0, sizeof(link.auth)); + + net_buf_simple_add_u8(&start, 0); /* Auth Action */ + net_buf_simple_add_u8(&start, 0); /* Auth Size */ + + memcpy(&link.conf_inputs[12], &start.data[1], 5); + + if (prov_send(&start)) { + BT_ERR("Failed to send start"); + } +} + static void prov_capabilities(const u8_t *data) { u16_t algorithms, output_action, input_action; + if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + return; + } + BT_DBG("Elements: %u", data[0]); algorithms = sys_get_be16(&data[1]); @@ -599,6 +683,26 @@ static void prov_capabilities(const u8_t *data) input_action = sys_get_be16(&data[9]); BT_DBG("Input OOB Action: 0x%04x", input_action); + + if (data[0] == 0) { + BT_ERR("Invalid number of elements"); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + link.provisioner->node = bt_mesh_node_alloc(link.provisioner->addr, + data[0], + link.provisioner->net_idx); + if (link.provisioner->node == NULL) { + prov_send_fail_msg(PROV_ERR_RESOURCES); + return; + } + + memcpy(&link.conf_inputs[1], data, 11); + + atomic_set_bit(link.flags, SEND_PUB_KEY); + + send_start(); } static bt_mesh_output_action_t output_action(u8_t action) @@ -816,7 +920,11 @@ static void send_confirm(void) return; } - link.expect = PROV_RANDOM; + if (atomic_test_bit(link.flags, PROVISIONER)) { + link.expect = PROV_CONFIRM; + } else { + link.expect = PROV_RANDOM; + } } static void send_input_complete(void) @@ -860,23 +968,11 @@ int bt_mesh_input_string(const char *str) return 0; } -static void send_pub_key(const u8_t dhkey[32]) +static void send_pub_key(void) { PROV_BUF(buf, 65); const u8_t *key; - BT_DBG("%p", dhkey); - - if (!dhkey) { - BT_ERR("DHKey generation failed"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); - return; - } - - sys_memcpy_swap(link.dhkey, dhkey, 32); - - BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32)); - key = bt_pub_key_get(); if (!key) { BT_ERR("No public key available"); @@ -892,33 +988,70 @@ static void send_pub_key(const u8_t dhkey[32]) sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32); sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32); - memcpy(&link.conf_inputs[81], &buf.data[1], 64); + if (atomic_test_bit(link.flags, PROVISIONER)) { + /* PublicKeyProvisioner */ + memcpy(&link.conf_inputs[17], &buf.data[1], 64); + } else { + /* PublicKeyRemote */ + memcpy(&link.conf_inputs[81], &buf.data[1], 64); + } if (prov_send(&buf)) { BT_ERR("Failed to send Public Key"); return; } - if (atomic_test_bit(link.flags, WAIT_NUMBER) || atomic_test_bit(link.flags, WAIT_STRING)) { - link.expect = PROV_NO_PDU; /* Wait for input */ + if (atomic_test_bit(link.flags, PROVISIONER)) { + link.expect = PROV_PUB_KEY; } else { - link.expect = PROV_CONFIRM; + if (atomic_test_bit(link.flags, WAIT_NUMBER) || + atomic_test_bit(link.flags, WAIT_STRING)) { + link.expect = PROV_NO_PDU; /* Wait for input */ + } else { + link.expect = PROV_CONFIRM; + } + } +} + +static void prov_dh_key_cb(const u8_t dhkey[32]) +{ + BT_DBG("%p", dhkey); + + if (!dhkey) { + BT_ERR("DHKey generation failed"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + sys_memcpy_swap(link.dhkey, dhkey, 32); + + BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32)); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_confirm(); + } else { + send_pub_key(); } } static void prov_dh_key_gen(void) { - u8_t remote_pk[64]; + u8_t remote_pk_le[64], *remote_pk; + + if (atomic_test_bit(link.flags, PROVISIONER)) { + remote_pk = &link.conf_inputs[81]; + } else { + remote_pk = &link.conf_inputs[17]; + } /* Copy remote key in little-endian for bt_dh_key_gen(). - * X and Y halves are swapped independently. Use response - * buffer as a temporary storage location. The bt_dh_key_gen() + * X and Y halves are swapped independently. The bt_dh_key_gen() * will also take care of validating the remote public key. */ - sys_memcpy_swap(remote_pk, &link.conf_inputs[17], 32); - sys_memcpy_swap(&remote_pk[32], &link.conf_inputs[49], 32); + sys_memcpy_swap(remote_pk_le, remote_pk, 32); + sys_memcpy_swap(&remote_pk_le[32], &remote_pk[32], 32); - if (bt_dh_key_gen(remote_pk, send_pub_key)) { + if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) { BT_ERR("Failed to generate DHKey"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); } @@ -928,16 +1061,27 @@ static void prov_pub_key(const u8_t *data) { BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); - memcpy(&link.conf_inputs[17], data, 64); + if (atomic_test_bit(link.flags, PROVISIONER)) { + /* PublicKeyDevice */ + memcpy(&link.conf_inputs[81], data, 64); - if (!bt_pub_key_get()) { - /* Clear retransmit timer */ #if defined(CONFIG_BT_MESH_PB_ADV) prov_clear_tx(); #endif - atomic_set_bit(link.flags, REMOTE_PUB_KEY); - BT_WARN("Waiting for local public key"); - return; + } else { + /* PublicKeyProvisioner */ + memcpy(&link.conf_inputs[17], data, 64); + + if (!bt_pub_key_get()) { + /* Clear retransmit timer */ +#if defined(CONFIG_BT_MESH_PB_ADV) + prov_clear_tx(); +#endif + + atomic_set_bit(link.flags, WAIT_PUB_KEY); + BT_WARN("Waiting for local public key"); + return; + } } prov_dh_key_gen(); @@ -952,8 +1096,12 @@ static void pub_key_ready(const u8_t *pkey) BT_DBG("Local public key ready"); - if (atomic_test_and_clear_bit(link.flags, REMOTE_PUB_KEY)) { - prov_dh_key_gen(); + if (atomic_test_and_clear_bit(link.flags, WAIT_PUB_KEY)) { + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_pub_key(); + } else { + prov_dh_key_gen(); + } } } @@ -971,20 +1119,138 @@ static void prov_input_complete(const u8_t *data) notify_input_complete(); } -static void prov_confirm(const u8_t *data) +static void send_prov_data(void) { - BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); + PROV_BUF(pdu, 34); + struct bt_mesh_subnet *sub; + u8_t session_key[16]; + u8_t nonce[13]; + int err; - memcpy(link.conf, data, 16); + err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); + if (err) { + BT_ERR("Unable to generate session key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } - notify_input_complete(); - send_confirm(); + BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); + + err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); + if (err) { + BT_ERR("Unable to generate session nonce"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + BT_DBG("Nonce: %s", bt_hex(nonce, 13)); + + err = bt_mesh_dev_key(link.dhkey, link.prov_salt, + link.provisioner->node->dev_key); + if (err) { + BT_ERR("Unable to generate device key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + BT_DBG("DevKey: %s", bt_hex(link.provisioner->node->dev_key, 16)); + + sub = bt_mesh_subnet_get(link.provisioner->node->net_idx); + if (sub == NULL) { + BT_ERR("No subnet with net_idx %u", + link.provisioner->node->net_idx); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + prov_buf_init(&pdu, PROV_DATA); + net_buf_simple_add_mem(&pdu, sub->keys[sub->kr_flag].net, 16); + net_buf_simple_add_be16(&pdu, link.provisioner->node->net_idx); + net_buf_simple_add_u8(&pdu, bt_mesh_net_flags(sub)); + net_buf_simple_add_be32(&pdu, bt_mesh.iv_index); + net_buf_simple_add_be16(&pdu, link.provisioner->node->addr); + net_buf_simple_add(&pdu, 8); /* For MIC */ + + BT_DBG("net_idx %u, iv_index 0x%08x, addr 0x%04x", + link.provisioner->node->net_idx, bt_mesh.iv_index, + link.provisioner->node->addr); + + err = bt_mesh_prov_encrypt(session_key, nonce, &pdu.data[1], + &pdu.data[1]); + if (err) { + BT_ERR("Unable to encrypt provisioning data"); + prov_send_fail_msg(PROV_ERR_DECRYPT); + return; + } + + if (prov_send(&pdu)) { + BT_ERR("Failed to send Provisioning Data"); + return; + } + + link.expect = PROV_COMPLETE; +} + +static void prov_complete(const u8_t *data) +{ + if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + return; + } + + struct bt_mesh_node *node = link.provisioner->node; +#if defined(CONFIG_BT_MESH_PB_ADV) + u8_t reason = CLOSE_REASON_SUCCESS; +#endif + + BT_DBG("key %s, net_idx %u, num_elem %u, addr 0x%04x", + bt_hex(node->dev_key, 16), node->net_idx, node->num_elem, + node->addr); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_node(node); + } + + link.provisioner->node = NULL; + link.expect = PROV_NO_PDU; + atomic_set_bit(link.flags, LINK_CLOSING); + +#if defined(CONFIG_BT_MESH_PB_ADV) + bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); +#endif + + bt_mesh_prov_node_added(node->net_idx, node->addr, node->num_elem); + + /* + * According to mesh profile spec (5.3.1.4.3), the close message should + * be restransmitted at least three times. Retransmit the LINK_CLOSE + * message until CLOSING_TIMEOUT has elapsed instead of resetting the + * link here. + */ +} + +static void send_random(void) +{ + PROV_BUF(rnd, 17); + + prov_buf_init(&rnd, PROV_RANDOM); + net_buf_simple_add_mem(&rnd, link.rand, 16); + + if (prov_send(&rnd)) { + BT_ERR("Failed to send Provisioning Random"); + return; + } + + if (atomic_test_bit(link.flags, PROVISIONER)) { + link.expect = PROV_RANDOM; + } else { + link.expect = PROV_DATA; + } } static void prov_random(const u8_t *data) { - PROV_BUF(rnd, 17); u8_t conf_verify[16]; + const u8_t *prov_rand, *dev_rand; BT_DBG("Remote Random: %s", bt_hex(data, 16)); @@ -1002,15 +1268,15 @@ static void prov_random(const u8_t *data) return; } - prov_buf_init(&rnd, PROV_RANDOM); - net_buf_simple_add_mem(&rnd, link.rand, 16); - - if (prov_send(&rnd)) { - BT_ERR("Failed to send Provisioning Random"); - return; + if (atomic_test_bit(link.flags, PROVISIONER)) { + prov_rand = link.rand; + dev_rand = data; + } else { + prov_rand = data; + dev_rand = link.rand; } - if (bt_mesh_prov_salt(link.conf_salt, data, link.rand, + if (bt_mesh_prov_salt(link.conf_salt, prov_rand, dev_rand, link.prov_salt)) { BT_ERR("Failed to generate provisioning salt"); prov_send_fail_msg(PROV_ERR_UNEXP_ERR); @@ -1019,7 +1285,27 @@ static void prov_random(const u8_t *data) BT_DBG("ProvisioningSalt: %s", bt_hex(link.prov_salt, 16)); - link.expect = PROV_DATA; + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_bit(link.flags, PROVISIONER)) { + send_prov_data(); + } else { + send_random(); + } +} + +static void prov_confirm(const u8_t *data) +{ + BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); + + memcpy(link.conf, data, 16); + + notify_input_complete(); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_random(); + } else { + send_confirm(); + } } static inline bool is_pb_gatt(void) @@ -1119,11 +1405,6 @@ static void prov_data(const u8_t *data) } } -static void prov_complete(const u8_t *data) -{ - BT_DBG(""); -} - static void prov_failed(const u8_t *data) { BT_WARN("Error: 0x%02x", data[0]); @@ -1148,7 +1429,7 @@ static const struct { #if defined(CONFIG_BT_MESH_PB_ADV) static void prov_retransmit(struct k_work *work) { - int i; + int i, timeout; BT_DBG(""); @@ -1157,7 +1438,13 @@ static void prov_retransmit(struct k_work *work) return; } - if (k_uptime_get() - link.tx.start > TRANSACTION_TIMEOUT) { + if (atomic_test_bit(link.flags, LINK_CLOSING)) { + timeout = CLOSING_TIMEOUT; + } else { + timeout = TRANSACTION_TIMEOUT; + } + + if (k_uptime_get() - link.tx.start > timeout) { BT_WARN("Giving up transaction"); reset_adv_link(); return; @@ -1227,6 +1514,21 @@ static void link_open(struct prov_rx *rx, struct net_buf_simple *buf) static void link_ack(struct prov_rx *rx, struct net_buf_simple *buf) { BT_DBG("len %u", buf->len); + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_bit(link.flags, PROVISIONER)) { + if (atomic_test_and_set_bit(link.flags, LINK_ACK_RECVD)) { + return; + } + + prov_clear_tx(); + + if (prov->link_open) { + prov->link_open(BT_MESH_PROV_ADV); + } + + send_invite(); + } } static void link_close(struct prov_rx *rx, struct net_buf_simple *buf) @@ -1371,7 +1673,21 @@ static void gen_prov_ack(struct prov_rx *rx, struct net_buf_simple *buf) } if (rx->xact_id == link.tx.id) { - prov_clear_tx(); + /* Don't clear resending of LINK_CLOSE messages */ + if (!atomic_test_bit(link.flags, LINK_CLOSING)) { + prov_clear_tx(); + } + + /* Send the PubKey when the the Start message is ACK'ed */ + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_and_clear_bit(link.flags, SEND_PUB_KEY)) { + if (!bt_pub_key_get()) { + atomic_set_bit(link.flags, WAIT_PUB_KEY); + BT_WARN("Waiting for local public key"); + } else { + send_pub_key(); + } + } } } @@ -1478,6 +1794,31 @@ void bt_mesh_pb_adv_recv(struct net_buf_simple *buf) gen_prov_recv(&rx, buf); } + +int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, + u8_t attention_duration) +{ + BT_DBG("uuid %s", bt_hex(uuid, 16)); + + if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { + return -EBUSY; + } + + atomic_set_bit(link.flags, PROVISIONER); + + bt_rand(&link.id, sizeof(link.id)); + link.tx.id = 0x7F; + link.provisioner->addr = addr; + link.provisioner->net_idx = net_idx; + link.provisioner->attention_duration = attention_duration; + + net_buf_simple_reset(link.rx.buf); + + bearer_ctl_send(LINK_OPEN, uuid, 16); + + return 0; +} + #endif /* CONFIG_BT_MESH_PB_ADV */ #if defined(CONFIG_BT_MESH_PB_GATT) @@ -1610,6 +1951,13 @@ void bt_mesh_prov_complete(u16_t net_idx, u16_t addr) } } +void bt_mesh_prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem) +{ + if (prov->node_added) { + prov->node_added(net_idx, addr, num_elem); + } +} + void bt_mesh_prov_reset(void) { if (prov->reset) { diff --git a/subsys/bluetooth/mesh/prov.h b/subsys/bluetooth/mesh/prov.h index 30ab808768f..9b3f0096613 100644 --- a/subsys/bluetooth/mesh/prov.h +++ b/subsys/bluetooth/mesh/prov.h @@ -6,6 +6,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, + u8_t attention_duration); + void bt_mesh_pb_adv_recv(struct net_buf_simple *buf); bool bt_prov_active(void); @@ -19,4 +22,5 @@ const struct bt_mesh_prov *bt_mesh_prov_get(void); int bt_mesh_prov_init(const struct bt_mesh_prov *prov); void bt_mesh_prov_complete(u16_t net_idx, u16_t addr); +void bt_mesh_prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem); void bt_mesh_prov_reset(void); diff --git a/subsys/bluetooth/mesh/settings.c b/subsys/bluetooth/mesh/settings.c index 182330f9bf6..e0225d07c9f 100644 --- a/subsys/bluetooth/mesh/settings.c +++ b/subsys/bluetooth/mesh/settings.c @@ -32,6 +32,7 @@ #include "foundation.h" #include "proxy.h" #include "settings.h" +#include "nodes.h" /* Tracking of what storage changes are pending for App and Net Keys. We * track this in a separate array here instead of within the respective @@ -123,6 +124,24 @@ struct va_val { u8_t uuid[16]; } __packed; +/* Node storage information */ +struct node_val { + u16_t net_idx; + u8_t dev_key[16]; + u8_t num_elem; +} __packed; + +struct node_update { + u16_t addr; + bool clear; +}; + +#if defined(CONFIG_BT_MESH_PROVISIONER) +static struct node_update node_updates[CONFIG_BT_MESH_NODE_COUNT]; +#else +static struct node_update node_updates[0]; +#endif + /* We need this so we don't overwrite app-hardcoded values in case FCB * contains a history of changes but then has a NULL at the end. */ @@ -740,6 +759,58 @@ static int va_set(const char *name, size_t len_rd, } #endif +#if defined(CONFIG_BT_MESH_PROVISIONER) +static int node_set(const char *name, size_t len_rd, + settings_read_cb read_cb, void *cb_arg) +{ + struct bt_mesh_node *node; + struct node_val val; + u16_t addr; + int err; + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + addr = strtol(name, NULL, 16); + + if (len_rd == 0) { + BT_DBG("val (null)"); + BT_DBG("Deleting node 0x%04x", addr); + + node = bt_mesh_node_find(addr); + if (node) { + bt_mesh_node_del(node, false); + } + + return 0; + } + + err = mesh_x_set(read_cb, cb_arg, &val, sizeof(val)); + if (err) { + BT_ERR("Failed to set \'node\'"); + return err; + } + + node = bt_mesh_node_find(addr); + if (!node) { + node = bt_mesh_node_alloc(addr, val.num_elem, val.net_idx); + } + + if (!node) { + BT_ERR("No space for a new node"); + return -ENOMEM; + } + + memcpy(node->dev_key, &val.dev_key, 16); + + BT_DBG("Node 0x%04x recovered from storage", addr); + + return 0; +} +#endif + const struct mesh_setting { const char *name; int (*func)(const char *name, size_t len_rd, @@ -758,6 +829,9 @@ const struct mesh_setting { #if CONFIG_BT_MESH_LABEL_COUNT > 0 { "Va", va_set }, #endif +#if defined(CONFIG_BT_MESH_PROVISIONER) + { "Node", node_set }, +#endif }; static int mesh_set(const char *name, size_t len_rd, @@ -906,7 +980,8 @@ SETTINGS_STATIC_HANDLER_DEFINE(bt_mesh, "bt/mesh", NULL, mesh_set, mesh_commit, #define GENERIC_PENDING_BITS (BIT(BT_MESH_KEYS_PENDING) | \ BIT(BT_MESH_HB_PUB_PENDING) | \ BIT(BT_MESH_CFG_PENDING) | \ - BIT(BT_MESH_MOD_PENDING)) + BIT(BT_MESH_MOD_PENDING) | \ + BIT(BT_MESH_NODES_PENDING)) static void schedule_store(int flag) { @@ -1292,6 +1367,95 @@ static void store_pending_keys(void) } } +static void store_node(struct bt_mesh_node *node) +{ + struct node_val val; + char path[20]; + int err; + + val.net_idx = node->net_idx; + val.num_elem = node->num_elem; + memcpy(val.dev_key, node->dev_key, 16); + + snprintk(path, sizeof(path), "bt/mesh/Node/%x", node->addr); + + err = settings_save_one(path, &val, sizeof(val)); + if (err) { + BT_ERR("Failed to store Node %s value", log_strdup(path)); + } else { + BT_DBG("Stored Node %s value", log_strdup(path)); + } +} + +static void clear_node(u16_t addr) +{ + char path[20]; + int err; + + BT_DBG("Node 0x%04x", addr); + + snprintk(path, sizeof(path), "bt/mesh/Node/%x", addr); + err = settings_delete(path); + if (err) { + BT_ERR("Failed to clear Node 0x%04x", addr); + } else { + BT_DBG("Cleared Node 0x%04x", addr); + } +} + +static void store_pending_nodes(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(node_updates); ++i) { + struct node_update *update = &node_updates[i]; + + if (update->addr == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (update->clear) { + clear_node(update->addr); + } else { + struct bt_mesh_node *node; + + node = bt_mesh_node_find(update->addr); + if (node) { + store_node(node); + } else { + BT_WARN("Node 0x%04x not found", update->addr); + } + } + + update->addr = BT_MESH_ADDR_UNASSIGNED; + } +} + +static struct node_update *node_update_find(u16_t addr, + struct node_update **free_slot) +{ + struct node_update *match; + int i; + + match = NULL; + *free_slot = NULL; + + for (i = 0; i < ARRAY_SIZE(node_updates); i++) { + struct node_update *update = &node_updates[i]; + + if (update->addr == BT_MESH_ADDR_UNASSIGNED) { + *free_slot = update; + continue; + } + + if (update->addr == addr) { + match = update; + } + } + + return match; +} + static void encode_mod_path(struct bt_mesh_model *mod, bool vnd, const char *key, char *path, size_t path_len) { @@ -1507,6 +1671,11 @@ static void store_pending(struct k_work *work) if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_VA_PENDING)) { store_pending_va(); } + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NODES_PENDING)) { + store_pending_nodes(); + } } void bt_mesh_store_rpl(struct bt_mesh_rpl *entry) @@ -1688,11 +1857,58 @@ void bt_mesh_store_mod_pub(struct bt_mesh_model *mod) schedule_store(BT_MESH_MOD_PENDING); } + void bt_mesh_store_label(void) { schedule_store(BT_MESH_VA_PENDING); } +void bt_mesh_store_node(struct bt_mesh_node *node) +{ + struct node_update *update, *free_slot; + + BT_DBG("Node 0x%04x", node->addr); + + update = node_update_find(node->addr, &free_slot); + if (update) { + update->clear = false; + schedule_store(BT_MESH_NODES_PENDING); + return; + } + + if (!free_slot) { + store_node(node); + return; + } + + free_slot->addr = node->addr; + + schedule_store(BT_MESH_NODES_PENDING); +} + +void bt_mesh_clear_node(struct bt_mesh_node *node) +{ + struct node_update *update, *free_slot; + + BT_DBG("Node 0x%04x", node->addr); + + update = node_update_find(node->addr, &free_slot); + if (update) { + update->clear = true; + schedule_store(BT_MESH_NODES_PENDING); + return; + } + + if (!free_slot) { + clear_node(node->addr); + return; + } + + free_slot->addr = node->addr; + + schedule_store(BT_MESH_NODES_PENDING); +} + int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, const void *data, size_t data_len) { diff --git a/subsys/bluetooth/mesh/settings.h b/subsys/bluetooth/mesh/settings.h index 094a1e58757..c630814e518 100644 --- a/subsys/bluetooth/mesh/settings.h +++ b/subsys/bluetooth/mesh/settings.h @@ -16,10 +16,12 @@ void bt_mesh_store_mod_bind(struct bt_mesh_model *mod); void bt_mesh_store_mod_sub(struct bt_mesh_model *mod); void bt_mesh_store_mod_pub(struct bt_mesh_model *mod); void bt_mesh_store_label(void); +void bt_mesh_store_node(struct bt_mesh_node *node); void bt_mesh_clear_net(void); void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub); void bt_mesh_clear_app_key(struct bt_mesh_app_key *key); void bt_mesh_clear_rpl(void); +void bt_mesh_clear_node(struct bt_mesh_node *node); void bt_mesh_settings_init(void); diff --git a/subsys/bluetooth/mesh/transport.c b/subsys/bluetooth/mesh/transport.c index 6e6cad23c84..fa1ca7e7528 100644 --- a/subsys/bluetooth/mesh/transport.c +++ b/subsys/bluetooth/mesh/transport.c @@ -34,6 +34,7 @@ #include "foundation.h" #include "settings.h" #include "transport.h" +#include "nodes.h" /* The transport layer needs at least three buffers for itself to avoid * deadlocks. Ensure that there are a sufficient number of advertising @@ -131,7 +132,7 @@ static int send_unseg(struct bt_mesh_net_tx *tx, struct net_buf_simple *sdu, net_buf_reserve(buf, BT_MESH_NET_HDR_LEN); - if (tx->ctx->app_idx == BT_MESH_KEY_DEV) { + if (BT_MESH_IS_DEV_KEY(tx->ctx->app_idx)) { net_buf_add_u8(buf, UNSEG_HDR(0, 0)); } else { net_buf_add_u8(buf, UNSEG_HDR(1, tx->aid)); @@ -345,7 +346,7 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct net_buf_simple *sdu, return -EBUSY; } - if (net_tx->ctx->app_idx == BT_MESH_KEY_DEV) { + if (BT_MESH_IS_DEV_KEY(net_tx->ctx->app_idx)) { seg_hdr = SEG_HDR(0, 0); } else { seg_hdr = SEG_HDR(1, net_tx->aid); @@ -501,8 +502,8 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg, tx->ctx->app_idx, tx->ctx->addr); BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len)); - err = bt_mesh_app_key_get(tx->sub, tx->ctx->app_idx, &key, - &aid); + err = bt_mesh_app_key_get(tx->sub, tx->ctx->app_idx, + tx->ctx->addr, &key, &aid); if (err) { return err; } @@ -521,10 +522,9 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg, ad = NULL; } - err = bt_mesh_app_encrypt(key, tx->ctx->app_idx == BT_MESH_KEY_DEV, - tx->aszmic, msg, ad, tx->src, - tx->ctx->addr, bt_mesh.seq, - BT_MESH_NET_IVI_TX); + err = bt_mesh_app_encrypt(key, BT_MESH_IS_DEV_KEY(tx->ctx->app_idx), + tx->aszmic, msg, ad, tx->src, tx->ctx->addr, + bt_mesh.seq, BT_MESH_NET_IVI_TX); if (err) { return err; } @@ -644,13 +644,45 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr, rx->ctx.recv_dst, seq, BT_MESH_NET_IVI_RX(rx)); if (err) { - BT_ERR("Unable to decrypt with DevKey"); - return -EINVAL; + BT_WARN("Unable to decrypt with local DevKey"); + } else { + rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL; + bt_mesh_model_recv(rx, &sdu); + return 0; } - rx->ctx.app_idx = BT_MESH_KEY_DEV; - bt_mesh_model_recv(rx, &sdu); - return 0; + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + struct bt_mesh_node *node; + + /* + * There is no way of knowing if we should use our + * local DevKey or the remote DevKey to decrypt the + * message so we must try both. + */ + + node = bt_mesh_node_find(rx->ctx.addr); + if (node == NULL) { + BT_ERR("No node found for addr 0x%04x", + rx->ctx.addr); + return -EINVAL; + } + + net_buf_simple_reset(&sdu); + err = bt_mesh_app_decrypt(node->dev_key, true, aszmic, + buf, &sdu, ad, rx->ctx.addr, + rx->ctx.recv_dst, seq, + BT_MESH_NET_IVI_RX(rx)); + if (err) { + BT_ERR("Unable to decrypt with node DevKey"); + return -EINVAL; + } + + rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE; + bt_mesh_model_recv(rx, &sdu); + return 0; + } + + return -EINVAL; } for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { @@ -1590,14 +1622,29 @@ void bt_mesh_heartbeat_send(void) } int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, u16_t app_idx, - const u8_t **key, u8_t *aid) + u16_t addr, const u8_t **key, u8_t *aid) { struct bt_mesh_app_key *app_key; - if (app_idx == BT_MESH_KEY_DEV) { + if (app_idx == BT_MESH_KEY_DEV_LOCAL || + (app_idx == BT_MESH_KEY_DEV_REMOTE && + bt_mesh_elem_find(addr) != NULL)) { *aid = 0; *key = bt_mesh.dev_key; return 0; + } else if (app_idx == BT_MESH_KEY_DEV_REMOTE) { + if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + return -EINVAL; + } + + struct bt_mesh_node *node = bt_mesh_node_find(addr); + if (!node) { + return -EINVAL; + } + + *key = node->dev_key; + *aid = 0; + return 0; } if (!subnet) { diff --git a/subsys/bluetooth/mesh/transport.h b/subsys/bluetooth/mesh/transport.h index 1c787029c22..f21cc8f39be 100644 --- a/subsys/bluetooth/mesh/transport.h +++ b/subsys/bluetooth/mesh/transport.h @@ -99,4 +99,4 @@ void bt_mesh_rpl_clear(void); void bt_mesh_heartbeat_send(void); int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, u16_t app_idx, - const u8_t **key, u8_t *aid); + u16_t addr, const u8_t **key, u8_t *aid);