tests: bluetooth: mesh: add subnet bridge bsim tests

This commit adds the following tests:
- Simple test for bridging table
- Test bridging table state changes
- Test persistence storage of subnet bridge
- Test IV Index update with subnet bridge
- Test network key removal for subnet bridge

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Pavel Vasilyev 2024-09-10 11:32:02 +02:00 committed by Anas Nashif
commit ed051cddb9
12 changed files with 1156 additions and 10 deletions

View file

@ -23,6 +23,7 @@ if(CONFIG_SETTINGS)
src/test_blob.c
src/test_sar.c
src/test_lcd.c
src/test_brg.c
)
if(CONFIG_BT_MESH_USES_MBEDTLS_PSA)
@ -72,6 +73,7 @@ else()
src/test_op_agg.c
src/test_sar.c
src/test_cdp1.c
src/test_brg.c
)
endif()

View file

@ -11,9 +11,6 @@ CONFIG_BT_MESH_PB_GATT=y
CONFIG_BT_MESH_GATT_PROXY_ENABLED=n
CONFIG_BT_MESH_MODEL_GROUP_COUNT=4
CONFIG_BT_MESH_CDB_NODE_COUNT=3
CONFIG_BT_MESH_CDB_SUBNET_COUNT=2
CONFIG_BT_MESH_SUBNET_COUNT=2
CONFIG_BT_MESH_SEQ_STORE_RATE=1
CONFIG_BT_MESH_RPL_STORE_TIMEOUT=1
CONFIG_BT_MESH_STORE_TIMEOUT=1

View file

@ -43,6 +43,7 @@ CONFIG_BT_MESH_CDB=y
CONFIG_BT_MESH_CDB_NODE_COUNT=4
CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY=y
CONFIG_BT_MESH_MODEL_EXTENSIONS=y
CONFIG_BT_MESH_CDB_SUBNET_COUNT=3
CONFIG_BT_MESH_SUBNET_COUNT=5
CONFIG_BT_MESH_SAR_CFG_CLI=y
CONFIG_BT_MESH_SAR_CFG_SRV=y

View file

