diff --git a/subsys/bluetooth/mesh/brg_cfg_srv.c b/subsys/bluetooth/mesh/brg_cfg_srv.c index d622ebf48a252..0d0bda534b668 100644 --- a/subsys/bluetooth/mesh/brg_cfg_srv.c +++ b/subsys/bluetooth/mesh/brg_cfg_srv.c @@ -145,60 +145,34 @@ static int bridged_subnets_get(const struct bt_mesh_model *model, struct bt_mesh net_buf_simple_add_le16(&msg, net_idx_filter); net_buf_simple_add_u8(&msg, start_id); - uint8_t cnt = 0; - uint16_t net_idx1, net_idx2; - for (int i = 0; i < rows; i++) { - net_idx1 = brg_tbl[i].net_idx1; - net_idx2 = brg_tbl[i].net_idx2; + uint16_t net_idx1 = brg_tbl[i].net_idx1; + uint16_t net_idx2 = brg_tbl[i].net_idx2; + bool is_first_instance; if (net_buf_simple_tailroom(&msg) < 3 + BT_MESH_MIC_SHORT) { break; } - switch (filter_net_idx.filter) { - /* Report pair of NetKeys from the table, starting from start_id. */ - case 0: - if (i >= start_id) { - key_idx_pack_pair(&msg, net_idx1, net_idx2); + is_first_instance = true; + for (int j = 0; j < i; j++) { + if (net_idx1 == brg_tbl[j].net_idx1 && net_idx2 == brg_tbl[j].net_idx2) { + is_first_instance = false; + break; } - break; - - /* Report pair of NetKeys in which (NetKeyIndex1) matches the net_idx */ - case 1: - if (net_idx1 == filter_net_idx.net_idx) { - if (cnt >= start_id) { - key_idx_pack_pair(&msg, net_idx1, net_idx2); - } - cnt++; - } - break; - - /* Report pair of NetKeys in which (NetKeyIndex2) matches the net_idx */ - case 2: - if (net_idx2 == filter_net_idx.net_idx) { - if (cnt >= start_id) { - key_idx_pack_pair(&msg, net_idx1, net_idx2); - } - cnt++; - } - break; + } - /* Report pair of NetKeys in which (NetKeyIndex1 or NetKeyIndex2) matches the - * net_idx - */ - case 3: - if (net_idx1 == filter_net_idx.net_idx || - net_idx2 == filter_net_idx.net_idx) { - if (cnt >= start_id) { - key_idx_pack_pair(&msg, net_idx1, net_idx2); - } - cnt++; + if (is_first_instance && + (filter_net_idx.filter == 0 || + (filter_net_idx.filter == 1 && net_idx1 == filter_net_idx.net_idx) || + (filter_net_idx.filter == 2 && net_idx2 == filter_net_idx.net_idx) || + (filter_net_idx.filter == 3 && (net_idx1 == filter_net_idx.net_idx || + net_idx2 == filter_net_idx.net_idx)))) { + if (start_id > 0) { + start_id--; + } else { + key_idx_pack_pair(&msg, net_idx1, net_idx2); } - break; - - default: - CODE_UNREACHABLE; } } diff --git a/tests/bsim/bluetooth/mesh/src/test_brg.c b/tests/bsim/bluetooth/mesh/src/test_brg.c index e424c31f7009d..470f07e7929bd 100644 --- a/tests/bsim/bluetooth/mesh/src/test_brg.c +++ b/tests/bsim/bluetooth/mesh/src/test_brg.c @@ -792,6 +792,105 @@ static void test_tester_net_key_remove(void) PASS(); } +static const struct subnet_pair { + uint16_t idx1; + uint16_t idx2; +} subnet_pairs[] = { + { 0, 1 }, + { 0, 2 }, + { 0, 3 }, + { 2, 3 }, +}; + +#define MAX_EXPECTED_PAIRS 4 + +static const struct { + struct bt_mesh_brg_cfg_filter_netkey filter; + struct subnet_pair expected[MAX_EXPECTED_PAIRS]; +} subnet_duplicate_test_vector[] = { + { + .filter = { .filter = 0 }, + .expected = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 2, 3 } } + }, + { + .filter = { .filter = 1, .net_idx = 0 }, + .expected = { { 0, 1 }, { 0, 2 }, { 0, 3 } } + }, + { + .filter = { .filter = 2, .net_idx = 3 }, + .expected = { { 0, 3 }, { 2, 3 } } + }, + { + .filter = { .filter = 3, .net_idx = 2 }, + .expected = { { 0, 2 }, { 2, 3 } } + } +}; + +static void check_subnet_list_get(struct bt_mesh_brg_cfg_filter_netkey filter, uint8_t start_idx, + const struct subnet_pair *expected) +{ + uint32_t encoded_pair; + struct subnet_pair pair; + struct bt_mesh_brg_cfg_subnets_list rsp = { + .list = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX), + }; + + net_buf_simple_init(rsp.list, 0); + + LOG_INF("Getting subnet list, filter = (filter: %d, subnet: %d), start_idx = %d", + filter.filter, filter.net_idx, start_idx); + ASSERT_OK(bt_mesh_brg_cfg_cli_subnets_get(0, BRIDGE_ADDR, filter, start_idx, &rsp)); + + for (int j = start_idx; j < MAX_EXPECTED_PAIRS && rsp.list->len >= 3; j++) { + /* Assert if we got more pairs than expected. */ + ASSERT_FALSE(expected[j].idx1 == 0 && expected[j].idx2 == 0); + + encoded_pair = net_buf_simple_pull_le24(rsp.list); + pair.idx1 = encoded_pair & 0xfff; + pair.idx2 = encoded_pair >> 12; + + LOG_DBG("Received pair (%d, %d)", pair.idx1, pair.idx2); + + ASSERT_EQUAL(expected[j].idx1, pair.idx1); + ASSERT_EQUAL(expected[j].idx2, pair.idx2); + } + + ASSERT_TRUE(rsp.list->len == 0); +} + +static void test_tester_subnet_duplicate_filtering(void) +{ + remote_nodes = 3; + bt_mesh_test_cfg_set(NULL, WAIT_TIME); + bt_mesh_device_setup(&tester_prov, &comp); + tester_setup(); + + LOG_INF("Waiting for bridge to be provisioned."); + ASSERT_OK(k_sem_take(&prov_sem, K_SECONDS(40))); + + tester_bridge_configure(); + + LOG_INF("Adding duplicate subnet pairs."); + for (int i = 0; i < remote_nodes; i++) { + for (int j = 0; j < ARRAY_SIZE(subnet_pairs); j++) { + bridge_entry_add(PROV_ADDR, DEVICE_ADDR_START + i, subnet_pairs[j].idx1, + subnet_pairs[j].idx2, BT_MESH_BRG_CFG_DIR_TWOWAY); + } + } + + for (int i = 0; i < ARRAY_SIZE(subnet_duplicate_test_vector); i++) { + struct bt_mesh_brg_cfg_filter_netkey filter = + subnet_duplicate_test_vector[i].filter; + const struct subnet_pair *expected = subnet_duplicate_test_vector[i].expected; + + for (int start_idx = 0; start_idx < MAX_EXPECTED_PAIRS; start_idx++) { + check_subnet_list_get(filter, start_idx, expected); + } + } + + PASS(); +} + #if CONFIG_BT_SETTINGS static void test_tester_persistence(void) { @@ -1158,6 +1257,8 @@ static const struct bst_test_instance test_brg[] = { TEST_CASE(tester, net_key_remove, "Tester node: tests removing net key from Subnet " "Bridge"), + TEST_CASE(tester, subnet_duplicate_filtering, + "Tester node: tests that Bridged Subnets List does not contain duplicates"), #if CONFIG_BT_SETTINGS TEST_CASE(tester, persistence, "Tester node: test persistence of subnet bridge states"), #endif diff --git a/tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_subnet_duplicate_filtering.sh b/tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_subnet_duplicate_filtering.sh new file mode 100755 index 0000000000000..232a5df3df796 --- /dev/null +++ b/tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_subnet_duplicate_filtering.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Copyright 2025 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh + +# This test verifies that Subnet Bridge returns only unique subnet pairs in the Bridged Subnets +# List message, and that the start index is indexing into the list of unique pairs (not the entire +# briding table) +# +# Test procedure: +# 1. Tester configures itself and creates a number of subnets. +# 2. Tester provisions and configures Subnet Bridge node with multiple rows per subnet pair. +# 3. Tester verifies that the Bridged Subnets List message does not contain duplicate subnet +# pairs and that start indexes index into the filtered Bridged Subnets List. This is done +# for each filter type. + +RunTest mesh_brg_subnet_duplicate_filtering \ + brg_tester_subnet_duplicate_filtering brg_bridge_simple