/* * Copyright (c) 2021 Nordic Semiconductor * * SPDX-License-Identifier: Apache-2.0 */ #include "mesh_test.h" #include #include #include "mesh/net.h" #include "mesh/app_keys.h" #include "mesh/keys.h" #include #define LOG_MODULE_NAME test_persistence #include LOG_MODULE_REGISTER(LOG_MODULE_NAME); #define WAIT_TIME 60 /*seconds*/ static bool provisioner_ready; extern const struct bt_mesh_comp comp; struct test_va_t { uint16_t addr; uint8_t uuid[16]; }; struct test_appkey_t { uint8_t idx; uint8_t key[16]; }; #define TEST_PROV_ADDR 0x0001 #define TEST_ADDR 0x0123 static uint8_t test_prov_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0xaa }; static uint8_t test_dev_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0x6f }; static int test_ividx = 0x123456; static uint8_t test_flags; static uint8_t test_netkey_idx = 0x77; static uint8_t test_netkey[16] = { 0xaa }; static uint8_t test_devkey[16] = { 0xdd }; static uint8_t test_prov_devkey[16] = { 0x11 }; #define TEST_GROUP_0 0xc001 #define TEST_GROUP_1 0xfab3 #define TEST_VA_0_ADDR 0xb6f0 #define TEST_VA_0_UUID (uint8_t[16]) { 0xca, 0xcd, 0x13, 0xbd, 0x54, 0xfe, 0x43, 0xed, \ 0x12, 0x3d, 0xa3, 0xe3, 0xb9, 0x03, 0x70, 0xaa } #define TEST_VA_1_ADDR 0x8700 #define TEST_VA_1_UUID (uint8_t[16]) { 0xdf, 0xca, 0xa3, 0x54, 0x23, 0xfa, 0x33, 0xed, \ 0x1a, 0xbe, 0xa0, 0xaa, 0xbd, 0xfa, 0x0f, 0xaf } #define TEST_VA_1_ADDR_COL 0x8700 #define TEST_VA_1_UUID_COL (uint8_t[16]) { 0x01, 0xcc, 0x74, 0x51, 0x71, 0x9e, 0x56, 0x71, \ 0x5b, 0x8a, 0x18, 0xaf, 0x13, 0x86, 0x0e, 0x4a } #define TEST_APPKEY_0_IDX 0x12 #define TEST_APPKEY_0_KEY { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, \ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f } #define TEST_APPKEY_1_IDX 0x43 #define TEST_APPKEY_1_KEY { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f } #define TEST_MOD_PUB_PARAMS { \ .addr = TEST_ADDR, \ .uuid = NULL, \ .app_idx = TEST_APPKEY_0_IDX, \ .cred_flag = true, \ .ttl = 5, \ .period = BT_MESH_PUB_PERIOD_10SEC(2), \ .transmit = BT_MESH_TRANSMIT(2, 20), \ } #define TEST_VND_MOD_PUB_PARAMS { \ .addr = TEST_VA_0_ADDR, \ .uuid = TEST_VA_0_UUID, \ .app_idx = TEST_APPKEY_1_IDX, \ .cred_flag = true, \ .ttl = 5, \ .period = BT_MESH_PUB_PERIOD_10SEC(1), \ .transmit = BT_MESH_TRANSMIT(2, 20), \ } #define TEST_MOD_PUB_PARAMS_2 { \ .addr = TEST_GROUP_1, \ .uuid = NULL, \ .app_idx = TEST_APPKEY_1_IDX, \ .cred_flag = false, \ .ttl = 3, \ .period = BT_MESH_PUB_PERIOD_10SEC(3), \ .transmit = BT_MESH_TRANSMIT(3, 20), \ } #define TEST_VND_MOD_PUB_PARAMS_2 { \ .addr = TEST_VA_1_ADDR, \ .uuid = TEST_VA_1_UUID, \ .app_idx = TEST_APPKEY_0_IDX, \ .cred_flag = false, \ .ttl = 3, \ .period = BT_MESH_PUB_PERIOD_10SEC(2), \ .transmit = BT_MESH_TRANSMIT(3, 20), \ } #define DISABLED_MOD_PUB_PARAMS { \ .addr = 0, \ .uuid = NULL, \ .app_idx = 0, \ .cred_flag = false, \ .ttl = 0, \ .period = 0, \ .transmit = 0, \ } #define TEST_MOD_DATA_NAME "tmdata" static uint8_t test_mod_data[] = { 0xfa, 0xff, 0xf4, 0x43 }; #define TEST_VND_MOD_DATA_NAME "vtmdata" static uint8_t vnd_test_mod_data[] = { 0xad, 0xdf, 0x14, 0x53, 0x54, 0x1f }; struct access_cfg { struct bt_mesh_cfg_cli_mod_pub pub_params; size_t appkeys_count; uint16_t appkeys[CONFIG_BT_MESH_MODEL_KEY_COUNT]; size_t subs_count; uint16_t subs[CONFIG_BT_MESH_MODEL_GROUP_COUNT]; size_t mod_data_len; }; static const struct access_cfg (*current_access_cfg)[2]; enum { CONFIGURED, NEW_SUBS, NOT_CONFIGURED, }; static const struct access_cfg access_cfgs[][2] = { [CONFIGURED] = { /* SIG model. */ { .pub_params = TEST_MOD_PUB_PARAMS, .appkeys_count = 2, .appkeys = { TEST_APPKEY_0_IDX, TEST_APPKEY_1_IDX }, .subs_count = 4, .subs = { TEST_GROUP_0, TEST_VA_0_ADDR, TEST_VA_1_ADDR, TEST_VA_1_ADDR /* collision */ }, .mod_data_len = sizeof(test_mod_data), }, /* Vendor model. */ { .pub_params = TEST_VND_MOD_PUB_PARAMS, .appkeys_count = 2, .appkeys = { TEST_APPKEY_0_IDX, TEST_APPKEY_1_IDX }, .subs_count = 4, .subs = { TEST_GROUP_0, TEST_VA_0_ADDR, TEST_VA_1_ADDR, TEST_VA_1_ADDR /* collision */ }, .mod_data_len = sizeof(vnd_test_mod_data), }, }, [NEW_SUBS] = { /* SIG model. */ { .pub_params = TEST_MOD_PUB_PARAMS_2, .appkeys_count = 2, .appkeys = { TEST_APPKEY_0_IDX, TEST_APPKEY_1_IDX }, .subs_count = 1, .subs = { TEST_GROUP_0 }, .mod_data_len = sizeof(test_mod_data), }, /* Vendor model. */ { .pub_params = TEST_VND_MOD_PUB_PARAMS_2, .appkeys_count = 2, .appkeys = { TEST_APPKEY_0_IDX, TEST_APPKEY_1_IDX }, .subs_count = 1, .subs = { TEST_VA_0_ADDR }, .mod_data_len = sizeof(vnd_test_mod_data), }, }, [NOT_CONFIGURED] = { /* SIG model. */ { .pub_params = DISABLED_MOD_PUB_PARAMS, .appkeys_count = 0, .appkeys = {}, .subs_count = 0, .subs = {}, .mod_data_len = 0, }, /* Vendor model. */ { .pub_params = DISABLED_MOD_PUB_PARAMS, .appkeys_count = 0, .appkeys = {}, .subs_count = 0, .subs = {}, .mod_data_len = 0, }, }, }; static const struct stack_cfg { uint8_t beacon; uint8_t ttl; uint8_t gatt_proxy; uint8_t friend; uint8_t net_transmit; struct { enum bt_mesh_feat_state state; uint8_t transmit; } relay; #ifdef CONFIG_BT_MESH_PRIV_BEACONS uint8_t priv_beacon; uint8_t priv_beacon_int; uint8_t priv_beacon_gatt; #endif #ifdef CONFIG_BT_MESH_OD_PRIV_PROXY_SRV uint8_t priv_proxy_val; #endif } stack_cfgs[] = { { .beacon = 1, .ttl = 12, .gatt_proxy = 1, .friend = 1, .net_transmit = BT_MESH_TRANSMIT(3, 20), .relay = { .state = BT_MESH_FEATURE_ENABLED, .transmit = BT_MESH_TRANSMIT(2, 20) }, #ifdef CONFIG_BT_MESH_PRIV_BEACONS .priv_beacon = 1, .priv_beacon_int = 123, .priv_beacon_gatt = 0, #endif #ifdef CONFIG_BT_MESH_OD_PRIV_PROXY_SRV .priv_proxy_val = 10, #endif }, { .beacon = 0, .ttl = 0, .gatt_proxy = 0, .friend = 0, .net_transmit = BT_MESH_TRANSMIT(1, 30), .relay = { .state = BT_MESH_FEATURE_ENABLED, .transmit = BT_MESH_TRANSMIT(1, 10) }, #ifdef CONFIG_BT_MESH_PRIV_BEACONS .priv_beacon = 1, .priv_beacon_int = 100, .priv_beacon_gatt = 1, #endif #ifdef CONFIG_BT_MESH_OD_PRIV_PROXY_SRV .priv_proxy_val = 20, #endif }, }; static const struct stack_cfg *current_stack_cfg; static void test_args_parse(int argc, char *argv[]) { char *access_cfg_str = NULL; int32_t stack_cfg = -1; bs_args_struct_t args_struct[] = { { .dest = &access_cfg_str, .type = 's', .name = "{configured, new-subs, not-configured}", .option = "access-cfg", .descript = "" }, { .dest = &stack_cfg, .type = 'i', .name = "{0, 1}", .option = "stack-cfg", .descript = "" } }; bs_args_parse_all_cmd_line(argc, argv, args_struct); if (access_cfg_str != NULL) { if (!strcmp(access_cfg_str, "configured")) { current_access_cfg = &access_cfgs[CONFIGURED]; } else if (!strcmp(access_cfg_str, "new-subs")) { current_access_cfg = &access_cfgs[NEW_SUBS]; } else if (!strcmp(access_cfg_str, "not-configured")) { current_access_cfg = &access_cfgs[NOT_CONFIGURED]; } } if (stack_cfg >= 0 && stack_cfg < ARRAY_SIZE(stack_cfgs)) { current_stack_cfg = &stack_cfgs[stack_cfg]; } } static struct k_sem prov_sem; static void prov_complete(uint16_t net_idx, uint16_t addr) { LOG_INF("Device provisioning is complete, addr: %d", addr); k_sem_give(&prov_sem); } static void device_reset(void) { LOG_INF("Device is reset"); k_sem_give(&prov_sem); } static void unprovisioned_beacon(uint8_t uuid[16], bt_mesh_prov_oob_info_t oob_info, uint32_t *uri_hash) { static bool once; /* Subnet may not be ready yet when provisioner receives a beacon. */ if (!provisioner_ready) { LOG_INF("Provisioner is not ready yet"); return; } if (once) { return; } once = !once; ASSERT_OK(bt_mesh_provision_adv(uuid, test_netkey_idx, TEST_ADDR, 0)); } static void prov_node_added(uint16_t net_idx, uint8_t uuid[16], uint16_t addr, uint8_t num_elem) { LOG_INF("Device 0x%04x provisioned", addr); k_sem_give(&prov_sem); } static void check_mod_pub_params(const struct bt_mesh_cfg_cli_mod_pub *expected, const struct bt_mesh_cfg_cli_mod_pub *got) { ASSERT_EQUAL(expected->addr, got->addr); ASSERT_EQUAL(expected->app_idx, got->app_idx); ASSERT_EQUAL(expected->cred_flag, got->cred_flag); ASSERT_EQUAL(expected->ttl, got->ttl); ASSERT_EQUAL(expected->period, got->period); ASSERT_EQUAL(expected->transmit, got->transmit); } int test_model_settings_set(const struct bt_mesh_model *model, const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg) { uint8_t data[sizeof(test_mod_data)]; ssize_t result; ASSERT_TRUE(name != NULL); if (strncmp(name, TEST_MOD_DATA_NAME, strlen(TEST_MOD_DATA_NAME))) { FAIL("Invalid entry name: [%s]", name); } settings_name_next(name, &name); ASSERT_TRUE(name == NULL); ASSERT_TRUE(current_access_cfg != NULL); result = read_cb(cb_arg, &data, sizeof(data)); ASSERT_EQUAL((*current_access_cfg)[0].mod_data_len, result); if (memcmp(data, test_mod_data, (*current_access_cfg)[0].mod_data_len)) { FAIL("Incorrect data restored"); } return 0; } void test_model_reset(const struct bt_mesh_model *model) { ASSERT_OK(bt_mesh_model_data_store(test_model, false, TEST_MOD_DATA_NAME, NULL, 0)); } int test_vnd_model_settings_set(const struct bt_mesh_model *model, const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg) { uint8_t data[sizeof(vnd_test_mod_data)]; ssize_t result; ASSERT_TRUE(name != NULL); if (strncmp(name, TEST_VND_MOD_DATA_NAME, strlen(TEST_VND_MOD_DATA_NAME))) { FAIL("Invalid entry name: %s", name); } settings_name_next(name, &name); ASSERT_TRUE(name == NULL); ASSERT_TRUE(current_access_cfg != NULL); result = read_cb(cb_arg, &data, sizeof(data)); ASSERT_EQUAL((*current_access_cfg)[1].mod_data_len, result); if (memcmp(data, vnd_test_mod_data, (*current_access_cfg)[1].mod_data_len)) { FAIL("Incorrect data restored"); } return 0; } void test_vnd_model_reset(const struct bt_mesh_model *model) { ASSERT_OK(bt_mesh_model_data_store(test_vnd_model, true, TEST_VND_MOD_DATA_NAME, NULL, 0)); } static void device_setup(void) { static struct bt_mesh_prov prov = { .uuid = test_dev_uuid, .complete = prov_complete, .reset = device_reset, }; k_sem_init(&prov_sem, 0, 1); bt_mesh_device_setup(&prov, &comp); } static int device_setup_and_self_provision(void) { device_setup(); return bt_mesh_provision(test_netkey, test_netkey_idx, test_flags, test_ividx, TEST_ADDR, test_devkey); } static void provisioner_setup(void) { static struct bt_mesh_prov prov = { .uuid = test_prov_uuid, .unprovisioned_beacon = unprovisioned_beacon, .node_added = prov_node_added, }; uint8_t primary_netkey[16] = { 0xad, 0xde, 0xfa, 0x32 }; struct bt_mesh_cdb_subnet *subnet; uint8_t status; int err; k_sem_init(&prov_sem, 0, 1); bt_mesh_device_setup(&prov, &comp); ASSERT_OK(bt_mesh_cdb_create(primary_netkey)); ASSERT_OK(bt_mesh_provision(primary_netkey, 0, test_flags, test_ividx, TEST_PROV_ADDR, test_prov_devkey)); /* Adding a subnet for test_netkey as it is not primary. */ subnet = bt_mesh_cdb_subnet_alloc(test_netkey_idx); ASSERT_TRUE(subnet != NULL); err = bt_mesh_cdb_subnet_key_import(subnet, 0, test_netkey); if (err) { FAIL("Unable to import test_netkey (err: %d)", err); } bt_mesh_cdb_subnet_store(subnet); err = bt_mesh_cfg_cli_net_key_add(0, TEST_PROV_ADDR, test_netkey_idx, test_netkey, &status); if (err || status) { FAIL("Failed to add test_netkey (err: %d, status: %d)", err, status); } provisioner_ready = true; } static void test_provisioning_data_save(void) { bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision()) { FAIL("Mesh setup failed. Settings should not be loaded."); } k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); PASS(); } static void test_provisioning_data_load(void) { /* In this test stack should boot as provisioned */ bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision() != -EALREADY) { FAIL("Device should boot up as already provisioned"); } /* verify: */ /* explicitly verify that the keys resolves for a given addr and net_idx */ struct bt_mesh_msg_ctx ctx; struct bt_mesh_net_tx tx = { .ctx = &ctx }; const struct bt_mesh_key *dkey; uint8_t aid; uint8_t net_key[16]; uint8_t dev_key[16]; tx.ctx->addr = TEST_ADDR; tx.ctx->net_idx = test_netkey_idx; tx.ctx->app_idx = BT_MESH_KEY_DEV_REMOTE; /* to resolve devkey */ int err = bt_mesh_keys_resolve(tx.ctx, &tx.sub, &dkey, &aid); if (err) { FAIL("Failed to resolve keys"); } ASSERT_OK(bt_mesh_key_export(dev_key, dkey)); LOG_HEXDUMP_INF(dev_key, sizeof(dev_key), "Exported device key:"); if (memcmp(dev_key, test_devkey, sizeof(test_devkey))) { FAIL("Resolved dev_key does not match"); } ASSERT_OK(bt_mesh_key_export(net_key, &tx.sub->keys[0].net)); LOG_HEXDUMP_INF(net_key, sizeof(net_key), "Exported network key:"); if (memcmp(net_key, test_netkey, sizeof(test_netkey))) { FAIL("Resolved raw value of the net_key does not match"); } if (tx.sub->kr_phase != ((test_flags & 1) << 1)) { FAIL("Incorrect KR phase loaded"); } /* send TTL Get to verify Tx/Rx path works with loaded config */ uint8_t ttl; err = bt_mesh_cfg_cli_ttl_get(test_netkey_idx, TEST_ADDR, &ttl); if (err) { FAIL("Failed to read ttl value"); } /* verify IV index state */ if (bt_mesh.iv_index != test_ividx || bt_mesh.ivu_duration != 0 || atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { FAIL("IV loading verification failed"); } k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); PASS(); } static void node_configure(void) { int err; uint8_t status; uint16_t va; struct bt_mesh_cfg_cli_mod_pub pub_params; /* Set Network Transmit Count state on the device greater than on provisioner to increase * probability of reception responses. */ uint8_t net_transmit; net_transmit = BT_MESH_TRANSMIT(3, 50); err = bt_mesh_cfg_cli_net_transmit_set(test_netkey_idx, TEST_ADDR, net_transmit, &status); if (err || status != net_transmit) { FAIL("Net transmit set failed (err %d, transmit %x)", err, status); } struct test_appkey_t test_appkeys[] = { { .idx = TEST_APPKEY_0_IDX, .key = TEST_APPKEY_0_KEY }, { .idx = TEST_APPKEY_1_IDX, .key = TEST_APPKEY_1_KEY }, }; for (size_t i = 0; i < ARRAY_SIZE(test_appkeys); i++) { err = bt_mesh_cfg_cli_app_key_add(test_netkey_idx, TEST_ADDR, test_netkey_idx, test_appkeys[i].idx, test_appkeys[i].key, &status); if (err || status) { FAIL("AppKey add failed (err %d, status %u, i %d)", err, status, i); } } /* SIG model. */ for (size_t i = 0; i < ARRAY_SIZE(test_appkeys); i++) { err = bt_mesh_cfg_cli_mod_app_bind(test_netkey_idx, TEST_ADDR, TEST_ADDR, test_appkeys[i].idx, TEST_MOD_ID, &status); if (err || status) { FAIL("Mod app bind failed (err %d, status %u, i %d)", err, status, i); } } err = bt_mesh_cfg_cli_mod_sub_add(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_GROUP_0, TEST_MOD_ID, &status); if (err || status) { FAIL("Mod sub add failed (err %d, status %u)", err, status); } struct { const uint8_t *uuid; uint16_t addr; } va_subs[] = { { .uuid = TEST_VA_0_UUID, .addr = TEST_VA_0_ADDR, }, { .uuid = TEST_VA_1_UUID, .addr = TEST_VA_1_ADDR, }, { .uuid = TEST_VA_1_UUID_COL, .addr = TEST_VA_1_ADDR, }, }; for (size_t i = 0; i < ARRAY_SIZE(va_subs); i++) { err = bt_mesh_cfg_cli_mod_sub_va_add(test_netkey_idx, TEST_ADDR, TEST_ADDR, va_subs[i].uuid, TEST_MOD_ID, &va, &status); if (err || status) { FAIL("Mod sub add failed (err %d, status %u)", err, status); } ASSERT_EQUAL(va_subs[i].addr, va); } memcpy(&pub_params, &(struct bt_mesh_cfg_cli_mod_pub)TEST_MOD_PUB_PARAMS, sizeof(struct bt_mesh_cfg_cli_mod_pub)); err = bt_mesh_cfg_cli_mod_pub_set(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &pub_params, &status); if (err || status) { FAIL("Mod pub set failed (err %d, status %u)", err, status); } err = bt_mesh_model_data_store(test_model, false, TEST_MOD_DATA_NAME, test_mod_data, sizeof(test_mod_data)); if (err) { FAIL("Mod data store failed (err %d)", err); } /* Vendor model. */ for (size_t i = 0; i < ARRAY_SIZE(test_appkeys); i++) { err = bt_mesh_cfg_cli_mod_app_bind_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, test_appkeys[i].idx, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &status); if (err || status) { FAIL("Mod app bind failed (err %d, status %u, i %d)", err, status, i); } } err = bt_mesh_cfg_cli_mod_sub_add_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_GROUP_0, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &status); if (err || status) { FAIL("Mod sub add failed (err %d, status %u)", err, status); } for (size_t i = 0; i < ARRAY_SIZE(va_subs); i++) { err = bt_mesh_cfg_cli_mod_sub_va_add_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, va_subs[i].uuid, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &va, &status); if (err || status) { FAIL("Mod sub add failed (err %d, status %u)", err, status); } ASSERT_EQUAL(va_subs[i].addr, va); } memcpy(&pub_params, &(struct bt_mesh_cfg_cli_mod_pub)TEST_VND_MOD_PUB_PARAMS, sizeof(struct bt_mesh_cfg_cli_mod_pub)); err = bt_mesh_cfg_cli_mod_pub_set_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &pub_params, &status); if (err || status) { FAIL("Mod pub set failed (err %d, status %u)", err, status); } err = bt_mesh_model_data_store(test_vnd_model, true, TEST_VND_MOD_DATA_NAME, vnd_test_mod_data, sizeof(vnd_test_mod_data)); if (err) { FAIL("Vnd mod data store failed (err %d)", err); return; } } static void test_access_data_save(void) { bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision()) { FAIL("Mesh setup failed. Settings should not be loaded."); } node_configure(); k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); PASS(); } static void node_configuration_check(const struct access_cfg (*cfg)[2]) { uint16_t appkeys[CONFIG_BT_MESH_MODEL_KEY_COUNT + 1]; size_t appkeys_count = ARRAY_SIZE(appkeys); uint16_t subs[CONFIG_BT_MESH_MODEL_GROUP_COUNT + 1]; size_t subs_count = ARRAY_SIZE(subs); uint8_t status; int err; for (size_t m = 0; m < 2; m++) { bool vnd = m == 1; if (!vnd) { err = bt_mesh_cfg_cli_mod_app_get(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &status, appkeys, &appkeys_count); } else { err = bt_mesh_cfg_cli_mod_app_get_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &status, appkeys, &appkeys_count); } if (err || status) { FAIL("Mod app get failed (err %d, status %u)", err, status); } ASSERT_EQUAL((*cfg)[m].appkeys_count, appkeys_count); for (size_t i = 0; i < appkeys_count; i++) { ASSERT_EQUAL((*cfg)[m].appkeys[i], appkeys[i]); } if (!vnd) { err = bt_mesh_cfg_cli_mod_sub_get(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &status, subs, &subs_count); } else { err = bt_mesh_cfg_cli_mod_sub_get_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &status, subs, &subs_count); } if (err || status) { FAIL("Mod sub get failed (err %d, status %u)", err, status); } ASSERT_EQUAL((*cfg)[m].subs_count, subs_count); for (size_t i = 0; i < subs_count; i++) { ASSERT_EQUAL((*cfg)[m].subs[i], subs[i]); } struct bt_mesh_cfg_cli_mod_pub pub_params = {}; if (!vnd) { err = bt_mesh_cfg_cli_mod_pub_get(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &pub_params, &status); } else { err = bt_mesh_cfg_cli_mod_pub_get_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &pub_params, &status); } if (err || status) { FAIL("Mod pub get failed (err %d, status %u)", err, status); } check_mod_pub_params(&(*cfg)[m].pub_params, &pub_params); } } static void test_access_data_load(void) { ASSERT_TRUE(current_access_cfg != NULL); /* In this test stack should boot as provisioned */ bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision() != -EALREADY) { FAIL("Device should boot up as already provisioned"); } node_configuration_check(current_access_cfg); PASS(); } static void test_access_sub_overwrite(void) { struct bt_mesh_cfg_cli_mod_pub pub_params; uint16_t va; uint8_t status; int err; /* In this test stack should boot as provisioned */ bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision() != -EALREADY) { FAIL("Device should boot up as already provisioned"); } err = bt_mesh_cfg_cli_mod_sub_overwrite(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_GROUP_0, TEST_MOD_ID, &status); if (err || status) { FAIL("Mod sub overwrite failed (err %d, status %u)", err, status); } memcpy(&pub_params, &(struct bt_mesh_cfg_cli_mod_pub)TEST_MOD_PUB_PARAMS_2, sizeof(struct bt_mesh_cfg_cli_mod_pub)); err = bt_mesh_cfg_cli_mod_pub_set(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &pub_params, &status); if (err || status) { FAIL("Mod pub set failed (err %d, status %u)", err, status); } /* Vendor model. */ err = bt_mesh_cfg_cli_mod_sub_va_overwrite_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VA_0_UUID, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &va, &status); if (err || status) { FAIL("Mod sub va overwrite failed (err %d, status %u)", err, status); } ASSERT_EQUAL(TEST_VA_0_ADDR, va); memcpy(&pub_params, &(struct bt_mesh_cfg_cli_mod_pub)TEST_VND_MOD_PUB_PARAMS_2, sizeof(struct bt_mesh_cfg_cli_mod_pub)); err = bt_mesh_cfg_cli_mod_pub_set_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &pub_params, &status); if (err || status) { FAIL("Mod pub set failed (err %d, status %u)", err, status); } k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); PASS(); } static void test_access_data_remove(void) { int err; uint8_t status; struct bt_mesh_cfg_cli_mod_pub pub_params; /* In this test stack should boot as provisioned */ bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision() != -EALREADY) { FAIL("Device should boot up as already provisioned"); } struct test_appkey_t test_appkeys[] = { { .idx = TEST_APPKEY_0_IDX, .key = TEST_APPKEY_0_KEY }, { .idx = TEST_APPKEY_1_IDX, .key = TEST_APPKEY_1_KEY }, }; /* SIG Model. */ for (size_t i = 0; i < ARRAY_SIZE(test_appkeys); i++) { err = bt_mesh_cfg_cli_mod_app_unbind(test_netkey_idx, TEST_ADDR, TEST_ADDR, test_appkeys[i].idx, TEST_MOD_ID, &status); if (err || status) { FAIL("Mod app bind failed (err %d, status %u, i %d)", err, status, i); } } err = bt_mesh_cfg_cli_mod_sub_del_all(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &status); if (err || status) { FAIL("Mod sub del all failed (err %d, status %u)", err, status); } memcpy(&pub_params, &(struct bt_mesh_cfg_cli_mod_pub)TEST_MOD_PUB_PARAMS, sizeof(struct bt_mesh_cfg_cli_mod_pub)); pub_params.addr = BT_MESH_ADDR_UNASSIGNED; err = bt_mesh_cfg_cli_mod_pub_set(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_MOD_ID, &pub_params, &status); if (err || status) { FAIL("Mod pub set failed (err %d, status %u)", err, status); } err = bt_mesh_model_data_store(test_model, false, TEST_MOD_DATA_NAME, NULL, 0); if (err) { FAIL("Mod data erase failed (err %d)", err); } /* Vendor model. */ for (size_t i = 0; i < ARRAY_SIZE(test_appkeys); i++) { err = bt_mesh_cfg_cli_mod_app_unbind_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, test_appkeys[i].idx, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &status); if (err || status) { FAIL("Mod app bind failed (err %d, status %u, i %d)", err, status, i); } } err = bt_mesh_cfg_cli_mod_sub_del_all_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &status); if (err || status) { FAIL("Mod sub del all failed (err %d, status %u)", err, status); } memcpy(&pub_params, &(struct bt_mesh_cfg_cli_mod_pub)TEST_VND_MOD_PUB_PARAMS, sizeof(struct bt_mesh_cfg_cli_mod_pub)); pub_params.addr = BT_MESH_ADDR_UNASSIGNED; pub_params.uuid = NULL; err = bt_mesh_cfg_cli_mod_pub_set_vnd(test_netkey_idx, TEST_ADDR, TEST_ADDR, TEST_VND_MOD_ID, TEST_VND_COMPANY_ID, &pub_params, &status); if (err || status) { FAIL("Mod pub set failed (err %d, status %u)", err, status); } err = bt_mesh_model_data_store(test_vnd_model, true, TEST_VND_MOD_DATA_NAME, NULL, 0); if (err) { FAIL("Vnd mod data erase failed (err %d)", err); return; } k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); PASS(); } static void test_cfg_save(void) { uint8_t transmit; uint8_t status; int err; ASSERT_TRUE(current_stack_cfg != NULL); bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision()) { FAIL("Mesh setup failed. Settings should not be loaded."); } err = bt_mesh_cfg_cli_beacon_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->beacon, &status); if (err || status != current_stack_cfg->beacon) { FAIL("Beacon set failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_ttl_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->ttl, &status); if (err || status != current_stack_cfg->ttl) { FAIL("TTL set failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_gatt_proxy_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->gatt_proxy, &status); if (err || status != current_stack_cfg->gatt_proxy) { FAIL("GATT Proxy set failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_friend_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->friend, &status); if (err || status != current_stack_cfg->friend) { FAIL("Friend set failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_net_transmit_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->net_transmit, &transmit); if (err || transmit != current_stack_cfg->net_transmit) { FAIL("Net transmit set failed (err %d, transmit %x)", err, transmit); } err = bt_mesh_cfg_cli_relay_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->relay.state, current_stack_cfg->relay.transmit, &status, &transmit); if (err || status != current_stack_cfg->relay.state || transmit != current_stack_cfg->relay.transmit) { FAIL("Relay set failed (err %d, status %u, transmit %x)", err, status, current_stack_cfg->relay.transmit); } #ifdef CONFIG_BT_MESH_PRIV_BEACONS struct bt_mesh_priv_beacon priv_beacon_state = { .enabled = current_stack_cfg->priv_beacon, .rand_interval = current_stack_cfg->priv_beacon_int, }; err = bt_mesh_priv_beacon_cli_set(test_netkey_idx, TEST_ADDR, &priv_beacon_state, &priv_beacon_state); if (err) { FAIL("Failed to enable Private Beacon (err %d)", err); } uint8_t priv_beacon_gatt = current_stack_cfg->priv_beacon_gatt; err = bt_mesh_priv_beacon_cli_gatt_proxy_set(test_netkey_idx, TEST_ADDR, priv_beacon_gatt, &priv_beacon_gatt); if (err) { FAIL("Failed to enable Private Beacon GATT proxy (err %d)", err); } #endif #if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) && defined(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI) uint8_t priv_proxy_val; err = bt_mesh_od_priv_proxy_cli_set(test_netkey_idx, TEST_ADDR, current_stack_cfg->priv_proxy_val, &priv_proxy_val); if (err || priv_proxy_val != current_stack_cfg->priv_proxy_val) { FAIL("Failed to set OD Private proxy (err %d, value %d)", err, priv_proxy_val); } #endif k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); PASS(); } static void test_cfg_load(void) { uint8_t transmit; uint8_t status; int err; ASSERT_TRUE(current_stack_cfg != NULL); /* In this test stack should boot as provisioned */ bt_mesh_test_cfg_set(NULL, WAIT_TIME); if (device_setup_and_self_provision() != -EALREADY) { FAIL("Device should boot up as already provisioned"); } err = bt_mesh_cfg_cli_beacon_get(test_netkey_idx, TEST_ADDR, &status); if (err || status != current_stack_cfg->beacon) { FAIL("Beacon get failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_ttl_get(test_netkey_idx, TEST_ADDR, &status); if (err || status != current_stack_cfg->ttl) { FAIL("TTL get failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_gatt_proxy_get(test_netkey_idx, TEST_ADDR, &status); if (err || status != current_stack_cfg->gatt_proxy) { FAIL("GATT Proxy get failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_friend_get(test_netkey_idx, TEST_ADDR, &status); if (err || status != current_stack_cfg->friend) { FAIL("Friend get failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_net_transmit_get(test_netkey_idx, TEST_ADDR, &status); if (err || status != current_stack_cfg->net_transmit) { FAIL("Net transmit get failed (err %d, status %u)", err, status); } err = bt_mesh_cfg_cli_relay_get(test_netkey_idx, TEST_ADDR, &status, &transmit); if (err || status != current_stack_cfg->relay.state || transmit != current_stack_cfg->relay.transmit) { FAIL("Relay get failed (err %d, state %u, transmit %x)", err, status, transmit); } #ifdef CONFIG_BT_MESH_PRIV_BEACONS struct bt_mesh_priv_beacon priv_beacon_state; uint8_t priv_beacon_gatt; err = bt_mesh_priv_beacon_cli_get(test_netkey_idx, TEST_ADDR, &priv_beacon_state); if (err || priv_beacon_state.enabled != current_stack_cfg->priv_beacon || priv_beacon_state.rand_interval != current_stack_cfg->priv_beacon_int) { FAIL("Private beacon get failed (err %d, enabled %u, interval %x)", err, priv_beacon_state.enabled, priv_beacon_state.rand_interval); } err = bt_mesh_priv_beacon_cli_gatt_proxy_get(test_netkey_idx, TEST_ADDR, &priv_beacon_gatt); if (err || priv_beacon_gatt != current_stack_cfg->priv_beacon_gatt) { FAIL("Private beacon GATT proxy get failed (err %d, enabled %u)", err, priv_beacon_gatt); } #endif #if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) && defined(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI) uint8_t priv_proxy_val; err = bt_mesh_od_priv_proxy_cli_get(test_netkey_idx, TEST_ADDR, &priv_proxy_val); if (err || priv_proxy_val != current_stack_cfg->priv_proxy_val) { FAIL("Private proxy get failed (err %d, value %u)", err, priv_proxy_val); } #endif PASS(); } static int mesh_settings_load_cb(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg, void *param) { ASSERT_TRUE(len == 0); return 0; } /** @brief Test reprovisioning with persistent storage, device side. * * Wait for being provisioned and configured, then wait for the node reset and store settings. */ static void test_reprovisioning_device(void) { bt_mesh_test_cfg_set(NULL, WAIT_TIME); device_setup(); ASSERT_FALSE(bt_mesh_is_provisioned()); ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV)); LOG_INF("Waiting for being provisioned..."); ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40))); LOG_INF("Waiting for the node reset..."); ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40))); k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT)); /* Check that all mesh settings were removed. */ settings_load_subtree_direct("bt/mesh", mesh_settings_load_cb, NULL); PASS(); } /** @brief Test reprovisioning with persistent storage, provisioner side. * * Verify that a device can clear its data from persistent storage after node reset. */ static void test_reprovisioning_provisioner(void) { int err; bool status; bt_mesh_test_cfg_set(NULL, WAIT_TIME); provisioner_setup(); LOG_INF("Provisioning a remote device..."); ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40))); /* Verify that the remote device is not configured. */ node_configuration_check(&access_cfgs[NOT_CONFIGURED]); /* Configure the remote device. */ node_configure(); /* Let the remote device store configuration. */ k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT * 2)); err = bt_mesh_cfg_cli_node_reset(test_netkey_idx, TEST_ADDR, &status); if (err || !status) { FAIL("Reset failed (err %d, status: %d)", err, status); } /* Let the remote device store configuration. */ k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT * 2)); PASS(); } #define TEST_CASE(role, name, description) \ { \ .test_id = "persistence_" #role "_" #name, \ .test_args_f = test_args_parse, \ .test_descr = description, \ .test_tick_f = bt_mesh_test_timeout, \ .test_main_f = test_##role##_##name, \ } static const struct bst_test_instance test_persistence[] = { TEST_CASE(provisioning, data_save, "Save provisioning data"), TEST_CASE(provisioning, data_load, "Load previously saved data and verify"), TEST_CASE(access, data_save, "Save access data"), TEST_CASE(access, data_load, "Load previously saved access data and verify"), TEST_CASE(access, sub_overwrite, "Overwrite Subscription List and store"), TEST_CASE(access, data_remove, "Remove stored access data"), TEST_CASE(cfg, save, "Save mesh configuration"), TEST_CASE(cfg, load, "Load previously stored mesh configuration and verify"), TEST_CASE(reprovisioning, device, "Reprovisioning test, device role"), TEST_CASE(reprovisioning, provisioner, "Reprovisioning test, provisioner role"), BSTEST_END_MARKER }; struct bst_test_list *test_persistence_install(struct bst_test_list *tests) { return bst_add_tests(tests, test_persistence); }