@ -15,6 +15,7 @@ extern struct bst_test_list *test_dfu_install(struct bst_test_list *test);
extern struct bst_test_list *test_blob_pst_install(struct bst_test_list *test);
extern struct bst_test_list *test_lcd_install(struct bst_test_list *test);
extern struct bst_test_list *test_sar_pst_install(struct bst_test_list *test);
extern struct bst_test_list *test_brg_install(struct bst_test_list *test);
#if (CONFIG_BT_MESH_GATT_PROXY && CONFIG_BT_MESH_PROXY_SOLICITATION)
extern struct bst_test_list *test_proxy_sol_install(struct bst_test_list *test);
#endif
@ -42,6 +43,7 @@ extern struct bst_test_list *test_blob_install(struct bst_test_list *test);
extern struct bst_test_list *test_op_agg_install(struct bst_test_list *test);
extern struct bst_test_list *test_sar_install(struct bst_test_list *test);
extern struct bst_test_list *test_cdp1_install(struct bst_test_list *test);
extern struct bst_test_list *test_brg_install(struct bst_test_list *test);
#endif
bst_test_install_t test_installers[] = {
@ -53,6 +55,7 @@ bst_test_install_t test_installers[] = {
test_blob_pst_install,
test_lcd_install,
test_sar_pst_install,
test_brg_install,
#if (CONFIG_BT_MESH_GATT_PROXY && CONFIG_BT_MESH_PROXY_SOLICITATION)
test_proxy_sol_install,
#endif
@ -80,6 +83,7 @@ bst_test_install_t test_installers[] = {
test_op_agg_install,
test_sar_install,
test_cdp1_install,
test_brg_install,
#endif
NULL
};

View file

@ -88,7 +88,7 @@ static int ra_rx(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
LOG_INF("\trssi: %d", ctx->recv_rssi);
if (ra_cb) {
ra_cb(net_buf_simple_pull_mem(buf, buf->len), buf->len);
ra_cb(buf->data, buf->len);
}
return 0;
@ -186,6 +186,10 @@ static struct bt_mesh_priv_beacon_cli priv_beacon_cli;
static struct bt_mesh_od_priv_proxy_cli priv_proxy_cli;
#endif
#if defined(CONFIG_BT_MESH_BRG_CFG_CLI)
static struct bt_mesh_brg_cfg_cli brg_cfg_cli;
#endif
static const struct bt_mesh_model models[] = {
BT_MESH_MODEL_CFG_SRV,
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
@ -205,6 +209,12 @@ static const struct bt_mesh_model models[] = {
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI)
BT_MESH_MODEL_OD_PRIV_PROXY_CLI(&priv_proxy_cli),
#endif
#if defined(CONFIG_BT_MESH_BRG_CFG_SRV)
BT_MESH_MODEL_BRG_CFG_SRV,
#endif
#if defined(CONFIG_BT_MESH_BRG_CFG_CLI)
BT_MESH_MODEL_BRG_CFG_CLI(&brg_cfg_cli),
#endif
};
const struct bt_mesh_model *test_model = &models[2];

View file

@ -0,0 +1,978 @@
/*
* Copyright (c) 2024 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*
* Subnet bridge test
*/
#include "mesh_test.h"
#include <zephyr/bluetooth/mesh.h>
#include "mesh/net.h"
#include "mesh/keys.h"
#include "bsim_args_runner.h"
#include "common/bt_str.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(test_brg, LOG_LEVEL_INF);
#define WAIT_TIME 32 /*seconds*/
#define WAIT_TIME_IVU_TEST 240 /* seconds */
#define BEACON_INTERVAL 10 /*seconds */
#define PROV_ADDR 0x0001
/* Bridge address must be less than DEVICE_ADDR_START */
#define BRIDGE_ADDR 0x0002
#define DEVICE_ADDR_START 0x0003
#define REMOTE_NODES 2
static const uint8_t prov_dev_key[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef };
static const uint8_t subnet_keys[][16] = {
{ 0xaa, 0xbb, 0xcc },
{ 0xdd, 0xee, 0xff },
{ 0x11, 0x22, 0x33 },
};
static uint8_t prov_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0xaa };
static uint8_t bridge_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0xbb };
static uint8_t dev_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0xcc };
static int test_ividx = 0x123456;
extern const struct bt_mesh_comp comp;
static bool tester_ready;
enum {
MSG_TYPE_DATA = 0,
MSG_TYPE_GET = 1,
MSG_TYPE_STATUS = 2,
};
static uint8_t recvd_msgs[10];
static uint8_t recvd_msgs_cnt;
BUILD_ASSERT((2 /* opcode */ + 1 /* type */ + 1 /* msgs cnt */ + sizeof(recvd_msgs) +
BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
"Status message does not fit into the maximum incoming SDU size.");
BUILD_ASSERT((2 /* opcode */ + 1 /* type */ + 1 /* msgs cnt */ + sizeof(recvd_msgs) +
BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX,
"Status message does not fit into the maximum outgoing SDU size.");
static K_SEM_DEFINE(status_msg_recvd_sem, 0, 1);
static K_SEM_DEFINE(prov_sem, 0, 1);
static void test_tester_init(void)
{
/* Stub */
}
static void test_bridge_init(void)
{
/* Bridge device must always be the second device. */
ASSERT_EQUAL(1, get_device_nbr());
}
static void test_device_init(void)
{
ASSERT_TRUE_MSG(get_device_nbr() >= 2, "Regular devices must be initialized after "
"tester and Bridge devices.");
/* Regular devices addresses starts from address 0x0003.*/
dev_uuid[6] = get_device_nbr() + 1;
/* Regular devices are provisioned into subnets starting with idx 1. */
dev_uuid[8] = get_device_nbr() - 1;
}
static void unprovisioned_beacon(uint8_t uuid[16], bt_mesh_prov_oob_info_t oob_info,
uint32_t *uri_hash)
{
int err;
/* Subnet may not be ready yet when tester receives a beacon. */
if (!tester_ready) {
LOG_INF("tester is not ready yet");
return;
}
LOG_INF("Received unprovisioned beacon, uuid %s", bt_hex(uuid, 16));
if (!memcmp(uuid, bridge_uuid, 16)) {
err = bt_mesh_provision_adv(uuid, 0, BRIDGE_ADDR, 0);
if (!err) {
LOG_INF("Provisioning bridge at address 0x%04x", BRIDGE_ADDR);
}
return;
}
/* UUID[6] - address to be used for provisioning.
* UUID[8] - subnet to be used for provisioning.
*/
uint16_t addr = uuid[6];
int subnet_idx = uuid[8];
err = bt_mesh_provision_adv(uuid, subnet_idx, addr, 0);
if (!err) {
LOG_INF("Provisioning device at address 0x%04x with NetKeyIdx 0x%04x", addr,
subnet_idx);
}
}
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, NetKeyIdx 0x%04x", addr, net_idx);
k_sem_give(&prov_sem);
}
static struct bt_mesh_prov tester_prov = {
.uuid = prov_uuid,
.unprovisioned_beacon = unprovisioned_beacon,
.node_added = prov_node_added
};
static void prov_complete(uint16_t net_idx, uint16_t addr)
{
LOG_INF("Device 0x%04x provisioning is complete, NetKeyIdx 0x%04x", addr, net_idx);
k_sem_give(&prov_sem);
}
static struct bt_mesh_prov device_prov = {
.uuid = dev_uuid,
.complete = prov_complete,
};
static struct bt_mesh_prov bridge_prov = {
.uuid = bridge_uuid,
.complete = prov_complete,
};
static void tester_setup(void)
{
uint8_t status;
int err;
ASSERT_OK(bt_mesh_cdb_create(test_net_key));
ASSERT_OK(bt_mesh_provision(test_net_key, 0, 0, test_ividx, PROV_ADDR, prov_dev_key));
err = bt_mesh_cfg_cli_app_key_add(0, PROV_ADDR, 0, 0, test_app_key, &status);
if (err || status) {
FAIL("AppKey add failed (err %d, status %u)", err, status);
return;
}
err = bt_mesh_cfg_cli_mod_app_bind(0, PROV_ADDR, PROV_ADDR, 0, TEST_MOD_ID, &status);
if (err || status) {
FAIL("Mod app bind failed (err %d, status %u)", err, status);
return;
}
for (int i = 0; i < REMOTE_NODES; i++) {
LOG_INF("Creating subnet idx %d", i);
ASSERT_OK(bt_mesh_cfg_cli_net_key_add(0, PROV_ADDR, i + 1, subnet_keys[i],
&status));
if (status) {
FAIL("NetKey add failed (status %u)", status);
return;
}
struct bt_mesh_cdb_subnet *subnet = bt_mesh_cdb_subnet_alloc(i + 1);
ASSERT_TRUE(subnet != NULL);
ASSERT_OK(bt_mesh_cdb_subnet_key_import(subnet, 0, subnet_keys[i]));
bt_mesh_cdb_subnet_store(subnet);
}
uint8_t transmit;
ASSERT_OK(bt_mesh_cfg_cli_relay_set(0, PROV_ADDR, BT_MESH_RELAY_DISABLED,
BT_MESH_TRANSMIT(2, 20), &status, &transmit));
if (status) {
FAIL("Relay set failed (status %u)", status);
return;
}
tester_ready = true;
}
static void bridge_entry_add(uint16_t src, uint16_t dst, uint16_t net_idx1, uint16_t net_idx2,
uint8_t dir)
{
struct bt_mesh_bridging_table_entry entry;
struct bt_mesh_bridging_table_status rsp;
int err;
entry.directions = dir;
entry.net_idx1 = net_idx1;
entry.net_idx2 = net_idx2;
entry.addr1 = src;
entry.addr2 = dst;
err = bt_mesh_brg_cfg_cli_bridging_table_add(0, BRIDGE_ADDR, &entry, &rsp);
if (err || rsp.status ||
rsp.entry.directions != dir ||
rsp.entry.net_idx1 != net_idx1 || rsp.entry.net_idx2 != net_idx2 ||
rsp.entry.addr1 != src || rsp.entry.addr2 != dst) {
FAIL("Bridging table add failed (err %d) (status %u)", err, rsp.status);
return;
}
}
static void bridge_entry_remove(uint16_t src, uint16_t dst, uint16_t net_idx1, uint16_t net_idx2)
{
struct bt_mesh_bridging_table_status rsp;
ASSERT_OK(bt_mesh_brg_cfg_cli_bridging_table_remove(0, BRIDGE_ADDR, net_idx1, net_idx2,
src, dst, &rsp));
if (rsp.status) {
FAIL("Bridging table remove failed (status %u)", rsp.status);
return;
}
}
static void tester_bridge_configure(int subnets)
{
uint8_t status;
int err;
LOG_INF("Configuring bridge...");
for (int i = 0; i < subnets; i++) {
err = bt_mesh_cfg_cli_net_key_add(0, BRIDGE_ADDR, i + 1, subnet_keys[i], &status);
if (err || status) {
FAIL("NetKey add failed (err %d, status %u)", err, status);
return;
}
}
ASSERT_OK(bt_mesh_brg_cfg_cli_subnet_bridge_set(0, BRIDGE_ADDR,
BT_MESH_SUBNET_BRIDGE_ENABLED, &status));
if (status != BT_MESH_SUBNET_BRIDGE_ENABLED) {
FAIL("Subnet bridge set failed (status %u)", status);
return;
}
/* Disable Relay feature to avoid interference in the test. */
uint8_t transmit;
ASSERT_OK(bt_mesh_cfg_cli_relay_set(0, BRIDGE_ADDR, BT_MESH_RELAY_DISABLED,
BT_MESH_TRANSMIT(2, 20), &status, &transmit));
if (status) {
FAIL("Relay set failed (status %u)", status);
return;
}
LOG_INF("Bridge configured");
}
static void tester_device_configure(uint16_t net_key_idx, uint16_t addr)
{
int err;
uint8_t status;
err = bt_mesh_cfg_cli_app_key_add(net_key_idx, addr, net_key_idx, 0, test_app_key, &status);
if (err || status) {
FAIL("AppKey add failed (err %d, status %u)", err, status);
return;
}
err = bt_mesh_cfg_cli_mod_app_bind(net_key_idx, addr, addr, 0, TEST_MOD_ID, &status);
if (err || status) {
FAIL("Mod app bind failed (err %d, status %u)", err, status);
return;
}
/* Disable SNB on devices to let Subnet Bridge propagate new IV index value. */
err = bt_mesh_cfg_cli_beacon_set(net_key_idx, addr, BT_MESH_BEACON_DISABLED, &status);
if (err || status) {
FAIL("Beacon set failed (err %d, status %u)", err, status);
return;
}
LOG_INF("Device 0x%04x configured", addr);
}
static void tester_ra_cb(uint8_t *data, size_t length)
{
uint8_t type = data[0];
LOG_HEXDUMP_DBG(data, length, "tester received message");
ASSERT_TRUE_MSG(length > 1, "Too short message");
ASSERT_EQUAL(type, MSG_TYPE_STATUS);
recvd_msgs_cnt = data[1];
ASSERT_EQUAL(recvd_msgs_cnt * sizeof(recvd_msgs[0]), length - 2);
memcpy(recvd_msgs, &data[2], recvd_msgs_cnt * sizeof(recvd_msgs[0]));
k_sem_give(&status_msg_recvd_sem);
}
static int send_data(uint16_t dst, uint8_t payload)
{
uint8_t data[2] = {MSG_TYPE_DATA, payload};
return bt_mesh_test_send_ra(dst, data, sizeof(data), NULL, NULL);
}
static int send_get(uint16_t dst)
{
uint8_t data[1] = {MSG_TYPE_GET};
return bt_mesh_test_send_ra(dst, data, sizeof(data), NULL, NULL);
}
struct bridged_addresses_entry {
uint16_t addr1;
uint16_t addr2;
uint8_t dir;
};
static void bridge_table_verify(uint16_t net_idx1, uint16_t net_idx2, uint16_t start_idx,
struct bridged_addresses_entry *list, size_t list_len)
{
struct bt_mesh_bridging_table_list rsp = {
.list = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX),
};
net_buf_simple_init(rsp.list, 0);
ASSERT_OK(bt_mesh_brg_cfg_cli_bridging_table_get(0, BRIDGE_ADDR, net_idx1,
net_idx2, start_idx,
&rsp));
ASSERT_EQUAL(rsp.status, 0);
ASSERT_EQUAL(rsp.net_idx1, net_idx1);
ASSERT_EQUAL(rsp.net_idx2, net_idx2);
ASSERT_EQUAL(rsp.start_idx, start_idx);
LOG_HEXDUMP_DBG(rsp.list->data, rsp.list->len, "Received table");
ASSERT_EQUAL(rsp.list->len / 5, list_len);
ASSERT_EQUAL(rsp.list->len % 5, 0);
for (int i = 0; i < list_len; i++) {
struct bridged_addresses_entry entry;
entry.addr1 = net_buf_simple_pull_le16(rsp.list);
entry.addr2 = net_buf_simple_pull_le16(rsp.list);
entry.dir = net_buf_simple_pull_u8(rsp.list);
ASSERT_EQUAL(entry.addr1, list[i].addr1);
ASSERT_EQUAL(entry.addr2, list[i].addr2);
ASSERT_EQUAL(entry.dir, list[i].dir);
}
}
static void device_ra_cb(uint8_t *data, size_t length)
{
uint8_t type = data[0];
LOG_HEXDUMP_DBG(data, length, "Device received message");
switch (type) {
case MSG_TYPE_DATA:
ASSERT_EQUAL(2, length);
ASSERT_TRUE_MSG(recvd_msgs_cnt < ARRAY_SIZE(recvd_msgs), "Too many messages");
recvd_msgs[recvd_msgs_cnt] = data[1];
recvd_msgs_cnt++;
break;
case MSG_TYPE_GET: {
uint8_t test_data[1 /*type */ + 1 /* msgs cnt */ + sizeof(recvd_msgs)] = {
MSG_TYPE_STATUS,
recvd_msgs_cnt
};
memcpy(&test_data[2], recvd_msgs, recvd_msgs_cnt * sizeof(recvd_msgs[0]));
ASSERT_OK(bt_mesh_test_send_ra(PROV_ADDR, test_data,
2 + recvd_msgs_cnt * sizeof(recvd_msgs[0]), NULL,
NULL));
memset(recvd_msgs, 0, sizeof(recvd_msgs));
recvd_msgs_cnt = 0;
break;
}
case MSG_TYPE_STATUS:
ASSERT_TRUE_MSG(false, "Unexpected message");
break;
}
}
/**
* This is a workaround that removes secondary subnets from the tester to avoid message cache
* hit when the devices send STATUS message encrypted with the subnet key known by the tester,
* but with different app key pair (app key is the same, but net key <-> app key pair is different).
*/
static void tester_workaround(void)
{
uint8_t status;
int err;
LOG_INF("Applying subnet's workaround for tester...");
for (int i = 0; i < REMOTE_NODES; i++) {
err = bt_mesh_cfg_cli_net_key_del(0, PROV_ADDR, i + 1, &status);
if (err || status) {
FAIL("NetKey del failed (err %d, status %u)", err, status);
return;
}
}
}
static void send_and_receive(void)
{
const int msgs_cnt = 3;
LOG_INF("Sending data...");
for (int i = 0; i < REMOTE_NODES; i++) {
uint8_t payload = i | i << 4;
for (int j = 0; j < msgs_cnt; j++) {
ASSERT_OK(send_data(DEVICE_ADDR_START + i, payload + j));
}
}
LOG_INF("Checking data...");
for (int i = 0; i < REMOTE_NODES; i++) {
uint8_t payload = i | i << 4;
ASSERT_OK(send_get(DEVICE_ADDR_START + i));
ASSERT_OK(k_sem_take(&status_msg_recvd_sem, K_SECONDS(5)));
ASSERT_EQUAL(recvd_msgs_cnt, msgs_cnt);
for (int j = 0; j < recvd_msgs_cnt; j++) {
ASSERT_EQUAL(recvd_msgs[j], payload + j);
}
}
}
static void test_tester_simple(void)
{
uint8_t status;
int err;
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&tester_prov, &comp);
tester_setup();
for (int i = 0; i < 1 /* bridge */ + REMOTE_NODES; i++) {
LOG_INF("Waiting for a device to provision...");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)));
}
tester_bridge_configure(REMOTE_NODES);
/* Adding devices to bridge table */
for (int i = 0; i < REMOTE_NODES; i++) {
bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START + i, 0, i + 1,
BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY);
}
for (int i = 0; i < REMOTE_NODES; i++) {
tester_device_configure(i + 1, DEVICE_ADDR_START + i);
}
tester_workaround();
bt_mesh_test_ra_cb_setup(tester_ra_cb);
LOG_INF("Step 1: Checking bridging table...");
send_and_receive();
LOG_INF("Step 2: Disabling bridging...");
err = bt_mesh_brg_cfg_cli_subnet_bridge_set(0, BRIDGE_ADDR, BT_MESH_SUBNET_BRIDGE_DISABLED,
&status);
if (err || status != BT_MESH_SUBNET_BRIDGE_DISABLED) {
FAIL("Subnet bridge set failed (err %d) (status %u)", err, status);
return;
}
LOG_INF("Sending data...");
for (int i = 0; i < REMOTE_NODES; i++) {
uint8_t payload = i | i << 4;
for (int j = 0; j < 3; j++) {
ASSERT_OK(send_data(DEVICE_ADDR_START + i, payload + j));
}
}
LOG_INF("Step3: Enabling bridging...");
err = bt_mesh_brg_cfg_cli_subnet_bridge_set(0, BRIDGE_ADDR, BT_MESH_SUBNET_BRIDGE_ENABLED,
&status);
if (err || status != BT_MESH_SUBNET_BRIDGE_ENABLED) {
FAIL("Subnet bridge set failed (err %d) (status %u)", err, status);
return;
}
LOG_INF("Checking data...");
for (int i = 0; i < REMOTE_NODES; i++) {
ASSERT_OK(send_get(DEVICE_ADDR_START + i));
ASSERT_OK(k_sem_take(&status_msg_recvd_sem, K_SECONDS(5)));
ASSERT_EQUAL(recvd_msgs_cnt, 0);
}
PASS();
}
static void test_tester_table_state_change(void)
{
int err;
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&tester_prov, &comp);
tester_setup();
for (int i = 0; i < 1 /* bridge */ + REMOTE_NODES; i++) {
LOG_INF("Waiting for a device to provision...");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)));
}
tester_bridge_configure(REMOTE_NODES);
for (int i = 0; i < REMOTE_NODES; i++) {
tester_device_configure(i + 1, DEVICE_ADDR_START + i);
}
tester_workaround();
bt_mesh_test_ra_cb_setup(tester_ra_cb);
/* Bridge Table is empty, will not get any message back. */
ASSERT_OK(send_get(DEVICE_ADDR_START));
err = k_sem_take(&status_msg_recvd_sem, K_SECONDS(5));
ASSERT_EQUAL(err, -EAGAIN);
/* DATA and GET messages should reach Device 1, but STATUS message won't be received. */
bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START, 0, 1, BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY);
ASSERT_OK(send_data(DEVICE_ADDR_START, 0xAA));
ASSERT_OK(send_get(DEVICE_ADDR_START));
err = k_sem_take(&status_msg_recvd_sem, K_SECONDS(5));
ASSERT_EQUAL(err, -EAGAIN);
/* Sending DATA message again before adding a new entry as the previous GET message resets
* received messages counter on Devices
*/
ASSERT_OK(send_data(DEVICE_ADDR_START, 0xAA));
/* Adding a reverse entry. This should be added to the bridge table as a separate entry as
* the addresses and net keys indexs are provided in the opposite order.
*/
bridge_entry_add(DEVICE_ADDR_START, PROV_ADDR, 1, 0, BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY);
bridge_table_verify(0, 1, 0, (struct bridged_addresses_entry[]) {
{ PROV_ADDR, DEVICE_ADDR_START, BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY },
}, 1);
bridge_table_verify(1, 0, 0, (struct bridged_addresses_entry[]) {
{ DEVICE_ADDR_START, PROV_ADDR, BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY },
}, 1);
k_sleep(K_SECONDS(1));
/* Now we should receive STATUS message. */
ASSERT_OK(send_get(DEVICE_ADDR_START));
ASSERT_OK(k_sem_take(&status_msg_recvd_sem, K_SECONDS(5)));
ASSERT_EQUAL(recvd_msgs_cnt, 1);
ASSERT_EQUAL(recvd_msgs[0], 0xAA);
/* Removing the reverse entry and changing direction on the first entry.
* tester should still receive STATUS message.
*/
bridge_entry_remove(DEVICE_ADDR_START, PROV_ADDR, 1, 0);
bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START, 0, 1, BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY);
bridge_table_verify(0, 1, 0, (struct bridged_addresses_entry[]) {
{ PROV_ADDR, DEVICE_ADDR_START, BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY },
}, 1);
bridge_table_verify(1, 0, 0, NULL, 0);
ASSERT_OK(send_get(DEVICE_ADDR_START));
ASSERT_OK(k_sem_take(&status_msg_recvd_sem, K_SECONDS(5)));
ASSERT_EQUAL(recvd_msgs_cnt, 0);
PASS();
}
static void net_key_remove(uint16_t dst, uint16_t net_idx, uint16_t net_idx_to_remove)
{
uint8_t status;
int err;
err = bt_mesh_cfg_cli_net_key_del(net_idx, dst, net_idx_to_remove, &status);
if (err || status) {
FAIL("NetKey del failed (err %d, status %u)", err, status);
return;
}
}
static void test_tester_net_key_remove(void)
{
int err;
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&tester_prov, &comp);
tester_setup();
for (int i = 0; i < 1 /* bridge */ + REMOTE_NODES; i++) {
LOG_INF("Waiting for a device to provision...");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)));
}
tester_bridge_configure(REMOTE_NODES);
for (int i = 0; i < REMOTE_NODES; i++) {
tester_device_configure(i + 1, DEVICE_ADDR_START + i);
}
tester_workaround();
bt_mesh_test_ra_cb_setup(tester_ra_cb);
/* Adding devices to bridge table */
for (int i = 0; i < REMOTE_NODES; i++) {
bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START + i, 0, i + 1,
BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY);
}
ASSERT_OK(send_data(DEVICE_ADDR_START, 0xAA));
ASSERT_OK(send_get(DEVICE_ADDR_START));
ASSERT_OK(k_sem_take(&status_msg_recvd_sem, K_SECONDS(5)));
ASSERT_EQUAL(recvd_msgs_cnt, 1);
ASSERT_EQUAL(recvd_msgs[0], 0xAA);
/* Removing subnet 1 from Subnet Bridge. */
net_key_remove(BRIDGE_ADDR, 0, 1);
ASSERT_OK(send_get(DEVICE_ADDR_START));
err = k_sem_take(&status_msg_recvd_sem, K_SECONDS(5));
ASSERT_EQUAL(err, -EAGAIN);
bridge_table_verify(0, 2, 0, (struct bridged_addresses_entry[]) {
{ PROV_ADDR, DEVICE_ADDR_START + 1, BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY },
}, 1);
/* Bridging Table Get message will return Invalid NetKey Index error because Subnet 1 is
* removed.
*/
struct bt_mesh_bridging_table_list rsp = {
.list = NULL,
};
ASSERT_OK(bt_mesh_brg_cfg_cli_bridging_table_get(0, BRIDGE_ADDR, 0, 1, 0, &rsp));
ASSERT_EQUAL(rsp.status, 4);
PASS();
}
#if CONFIG_BT_SETTINGS
static void test_tester_persistence(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&tester_prov, &comp);
if (bt_mesh_is_provisioned()) {
uint8_t status;
LOG_INF("Already provisioned, skipping provisioning");
ASSERT_OK(bt_mesh_brg_cfg_cli_subnet_bridge_get(0, BRIDGE_ADDR, &status));
if (status != BT_MESH_SUBNET_BRIDGE_ENABLED) {
FAIL("Subnet bridge set failed (status %u)", status);
return;
}
bridge_table_verify(0, 1, 0, (struct bridged_addresses_entry[]) {
{ PROV_ADDR, DEVICE_ADDR_START, BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY },
}, 1);
bridge_table_verify(0, 2, 0, (struct bridged_addresses_entry[]) {
{ PROV_ADDR, DEVICE_ADDR_START + 1, BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY },
}, 1);
bridge_table_verify(1, 0, 0, (struct bridged_addresses_entry[]) {
{ DEVICE_ADDR_START, PROV_ADDR, BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY },
}, 1);
bridge_table_verify(2, 0, 0, (struct bridged_addresses_entry[]) {
{ DEVICE_ADDR_START + 1, PROV_ADDR, BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY },
}, 1);
} else {
tester_setup();
LOG_INF("Waiting for a bridge to provision...");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)));
LOG_INF("Configuring bridge...");
tester_bridge_configure(REMOTE_NODES);
/* Adding devices to bridge table */
for (int i = 0; i < REMOTE_NODES; i++) {
bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START + i, 0, i + 1,
BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY);
bridge_entry_add(DEVICE_ADDR_START + i, PROV_ADDR, i + 1, 0,
BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY);
}
k_sleep(K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT));
}
PASS();
}
#endif
/* When testing IV Index update, after the IV Index incremented devices starts sending messages
* with SEQ number 0 that is lower than the SEQ number of the last message received before IV Index.
* The Network Message Cache is not cleared and thus will drop these messages.
*
* The workaround is to send GET message to each device to bump SEQ number and overflow the cache so
* that after IV Index update there is no message with SEQ 0 in the cache.
*/
static void msg_cache_workaround(void)
{
LOG_INF("Applying Msg Cache workaround...");
for (int i = 0; i < REMOTE_NODES; i++) {
for (int j = 0; j < CONFIG_BT_MESH_MSG_CACHE_SIZE; j++) {
ASSERT_OK(send_get(DEVICE_ADDR_START + i));
/* k_sem_take is needed to not overflow network buffer pool. The result
* of the semaphor is not important as we just need to bump sequence number
* enough to bypass message cache.
*/
(void) k_sem_take(&status_msg_recvd_sem, K_SECONDS(1));
}
}
LOG_INF("Msg Cache workaround applied");
k_sleep(K_SECONDS(10));
}
static int beacon_set(uint16_t dst, uint8_t val)
{
uint8_t status;
int err;
err = bt_mesh_cfg_cli_beacon_set(0, dst, val, &status);
if (err || status != val) {
FAIL("Beacon set failed (err %d, status %u)", err, status);
return -EINVAL;
}
return 0;
}
/* This function guarantees that IV Update procedure state is propagated to all nodes by togging off
* Beacon features on Subnet Bridge and Tester nodes. When Beacon feature is disabled on Subnet
* Bridge, Tester will be able to send beacon with new IVI flag and vice versa.
*
* Beacon feature is disabled on other nodes at the setup.
*/
static void propagate_ivi_update_state(void)
{
/* Disable Beacon feature on subnet bridge to let tester send beacon first. */
ASSERT_OK(beacon_set(BRIDGE_ADDR, BT_MESH_BEACON_DISABLED));
LOG_INF("Waiting for IV Update state to propagate to Subnet Bridge");
k_sleep(K_SECONDS(BEACON_INTERVAL * 2));
/* Disable Beacon feature on tester and enable it on subnet bridge to let it send beacon. */
ASSERT_OK(beacon_set(PROV_ADDR, BT_MESH_BEACON_DISABLED));
ASSERT_OK(beacon_set(BRIDGE_ADDR, BT_MESH_BEACON_ENABLED));
LOG_INF("Waiting for IV Update state to propagate to other nodes");
k_sleep(K_SECONDS(BEACON_INTERVAL * 2));
/* Restore Beacon feature on tester. */
ASSERT_OK(beacon_set(PROV_ADDR, BT_MESH_BEACON_ENABLED));
}
static void test_tester_ivu(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME_IVU_TEST);
bt_mesh_device_setup(&tester_prov, &comp);
bt_mesh_iv_update_test(true);
tester_setup();
for (int i = 0; i < 1 /* bridge */ + REMOTE_NODES; i++) {
LOG_INF("Waiting for a device to provision...");
ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40)));
}
tester_bridge_configure(REMOTE_NODES);
/* Adding devices to bridge table */
for (int i = 0; i < REMOTE_NODES; i++) {
bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START + i, 0, i + 1,
BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY);
}
for (int i = 0; i < REMOTE_NODES; i++) {
tester_device_configure(i + 1, DEVICE_ADDR_START + i);
}
tester_workaround();
bt_mesh_test_ra_cb_setup(tester_ra_cb);
ASSERT_TRUE(!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS));
ASSERT_TRUE(bt_mesh.iv_index == test_ividx);
LOG_INF("IV Update procedure state: Normal");
k_sleep(K_SECONDS(BEACON_INTERVAL));
send_and_receive();
for (int i = 0; i < 2; i++) {
uint32_t iv_index = bt_mesh.iv_index;
LOG_INF("Round: %d", i);
msg_cache_workaround();
LOG_INF("Starting IV Update procedure, IVI %d -> %d", bt_mesh.iv_index,
bt_mesh.iv_index + 1);
iv_index = bt_mesh.iv_index;
ASSERT_TRUE(bt_mesh_iv_update());
ASSERT_TRUE(atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS));
ASSERT_TRUE(bt_mesh.iv_index == iv_index + 1);
send_and_receive();
propagate_ivi_update_state();
LOG_INF("Finishing IV Update procedure");
ASSERT_TRUE(!bt_mesh_iv_update());
ASSERT_TRUE(!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS));
ASSERT_TRUE(bt_mesh.iv_index == iv_index + 1);
propagate_ivi_update_state();
send_and_receive();
}
PASS();
}
static void bridge_setup(void)
{
bt_mesh_device_setup(&bridge_prov, &comp);
if (IS_ENABLED(CONFIG_BT_SETTINGS) && bt_mesh_is_provisioned()) {
LOG_INF("Already provisioned, skipping provisioning");
} else {
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("Bridge is provisioned");
}
}
static void test_bridge_simple(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bridge_setup();
PASS();
}
static void test_bridge_simple_iv_test_mode(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME_IVU_TEST);
bt_mesh_iv_update_test(true);
bridge_setup();
PASS();
}
static void device_setup(void)
{
bt_mesh_device_setup(&device_prov, &comp);
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("Node is provisioned");
bt_mesh_test_ra_cb_setup(device_ra_cb);
}
static void test_device_simple(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
device_setup();
PASS();
}
static void test_device_simple_iv_test_mode(void)
{
bt_mesh_test_cfg_set(NULL, WAIT_TIME_IVU_TEST);
bt_mesh_iv_update_test(true);
device_setup();
PASS();
}
#define TEST_CASE(role, name, description) \
{ \
.test_id = "brg_" #role "_" #name, \
.test_post_init_f = test_##role##_init, \
.test_descr = description, \
.test_tick_f = bt_mesh_test_timeout, \
.test_main_f = test_##role##_##name, \
}
static const struct bst_test_instance test_brg[] = {
TEST_CASE(tester, simple, "Tester node: provisions network, exchanges messages with "
"mesh nodes"),
TEST_CASE(tester, table_state_change, "Tester node: tests changing bridging table "
"state"),
TEST_CASE(tester, net_key_remove, "Tester node: tests removing net key from Subnet "
"Bridge"),
#if CONFIG_BT_SETTINGS
TEST_CASE(tester, persistence, "Tester node: test persistence of subnet bridge states"),
#endif
TEST_CASE(tester, ivu, "Tester node: tests subnet bridge with IV Update procedure"),
TEST_CASE(bridge, simple, "Subnet Bridge node"),
TEST_CASE(device, simple, "A mesh node"),
TEST_CASE(bridge, simple_iv_test_mode, "Subnet Bridge node with IV test mode enabled"),
TEST_CASE(device, simple_iv_test_mode, "A mesh node with IV test mode enabled"),
BSTEST_END_MARKER
};
struct bst_test_list *test_brg_install(struct bst_test_list *tests)
{
tests = bst_add_tests(tests, test_brg);
return tests;
}

