tests: bluetooth: mesh: Implement robustness test for NPPI procedures

Test Dev Key Refresh, Node Composition Refresh and Node Address Refresh
procedures for robutness.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Pavel Vasilyev 2022-10-11 15:31:58 +02:00 committed by Carles Cufí
commit 89c01e8686
3 changed files with 313 additions and 33 deletions

View file

@ -54,3 +54,6 @@ CONFIG_BT_MESH_RPR_SRV=y
CONFIG_BT_MESH_DFU_SRV=y CONFIG_BT_MESH_DFU_SRV=y
CONFIG_BT_MESH_DFU_CLI=y CONFIG_BT_MESH_DFU_CLI=y
CONFIG_BT_MESH_DFD_SRV=y CONFIG_BT_MESH_DFD_SRV=y
# Needed for RPR tests due to huge amount of retransmitted messages
CONFIG_BT_MESH_MSG_CACHE_SIZE=64

View file

@ -6,6 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "mesh_test.h" #include "mesh_test.h"
#include "mesh/access.h"
#include "mesh/net.h" #include "mesh/net.h"
#include "argparse.h" #include "argparse.h"
#include <bs_pc_backchannel.h> #include <bs_pc_backchannel.h>
@ -29,17 +30,11 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#define PROV_MULTI_COUNT 3 #define PROV_MULTI_COUNT 3
#define PROV_REPROV_COUNT 3 #define PROV_REPROV_COUNT 3
#define WAIT_TIME 80 /*seconds*/ #define WAIT_TIME 120 /*seconds*/
#define IS_RPR_PRESENT (CONFIG_BT_MESH_RPR_SRV && CONFIG_BT_MESH_RPR_CLI) #define IS_RPR_PRESENT (CONFIG_BT_MESH_RPR_SRV && CONFIG_BT_MESH_RPR_CLI)
/* Used to identify a device before provisioning it. */
#define HAS_PB_REMOTE_SERVER BIT(0)
#define UUID_FLAG_SET(uuid, flag) ((uuid)[7] |= (flag))
#define UUID_FLAG_CHECK(uuid, flag) ((uuid)[7] & (flag))
enum test_flags { enum test_flags {
IS_PROVISIONER, IS_PROVISIONER,
PROVISION_RPR_SRV,
TEST_FLAGS, TEST_FLAGS,
}; };
@ -85,9 +80,12 @@ static uint16_t prov_addr = 0x0002;
static uint16_t current_dev_addr; static uint16_t current_dev_addr;
static const uint8_t dev_key[16] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; static const uint8_t dev_key[16] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
static uint8_t dev_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0x6f }; static uint8_t dev_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0x6f };
static uint8_t *uuid_to_provision;
static struct k_sem reprov_sem;
#if IS_RPR_PRESENT #if IS_RPR_PRESENT
/* Remote Provisioning models related variables. */ /* Remote Provisioning models related variables. */
static uint8_t *uuid_to_provision_remote;
static void rpr_scan_report(struct bt_mesh_rpr_cli *cli, const struct bt_mesh_rpr_node *srv, static void rpr_scan_report(struct bt_mesh_rpr_cli *cli, const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_unprov *unprov, struct net_buf_simple *adv_data); struct bt_mesh_rpr_unprov *unprov, struct net_buf_simple *adv_data);
@ -167,8 +165,7 @@ static void unprovisioned_beacon(uint8_t uuid[16],
return; return;
} }
if (atomic_test_bit(test_flags, PROVISION_RPR_SRV) && if (uuid_to_provision && memcmp(uuid, uuid_to_provision, 16)) {
!UUID_FLAG_CHECK(uuid, HAS_PB_REMOTE_SERVER)) {
return; return;
} }
@ -190,6 +187,12 @@ static void prov_node_added(uint16_t net_idx, uint8_t uuid[16], uint16_t addr,
k_sem_give(&prov_sem); k_sem_give(&prov_sem);
} }
static void prov_reprovisioned(uint16_t addr)
{
LOG_INF("Device reprovisioned. New address: 0x%04x", addr);
k_sem_give(&reprov_sem);
}
static void prov_reset(void) static void prov_reset(void)
{ {
ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV)); ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV));
@ -251,6 +254,7 @@ static struct bt_mesh_prov prov = {
.uuid = dev_uuid, .uuid = dev_uuid,
.unprovisioned_beacon = unprovisioned_beacon, .unprovisioned_beacon = unprovisioned_beacon,
.complete = prov_complete, .complete = prov_complete,
.reprovisioned = prov_reprovisioned,
.node_added = prov_node_added, .node_added = prov_node_added,
.output_number = output_number, .output_number = output_number,
.output_string = output_string, .output_string = output_string,
@ -430,7 +434,6 @@ static void node_configure_and_reset(void)
ASSERT_EQUAL(0, status); ASSERT_EQUAL(0, status);
ASSERT_TRUE(healthpub.addr == BT_MESH_ADDR_UNASSIGNED, "Pub not cleared"); ASSERT_TRUE(healthpub.addr == BT_MESH_ADDR_UNASSIGNED, "Pub not cleared");
/* Set pub and sub to check that they are reset */ /* Set pub and sub to check that they are reset */
healthpub.addr = 0xc001; healthpub.addr = 0xc001;
healthpub.app_idx = 0; healthpub.app_idx = 0;
@ -645,13 +648,74 @@ static void test_provisioner_pb_adv_reprovision(void)
} }
#if IS_RPR_PRESENT #if IS_RPR_PRESENT
static int provision_adv(uint8_t dev_idx, uint16_t *addr)
{
static uint8_t uuid[16];
int err;
memcpy(uuid, dev_uuid, 16);
uuid[6] = '0' + dev_idx;
uuid_to_provision = uuid;
LOG_INF("Waiting for a device with RPR Server to be provisioned over PB-Adv...");
err = k_sem_take(&prov_sem, K_SECONDS(10));
*addr = current_dev_addr;
return err;
}
static int provision_remote(struct bt_mesh_rpr_node *srv, uint8_t dev_idx, uint16_t *addr)
{
static uint8_t uuid[16];
struct bt_mesh_rpr_scan_status scan_status;
int err;
memcpy(uuid, dev_uuid, 16);
uuid[6] = '0' + dev_idx;
uuid_to_provision_remote = uuid;
LOG_INF("Starting scanning for an unprov device...");
ASSERT_OK(bt_mesh_rpr_scan_start(&rpr_cli, srv, NULL, 5, 1, &scan_status));
ASSERT_EQUAL(BT_MESH_RPR_SUCCESS, scan_status.status);
ASSERT_EQUAL(BT_MESH_RPR_SCAN_MULTI, scan_status.scan);
ASSERT_EQUAL(1, scan_status.max_devs);
ASSERT_EQUAL(5, scan_status.timeout);
err = k_sem_take(&prov_sem, K_SECONDS(20));
*addr = current_dev_addr;
return err;
}
static void rpr_scan_report(struct bt_mesh_rpr_cli *cli, const struct bt_mesh_rpr_node *srv, static void rpr_scan_report(struct bt_mesh_rpr_cli *cli, const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_unprov *unprov, struct net_buf_simple *adv_data) struct bt_mesh_rpr_unprov *unprov, struct net_buf_simple *adv_data)
{ {
if (!uuid_to_provision_remote || memcmp(uuid_to_provision_remote, unprov->uuid, 16)) {
return;
}
LOG_INF("Remote device discovered. Provisioning..."); LOG_INF("Remote device discovered. Provisioning...");
ASSERT_OK(bt_mesh_provision_remote(cli, srv, unprov->uuid, 0, prov_addr)); ASSERT_OK(bt_mesh_provision_remote(cli, srv, unprov->uuid, 0, prov_addr));
} }
static void prov_node_added_rpr(uint16_t net_idx, uint8_t uuid[16], uint16_t addr,
uint8_t num_elem)
{
LOG_INF("Device 0x%04x reprovisioned", addr);
k_sem_give(&reprov_sem);
}
static void provisioner_pb_remote_client_setup(void)
{
k_sem_init(&prov_sem, 0, 1);
k_sem_init(&reprov_sem, 0, 1);
bt_mesh_device_setup(&prov, &rpr_cli_comp);
ASSERT_OK(bt_mesh_cdb_create(test_net_key));
ASSERT_OK(bt_mesh_provision(test_net_key, 0, 0, 0, 0x0001, dev_key));
}
/** @brief Verify that the provisioner can provision a device multiple times after resets using /** @brief Verify that the provisioner can provision a device multiple times after resets using
* PB-Remote and RPR models. * PB-Remote and RPR models.
*/ */
@ -659,19 +723,10 @@ static void test_provisioner_pb_remote_client_reprovision(void)
{ {
uint16_t pb_remote_server_addr; uint16_t pb_remote_server_addr;
k_sem_init(&prov_sem, 0, 1); provisioner_pb_remote_client_setup();
bt_mesh_device_setup(&prov, &rpr_cli_comp); /* Only provision the second device over PB-ADV. This device has RPR Server. */
ASSERT_OK(provision_adv(1, &pb_remote_server_addr));
atomic_set_bit(test_flags, PROVISION_RPR_SRV);
ASSERT_OK(bt_mesh_cdb_create(test_net_key));
ASSERT_OK(bt_mesh_provision(test_net_key, 0, 0, 0, 0x0001, dev_key));
LOG_INF("Waiting for a device with RPR Server to be provisioned over PB-Adv...");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(10)));
pb_remote_server_addr = current_dev_addr;
for (int i = 0; i < PROV_REPROV_COUNT; i++) { for (int i = 0; i < PROV_REPROV_COUNT; i++) {
struct bt_mesh_rpr_node srv = { struct bt_mesh_rpr_node srv = {
@ -679,17 +734,9 @@ static void test_provisioner_pb_remote_client_reprovision(void)
.net_idx = 0, .net_idx = 0,
.ttl = 3, .ttl = 3,
}; };
struct bt_mesh_rpr_scan_status scan_status;
LOG_INF("Starting scanning for an unprov device...");
ASSERT_OK(bt_mesh_rpr_scan_start(&rpr_cli, &srv, NULL, 5, 1, &scan_status));
ASSERT_EQUAL(BT_MESH_RPR_SUCCESS, scan_status.status);
ASSERT_EQUAL(BT_MESH_RPR_SCAN_MULTI, scan_status.scan);
ASSERT_EQUAL(1, scan_status.max_devs);
ASSERT_EQUAL(5, scan_status.timeout);
LOG_INF("Provisioner prov loop #%d, waiting for prov ...\n", i); LOG_INF("Provisioner prov loop #%d, waiting for prov ...\n", i);
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(20))); ASSERT_OK(provision_remote(&srv, 2, &srv.addr));
node_configure_and_reset(); node_configure_and_reset();
} }
@ -697,6 +744,131 @@ static void test_provisioner_pb_remote_client_reprovision(void)
PASS(); PASS();
} }
/** @brief Verify robustness of NPPI procedures on a RPR Client by running Device Key Refresh,
* Node Composition Refresh and Node Address Refresh procedures.
*/
static void test_provisioner_pb_remote_client_nppi_robustness(void)
{
NET_BUF_SIMPLE_DEFINE(dev_comp, BT_MESH_RX_SDU_MAX);
NET_BUF_SIMPLE_DEFINE(new_dev_comp, BT_MESH_RX_SDU_MAX);
uint8_t page;
uint16_t pb_remote_server_addr;
uint8_t status;
struct bt_mesh_cdb_node *node;
uint8_t prev_node_dev_key[16];
provisioner_pb_remote_client_setup();
/* Only provision the second device over PB-ADV. This device has RPR Server. */
ASSERT_OK(provision_adv(1, &pb_remote_server_addr));
/* Provision a remote device with RPR Server. */
struct bt_mesh_rpr_node srv = {
.addr = pb_remote_server_addr,
.net_idx = 0,
.ttl = 3,
};
ASSERT_OK(provision_remote(&srv, 2, &srv.addr));
/* Check device key by adding appkey. */
ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key, &status));
ASSERT_OK(status);
/* Swap callback to catch when device reprovisioned. */
prov.node_added = prov_node_added_rpr;
/* Store initial Composition Data Page 0. */
ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, current_dev_addr, 0, &page, &dev_comp));
node = bt_mesh_cdb_node_get(current_dev_addr);
ASSERT_TRUE(node);
memcpy(prev_node_dev_key, node->dev_key, 16);
LOG_INF("Testing DevKey refresh...");
for (int i = 0; i < PROV_REPROV_COUNT; i++) {
LOG_INF("Refreshing device key #%d...\n", i);
bt_mesh_reprovision_remote(&rpr_cli, &srv, current_dev_addr, false);
ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20)));
/* Check that CDB has updated Device Key for the node. */
ASSERT_TRUE(memcmp(prev_node_dev_key, node->dev_key, 16));
memcpy(prev_node_dev_key, node->dev_key, 16);
/* Check device key by adding appkey. */
ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key,
&status));
ASSERT_OK(status);
/* Let RPR Server verify Device Key. */
k_sleep(K_SECONDS(2));
}
LOG_INF("Testing Composition Data refresh...");
for (int i = 0; i < PROV_REPROV_COUNT; i++) {
LOG_INF("Changing Composition Data #%d...\n", i);
bt_mesh_reprovision_remote(&rpr_cli, &srv, current_dev_addr, true);
ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20)));
/* Check that CDB has updated Device Key for the node. */
ASSERT_TRUE(memcmp(prev_node_dev_key, node->dev_key, 16));
memcpy(prev_node_dev_key, node->dev_key, 16);
/* Check that Composition Data Page 128 is now Page 0. */
net_buf_simple_reset(&new_dev_comp);
ASSERT_OK(bt_mesh_cfg_cli_comp_data_get(0, current_dev_addr, 0, &page,
&new_dev_comp));
ASSERT_EQUAL(0, page);
ASSERT_EQUAL(dev_comp.len, new_dev_comp.len);
if (memcmp(dev_comp.data, new_dev_comp.data, dev_comp.len)) {
FAIL("Wrong composition data page 0");
}
/* Let RPR Server verify Device Key. */
k_sleep(K_SECONDS(2));
}
LOG_INF("Testing address refresh...");
for (int i = 0; i < PROV_REPROV_COUNT; i++) {
LOG_INF("Changing address #%d...\n", i);
bt_mesh_reprovision_remote(&rpr_cli, &srv, current_dev_addr + 1, false);
ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(20)));
current_dev_addr++;
srv.addr++;
/* Check that device doesn't respond to old address with old and new device key. */
struct bt_mesh_cdb_node *prev_node;
prev_node = bt_mesh_cdb_node_alloc((uint8_t[16]) {}, current_dev_addr - 1, 1, 0);
ASSERT_TRUE(node);
memcpy(prev_node->dev_key, prev_node_dev_key, 16);
ASSERT_EQUAL(-ETIMEDOUT, bt_mesh_cfg_cli_app_key_add(0, current_dev_addr - 1, 0, 0,
test_app_key, &status));
memcpy(prev_node->dev_key, node->dev_key, 16);
ASSERT_EQUAL(-ETIMEDOUT, bt_mesh_cfg_cli_app_key_add(0, current_dev_addr - 1, 0, 0,
test_app_key, &status));
bt_mesh_cdb_node_del(prev_node, false);
/* Check that CDB has updated Device Key for the node. */
ASSERT_TRUE(memcmp(prev_node_dev_key, node->dev_key, 16));
memcpy(prev_node_dev_key, node->dev_key, 16);
/* Check new device address by adding appkey. */
ASSERT_OK(bt_mesh_cfg_cli_app_key_add(0, current_dev_addr, 0, 0, test_app_key,
&status));
ASSERT_OK(status);
/* Let RPR Server verify Device Key. */
k_sleep(K_SECONDS(2));
}
PASS();
}
/** @brief A device running a Remote Provisioning server that can be used to provision /** @brief A device running a Remote Provisioning server that can be used to provision
* unprovisioned devices. * unprovisioned devices.
*/ */
@ -704,8 +876,6 @@ static void test_device_pb_remote_server(void)
{ {
k_sem_init(&prov_sem, 0, 1); k_sem_init(&prov_sem, 0, 1);
UUID_FLAG_SET(dev_uuid, HAS_PB_REMOTE_SERVER);
bt_mesh_device_setup(&prov, &rpr_srv_comp); bt_mesh_device_setup(&prov, &rpr_srv_comp);
ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV)); ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV));
@ -719,6 +889,89 @@ static void test_device_pb_remote_server(void)
PASS(); PASS();
} }
/** @brief Verify robustness of NPPI procedures on a RPR Server by running Device Key Refresh,
* Node Composition Refresh and Node Address Refresh procedures multiple times each.
*/
static void test_device_pb_remote_server_nppi_robustness(void)
{
uint8_t prev_dev_key[16];
k_sem_init(&prov_sem, 0, 1);
k_sem_init(&reprov_sem, 0, 1);
bt_mesh_device_setup(&prov, &rpr_srv_comp);
ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_ADV));
LOG_INF("Mesh initialized\n");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(20)));
const uint16_t initial_addr = bt_mesh_primary_addr();
memcpy(prev_dev_key, bt_mesh.dev_key, 16);
LOG_INF("Enabling PB-Remote server");
ASSERT_OK(bt_mesh_prov_enable(BT_MESH_PROV_REMOTE));
/* Test Device Key Refresh procedure robustness. */
for (int i = 0; i < PROV_REPROV_COUNT; i++) {
LOG_INF("Devkey refresh loop #%d, waiting for being reprov ...\n", i);
ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30)));
ASSERT_EQUAL(initial_addr, bt_mesh_primary_addr());
/* Let Configuration Client activate the new Device Key and verify that it has
* been changed.
*/
k_sleep(K_SECONDS(2));
ASSERT_TRUE(memcmp(prev_dev_key, bt_mesh.dev_key, 16));
memcpy(prev_dev_key, bt_mesh.dev_key, 16);
}
/* Test Node Composition Refresh procedure robustness. */
for (int i = 0; i < PROV_REPROV_COUNT; i++) {
/* The RPR Server won't let to run Node Composition Refresh procedure without first
* setting the BT_MESH_COMP_DIRTY flag. The flag is set on a boot if there is a
* "bt/mesh/cmp" entry in settings. The entry is added by the
* `bt_mesh_comp_change_prepare() call. The test suite is not compiled
* with CONFIG_BT_SETTINGS, so the flag will never be set. Since the purpose of the
* test is to check RPR Server behavior, but not the actual swap of the Composition
* Data, the flag is toggled directly from the test.
*/
atomic_set_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY);
LOG_INF("Composition data refresh loop #%d, waiting for being reprov ...\n", i);
ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30)));
/* Drop the flag manually as CONFIG_BT_SETTINGS is not enabled. */
atomic_clear_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY);
ASSERT_EQUAL(initial_addr, bt_mesh_primary_addr());
/* Let Configuration Client activate the new Device Key and verify that it has
* been changed.
*/
k_sleep(K_SECONDS(2));
ASSERT_TRUE(memcmp(prev_dev_key, bt_mesh.dev_key, 16));
memcpy(prev_dev_key, bt_mesh.dev_key, 16);
}
/* Node Address Refresh robustness. */
for (int i = 0; i < PROV_REPROV_COUNT; i++) {
LOG_INF("Address refresh loop #%d, waiting for being reprov ...\n", i);
ASSERT_OK(k_sem_take(&reprov_sem, K_SECONDS(30)));
ASSERT_EQUAL(initial_addr + 1 + i, bt_mesh_primary_addr());
/* Let Configuration Client activate the new Device Key and verify that it has
* been changed.
*/
k_sleep(K_SECONDS(2));
ASSERT_TRUE(memcmp(prev_dev_key, bt_mesh.dev_key, 16));
memcpy(prev_dev_key, bt_mesh.dev_key, 16);
}
PASS();
}
#endif /* IS_RPR_PRESENT */ #endif /* IS_RPR_PRESENT */
#define TEST_CASE(role, name, description) \ #define TEST_CASE(role, name, description) \
@ -742,6 +995,8 @@ static const struct bst_test_instance test_connect[] = {
#if IS_RPR_PRESENT #if IS_RPR_PRESENT
TEST_CASE(device, pb_remote_server, TEST_CASE(device, pb_remote_server,
"Device: pb-adv provisioning, running pb-remote server"), "Device: pb-adv provisioning, running pb-remote server"),
TEST_CASE(device, pb_remote_server_nppi_robustness,
"Device: pb-adv provisioning, running pb-remote server, NPPI robustness"),
#endif #endif
TEST_CASE(provisioner, pb_adv_no_oob, TEST_CASE(provisioner, pb_adv_no_oob,
@ -764,6 +1019,8 @@ static const struct bst_test_instance test_connect[] = {
TEST_CASE(provisioner, pb_remote_client_reprovision, TEST_CASE(provisioner, pb_remote_client_reprovision,
"Provisioner: pb-remote client provisioning, resetting and reprovisioning " "Provisioner: pb-remote client provisioning, resetting and reprovisioning "
"multiple times."), "multiple times."),
TEST_CASE(provisioner, pb_remote_client_nppi_robustness,
"Provisioner: pb-remote client provisioning, NPPI robustness testing."),
#endif #endif
BSTEST_END_MARKER BSTEST_END_MARKER

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test robustness of NPPI procedures by running them multiple times each. Test procedure:
# 1. Provisioner provisions the second device (prov_device_pb_remote_server) with RPR server through
# PB-Adv
# 2. RPR client starts to scan for the third device (prov_device_pb_remote_server_nppi_robustness)
# through RPR (5 second timeout) and provisions it
# 3. Execute device key refresh procedure 3 times for the third device.
# 4. Execute composition refresh procedure 3 times for the third device.
# 5. Execute address refresh procedure 3 times for the third device.
# (Step 3-5 is executed without sending a node reset message)
conf=prj_mesh1d1_conf
RunTest mesh_provision_pb_remote_nppi_robustness \
prov_provisioner_pb_remote_client_nppi_robustness \
prov_device_pb_remote_server \
prov_device_pb_remote_server_nppi_robustness