View file

@ -381,11 +381,11 @@ static void test_rx_rpl_frag(void)
.ctx.addr = 100,
.local_match = 1,
};
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl, false));
rx.ctx.addr = 101;
ASSERT_FALSE(bt_mesh_rpl_check(&rx, &rpl));
ASSERT_FALSE(bt_mesh_rpl_check(&rx, &rpl, false));
rx.ctx.addr = 102;
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl, false));
/* Let the settings store RPL. */
k_sleep(K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT));
@ -455,11 +455,11 @@ static void test_rx_reboot_after_defrag(void)
.ctx.addr = 100,
.local_match = 1,
};
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl, false));
rx.ctx.addr = 101;
ASSERT_FALSE(bt_mesh_rpl_check(&rx, &rpl));
ASSERT_FALSE(bt_mesh_rpl_check(&rx, &rpl, false));
rx.ctx.addr = 102;
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl, false));
PASS();
}

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test that correct Net Keys are used when bridged subnets in a different IVU phase.
#
# Test Procedure:
# 1. All nodes have IV Update test mode enabled.
# 2. Provisioner configures itself and creates subnets equal to number of non-bridge devices.
# 3. Provisioner provisions and configures Subnet Bridge node to bridge the subnets.
# 4. Provisioner provisions and configures non-bridge devices for each subnet.
RunTest mesh_brg_ivu \
brg_tester_ivu brg_bridge_simple_iv_test_mode brg_device_simple_iv_test_mode \
brg_device_simple_iv_test_mode
overlay=overlay_psa_conf
RunTest mesh_brg_ivu \
brg_tester_ivu brg_bridge_simple_iv_test_mode brg_device_simple_iv_test_mode \
brg_device_simple_iv_test_mode

View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test verifies that an entry is removed from the bridging table when the NetKey is removed
#
# Test procedure:
# 1. Tester configures itself and creates subnets equal to number of non-bridge nodes.
# 2. Tester provisions and configures Subnet Bridge node.
# 3. Tester provisions and configures non-bridge nodes for each subnet.
# 4. Tester sends DATA and GET messages to the device 1 encrypted with primary key and verifies that
# device 1 sends a STATUS message with the content of the DATA message.
# 5. Tester removes the NetKey from Subnet Bridge node.
# 6. Tester sends a GET message and verifies that no response is received.
# 7. Tester retrieves entries from the bridging table and verifies that the entry with NetKeyIndex2
# set to the removed NetKey is removed.
RunTest mesh_brg_net_key_remove \
brg_tester_net_key_remove brg_bridge_simple brg_device_simple brg_device_simple
overlay=overlay_psa_conf
RunTest mesh_brg_net_key_remove \
brg_tester_net_key_remove brg_bridge_simple brg_device_simple brg_device_simple

View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test verifies that subnet bridge states are restored correctly after reboot
#
# Test procedure:
# 1. Tester configures itself and creates subnets equal to number of non-bridge nodes.
# 2. Tester provisions and configures Subnet Bridge node to bridge the subnets.
# 3. Devices reboot
# 4. Tester retrieves and verifies configuration of the Subnet Bridge node
overlay=overlay_pst_conf
RunTestFlash mesh_brg_persistence \
brg_tester_persistence -flash_erase brg_bridge_simple -flash_erase
overlay=overlay_pst_conf
RunTestFlash mesh_brg_persistence \
brg_tester_persistence brg_bridge_simple
# The same test but with PSA crypto
overlay="overlay_pst_conf_overlay_psa_conf"
RunTestFlash mesh_brg_persistence \
brg_tester_persistence -flash_erase brg_bridge_simple -flash_erase
overlay="overlay_pst_conf_overlay_psa_conf"
RunTestFlash mesh_brg_persistence \
brg_tester_persistence brg_bridge_simple

View file

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# This test verifies that Subnet Bridge stops bridging messages when the Subnet Bridge state is
# disabled.
#
# 3 roles are used in this test: Tester (Tester), Subnet Bridge node, and Mesh node.
#
# Subnets topology*:
# Tester
# |
# (subnet 0)
# |
# Subnet Bridge (bridges subnets 0 <-> 1, 0 <-> 2)
# / \
# (subnet 1) (subnet 2)
# | |
# Node Node
#
# (*) - All nodes are in the tester's range
#
# Test procedure:
# 1. Tester configures itself and creates subnets equal to number of non-bridge nodes.
# 2. Tester provisions and configures Subnet Bridge node to bridge the subnets.
# 3. Tester provisions and configures non-bridge nodes for each subnet.
# 4. Tester sends a DATA message to each node encrypted with primary key.
# 5. Nodes store the received messages.
# 6. Tester sends a GET message to each node encrypted with primary key.
# 7. Nodes send the stored messages back to the tester through a STATUS message encrypted
# with the key of the subnets they are provisioned to.
# 8. Tester verifies that each node received messages.
# 9. Tester disables Subnet Bridge state.
# 10. Tester sends a DATA message to each node encrypted with primary key.
# 11. Tester sends a GET message to each node encrypted with primary key.
# 12. Tester verifies that each node didn't receive DATA messages.
RunTest mesh_brg_simple \
brg_tester_simple brg_bridge_simple brg_device_simple brg_device_simple
overlay=overlay_psa_conf
RunTest mesh_brg_simple \
brg_tester_simple brg_bridge_simple brg_device_simple brg_device_simple

View file

@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test verifies that bridging table works correctly
#
# Test procedure:
# 1. Tester configures itself and creates subnets equal to number of non-bridge nodes.
# 2. Tester provisions and configures Subnet Bridge node.
# 3. Tester provisions and configures non-bridge nodes for each subnet.
# 4. While bridging table is empty, Tester sends a GET message to each node encrypted with primary
# key and verifies that no response is received.
# 5. Tester adds an entry to the bridging table to bridge traffic in one direction from tester to
# device 1
# 6. Tester sends a DATA and GET messages to device 1 encrypted with primary key and verifies that
# no response is received.
# 9. Tester adds a reverse entry to the bridging table to bridge traffic in the other direction from
# device 1 to tester.
# 10. Tester sends a GET message to device 1 encrypted with primary key and verifies that a STATUS
# message is received with the content of the previously sent DATA message.
# 11. Tester removes the reverse entry from the bridging table and updates direction of the first
# entry to bridge traffic in the both directions between tester and device 1.
# 12. Tester sends a GET message to device 1 encrypted with primary key and verifies that a STATUS
# message is received with the empty content.
RunTest mesh_brg_table_state_change \
brg_tester_table_state_change brg_bridge_simple brg_device_simple brg_device_simple
overlay=overlay_psa_conf
RunTest mesh_brg_table_state_change \
brg_tester_table_state_change brg_bridge_simple brg_device_simple brg_device_simple