Compare commits

...

24 Commits

Author SHA1 Message Date
Pavel Vasilyev
e5e7142b3d bluetooth: mesh: brg_cfg_srv: ignore message with invalid parameters
When a message with invalid parameters is received, we must ignore it.

In this commit we check invalid parameters first.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
955b1f677b 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>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
dab117c85e tests: bluetooth: mesh_shell: add brg_cfg_srv instance
This commit adds Bridge Configuration Server to the mesh_shell sample.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
aeeb2053c6 bluetooth: mesh: brg_cfg: use IS_ENABLED macro optimally
If use `IS_ENABLED` macro inside if statements but not as preprocessor
directives, we can always compile the code for settings even if settings
are not used. This allows to reduce amount of firmwares we need to build
in CI to ensure that code compiles.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
1b44e607a8 bluetooth: mesh: brg_cfg_srv: check buf tailroom before packing data
We must tcheck if buf has enough tailroom to fit another pair of keys or
pair addresses with direction.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
c1f5ae30c6 bluetooth: mesh: adv_ext: resched main adv set if relay doesn't have own
If relay feature doesn't have own adv set, it should use the main adv
set to send messages.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
bbd0498803 bluetooth: mesh: brg_cfg_cli: copy buf in synchronous api
When a synchronous API is used, the content of `buf` will not be
valid by the time the thread that called the synchronous API is woken up
again.

Therefore, the simplest way to solve this is when a user allocates the
buffer which will be filled up with the content of the buffer passed to
the model callback.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
85a856d79a bluetooth: mesh: net: bridge traffic regardless of relay state
The Subnet Bridge node must bridge traffic regardless of the Relay
state.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
ef43b94495 bluetooth: mesh: net: use subnet credentials when relaying
When relaying a Network PDU to another subnet, we need to update sub
field of rx structure so that correct credentials are used. We also need
to update NID field of the Network PDU.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
62f08da47b bluetooth: mesh: brg_cfg: add constants for directions
Add constants for directions so that they can be used in the
subnet bridge client api.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
81dbc697f4 bluetooth: mesh: rpl: fix rpl for subnet bridge
The initial implementation of RPL check for Subnet Bridge wasn't fully
correct. It decides whether to relay or not a PDU based on RPL of only
received messages. However, the spec section 3.9.8 says:

```
In addition, a Subnet Bridge node shall implement replay protection for
all Access and Transport Control messages that are sent to bridged
subnets.

A Subnet Bridge node shall maintain the most recent IVISeq value for
each source address authorized to send messages to bridged subnets.
Messages received by the Subnet Bridge node with the IVISeq value less
than or equal to the last stored value from that source address shall
be discarded immediately upon reception. When a message is retransmitted
to a bridged subnet, the stored IVISeq value shall be updated. In this
way, bridged subnets are protected against replay attacks from other
subnets.
```

This was noticed with MESH/NODE/SBR/NET/BV-02-C test. It passes when
PTS misses the message at step 3 and 4, and fails when PTS manages to
receive the message.

This commit fixes the wrong behavior by removing argument for
`bt_mesh_rpl_check` function making store a new message and bypassing
`local_match` check in case of Subnet Bridge node.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
dfdce2c122 bluetooth: mesh: brg_cfg: store tbl when changed only
Though nvs backend checks whether the data passed to it is identical to
the stored one, this isn't guaranteed by other the settings api. We can
also avoid going into the settings subsystem since we know what was
actually changed.

This commit adds 2 flags that allow to control whether Subnet Bridge
or Bridging Table states were changed and skips storing if the certain
state was not changed.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Pavel Vasilyev
6724345372 bluetooth: mesh: brg_cfg: fix restoring bridging table
We store only filled up entries, but want to restore the entire table.
`bt_mesh_setting_set` fails if the restored length didn't match the
provided length.

This commit fixes the restoring of the bridging table by allowing to
restore any size as long as the stored size is less than the allocated
one and it is a multiple of the size of a single entry.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2024-09-12 16:26:04 +02:00
Stine Åkredalen
0bb34284d9 docs: Bluetooth: Mesh: add Subnet Bridge doc
Added high-level documentation for Subnet Bridge,
connecting models together and explaining usage.

Signed-off-by: Stine Åkredalen <stine.akredalen@nordicsemi.no>
2024-09-12 16:25:51 +02:00
Ingar Kulbrandstad
55175d63e4 Bluetooth: Mesh: Fixed bugs found in PTS testing
Fixed bugs found while running PTS test.

Signed-off-by: Ingar Kulbrandstad <ingar.kulbrandstad@nordicsemi.no>
2024-09-09 09:46:58 +02:00
Ingar Kulbrandstad
d7b4497054 Bluetooth: Tester: Mesh: Added support for Bridge client/server
Added support for the Bridge Configuration client and server
to the tester application.

Signed-off-by: Ingar Kulbrandstad <ingar.kulbrandstad@nordicsemi.no>
2024-09-09 09:46:58 +02:00
Omkar Kulkarni
9d61afa758 Bluetooth: Mesh: Add bridging functionality
Adds subnet bridging functionality to the network layer.
Also fixes brg_cfg_srv for minor issues to get it working.

Signed-off-by: Omkar Kulkarni <omkar.kulkarni@nordicsemi.no>
2024-08-26 18:53:09 +02:00
Omkar Kulkarni
cddbafe374 Bluetooth: Mesh: Populate Bridge Config Server
Adds implementation for the Bridge Configuration Server model.

Updates `brg_cfg` module to add sanity check for
bt_mesh_brg_cfg_tbl_remove() API. Also, updates the unit test
accordingly.

Adds documentation for the Bridge Configuration Server model

Signed-off-by: Omkar Kulkarni <omkar.kulkarni@nordicsemi.no>
2024-08-07 13:50:22 +02:00
Håvard Reierstad
71b29f4796 Bluetooth: Mesh: Add brg_cfg_cli commands to shell
Adds the Bridge Configuration Client commands to Mesh shell.

Unifies documentation for shell Kconfig options.

Signed-off-by: Håvard Reierstad <haavard.reierstad@nordicsemi.no>
2024-08-07 13:50:14 +02:00
Håvard Reierstad
563aa9be92 Bluetooth: Mesh: Implement Bridge Config Client
Implements the Bridge Configuration Client model.
Adds opcodes for all Bridge messages to `foundation.h`. Adds client-side
support for these messages.
Adds a new Kconfig option for the Bridge Configuration Client model to
configure the waiting time for respones for acked messages.

Signed-off-by: Håvard Reierstad <haavard.reierstad@nordicsemi.no>
2024-07-22 14:57:39 +02:00
Håvard Reierstad
6874a4a719 Bluetooth: Mesh: Change bridge table add params
Change the parameters of the bridge table add function to take an
entry struct rather than the fields of an entry.

Signed-off-by: Håvard Reierstad <haavard.reierstad@nordicsemi.no>
2024-07-22 14:57:39 +02:00
Håvard Reierstad
380bd5483e Bluetooth: Mesh: Fix subnet bridge table status type
Changes status type to be status codes defined in Mesh Protocol 4.3.14
as defined for the `BRIDGING_TABLE_STATUS` message.

Signed-off-by: Håvard Reierstad <haavard.reierstad@nordicsemi.no>
2024-07-22 14:57:39 +02:00
Omkar Kulkarni
988ad5b0c2 Bluetooth: Mesh: Adds subnet bridge states
The `brg_cfg` module implements the states needed for subnet bridge
feature. It provides two states - enable state, and bridging table
state. APIs are provided to access and modify the states. The module
handles responsibility of persistence of the states.

Signed-off-by: Omkar Kulkarni <omkar.kulkarni@nordicsemi.no>
2024-07-22 14:49:24 +02:00
Ingar Kulbrandstad
470c0d13a0 Bluetooth: Mesh: Bridge Configuration Client/Server API
Adding documentation and function calles for the API's
in Bridge Configuration Client model and Bridge
Configuration Server model.

Signed-off-by: Ingar Kulbrandstad <ingar.kulbrandstad@nordicsemi.no>
2024-07-04 10:03:31 +02:00
52 changed files with 4355 additions and 39 deletions

View File

@@ -24,3 +24,4 @@ Read more about Bluetooth Mesh on the
mesh/cfg.rst
mesh/statistic.rst
mesh/shell.rst
mesh/brg_cfg.rst

View File

@@ -0,0 +1,160 @@
.. _bluetooth_mesh_brg_cfg:
Subnet Bridge
#############
With Bluetooth Mesh Protocol Specification version 1.1, the Bluetooth Mesh Subnet Bridge feature was
introduced.
This feature allows mesh networks to use subnets for area isolation, but to also allow communication
between specific devices in different, adjacent subnets without compromising security.
The Bluetooth Mesh Subnet Bridge feature makes it possible for selected nodes in the network to act
as Subnet Bridges, allowing controlled communication by relaying messages between nodes in adjacent
subnets.
The Subnet Bridge feature includes two models:
- :ref:`bluetooth_mesh_models_brg_cfg_srv`
- :ref:`bluetooth_mesh_models_brg_cfg_cli`
The Bridge Configuration Server model is mandatory for supporting the Subnet Bridge feature.
The Bridge Configuration Client model is optional and allows nodes to configure the Subnet Bridge on
other nodes.
These models define the necessary states, messages, and behaviors for configuring and managing the
Subnet Bridge feature.
The configuration and management of the Subnet Bridge feature are handled using the Bridge
Configuration Server and Client models.
A node implementing the Bridge Configuration Client model can act as a *Configuration Manager* for
the Subnet Bridge feature.
Concepts
********
To get a better understanding of the Subnet Bridge feature and its capabilities, an overview of a
few concepts is needed.
Subnet
======
A subnet is a group of nodes within a mesh network that share a common network key, enabling them to
communicate securely at the network layer.
Each subnet operates independently, with nodes exchanging messages exclusively within that group.
A node may belong to multiple subnets at the same time.
Subnet Bridge node
==================
A Subnet Bridge node is a node in a Bluetooth Mesh network that belongs to multiple subnets and has
a Subnet Bridge functionality enabled. Subnet bridging can be performed only by such a node. The
Subnet Bridge node connects the subnets, and allows communication between them by relaying
messages across the subnet groups.
The Subnet Bridge node has a primary subnet, based on the primary NetKey, which handles the
IV Update procedure and propagates updates to other subnets.
The secondary subnets, on which messages are relayed, are referred to as *bridged subnets*.
Bridging Table
==============
The Bridging Table contains the entries for the subnets that are bridged by the node, and is managed
by the Bridge Configuration Server model.
The maximum number of entries in the bridging table is defined by the
:kconfig:option:`CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX` option, which defaults to the minimum value of
16, with a maximum possible size of 255.
Enabling or disabling the Subnet Bridge feature
***********************************************
The Bridge Configuration Client (or Configuration Manager) can enable or disable the Subnet Bridge
feature on a node by sending a **Subnet Bridge Set** message to the Bridge Configuration
Server model on the target node, using the :c:func:`bt_mesh_brg_cfg_cli_subnet_bridge_set` function.
Adding or removing subnets
**************************
The Bridge Configuration Client can add or remove an entry from the Bridging Table by sending a
**Bridging Table Add** or **Bridging Table Remove** message to the Bridge Configuration
Server model on the target node, calling the :c:func:`bt_mesh_brg_cfg_cli_bridging_table_add` or
:c:func:`bt_mesh_brg_cfg_cli_bridging_table_remove` functions.
.. _bluetooth_mesh_brg_cfg_states:
Subnet Bridge states
********************
The Subnet Bridge has the following states:
- *Subnet Bridge*: This state indicates whether the Subnet Bridge feature is enabled or disabled on
the node.
The Bridge Configuration Client can retrieve this information by sending a **Subnet Bridge Get**
message to the Bridge Configuration Server using the
:c:func:`bt_mesh_brg_cfg_cli_subnet_bridge_get` function.
- *Bridging Table*: This state holds the bridging table. The Client can request a list of
entries from a Bridging Table by sending a **Bridging Table Get** message to the target node using
the :c:func:`bt_mesh_brg_cfg_cli_bridging_table_get` function.
The Client can get a list of subnets currently bridged by a Subnet Bridge by sending a
**Bridged Subnets Get** message to the target Server by calling the
:c:func:`bt_mesh_brg_cfg_cli_bridged_subnets_get` function.
- *Bridging Table Size*: This state reports the maximum number of entries the Bridging Table can
store. The Client can retrieve this information by sending a **Bridging Table Size Get** message
using the :c:func:`bt_mesh_brg_cfg_cli_bridging_table_size_get` function.
This is a read-only state.
Subnet bridging and replay protection
*************************************
The Subnet Bridge feature enables message relaying between subnets and requires effective replay
protection to ensure network security. Key considerations to take into account are described below.
Relay buffer considerations
===========================
When a message is relayed between subnets by a Subnet Bridge, it is allocated from the relay buffer.
To ensure that messages can be retransmitted to all subnetworks,
the :kconfig:option:`CONFIG_BT_MESH_RELAY_BUF_COUNT` option should be increased accordingly.
However, if the :kconfig:option:`CONFIG_BT_MESH_RELAY` feature is disabled, the messages will be
allocated from the advertising buffer instead. In this case, increase the
:kconfig:option:`CONFIG_BT_MESH_ADV_BUF_COUNT` option to allow for sufficient buffer space.
Replay protection and Bridging Table
====================================
A Subnet Bridge node must implement replay protection for all Access and Transport Control messages
sent to bridged subnets.
The Replay Protection List (RPL) works with the Bridging Table to ensure security:
- The Subnet Bridge stores the latest IVISeq for each source address authorized to send messages to
bridged subnets.
- Messages with an IVISeq less than or equal to the stored value are discarded, while valid messages
update the stored IVISeq before being relayed.
To ensure proper operation, it is important that the RPL and Bridging Table are synchronized,
as every bridged message must pass through the replay protection mechanism before being relayed.
.. note::
The RPL size should scale with the Bridging Table. As the number of bridged subnets grows,
more source addresses and IVISeq values must be tracked, requiring a larger RPL to maintain
effective replay protection.
Subnet Bridge and Directed Forwarding
*************************************
Bluetooth Mesh Directed Forwarding (MDF) enables efficient routing between nodes across subnets by
optimizing relay paths. While MDF can enhance Subnet Bridging by handling path discovery and
forwarding, the current implementation does not support this feature.
API reference
*************
This section contains types and defines common to the Bridge Configuration models.
.. doxygengroup:: bt_mesh_brg_cfg

View File

@@ -0,0 +1,21 @@
.. _bluetooth_mesh_models_brg_cfg_cli:
Bridge Configuration Client
###########################
The Bridge Configuration Client is a foundation model defined by the Bluetooth Mesh
specification. The model is optional, and is enabled through
the :kconfig:option:`CONFIG_BT_MESH_BRG_CFG_CLI` option.
The Bridge Configuration Client model provides functionality for configuring the
subnet bridge functionality of another Mesh node containing the
:ref:`bluetooth_mesh_models_brg_cfg_srv`. The device key of the node containing
the target Bridge Configuration Server is used for access layer security.
If present, the Bridge Configuration Client model must only be instantiated on the primary
element.
API reference
*************
.. doxygengroup:: bt_mesh_brg_cfg_cli

View File

@@ -0,0 +1,31 @@
.. _bluetooth_mesh_models_brg_cfg_srv:
Bridge Configuration Server
###########################
The Bridge Configuration Server model is a foundation model defined by the Bluetooth Mesh
specification. It is an optional model, enabled with the
:kconfig:option:`CONFIG_BT_MESH_BRG_CFG_SRV` configuration option. This model extends
the :ref:`bluetooth_mesh_models_cfg_srv` model.
The Bridge Configuration Server model was introduced in the Bluetooth Mesh Protocol Specification
version 1.1, and is used for supporting and configuring the Subnet Bridge feature.
The Bridge Configuration Server model relies on a :ref:`bluetooth_mesh_models_brg_cfg_cli` to
configure it. The Bridge Configuration Server model only accepts messages encrypted with the nodes
device key.
If present, the Bridge Configuration Server model must be instantiated on the primary element.
The Bridge Configuration Server model provides access to the following three states:
* Subnet Bridge
* Bridging Table
* Bridging Table Size
For more information about the states, see :ref:`bluetooth_mesh_brg_cfg_states`.
API reference
*************
.. doxygengroup:: bt_mesh_brg_cfg_srv

View File

@@ -12,6 +12,8 @@ used by network administrators to configure and diagnose mesh nodes.
.. toctree::
:maxdepth: 1
brg_cfg_cli
brg_cfg_srv
cfg_cli
cfg_srv
health_cli

View File

@@ -1788,6 +1788,80 @@ a Config Composition Data Status message, and reading the metadata of the model
* ``Offset``: Offset within the page.
Bridge Configuration Client
---------------------------
The Bridge Configuration Client model is an optional Bluetooth Mesh model that can be enabled through the
:kconfig:option:`CONFIG_BT_MESH_BRG_CFG_CLI` configuration option. The model provides functionality
for configuring the subnet bridge functionality of a mesh node.
``mesh models brg_cfg bridge-get``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get the current Subnet Bridge state.
``mesh models brg_cfg bridge-set <State(disable, enable)>``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Set the Subnet Bridge state.
* ``State``: Disable or enable the Subnet Bridge functionality.
``mesh models brg_cfg table-size-get``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get the current size of the Bridging Table.
``mesh models brg_cfg table-add <Directions> <NetIdx1> <NetIdx2> <Addr1> <Addr2>``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add an entry to the Bridging Table.
* ``Directions``: Allowed directions for the bridged traffic. Valid values are:
* ``0x01``: Bridging is allowed only for messages with ``Addr1`` as the source address and ``Addr2`` as the destination address.
* ``0x02``: Bridging is allowed in both directions.
* ``NetIdx1``: NetKey index of the first subnet.
* ``NetIdx2``: NetKey index of the second subnet.
* ``Addr1``: Address of the node in the first subnet.
* ``Addr2``: Address of the node in the second subnet.
``mesh models brg_cfg table-remove <NetIdx1> <NetIdx2> <Addr1> <Addr2>``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Remove an entry from the Bridging Table.
* ``NetIdx1``: NetKey index of the first subnet.
* ``NetIdx2``: NetKey index of the second subnet.
* ``Addr1``: Address of the node in the first subnet.
* ``Addr2``: Address of the node in the second subnet.
``mesh models brg_cfg subnets-get <Filter> <NetIdx> <StartIdx>``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get a filtered set of NetKey index pairs extracted from the Bridging Table.
* ``Filter``: Filter to be applied when reporting pairs of NetKey indexes extracted from the Bridging Table. Allowed values:
* ``0x00``: Report all pairs.
* ``0x01``: Report pairs in which the NetKey index of the first subnet matches ``NetIdx``.
* ``0x02``: Report pairs in which the NetKey index of the second subnet matches ``NetIdx``.
* ``0x03``: Report pairs in which one of the NetKey indexes matches ``NetIdx``.
* ``NetIdx``: NetKey index of any of the subnets.
* ``StartIdx``: Start offset in units of pairs of NetKey indexes to read.
``mesh models brg_cfg table-get <NetIdx1> <NetIdx2> <StartIdx>``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get a list of addresses and allowed traffic directions of the Bridging Table entries.
* ``NetIdx1``: NetKey index of the first subnet.
* ``NetIdx2``: NetKey index of the second subnet.
* ``StartIdx``: Start offset to read in units of Bridging Table state entries.
Configuration database
======================

View File

@@ -48,6 +48,8 @@
#include <zephyr/bluetooth/mesh/od_priv_proxy_cli.h>
#include <zephyr/bluetooth/mesh/sol_pdu_rpl_srv.h>
#include <zephyr/bluetooth/mesh/sol_pdu_rpl_cli.h>
#include <zephyr/bluetooth/mesh/brg_cfg_cli.h>
#include <zephyr/bluetooth/mesh/brg_cfg_srv.h>
#include <zephyr/bluetooth/mesh/statistic.h>
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_H_ */

View File

@@ -187,6 +187,10 @@ struct bt_mesh_elem {
#define BT_MESH_MODEL_ID_REMOTE_PROV_SRV 0x0004
/** Remote Provisioning Client */
#define BT_MESH_MODEL_ID_REMOTE_PROV_CLI 0x0005
/** Bridge Configuration Sever */
#define BT_MESH_MODEL_ID_BRG_CFG_SRV 0x0008
/** Bridge Configuration Client */
#define BT_MESH_MODEL_ID_BRG_CFG_CLI 0x0009
/** Private Beacon Server */
#define BT_MESH_MODEL_ID_PRIV_BEACON_SRV 0x000a
/** Private Beacon Client */

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_H__
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup bt_mesh_brg_cfg Bridge Configuration common header
* @ingroup bt_mesh
* @{
*/
/** Subnet Bridge states */
enum bt_mesh_subnet_bridge_state {
/** Subnet bridge functionality is disabled. */
BT_MESH_SUBNET_BRIDGE_DISABLED,
/** Subnet bridge state functionality is enabled. */
BT_MESH_SUBNET_BRIDGE_ENABLED,
};
/* Briding from Addr1 to Addr2. */
#define BT_MESH_SUBNET_BRIDGE_DIR_ONEWAY 1
/* Bidirectional briging between Addr1 and Addr2. */
#define BT_MESH_SUBNET_BRIDGE_DIR_TWOWAY 2
/** Bridging Table state entry corresponding to a entry in the Bridging Table. */
struct bt_mesh_bridging_table_entry {
/** Allowed directions for the bridged traffic (or bridged traffic not allowed) */
uint8_t directions;
/** NetKey Index of the first subnet */
uint16_t net_idx1;
/** NetKey Index of the second subnet */
uint16_t net_idx2;
/** Address of the node in the first subnet */
uint16_t addr1;
/** Address of the node in the second subnet */
uint16_t addr2;
};
/** Bridging Table Status response */
struct bt_mesh_bridging_table_status {
/** Status Code of the requesting message */
uint8_t status;
/** Requested Bridging Table entry */
struct bt_mesh_bridging_table_entry entry;
};
/** Used to filter set of pairs of NetKey Indexes from the Bridging Table */
struct bt_mesh_filter_netkey {
uint16_t filter:2, /* Filter applied to the set of pairs of NetKey Indexes */
prohibited:2, /* Prohibited */
net_idx:12; /* NetKey Index used for filtering or ignored */
};
/** Bridged Subnets List response */
struct bt_mesh_bridged_subnets_list {
/** Filter applied NetKey Indexes, and NetKey Index used for filtering. */
struct bt_mesh_filter_netkey net_idx_filter;
/** Start offset in units of bridges */
uint8_t start_idx;
/** Pointer to allocated buffer for storing filtered of NetKey Indexes */
struct net_buf_simple *list;
};
/** Bridging Table List response */
struct bt_mesh_bridging_table_list {
/** Status Code of the requesting message */
uint8_t status;
/** NetKey Index of the first subnet */
uint16_t net_idx1;
/** NetKey Index of the second subnet */
uint16_t net_idx2;
/** Start offset in units of bridging table state entries */
uint16_t start_idx;
/** Pointer to allocated buffer for storing list of bridged addresses and directions */
struct net_buf_simple *list;
};
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_H__ */

View File

@@ -0,0 +1,323 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_CLI_H__
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_CLI_H__
#include <zephyr/bluetooth/mesh/brg_cfg.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup bt_mesh_brg_cfg_cli Bridge Configuration Client Model
* @ingroup bt_mesh
* @{
* @brief API for the Bluetooth Mesh Bridge Configuration Client model
*/
struct bt_mesh_brg_cfg_cli;
/**
* @brief Bridge Configuration Client model Composition Data entry.
*
* @param _cli Pointer to a @ref bt_mesh_brg_cfg_cli instance.
*/
#define BT_MESH_MODEL_BRG_CFG_CLI(_cli) \
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BRG_CFG_CLI, _bt_mesh_brg_cfg_cli_op, NULL, _cli, \
&_bt_mesh_brg_cfg_cli_cb)
/** Mesh Bridge Configuration Client Status messages callback */
struct bt_mesh_brg_cfg_cli_cb {
/** @brief Optional callback for Subnet Bridge Status message.
*
* Handles received Subnet Bridge Status messages from a Bridge
* Configuration Server.
* @param cli Bridge Configuration Client context.
* @param addr Address of the sender.
* @param status Status received from the server.
*/
void (*subnet_bridge_status)(struct bt_mesh_brg_cfg_cli *cli, uint16_t addr,
enum bt_mesh_subnet_bridge_state status);
/** @brief Optional callback for Bridging Table Size Status message.
*
* Handles received Bridging Table Size Status messages from a Bridge
* Configuration Server.
*
* @param cli Bridge Configuration Client context.
* @param addr Address of the sender.
* @param size Size received from the server.
*/
void (*bridging_table_size_status)(struct bt_mesh_brg_cfg_cli *cli, uint16_t addr,
uint16_t size);
/** @brief Optional callback for Bridging Table Status message.
*
* Handles received Bridging Table status messages from a Bridge
* Configuration Server.
*
* @param cli Bridge Configuration Client context.
* @param addr Address of the sender.
* @param rsp Response received from the Bridging Configuration Server.
*/
void (*bridging_table_status)(struct bt_mesh_brg_cfg_cli *cli, uint16_t addr,
struct bt_mesh_bridging_table_status *rsp);
/** @brief Optional callback for Bridged Subnets List message.
*
* Handles received Bridged Subnets List messages from a Bridge
* Configuration Server.
*
* @param cli Bridge Configuration Client context.
* @param addr Address of the sender.
* @param rsp Response received from the Bridging Configuration Server.
*/
void (*bridged_subnets_list)(struct bt_mesh_brg_cfg_cli *cli, uint16_t addr,
struct bt_mesh_bridged_subnets_list *rsp);
/** @brief Optional callback for Bridging Table List message.
*
* Handles received Bridging Table List messages from a Bridge
* Configuration Server.
*
* @param cli Bridge Configuration Client context.
* @param addr Address of the sender.
* @param rsp Response received from the Bridging Configuration Server.
*/
void (*bridging_table_list)(struct bt_mesh_brg_cfg_cli *cli, uint16_t addr,
struct bt_mesh_bridging_table_list *rsp);
};
/** Bridge Configuration Client Model Context */
struct bt_mesh_brg_cfg_cli {
/** Bridge Configuration model entry pointer */
const struct bt_mesh_model *model;
/** Event handler callbacks */
const struct bt_mesh_brg_cfg_cli_cb *cb;
/* Internal parameters for tracking message responses. */
struct bt_mesh_msg_ack_ctx ack_ctx;
};
/** @brief Sends a Subnet Bridge Get message to the given destination address
*
* This function sends a Subnet Bridge Get message to the given destination
* address to query the value of the Subnet Bridge state of a subnet. The
* Subnet Bridge state indicates whether the subnet bridged feature is enabled
* or not. The function expects a Subnet Bridge Status message as a response
* from the destination node.
*
* This method can be used asynchronously by setting @p status as NULL. This
* way the method will not wait for response and will return immediately after
* sending the command.
*
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param status Status response parameter, returns one of
* @ref BT_MESH_SUBNET_BRIDGE_DISABLED or
* @ref BT_MESH_SUBNET_BRIDGE_ENABLED on success.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_subnet_bridge_get(uint16_t net_idx, uint16_t addr,
enum bt_mesh_subnet_bridge_state *status);
/** @brief Sends a Subnet Bridge Set message to the given destination address
* with the given parameters
*
* This function sends a Subnet Bridge Set message to the given destination
* address with the given parameters to set the value of the Subnet Bridge
* state of a subnet. The Subnet Bridge state indicates whether the subnet
* bridge feature is enabled or not. The function expects a Subnet Bridge
* Status message as a response from the destination node.
*
* This method can be used asynchronously by setting @p status as NULL. This
* way the method will not wait for response and will return immediately after
* sending the command.
*
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param val Value to set the Subnet Bridge state to. Must be one of
* @ref BT_MESH_SUBNET_BRIDGE_DISABLED or
* @ref BT_MESH_SUBNET_BRIDGE_ENABLED.
* @param status Status response parameter, returns one of
* @ref BT_MESH_SUBNET_BRIDGE_DISABLED or
* @ref BT_MESH_SUBNET_BRIDGE_ENABLED on success.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_subnet_bridge_set(uint16_t net_idx, uint16_t addr,
enum bt_mesh_subnet_bridge_state val,
enum bt_mesh_subnet_bridge_state *status);
/** @brief Sends a Bridging Table Size Get message to the given destination
* address with the given parameters
*
* This function sends a Bridging Table Size Get message to the given
* destination address with the given parameters to get the size of the Bridging
* Table of the node. The Bridging Table size indicates the maximum number of
* entries that can be stored in the Bridging Table. The function expects a
* Bridging Table Size Status message as a response from the destination node.
*
* This method can be used asynchronously by setting @p size as NULL. This way
* the method will not wait for response and will return immediately after
* sending the command.
*
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param size Bridging Table size response parameter.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_bridging_table_size_get(uint16_t net_idx, uint16_t addr, uint16_t *size);
/** @brief Sends a Bridging Table Add message to the given destination address
* with the given parameters
*
* This function sends a Bridging Table Add message to the given destination
* address with the given parameters to add an entry to the Bridging Table. The
* Bridging Table contains the net keys and addresses that are authorized to be
* bridged by the node. The function expects a Bridging Table Status message as
* a response from the destination node.
*
* This method can be used asynchronously by setting @p rsp as NULL. This way
* the method will not wait for response and will return immediately after
* sending the command.
*
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param entry Pointer to bridging Table entry to add.
* @param rsp Status response parameter
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_bridging_table_add(uint16_t net_idx, uint16_t addr,
struct bt_mesh_bridging_table_entry *entry,
struct bt_mesh_bridging_table_status *rsp);
/** @brief Sends a Bridging Table Remove message to the given destination
* address with the given parameters
*
* This function sends a Bridging Table Remove message to the given destination
* address with the given parameters to remove an entry from the Bridging
* Table. The Bridging Table contains the net keys and addresses that are
* authorized to be bridged by the node. The function expects a Bridging Table
* Status message as a response from the destination node.
*
* This method can be used asynchronously by setting @p rsp as NULL. This way
* the method will not wait for response and will return immediately after
* sending the command.
*
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param net_idx1 NetKey Index of the first subnet
* @param net_idx2 NetKey Index of the second subnet
* @param addr1 Address of the node in the first subnet
* @param addr2 Address of the node in the second subnet
* @param rsp Pointer to a struct storing the received response from the
* server, or NULL to not wait for a response.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_bridging_table_remove(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
uint16_t net_idx2, uint16_t addr1, uint16_t addr2,
struct bt_mesh_bridging_table_status *rsp);
/** @brief Sends a Bridged Subnets Get message to the given destination address
* with the given parameters
*
* This function sends a Bridged Subnets Get message to the given destination
* address with the given parameters to get the list of subnets that are
* bridged by the node. The function expects a Bridged Subnets List message as
* a response from the destination node.
*
* This method can be used asynchronously by setting @p rsp as NULL. This way
* the method will not wait for response and will return immediately after
* sending the command.
*
* When @c rsp is set, the user is responsible for providing a buffer for the
* filtered set of N pairs of NetKey Indexes in
* @ref bt_mesh_bridged_subnets_list::list. If a buffer is not provided, the
* bridged subnets won't be copied.
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param filter_net_idx Filter and NetKey Index used for filtering
* @param start_idx Start offset to read in units of Bridging Table state entries
* @param rsp Pointer to a struct storing the received response
* from the server, or NULL to not wait for a response.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_bridged_subnets_get(uint16_t net_idx, uint16_t addr,
struct bt_mesh_filter_netkey filter_net_idx,
uint8_t start_idx,
struct bt_mesh_bridged_subnets_list *rsp);
/** @brief Sends a Bridging Table Get message to the given destination address
* with the given parameters
*
* This function sends a Bridging Table Get message to the given destination
* address with the given parameters to get the contents of the Bridging Table.
* The Bridging Table contains the addresses that are authorized to be bridged
* by the node. The function expects a Bridging Table List message as a
* response from the destination node.
*
* This method can be used asynchronously by setting @p rsp as NULL. This way
* the method will not wait for response and will return immediately after
* sending the command.
*
* When @c rsp is set, the user is responsible for providing a buffer for the
* filtered set of N pairs of NetKey Indexes in
* @ref bt_mesh_bridging_table_list::list. If a buffer is not provided, the
* bridged addresses won't be copied. If a buffer size is shorter than received
* list, only those many entries that fit in the buffer will be copied from the
* list, and rest will be discarded.
*
* @param net_idx Network index to encrypt the message with.
* @param addr Target node address.
* @param net_idx1 NetKey Index of the first subnet.
* @param net_idx2 NetKey Index of the second subnet.
* @param start_idx Start offset to read in units of Bridging Table state entries.
* @param rsp Pointer to a struct storing the received response from the
* server, or NULL to not wait for a response.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_brg_cfg_cli_bridging_table_get(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
uint16_t net_idx2, uint16_t start_idx,
struct bt_mesh_bridging_table_list *rsp);
/** @brief Get the current transmission timeout value.
*
* @return The configured transmission timeout in milliseconds.
*/
int32_t bt_mesh_brg_cfg_cli_timeout_get(void);
/** @brief Set the transmission timeout value.
*
* @param timeout The new transmission timeout.
*/
void bt_mesh_brg_cfg_cli_timeout_set(int32_t timeout);
/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op _bt_mesh_brg_cfg_cli_op[];
extern const struct bt_mesh_model_cb _bt_mesh_brg_cfg_cli_cb;
/** @endcond */
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_CLI_H__ */

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief Bluetooth Mesh Bridge Configuration Server Model APIs.
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_SRV_H__
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_SRV_H__
#include <zephyr/bluetooth/mesh/brg_cfg.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup bt_mesh_brg_cfg_srv Bridge Configuration Server Model
* @ingroup bt_mesh
* @{
* @brief API for the Bluetooth Mesh Bridge Configuration Server model
*/
/**
*
* @brief Bridge Configuration Server model Composition Data entry.
*/
#define BT_MESH_MODEL_BRG_CFG_SRV \
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BRG_CFG_SRV, _bt_mesh_brg_cfg_srv_op, \
NULL, NULL, &_bt_mesh_brg_cfg_srv_cb)
/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op _bt_mesh_brg_cfg_srv_op[];
extern const struct bt_mesh_model_cb _bt_mesh_brg_cfg_srv_cb;
/** @endcond */
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BRG_CFG_SRV_H__ */

View File

@@ -328,6 +328,10 @@ module = BT_MESH_NET
module-str = "Network layer"
source "subsys/logging/Kconfig.template.log_config_inherit"
module = BT_MESH_BRG
module-str = "Subnet Bridging layer"
source "subsys/logging/Kconfig.template.log_config_inherit"
module = BT_MESH_RPL
module-str = "Replay protection list"
source "subsys/logging/Kconfig.template.log_config_inherit"

View File

@@ -113,6 +113,10 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_SOL_PDU_RPL_CLI sol_pdu_rpl_cli.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV sol_pdu_rpl_srv.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_BRG_CFG_CLI brg_cfg_cli.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_BRG_CFG_SRV brg_cfg_srv.c brg_cfg.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_SOLICITATION solicitation.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_STATISTIC statistic.c)

View File

@@ -665,11 +665,19 @@ menu "Replay Protection List"
config BT_MESH_CRPL
int "Maximum capacity of the replay protection list"
default 10
default 26 if BT_MESH_BRG_CFG_SRV
range 2 65535
help
This options specifies the maximum capacity of the replay
This option specifies the maximum capacity of the replay
protection list. This option is similar to the network message
cache size, but has a different purpose.
cache size, but has a purpose of preventing replay attacks.
Note that: To ensure sufficient space in CRPL for normal node
operations and as specified by other Bluetooth specification
requirements, when subnet bridge functionality is enabled on a node,
you should increase the CRPL capacity in your project configuration
file with the number of bridging table entries
(BT_MESH_BRG_TABLE_ITEMS_MAX) specified for the project as a minimum.
choice BT_MESH_RPL_STORAGE_MODE
prompt "Replay protection list storage mode"
@@ -1227,6 +1235,43 @@ config BT_MESH_SOL_PDU_RPL_CLI_TIMEOUT
for a response message to arrive. This value can be changed at runtime
using @ref bt_mesh_sol_pdu_rpl_cli_timeout_set.
config BT_MESH_BRG_CFG_SRV
bool "Support for Bridge Configuration Server model"
depends on BT_MESH_MODEL_EXTENSIONS
help
The Bridge Configuration Server model is used to support the configuration
of the subnet bridge functionality of a node.
menu "Subnet Bridge configuration"
visible if BT_MESH_BRG_CFG_SRV
config BT_MESH_BRG_TABLE_ITEMS_MAX
int "Maximum number of entries in the bridging table"
default 16
range 16 255
help
The maximum number of entries in the bridging table.
endmenu
config BT_MESH_BRG_CFG_CLI
bool "Support for Bridge Configuration Client model"
help
The Bridge Configuration Client is used to support the functionality of a
node that can configure the subnet bridge functionality of another node.
if BT_MESH_BRG_CFG_CLI
config BT_MESH_BRG_CFG_CLI_TIMEOUT
int "Bridge Configuration Client model timeout in milliseconds"
default 5000
help
This timeout controls how long the bridge configuration client waits for a
response message to arrive. This value can be changed at runtime using
@ref bt_mesh_brg_cfg_cli_timeout_set.
endif # BT_MESH_BRG_CFG_CLI
endmenu # Models
menu "Proxy"

View File

@@ -360,8 +360,9 @@ void bt_mesh_adv_relay_ready(void)
}
}
/* Attempt to use the main adv set for the sending of relay messages. */
if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET)) {
/* Use the main adv set for the sending of relay messages. */
if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET) ||
CONFIG_BT_MESH_RELAY_ADV_SETS == 0) {
(void)schedule_send(advs);
}
}

View File

@@ -0,0 +1,367 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Implementation for states of Subnet Bridge feature in Bluetooth Mesh Protocol v1.1
* specification
*/
#include <errno.h>
#include <zephyr/bluetooth/mesh.h>
#include "mesh.h"
#include "net.h"
#include "settings.h"
#include "brg_cfg.h"
#include "foundation.h"
#define LOG_LEVEL CONFIG_BT_MESH_BRG_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_brg_cfg);
/* Bridging table state and counter */
static struct bt_mesh_brg_cfg_row brg_tbl[CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX];
static uint32_t bt_mesh_brg_cfg_row_cnt;
/* Bridging enabled state */
static bool brg_enabled;
enum {
STATE_UPDATED,
TABLE_UPDATED,
BRG_CFG_FLAGS_COUNT,
};
static ATOMIC_DEFINE(brg_cfg_flags, BRG_CFG_FLAGS_COUNT);
static void brg_tbl_compact(void)
{
int j = 0;
for (int k = 0; k < bt_mesh_brg_cfg_row_cnt; k++) {
if (brg_tbl[k].direction != 0) {
brg_tbl[j] = brg_tbl[k];
j++;
}
}
memset(&brg_tbl[j], 0, sizeof(brg_tbl[j]));
bt_mesh_brg_cfg_row_cnt--;
}
/* Set function for initializing bridging enable state from value stored in settings. */
static int brg_en_set(const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg)
{
int err;
if (len_rd == 0) {
brg_enabled = 0;
LOG_DBG("Cleared bridge enable state");
return 0;
}
err = bt_mesh_settings_set(read_cb, cb_arg, &brg_enabled, sizeof(brg_enabled));
if (err) {
LOG_ERR("Failed to set bridge enable state");
return err;
}
LOG_DBG("Restored bridge enable state");
return 0;
}
/* Define a setting for storing enable state */
BT_MESH_SETTINGS_DEFINE(brg_en, "brg_en", brg_en_set);
/* Set function for initializing bridging table rows from values stored in settings. */
static int brg_tbl_set(const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg)
{
ssize_t len;
if (len_rd == 0) {
memset(brg_tbl, 0, sizeof(brg_tbl));
bt_mesh_brg_cfg_row_cnt = 0;
LOG_DBG("Cleared bridging table entries");
return 0;
}
if (len_rd % sizeof(brg_tbl[0])) {
LOG_ERR("Invalid data size");
return -EINVAL;
}
if (len_rd > sizeof(brg_tbl)) {
LOG_ERR("Too many entries to fit in bridging table");
return -ENOMEM;
}
len = read_cb(cb_arg, brg_tbl, sizeof(brg_tbl));
if (len < 0 || len % sizeof(brg_tbl[0])) {
LOG_ERR("Failed to read bridging table entries (err %zd)", len);
return len < 0 ? len : -EINVAL;
}
bt_mesh_brg_cfg_row_cnt = len / sizeof(brg_tbl[0]);
LOG_DBG("Restored %d entries in bridging table", bt_mesh_brg_cfg_row_cnt);
return 0;
}
/* Define a setting for storing briging table rows */
BT_MESH_SETTINGS_DEFINE(brg_tbl, "brg_tbl", brg_tbl_set);
bool bt_mesh_brg_cfg_enable_get(void)
{
return brg_enabled;
}
int bt_mesh_brg_cfg_enable_set(bool enable)
{
if (brg_enabled == enable) {
return 0;
}
brg_enabled = enable;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
atomic_set_bit(brg_cfg_flags, STATE_UPDATED);
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
}
return 0;
}
void bt_mesh_brg_cfg_pending_store(void)
{
char *path_en = "bt/mesh/brg_en";
char *path_tbl = "bt/mesh/brg_tbl";
int err;
if (atomic_test_and_clear_bit(brg_cfg_flags, STATE_UPDATED)) {
if (brg_enabled) {
err = settings_save_one(path_en, &brg_enabled, sizeof(brg_enabled));
} else {
err = settings_delete(path_en);
}
if (err) {
LOG_ERR("Failed to store %s value", path_en);
}
}
if (atomic_test_and_clear_bit(brg_cfg_flags, TABLE_UPDATED)) {
if (bt_mesh_brg_cfg_row_cnt) {
err = settings_save_one(path_tbl, &brg_tbl,
bt_mesh_brg_cfg_row_cnt * sizeof(brg_tbl[0]));
} else {
err = settings_delete(path_tbl);
}
if (err) {
LOG_ERR("Failed to store %s value", path_tbl);
}
}
}
/* Remove the entry from the bridging table that corresponds with the NetKey Index of the removed
* subnet.
*/
static void brg_tbl_netkey_removed_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
if (evt != BT_MESH_KEY_DELETED) {
return;
}
for (int i = 0; i < CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX; i++) {
if (brg_tbl[i].direction && (
brg_tbl[i].net_idx1 == sub->net_idx ||
brg_tbl[i].net_idx2 == sub->net_idx)) {
memset(&brg_tbl[i], 0, sizeof(brg_tbl[i]));
brg_tbl_compact();
}
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
atomic_set_bit(brg_cfg_flags, TABLE_UPDATED);
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
}
}
/* Add event hook for key deletion event */
BT_MESH_SUBNET_CB_DEFINE(sbr) = {
.evt_handler = brg_tbl_netkey_removed_evt,
};
int bt_mesh_brg_cfg_tbl_reset(void)
{
int err = 0;
brg_enabled = false;
bt_mesh_brg_cfg_row_cnt = 0;
memset(brg_tbl, 0, sizeof(brg_tbl));
atomic_clear(brg_cfg_flags);
if (!IS_ENABLED(CONFIG_BT_SETTINGS)) {
return 0;
}
err = settings_delete("bt/mesh/brg_en");
if (err) {
return err;
}
err = settings_delete("bt/mesh/brg_tbl");
return err;
}
int bt_mesh_brg_cfg_tbl_get(const struct bt_mesh_brg_cfg_row **rows)
{
*rows = brg_tbl;
return bt_mesh_brg_cfg_row_cnt;
}
static bool netkey_check(uint16_t net_idx1, uint16_t net_idx2)
{
return bt_mesh_subnet_get(net_idx1) && bt_mesh_subnet_get(net_idx2);
}
int bt_mesh_brg_cfg_tbl_add(enum bt_mesh_brg_cfg_dir direction, uint16_t net_idx1,
uint16_t net_idx2, uint16_t addr1, uint16_t addr2, uint8_t *status)
{
/* Sanity checks */
if (!BT_MESH_ADDR_IS_UNICAST(addr1) || net_idx1 == net_idx2 ||
addr1 == addr2 || net_idx1 > BT_MESH_BRG_CFG_KEY_INDEX_MAX ||
net_idx2 > BT_MESH_BRG_CFG_KEY_INDEX_MAX) {
return -EINVAL;
}
if (direction != BT_MESH_BRG_CFG_DIR_ONEWAY &&
direction != BT_MESH_BRG_CFG_DIR_TWOWAY) {
return -EINVAL;
}
if ((direction == BT_MESH_BRG_CFG_DIR_ONEWAY &&
(addr2 == BT_MESH_ADDR_UNASSIGNED || addr2 == BT_MESH_ADDR_ALL_NODES)) ||
(direction == BT_MESH_BRG_CFG_DIR_TWOWAY &&
!BT_MESH_ADDR_IS_UNICAST(addr2))) {
return -EINVAL;
}
if (!netkey_check(net_idx1, net_idx2)) {
*status = STATUS_INVALID_NETKEY;
return 0;
}
/* Check if entry already exists, if yes, then, update the direction field and it is a
* success.
* "If a Bridging Table state entry corresponding to the received message exists, the
* element shall set the Directions field in the entry to the value of the Directions field
* in the received message."
*/
for (int i = 0; i < bt_mesh_brg_cfg_row_cnt; i++) {
if (brg_tbl[i].net_idx1 == net_idx1 &&
brg_tbl[i].net_idx2 == net_idx2 && brg_tbl[i].addr1 == addr1 &&
brg_tbl[i].addr2 == addr2) {
brg_tbl[i].direction = direction;
goto store;
}
}
/* Empty element, is the current table row counter */
if (bt_mesh_brg_cfg_row_cnt == CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX) {
*status = STATUS_INSUFF_RESOURCES;
return 0;
}
/* Update the row */
brg_tbl[bt_mesh_brg_cfg_row_cnt].direction = direction;
brg_tbl[bt_mesh_brg_cfg_row_cnt].net_idx1 = net_idx1;
brg_tbl[bt_mesh_brg_cfg_row_cnt].net_idx2 = net_idx2;
brg_tbl[bt_mesh_brg_cfg_row_cnt].addr1 = addr1;
brg_tbl[bt_mesh_brg_cfg_row_cnt].addr2 = addr2;
bt_mesh_brg_cfg_row_cnt++;
store:
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
atomic_set_bit(brg_cfg_flags, TABLE_UPDATED);
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
}
*status = STATUS_SUCCESS;
return 0;
}
void bt_mesh_brg_cfg_tbl_foreach_subnet(uint16_t src, uint16_t dst, uint16_t net_idx,
bt_mesh_brg_cfg_cb_t cb, void *user_data)
{
for (int i = 0; i < bt_mesh_brg_cfg_row_cnt; i++) {
if ((brg_tbl[i].direction == BT_MESH_BRG_CFG_DIR_ONEWAY ||
brg_tbl[i].direction == BT_MESH_BRG_CFG_DIR_TWOWAY) &&
brg_tbl[i].net_idx1 == net_idx && brg_tbl[i].addr1 == src &&
brg_tbl[i].addr2 == dst) {
cb(brg_tbl[i].net_idx2, user_data);
} else if ((brg_tbl[i].direction == BT_MESH_BRG_CFG_DIR_TWOWAY &&
brg_tbl[i].net_idx2 == net_idx && brg_tbl[i].addr2 == src &&
brg_tbl[i].addr1 == dst)) {
cb(brg_tbl[i].net_idx1, user_data);
}
}
}
int bt_mesh_brg_cfg_tbl_remove(uint16_t net_idx1, uint16_t net_idx2, uint16_t addr1,
uint16_t addr2, uint8_t *status)
{
bool store = false;
/* Sanity checks */
if ((!BT_MESH_ADDR_IS_UNICAST(addr1) && addr1 != BT_MESH_ADDR_UNASSIGNED) ||
(BT_MESH_ADDR_IS_UNICAST(addr1) && addr1 == addr2) ||
addr2 == BT_MESH_ADDR_ALL_NODES) {
return -EINVAL;
}
if (net_idx1 == net_idx2 || net_idx1 > BT_MESH_BRG_CFG_KEY_INDEX_MAX ||
net_idx2 > BT_MESH_BRG_CFG_KEY_INDEX_MAX) {
return -EINVAL;
}
if (!netkey_check(net_idx1, net_idx2)) {
*status = STATUS_INVALID_NETKEY;
return 0;
}
/* Iterate over items and set matching row to 0, if nothing exist, or nothing matches, then
* it is success (similar to add)
*/
if (bt_mesh_brg_cfg_row_cnt == 0) {
*status = STATUS_SUCCESS;
return 0;
}
for (int i = 0; i < bt_mesh_brg_cfg_row_cnt; i++) {
/* Match according to remove behavior in Section 4.4.9.2.2 of MshPRT_v1.1 */
if (brg_tbl[i].direction) {
if (!(brg_tbl[i].net_idx1 == net_idx1 && brg_tbl[i].net_idx2 == net_idx2)) {
continue;
}
if ((brg_tbl[i].addr1 == addr1 && brg_tbl[i].addr2 == addr2) ||
(addr2 == BT_MESH_ADDR_UNASSIGNED && brg_tbl[i].addr1 == addr1) ||
(addr1 == BT_MESH_ADDR_UNASSIGNED && brg_tbl[i].addr2 == addr2)) {
memset(&brg_tbl[i], 0, sizeof(brg_tbl[i]));
store = true;
}
}
}
/* Compact when all rows have been deleted. */
brg_tbl_compact();
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
atomic_set_bit(brg_cfg_flags, TABLE_UPDATED);
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
}
*status = STATUS_SUCCESS;
return 0;
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_BLUETOOTH_MESH_BRG_CFG_H_
#define ZEPHYR_SUBSYS_BLUETOOTH_MESH_BRG_CFG_H_
/** These are internal APIs. They do not sanitize input params. */
#define BT_MESH_BRG_CFG_KEY_INDEX_MAX 0x0FFF
#define BT_MESH_BRG_CFG_NKEY_PRHB_FLT_MASK 0x000C
enum bt_mesh_brg_cfg_dir {
/* Value is prohibited. */
BT_MESH_BRG_CFG_DIR_PROHIBITED = 0,
/* Briging from Addr1 to Addr2. */
BT_MESH_BRG_CFG_DIR_ONEWAY = 1,
/* Briging to/from Addr1 from/to Addr2. */
BT_MESH_BRG_CFG_DIR_TWOWAY = 2,
/* Values above these are prohibited. */
BT_MESH_BRG_CFG_DIR_MAX = 3,
};
#define BT_MESH_BRG_CFG_NETIDX_NOMATCH 0xFFFF
/* One row of the bridging table */
struct bt_mesh_brg_cfg_row {
/* Direction of the entry in the bridging table
* 0 - no entry,
* 1 - bridge messages with src as addr1 and dst as addr2
* 2 - bridge messages with src as addr1 and dst as addr2 and vice-versa
*/
uint32_t direction:8;
uint32_t net_idx1:12;
uint32_t net_idx2:12;
uint16_t addr1;
uint16_t addr2;
};
bool bt_mesh_brg_cfg_enable_get(void);
int bt_mesh_brg_cfg_enable_set(bool enable);
void bt_mesh_brg_cfg_pending_store(void);
int bt_mesh_brg_cfg_tbl_reset(void);
int bt_mesh_brg_cfg_tbl_get(const struct bt_mesh_brg_cfg_row **rows);
int bt_mesh_brg_cfg_tbl_add(enum bt_mesh_brg_cfg_dir direction, uint16_t net_idx1,
uint16_t net_idx2, uint16_t addr1, uint16_t addr2, uint8_t *status);
int bt_mesh_brg_cfg_tbl_remove(uint16_t net_idx1, uint16_t net_idx2, uint16_t addr1,
uint16_t addr2, uint8_t *status);
typedef void (*bt_mesh_brg_cfg_cb_t)(uint16_t new_netidx, void *user_data);
/**
* @brief Iterate over the bridging table to find a matching entry for the given SRC, DST, and
* NetKey Index.
*
* This function iterates over the bridging table and checks if there is a match for the provided
* parameters. If a match is found, the callback function specified by the 'cb' parameter is
* invoked with the NetKey Index of each matching entry (there can be several). Relaying operation
* can then happen inside this callback.
*
* @param src The source address to match.
* @param dst The destination address to match.
* @param net_idx The NetKey Index to match.
* @param cb The callback function to be invoked for each matching entry.
* @param user_data User data to be passed to the callback function.
*/
void bt_mesh_brg_cfg_tbl_foreach_subnet(uint16_t src, uint16_t dst, uint16_t net_idx,
bt_mesh_brg_cfg_cb_t cb, void *user_data);
#endif /* ZEPHYR_SUBSYS_BLUETOOTH_MESH_BRG_CFG_H_ */

View File

@@ -0,0 +1,373 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#include "access.h"
#include "foundation.h"
#include "msg.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_brg_cfg_cli);
static int32_t msg_timeout;
static struct bt_mesh_brg_cfg_cli *cli;
static int subnet_bridge_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
enum bt_mesh_subnet_bridge_state status =
(enum bt_mesh_subnet_bridge_state)net_buf_simple_pull_u8(buf);
enum bt_mesh_subnet_bridge_state *rsp;
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_SUBNET_BRIDGE_STATUS, ctx->addr,
(void **)&rsp)) {
*rsp = status;
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
}
if (cli->cb && cli->cb->subnet_bridge_status) {
cli->cb->subnet_bridge_status(cli, ctx->addr, status);
}
return 0;
}
static int bridging_table_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_bridging_table_status table_status;
struct bt_mesh_bridging_table_status *rsp;
table_status.status = net_buf_simple_pull_u8(buf);
table_status.entry.directions = net_buf_simple_pull_u8(buf);
key_idx_unpack_pair(buf, &table_status.entry.net_idx1, &table_status.entry.net_idx2);
table_status.entry.addr1 = net_buf_simple_pull_le16(buf);
table_status.entry.addr2 = net_buf_simple_pull_le16(buf);
if (!(table_status.entry.addr1 == BT_MESH_ADDR_UNASSIGNED ||
BT_MESH_ADDR_IS_UNICAST(table_status.entry.addr1))) {
LOG_ERR("addr1 shall be a unicast address or unassigned.");
return -EINVAL;
} else if (table_status.entry.addr2 == BT_MESH_ADDR_ALL_NODES) {
LOG_ERR("addr2 shall not be the all-nodes fixed group address.");
return -EINVAL;
}
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_STATUS, ctx->addr,
(void **)&rsp)) {
*rsp = table_status;
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
}
if (cli->cb && cli->cb->bridging_table_status) {
cli->cb->bridging_table_status(cli, ctx->addr, &table_status);
}
return 0;
}
static int bridged_subnets_list(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_bridged_subnets_list subnets_list;
struct bt_mesh_bridged_subnets_list *rsp;
uint16_t net_idx_filter;
net_idx_filter = net_buf_simple_pull_le16(buf);
subnets_list.net_idx_filter.filter = net_idx_filter & BIT_MASK(2);
subnets_list.net_idx_filter.net_idx = (net_idx_filter >> 4) & BIT_MASK(12);
subnets_list.start_idx = net_buf_simple_pull_u8(buf);
if (buf->len && !(buf->len % 3)) {
subnets_list.list = buf;
} else {
subnets_list.list = NULL;
}
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGED_SUBNETS_LIST, ctx->addr,
(void **)&rsp)) {
rsp->net_idx_filter = subnets_list.net_idx_filter;
rsp->start_idx = subnets_list.start_idx;
if (rsp->list) {
size_t to_copy;
to_copy = MIN(net_buf_simple_tailroom(rsp->list), buf->len);
net_buf_simple_add_mem(rsp->list, buf->data, to_copy);
}
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
}
if (cli->cb && cli->cb->bridged_subnets_list) {
cli->cb->bridged_subnets_list(cli, ctx->addr, &subnets_list);
}
return 0;
}
static int bridging_table_list(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_bridging_table_list table_list;
struct bt_mesh_bridging_table_list *rsp;
table_list.status = net_buf_simple_pull_u8(buf);
key_idx_unpack_pair(buf, &table_list.net_idx1, &table_list.net_idx2);
table_list.start_idx = net_buf_simple_pull_le16(buf);
if ((table_list.status == STATUS_SUCCESS) && buf->len && !(buf->len % 5)) {
table_list.list = buf;
} else {
table_list.list = NULL;
}
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_LIST, ctx->addr,
(void **)&rsp)) {
rsp->status = table_list.status;
rsp->net_idx1 = table_list.net_idx1;
rsp->net_idx2 = table_list.net_idx2;
rsp->start_idx = table_list.start_idx;
if (rsp->list) {
size_t to_copy;
to_copy = MIN(net_buf_simple_tailroom(rsp->list), (buf->len / 5) * 5);
net_buf_simple_add_mem(rsp->list, buf->data, to_copy);
}
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
}
if (cli->cb && cli->cb->bridging_table_list) {
cli->cb->bridging_table_list(cli, ctx->addr, &table_list);
}
return 0;
}
static int bridging_table_size_status(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
{
uint16_t size = net_buf_simple_pull_le16(buf);
uint16_t *rsp;
if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_SIZE_STATUS, ctx->addr,
(void **)&rsp)) {
*rsp = size;
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
}
if (cli->cb && cli->cb->bridging_table_size_status) {
cli->cb->bridging_table_size_status(cli, ctx->addr, size);
}
return 0;
}
const struct bt_mesh_model_op _bt_mesh_brg_cfg_cli_op[] = {
{ OP_SUBNET_BRIDGE_STATUS, BT_MESH_LEN_EXACT(1), subnet_bridge_status },
{ OP_BRIDGING_TABLE_STATUS, BT_MESH_LEN_EXACT(9), bridging_table_status },
{ OP_BRIDGED_SUBNETS_LIST, BT_MESH_LEN_MIN(3), bridged_subnets_list },
{ OP_BRIDGING_TABLE_LIST, BT_MESH_LEN_MIN(6), bridging_table_list },
{ OP_BRIDGING_TABLE_SIZE_STATUS, BT_MESH_LEN_EXACT(2), bridging_table_size_status },
BT_MESH_MODEL_OP_END,
};
static int brg_cfg_cli_init(const struct bt_mesh_model *model)
{
if (!bt_mesh_model_in_primary(model)) {
LOG_ERR("Bridge Configuration Client only allowed in primary element");
return -EINVAL;
}
if (!model->rt->user_data) {
LOG_ERR("No Bridge Configuration Client context provided");
return -EINVAL;
}
cli = model->rt->user_data;
cli->model = model;
msg_timeout = CONFIG_BT_MESH_BRG_CFG_CLI_TIMEOUT;
model->keys[0] = BT_MESH_KEY_DEV_ANY;
model->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
return 0;
}
const struct bt_mesh_model_cb _bt_mesh_brg_cfg_cli_cb = {
.init = brg_cfg_cli_init,
};
int bt_mesh_brg_cfg_cli_subnet_bridge_get(uint16_t net_idx, uint16_t addr,
enum bt_mesh_subnet_bridge_state *status)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_GET, 0);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_SUBNET_BRIDGE_STATUS,
.user_data = status,
.timeout = msg_timeout,
};
bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_GET);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !status ? NULL : &rsp_ctx);
}
int bt_mesh_brg_cfg_cli_subnet_bridge_set(uint16_t net_idx, uint16_t addr,
enum bt_mesh_subnet_bridge_state val,
enum bt_mesh_subnet_bridge_state *status)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_SET, 1);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_SUBNET_BRIDGE_STATUS,
.user_data = status,
.timeout = msg_timeout,
};
bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_SET);
net_buf_simple_add_u8(&msg, (uint8_t)val);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !status ? NULL : &rsp_ctx);
}
int bt_mesh_brg_cfg_cli_bridging_table_size_get(uint16_t net_idx, uint16_t addr, uint16_t *size)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_SIZE_GET, 0);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_BRIDGING_TABLE_SIZE_STATUS,
.user_data = size,
.timeout = msg_timeout,
};
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_SIZE_GET);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !size ? NULL : &rsp_ctx);
}
int bt_mesh_brg_cfg_cli_bridging_table_add(uint16_t net_idx, uint16_t addr,
struct bt_mesh_bridging_table_entry *entry,
struct bt_mesh_bridging_table_status *rsp)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_ADD, 8);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_BRIDGING_TABLE_STATUS,
.user_data = rsp,
.timeout = msg_timeout,
};
if (entry->addr1 == entry->addr2) {
LOG_ERR("addr1 and addr2 shall have different values.");
return -EINVAL;
} else if (!BT_MESH_ADDR_IS_UNICAST(entry->addr1)) {
LOG_ERR("addr1 shall be a unicast address.");
return -EINVAL;
} else if (entry->directions == 0x01 && (entry->addr2 == BT_MESH_ADDR_UNASSIGNED ||
entry->addr2 == BT_MESH_ADDR_ALL_NODES)) {
LOG_ERR("For direction 0x01: addr2 shall not be unassigned or the all-nodes fixed "
"group address.");
return -EINVAL;
} else if (entry->directions == 0x02 && !BT_MESH_ADDR_IS_UNICAST(entry->addr2)) {
LOG_ERR("For direction 0x02: addr2 shall be a unicast address.");
return -EINVAL;
}
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_ADD);
net_buf_simple_add_u8(&msg, entry->directions);
key_idx_pack_pair(&msg, entry->net_idx1, entry->net_idx2);
net_buf_simple_add_le16(&msg, entry->addr1);
net_buf_simple_add_le16(&msg, entry->addr2);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
}
int bt_mesh_brg_cfg_cli_bridging_table_remove(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
uint16_t net_idx2, uint16_t addr1, uint16_t addr2,
struct bt_mesh_bridging_table_status *rsp)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_REMOVE, 7);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_BRIDGING_TABLE_STATUS,
.user_data = rsp,
.timeout = msg_timeout,
};
if (!(addr1 == BT_MESH_ADDR_UNASSIGNED || BT_MESH_ADDR_IS_UNICAST(addr1))) {
LOG_ERR("addr1 shall be a unicast address or unassigned.");
return -EINVAL;
} else if (addr2 == BT_MESH_ADDR_ALL_NODES) {
LOG_ERR("addr2 shall not be the all-nodes fixed group address.");
return -EINVAL;
}
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_REMOVE);
key_idx_pack_pair(&msg, net_idx1, net_idx2);
net_buf_simple_add_le16(&msg, addr1);
net_buf_simple_add_le16(&msg, addr2);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
}
int bt_mesh_brg_cfg_cli_bridged_subnets_get(uint16_t net_idx, uint16_t addr,
struct bt_mesh_filter_netkey filter_net_idx,
uint8_t start_idx,
struct bt_mesh_bridged_subnets_list *rsp)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGED_SUBNETS_GET, 3);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_BRIDGED_SUBNETS_LIST,
.user_data = rsp,
.timeout = msg_timeout,
};
bt_mesh_model_msg_init(&msg, OP_BRIDGED_SUBNETS_GET);
net_buf_simple_add_le16(&msg, (filter_net_idx.filter | filter_net_idx.net_idx << 4));
net_buf_simple_add_u8(&msg, start_idx);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
}
int bt_mesh_brg_cfg_cli_bridging_table_get(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
uint16_t net_idx2, uint16_t start_idx,
struct bt_mesh_bridging_table_list *rsp)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_GET, 5);
struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
.ack = &cli->ack_ctx,
.op = OP_BRIDGING_TABLE_LIST,
.user_data = rsp,
.timeout = msg_timeout,
};
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_GET);
key_idx_pack_pair(&msg, net_idx1, net_idx2);
net_buf_simple_add_le16(&msg, start_idx);
return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
}
int32_t bt_mesh_brg_cfg_cli_timeout_get(void)
{
return msg_timeout;
}
void bt_mesh_brg_cfg_cli_timeout_set(int32_t timeout)
{
msg_timeout = timeout;
}

View File

@@ -0,0 +1,323 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/bluetooth/mesh/brg_cfg.h>
#include "access.h"
#include "brg_cfg.h"
#include "foundation.h"
#include "subnet.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_brg_cfg_srv);
static void bridge_status_send(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_STATUS, 1);
bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_STATUS);
net_buf_simple_add_u8(&msg, bt_mesh_brg_cfg_enable_get() ? 1 : 0);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Brg Status send failed");
}
}
static int subnet_bridge_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
bridge_status_send(model, ctx);
return 0;
}
static int subnet_bridge_set(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t enable = net_buf_simple_pull_u8(buf);
if (enable > BT_MESH_SUBNET_BRIDGE_ENABLED) {
return -EINVAL;
}
bt_mesh_brg_cfg_enable_set(enable);
bridge_status_send(model, ctx);
return 0;
}
static void bridging_table_status_send(const struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint8_t status,
struct bt_mesh_bridging_table_entry *entry)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_STATUS, 9);
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_u8(&msg, entry->directions);
key_idx_pack_pair(&msg, entry->net_idx1, entry->net_idx2);
net_buf_simple_add_le16(&msg, entry->addr1);
net_buf_simple_add_le16(&msg, entry->addr2);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Brg Tbl Status send failed");
}
}
static bool netkey_check(uint16_t net_idx1, uint16_t net_idx2)
{
return bt_mesh_subnet_get(net_idx1) && bt_mesh_subnet_get(net_idx2);
}
static int bridging_table_add(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_bridging_table_entry entry;
uint8_t status = STATUS_SUCCESS;
int err;
entry.directions = net_buf_simple_pull_u8(buf);
key_idx_unpack_pair(buf, &entry.net_idx1, &entry.net_idx2);
entry.addr1 = net_buf_simple_pull_le16(buf);
entry.addr2 = net_buf_simple_pull_le16(buf);
err = bt_mesh_brg_cfg_tbl_add(entry.directions, entry.net_idx1, entry.net_idx2, entry.addr1,
entry.addr2, &status);
if (err) {
return err;
}
bridging_table_status_send(model, ctx, status, &entry);
return 0;
}
static int bridging_table_remove(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_bridging_table_entry entry;
uint8_t status = STATUS_SUCCESS;
int err;
entry.directions = 0;
key_idx_unpack_pair(buf, &entry.net_idx1, &entry.net_idx2);
entry.addr1 = net_buf_simple_pull_le16(buf);
entry.addr2 = net_buf_simple_pull_le16(buf);
err = bt_mesh_brg_cfg_tbl_remove(entry.net_idx1, entry.net_idx2, entry.addr1, entry.addr2,
&status);
if (err) {
return err;
}
bridging_table_status_send(model, ctx, status, &entry);
return 0;
}
static int bridged_subnets_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGED_SUBNETS_LIST,
BT_MESH_TX_SDU_MAX - BT_MESH_MODEL_OP_LEN(OP_BRIDGED_SUBNETS_LIST));
bt_mesh_model_msg_init(&msg, OP_BRIDGED_SUBNETS_LIST);
const struct bt_mesh_brg_cfg_row *brg_tbl;
int rows = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
int16_t net_idx_filter = net_buf_simple_pull_le16(buf);
if (net_idx_filter & BT_MESH_BRG_CFG_NKEY_PRHB_FLT_MASK) {
return -EINVAL;
}
struct bt_mesh_filter_netkey filter_net_idx;
filter_net_idx.filter = net_idx_filter & BIT_MASK(2);
filter_net_idx.net_idx = (net_idx_filter >> 4) & BIT_MASK(12);
uint8_t start_id = net_buf_simple_pull_u8(buf);
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;
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);
}
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++;
}
break;
default:
CODE_UNREACHABLE;
}
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Brg Subnet List send failed");
}
return 0;
}
static int bridging_table_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_LIST,
BT_MESH_TX_SDU_MAX - BT_MESH_MODEL_OP_LEN(OP_BRIDGING_TABLE_LIST));
uint8_t status = STATUS_SUCCESS;
uint16_t net_idx1, net_idx2;
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_LIST);
key_idx_unpack_pair(buf, &net_idx1, &net_idx2);
uint16_t start_id = net_buf_simple_pull_le16(buf);
if (!netkey_check(net_idx1, net_idx2)) {
status = STATUS_INVALID_NETKEY;
}
net_buf_simple_add_u8(&msg, status);
key_idx_pack_pair(&msg, net_idx1, net_idx2);
net_buf_simple_add_le16(&msg, start_id);
if (status != STATUS_SUCCESS) {
goto tbl_get_respond;
}
int cnt = 0;
const struct bt_mesh_brg_cfg_row *brg_tbl;
int rows = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
for (int i = 0; i < rows; i++) {
if (brg_tbl[i].net_idx1 == net_idx1 && brg_tbl[i].net_idx2 == net_idx2) {
if (cnt >= start_id) {
if (net_buf_simple_tailroom(&msg) < 5 + BT_MESH_MIC_SHORT) {
LOG_WRN("Bridging Table List message too large");
break;
}
net_buf_simple_add_le16(&msg, brg_tbl[i].addr1);
net_buf_simple_add_le16(&msg, brg_tbl[i].addr2);
net_buf_simple_add_u8(&msg, brg_tbl[i].direction);
}
cnt++;
}
}
tbl_get_respond:
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Brg Tbl List send failed");
}
return 0;
}
static int bridging_table_size_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_SIZE_STATUS, 2);
bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_SIZE_STATUS);
net_buf_simple_add_le16(&msg, CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
LOG_ERR("Brg Tbl Size Status send failed");
}
return 0;
}
const struct bt_mesh_model_op _bt_mesh_brg_cfg_srv_op[] = {
{ OP_SUBNET_BRIDGE_GET, BT_MESH_LEN_EXACT(0), subnet_bridge_get },
{ OP_SUBNET_BRIDGE_SET, BT_MESH_LEN_EXACT(1), subnet_bridge_set },
{ OP_BRIDGING_TABLE_ADD, BT_MESH_LEN_EXACT(8), bridging_table_add },
{ OP_BRIDGING_TABLE_REMOVE, BT_MESH_LEN_EXACT(7), bridging_table_remove },
{ OP_BRIDGED_SUBNETS_GET, BT_MESH_LEN_EXACT(3), bridged_subnets_get },
{ OP_BRIDGING_TABLE_GET, BT_MESH_LEN_EXACT(5), bridging_table_get },
{ OP_BRIDGING_TABLE_SIZE_GET, BT_MESH_LEN_EXACT(0), bridging_table_size_get },
BT_MESH_MODEL_OP_END,
};
static int brg_cfg_srv_init(const struct bt_mesh_model *model)
{
const struct bt_mesh_model *config_srv =
bt_mesh_model_find(bt_mesh_model_elem(model), BT_MESH_MODEL_ID_CFG_SRV);
if (config_srv == NULL) {
LOG_ERR("Not on primary element");
return -EINVAL;
}
/*
* Bridge Configuration Server model security is device key based and only the local
* device key is allowed to access this model.
*/
model->keys[0] = BT_MESH_KEY_DEV_LOCAL;
model->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
bt_mesh_model_extend(model, config_srv);
return 0;
}
void brg_cfg_srv_reset(const struct bt_mesh_model *model)
{
bt_mesh_brg_cfg_tbl_reset();
}
const struct bt_mesh_model_cb _bt_mesh_brg_cfg_srv_cb = {
.init = brg_cfg_srv_init,
.reset = brg_cfg_srv_reset,
};

View File

@@ -122,6 +122,19 @@
#define OP_SOL_PDU_RPL_ITEM_CLEAR_UNACKED BT_MESH_MODEL_OP_2(0x80, 0x79)
#define OP_SOL_PDU_RPL_ITEM_STATUS BT_MESH_MODEL_OP_2(0x80, 0x7a)
#define OP_SUBNET_BRIDGE_GET BT_MESH_MODEL_OP_2(0x80, 0xb1)
#define OP_SUBNET_BRIDGE_SET BT_MESH_MODEL_OP_2(0x80, 0xb2)
#define OP_SUBNET_BRIDGE_STATUS BT_MESH_MODEL_OP_2(0x80, 0xb3)
#define OP_BRIDGING_TABLE_ADD BT_MESH_MODEL_OP_2(0x80, 0xb4)
#define OP_BRIDGING_TABLE_REMOVE BT_MESH_MODEL_OP_2(0x80, 0xb5)
#define OP_BRIDGING_TABLE_STATUS BT_MESH_MODEL_OP_2(0x80, 0xb6)
#define OP_BRIDGED_SUBNETS_GET BT_MESH_MODEL_OP_2(0x80, 0xb7)
#define OP_BRIDGED_SUBNETS_LIST BT_MESH_MODEL_OP_2(0x80, 0xb8)
#define OP_BRIDGING_TABLE_GET BT_MESH_MODEL_OP_2(0x80, 0xb9)
#define OP_BRIDGING_TABLE_LIST BT_MESH_MODEL_OP_2(0x80, 0xba)
#define OP_BRIDGING_TABLE_SIZE_GET BT_MESH_MODEL_OP_2(0x80, 0xbb)
#define OP_BRIDGING_TABLE_SIZE_STATUS BT_MESH_MODEL_OP_2(0x80, 0xbc)
#define STATUS_SUCCESS 0x00
#define STATUS_INVALID_ADDRESS 0x01
#define STATUS_INVALID_MODEL 0x02

View File

@@ -36,6 +36,7 @@
#include "cfg.h"
#include "statistic.h"
#include "sar_cfg_internal.h"
#include "brg_cfg.h"
#define LOG_LEVEL CONFIG_BT_MESH_NET_LOG_LEVEL
#include <zephyr/logging/log.h>
@@ -54,6 +55,13 @@ LOG_MODULE_REGISTER(bt_mesh_net);
#define SRC(pdu) (sys_get_be16(&(pdu)[5]))
#define DST(pdu) (sys_get_be16(&(pdu)[7]))
/* Information needed for bridging the network PDUs */
struct pdu_ctx {
struct net_buf_simple *sbuf;
struct net_buf_simple_state *state;
struct bt_mesh_net_rx *rx;
};
/* Mesh network information for persistent storage. */
struct net_val {
uint16_t primary_addr;
@@ -675,7 +683,7 @@ static bool relay_to_adv(enum bt_mesh_net_if net_if)
}
static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
struct bt_mesh_net_rx *rx)
struct bt_mesh_net_rx *rx, bool bridge)
{
const struct bt_mesh_net_cred *cred;
struct bt_mesh_adv *adv;
@@ -687,6 +695,7 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
if (rx->net_if == BT_MESH_NET_IF_ADV &&
!rx->friend_cred &&
!bridge &&
bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED &&
bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED &&
bt_mesh_priv_gatt_proxy_get() != BT_MESH_PRIV_GATT_PROXY_ENABLED) {
@@ -722,8 +731,8 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
LOG_DBG("Relaying packet. TTL is now %u", TTL(adv->b.data));
/* Update NID if RX or RX was with friend credentials */
if (rx->friend_cred) {
/* Update NID if RX, RX was with friend credentials or when bridging the message */
if (rx->friend_cred || bridge) {
adv->b.data[0] &= 0x80; /* Clear everything except IVI */
adv->b.data[0] |= cred->nid;
}
@@ -748,7 +757,7 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
bt_mesh_proxy_relay(adv, rx->ctx.recv_dst);
}
if (relay_to_adv(rx->net_if) || rx->friend_cred) {
if (relay_to_adv(rx->net_if) || rx->friend_cred || bridge) {
bt_mesh_adv_send(adv, NULL, NULL);
}
@@ -756,6 +765,35 @@ done:
bt_mesh_adv_unref(adv);
}
#if IS_ENABLED(CONFIG_BT_MESH_BRG_CFG_SRV)
static bool find_subnet_cb(struct bt_mesh_subnet *sub, void *cb_data)
{
uint16_t *net_idx = cb_data;
return sub->net_idx == *net_idx;
}
static void bt_mesh_sbr_check_cb(uint16_t new_net_idx, void *user_data)
{
struct pdu_ctx *ctx = (struct pdu_ctx *)user_data;
if (new_net_idx < BT_MESH_BRG_CFG_NETIDX_NOMATCH) {
struct bt_mesh_subnet *subnet = bt_mesh_subnet_find(find_subnet_cb, &new_net_idx);
if (!subnet) {
LOG_ERR("Failed to find subnet 0x%04x", new_net_idx);
return;
}
ctx->rx->sub = subnet;
ctx->rx->ctx.net_idx = new_net_idx;
net_buf_simple_restore(ctx->sbuf, ctx->state);
bt_mesh_net_relay(ctx->sbuf, ctx->rx, true);
}
}
#endif
void bt_mesh_net_header_parse(struct net_buf_simple *buf,
struct bt_mesh_net_rx *rx)
{
@@ -891,8 +929,28 @@ void bt_mesh_net_recv(struct net_buf_simple *data, int8_t rssi,
if (!BT_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) ||
(!rx.local_match && !rx.friend_match)) {
net_buf_simple_restore(&buf, &state);
bt_mesh_net_relay(&buf, &rx);
bt_mesh_net_relay(&buf, &rx, false);
}
#if IS_ENABLED(CONFIG_BT_MESH_BRG_CFG_SRV)
struct pdu_ctx tx_ctx = {
.sbuf = &buf,
.state = &state,
.rx = &rx,
};
/* Bridge the traffic if enabled */
if (!bt_mesh_brg_cfg_enable_get()) {
return;
}
if (bt_mesh_rpl_check(&rx, NULL, true)) {
return;
}
bt_mesh_brg_cfg_tbl_foreach_subnet(rx.ctx.addr, rx.ctx.recv_dst, rx.ctx.net_idx,
bt_mesh_sbr_check_cb, &tx_ctx);
#endif
}
static void ivu_refresh(struct k_work *work)

View File

@@ -277,7 +277,7 @@ static void proxy_cfg(struct bt_mesh_proxy_role *role)
rx.local_match = 1U;
if (bt_mesh_rpl_check(&rx, NULL)) {
if (bt_mesh_rpl_check(&rx, NULL, false)) {
LOG_WRN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", rx.ctx.addr, rx.ctx.recv_dst,
rx.seq);
return;

View File

@@ -107,7 +107,7 @@ void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
* by upper logic (access, transport commands) and for receiving the segmented messages.
* If a NULL match is given the RPL is immediately updated (used for proxy configuration).
*/
bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match)
bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match, bool bridge)
{
struct bt_mesh_rpl *rpl;
int i;
@@ -117,8 +117,8 @@ bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match)
return false;
}
/* The RPL is used only for the local node */
if (!rx->local_match) {
/* The RPL is used only for the local node or Subnet Bridge. */
if (!rx->local_match && !bridge) {
return false;
}

View File

@@ -22,8 +22,7 @@ typedef void (*bt_mesh_rpl_func_t)(struct bt_mesh_rpl *rpl,
void *user_data);
void bt_mesh_rpl_reset(void);
bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx,
struct bt_mesh_rpl **match);
bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match, bool bridge);
void bt_mesh_rpl_clear(void);
void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
struct bt_mesh_net_rx *rx);

View File

@@ -29,6 +29,7 @@
#include "pb_gatt_srv.h"
#include "settings.h"
#include "cfg.h"
#include "brg_cfg.h"
#include "solicitation.h"
#include "va.h"
@@ -134,7 +135,8 @@ SETTINGS_STATIC_HANDLER_DEFINE(bt_mesh, "bt/mesh", NULL, NULL, mesh_commit,
BIT(BT_MESH_SETTINGS_VA_PENDING) | \
BIT(BT_MESH_SETTINGS_SSEQ_PENDING) | \
BIT(BT_MESH_SETTINGS_COMP_PENDING) | \
BIT(BT_MESH_SETTINGS_DEV_KEY_CAND_PENDING))
BIT(BT_MESH_SETTINGS_DEV_KEY_CAND_PENDING) | \
BIT(BT_MESH_SETTINGS_BRG_PENDING))
void bt_mesh_settings_store_schedule(enum bt_mesh_settings_flag flag)
{
@@ -262,6 +264,12 @@ static void store_pending(struct k_work *work)
BT_MESH_SETTINGS_SSEQ_PENDING)) {
bt_mesh_sseq_pending_store();
}
if (IS_ENABLED(CONFIG_BT_MESH_BRG_CFG_SRV) &&
atomic_test_and_clear_bit(pending_flags,
BT_MESH_SETTINGS_BRG_PENDING)) {
bt_mesh_brg_cfg_pending_store();
}
}
void bt_mesh_settings_init(void)

View File

@@ -21,6 +21,7 @@ enum bt_mesh_settings_flag {
BT_MESH_SETTINGS_SSEQ_PENDING,
BT_MESH_SETTINGS_COMP_PENDING,
BT_MESH_SETTINGS_DEV_KEY_CAND_PENDING,
BT_MESH_SETTINGS_BRG_PENDING,
BT_MESH_SETTINGS_FLAG_COUNT,
};

View File

@@ -34,3 +34,5 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_SHELL_DFD_SRV dfd.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_OD_PRIV_PROXY_CLI od_priv_proxy.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_SOL_PDU_RPL_CLI sol_pdu_rpl.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_SHELL_BRG_CFG_CLI brg_cfg.c)

View File

@@ -50,7 +50,7 @@ config BT_MESH_SHELL_TEST
This option enables support for test mesh shell commands.
config BT_MESH_SHELL_HEALTH_SRV_INSTANCE
bool "Support for shell Health Server model instance"
bool "Support for shell Health Server instance"
depends on BT_MESH_SHELL_TEST
help
This option enables Health Server model instance in the
@@ -67,14 +67,21 @@ config BT_MESH_SHELL_GATT_PROXY
This option enables support for GATT Proxy shell commands.
config BT_MESH_SHELL_HEALTH_CLI
bool "Support for Health Client Model shell commands"
bool "Support for Health Client shell commands"
depends on BT_MESH_HEALTH_CLI
default y
help
This option enables support of Health Client shell commands.
config BT_MESH_SHELL_BRG_CFG_CLI
bool "Support for Bridge Configuration Client shell commands"
depends on BT_MESH_BRG_CFG_CLI
default y
help
This option enables support of Bridge Configuration Client shell commands.
config BT_MESH_SHELL_CFG_CLI
bool "Support for Configuration Client Model shell commands"
bool "Support for Configuration Client shell commands"
depends on BT_MESH_CFG_CLI
default y
help

View File

@@ -0,0 +1,249 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/shell/shell.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/bluetooth/mesh/shell.h>
#include "mesh/foundation.h"
#include "utils.h"
static int cmd_subnet_bridge_get(const struct shell *sh, size_t argc, char *argv[])
{
enum bt_mesh_subnet_bridge_state rsp;
int err;
err = bt_mesh_brg_cfg_cli_subnet_bridge_get(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, &rsp);
if (err) {
shell_error(sh, "Failed to send Subnet Bridge Get (err %d)", err);
return -ENOEXEC;
}
shell_print(sh, "Subnet Bridge State: %s",
(rsp == BT_MESH_SUBNET_BRIDGE_ENABLED) ? "Enabled" : "Disabled");
return 0;
}
static int cmd_subnet_bridge_set(const struct shell *sh, size_t argc, char *argv[])
{
enum bt_mesh_subnet_bridge_state set, rsp;
int err = 0;
set = shell_strtobool(argv[1], 0, &err) ? BT_MESH_SUBNET_BRIDGE_ENABLED :
BT_MESH_SUBNET_BRIDGE_DISABLED;
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
err = bt_mesh_brg_cfg_cli_subnet_bridge_set(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, set, &rsp);
if (err) {
shell_error(sh, "Failed to send Subnet Bridge Set (err %d)", err);
return -ENOEXEC;
}
shell_print(sh, "Subnet Bridge State: %s",
(rsp == BT_MESH_SUBNET_BRIDGE_ENABLED) ? "Enabled" : "Disabled");
return 0;
}
static int cmd_bridging_table_size_get(const struct shell *sh, size_t argc, char *argv[])
{
uint16_t rsp;
int err;
err = bt_mesh_brg_cfg_cli_bridging_table_size_get(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, &rsp);
if (err) {
shell_error(sh, "Failed to send Bridging Table Size Get (err %d)", err);
return -ENOEXEC;
}
shell_print(sh, "Bridging Table Size: %u", rsp);
return 0;
}
static int cmd_bridging_table_add(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_bridging_table_entry entry;
struct bt_mesh_bridging_table_status rsp;
int err = 0;
entry.directions = shell_strtoul(argv[1], 0, &err);
entry.net_idx1 = shell_strtoul(argv[2], 0, &err);
entry.net_idx2 = shell_strtoul(argv[3], 0, &err);
entry.addr1 = shell_strtoul(argv[4], 0, &err);
entry.addr2 = shell_strtoul(argv[5], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
err = bt_mesh_brg_cfg_cli_bridging_table_add(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, &entry, &rsp);
if (err) {
shell_error(sh, "Failed to send Bridging Table Add (err %d)", err);
return -ENOEXEC;
}
if (rsp.status) {
shell_print(sh, "Bridging Table Add failed with status 0x%02x", rsp.status);
} else {
shell_print(sh, "Bridging Table Add was successful.");
}
return 0;
}
static int cmd_bridging_table_remove(const struct shell *sh, size_t argc, char *argv[])
{
uint16_t net_idx1, net_idx2, addr1, addr2;
struct bt_mesh_bridging_table_status rsp;
int err = 0;
net_idx1 = shell_strtoul(argv[1], 0, &err);
net_idx2 = shell_strtoul(argv[2], 0, &err);
addr1 = shell_strtoul(argv[3], 0, &err);
addr2 = shell_strtoul(argv[4], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
err = bt_mesh_brg_cfg_cli_bridging_table_remove(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, net_idx1,
net_idx2, addr1, addr2, &rsp);
if (err) {
shell_error(sh, "Failed to send Bridging Table Remove (err %d)", err);
return -ENOEXEC;
}
if (rsp.status) {
shell_print(sh, "Bridging Table Remove failed with status 0x%02x", rsp.status);
} else {
shell_print(sh, "Bridging Table Remove was successful.");
}
return 0;
}
static int cmd_bridged_subnets_get(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_filter_netkey filter_net_idx;
uint8_t start_idx;
struct bt_mesh_bridged_subnets_list rsp = {
.list = NET_BUF_SIMPLE(CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX * 3),
};
int err = 0;
net_buf_simple_init(rsp.list, 0);
filter_net_idx.filter = shell_strtoul(argv[1], 0, &err);
filter_net_idx.net_idx = shell_strtoul(argv[2], 0, &err);
start_idx = shell_strtoul(argv[3], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
err = bt_mesh_brg_cfg_cli_bridged_subnets_get(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, filter_net_idx,
start_idx, &rsp);
if (err) {
shell_error(sh, "Failed to send Bridged Subnets Get (err %d)", err);
return -ENOEXEC;
}
shell_print(sh, "Bridged Subnets List:");
shell_print(sh, "\tfilter: %02x", rsp.net_idx_filter.filter);
shell_print(sh, "\tnet_idx: %04x", rsp.net_idx_filter.net_idx);
shell_print(sh, "\tstart_idx: %u", rsp.start_idx);
if (rsp.list) {
uint16_t net_idx1, net_idx2;
int i = 0;
while (rsp.list->len) {
key_idx_unpack_pair(rsp.list, &net_idx1, &net_idx2);
shell_print(sh, "\tEntry %d:", i++);
shell_print(sh, "\t\tnet_idx1: 0x%04x, net_idx2: 0x%04x", net_idx1,
net_idx2);
}
}
return 0;
}
static int cmd_bridging_table_get(const struct shell *sh, size_t argc, char *argv[])
{
uint16_t net_idx1, net_idx2, start_idx;
struct bt_mesh_bridging_table_list rsp = {
.list = NET_BUF_SIMPLE(CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX * 5),
};
int err = 0;
net_buf_simple_init(rsp.list, 0);
net_idx1 = shell_strtoul(argv[1], 0, &err);
net_idx2 = shell_strtoul(argv[2], 0, &err);
start_idx = shell_strtoul(argv[3], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
err = bt_mesh_brg_cfg_cli_bridging_table_get(bt_mesh_shell_target_ctx.net_idx,
bt_mesh_shell_target_ctx.dst, net_idx1,
net_idx2, start_idx, &rsp);
if (err) {
shell_error(sh, "Failed to send Bridging Table Get (err %d)", err);
return -ENOEXEC;
}
if (rsp.status) {
shell_print(sh, "Bridging Table Get failed with status 0x%02x", rsp.status);
} else {
shell_print(sh, "Bridging Table List:");
shell_print(sh, "\tstatus: %02x", rsp.status);
shell_print(sh, "\tnet_idx1: %04x", rsp.net_idx1);
shell_print(sh, "\tnet_idx2: %04x", rsp.net_idx2);
shell_print(sh, "\tstart_idx: %u", rsp.start_idx);
if (rsp.list) {
uint16_t addr1, addr2;
uint8_t directions;
int i = 0;
while (rsp.list->len) {
addr1 = net_buf_simple_pull_le16(rsp.list);
addr2 = net_buf_simple_pull_le16(rsp.list);
directions = net_buf_simple_pull_u8(rsp.list);
shell_print(sh, "\tEntry %d:", i++);
shell_print(sh,
"\t\taddr1: 0x%04x, addr2: 0x%04x, directions: 0x%02x",
addr1, addr2, directions);
}
}
}
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(
brg_cfg_cmds,
SHELL_CMD_ARG(bridge-get, NULL, NULL, cmd_subnet_bridge_get, 1, 0),
SHELL_CMD_ARG(bridge-set, NULL, "<State(disable, enable)>", cmd_subnet_bridge_set, 2, 0),
SHELL_CMD_ARG(table-size-get, NULL, NULL, cmd_bridging_table_size_get, 1, 0),
SHELL_CMD_ARG(table-add, NULL, "<Directions> <NetIdx1> <NetIdx2> <Addr1> <Addr2>",
cmd_bridging_table_add, 6, 0),
SHELL_CMD_ARG(table-remove, NULL, "<NetIdx1> <NetIdx2> <Addr1> <Addr2>",
cmd_bridging_table_remove, 5, 0),
SHELL_CMD_ARG(subnets-get, NULL, "<Filter> <NetIdx> <StartIdx>", cmd_bridged_subnets_get,
4, 0),
SHELL_CMD_ARG(table-get, NULL, "<NetIdx1> <NetIdx2> <StartIdx>", cmd_bridging_table_get,
4, 0),
SHELL_SUBCMD_SET_END);
SHELL_SUBCMD_ADD((mesh, models), brg, &brg_cfg_cmds, "Bridge Configuration Cli commands",
bt_mesh_shell_mdl_cmds_help, 1, 1);

View File

@@ -1036,7 +1036,7 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
return -EBADMSG;
}
if (bt_mesh_rpl_check(rx, &rpl)) {
if (bt_mesh_rpl_check(rx, &rpl, false)) {
LOG_WRN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", rx->ctx.addr, rx->ctx.recv_dst,
rx->seq);
return -EINVAL;
@@ -1350,7 +1350,7 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx,
return -EBADMSG;
}
if (bt_mesh_rpl_check(net_rx, &rpl)) {
if (bt_mesh_rpl_check(net_rx, &rpl, false)) {
LOG_WRN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", net_rx->ctx.addr,
net_rx->ctx.recv_dst, net_rx->seq);
return -EINVAL;

View File

@@ -0,0 +1,22 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bluetooth_mesh_brg)
FILE(GLOB app_sources src/*.c)
target_sources(app
PRIVATE
${app_sources}
${ZEPHYR_BASE}/subsys/bluetooth/mesh/brg_cfg.c)
target_include_directories(app
PRIVATE
${ZEPHYR_BASE}/subsys/bluetooth/mesh)
target_compile_options(app
PRIVATE
-DCONFIG_BT_SETTINGS
-DCONFIG_BT_MESH_BRG_CFG_SRV
-DCONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX=16
-DCONFIG_BT_MESH_USES_TINYCRYPT)

View File

@@ -0,0 +1,3 @@
CONFIG_ZTEST=y
CONFIG_ZTEST_MOCKING=y
CONFIG_BT_MESH_BRG_CFG_SRV=y

View File

@@ -0,0 +1,549 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/mesh.h>
#include <stdlib.h>
#include "settings.h"
#include "brg_cfg.h"
#include "foundation.h"
#define TEST_VECT_SZ (CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX + 1)
static struct test_brg_cfg_row {
uint8_t direction;
uint16_t net_idx1;
uint16_t net_idx2;
uint16_t addr1;
uint16_t addr2;
} test_vector[TEST_VECT_SZ];
#define ADDR1_BASE (1)
#define ADDR2_BASE (100)
/**** Helper functions ****/
static void setup(void *f)
{
/* create test vector */
for (int i = 0; i < TEST_VECT_SZ; i++) {
test_vector[i].direction = i < (TEST_VECT_SZ / 2) ? 1 : 2;
test_vector[i].net_idx1 = (i/8);
test_vector[i].addr1 = ADDR1_BASE + i;
test_vector[i].net_idx2 = (i/8) + 16;
test_vector[i].addr2 = ADDR2_BASE + i;
}
}
/**** Mocked functions ****/
void bt_mesh_settings_store_schedule(enum bt_mesh_settings_flag flag)
{
ztest_check_expected_value(flag);
}
int settings_save_one(const char *name, const void *value, size_t val_len)
{
ztest_check_expected_data(name, strlen(name));
ztest_check_expected_value(val_len);
ztest_check_expected_data(value, val_len);
return 0;
}
int settings_delete(const char *name)
{
ztest_check_expected_data(name, strlen(name));
return 0;
}
struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx)
{
/* Return anything non-zero. */
return (struct bt_mesh_subnet *) 1;
}
/**** Mocked functions - end ****/
static void check_fill_all_bt_entries(void)
{
uint8_t status;
int err;
for (int i = 0; i < TEST_VECT_SZ; i++) {
if (i < CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX) {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_BRG_PENDING);
}
err = bt_mesh_brg_cfg_tbl_add(test_vector[i].direction, test_vector[i].net_idx1,
test_vector[i].net_idx2, test_vector[i].addr1, test_vector[i].addr2,
&status);
zassert_equal(err, 0);
if (i != CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX) {
zassert_equal(status, STATUS_SUCCESS);
} else {
zassert_equal(status, STATUS_INSUFF_RESOURCES);
}
}
}
static void check_delete_all_bt_entries(void)
{
uint8_t status;
for (int i = 0; i < TEST_VECT_SZ; i++) {
if (i < CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX) {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_BRG_PENDING);
}
int err = bt_mesh_brg_cfg_tbl_remove(test_vector[i].net_idx1,
test_vector[i].net_idx2, test_vector[i].addr1,
test_vector[i].addr2, &status);
zassert_equal(err, 0);
zassert_equal(status, STATUS_SUCCESS);
}
}
static void check_bt_mesh_brg_cfg_tbl_reset(void)
{
int err;
ztest_expect_data(settings_delete, name, "bt/mesh/brg_en");
ztest_expect_data(settings_delete, name, "bt/mesh/brg_tbl");
err = bt_mesh_brg_cfg_tbl_reset();
zassert_equal(err, 0);
}
/**** Tests ****/
ZTEST_SUITE(bt_mesh_brg_cfg, NULL, NULL, setup, NULL, NULL);
/* Test if basic functionality (add and remove entries) works correctly. */
ZTEST(bt_mesh_brg_cfg, test_basic_functionality_storage)
{
check_bt_mesh_brg_cfg_tbl_reset();
/* Test add entries to bridging table. */
check_fill_all_bt_entries();
/* Test remove entries from bridging table, and then fill it again. */
check_delete_all_bt_entries();
check_fill_all_bt_entries();
/* Test resetting of the table, and then fill it again. */
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries();
uint8_t status;
int err;
uint16_t net_idx1 = test_vector[TEST_VECT_SZ - 1].net_idx1;
uint16_t net_idx2 = test_vector[TEST_VECT_SZ - 1].net_idx2;
uint16_t addr1 = BT_MESH_ADDR_UNASSIGNED;
/* Try removing entries with invalid params */
uint16_t addr2 = BT_MESH_ADDR_ALL_NODES;
err = bt_mesh_brg_cfg_tbl_remove(net_idx1, net_idx2, addr1, addr2, &status);
zassert_equal(err, -EINVAL);
addr2 = BT_MESH_ADDR_UNASSIGNED;
addr1 = BT_MESH_ADDR_RELAYS;
err = bt_mesh_brg_cfg_tbl_remove(net_idx1, net_idx2, addr1, addr2, &status);
zassert_equal(err, -EINVAL);
addr1 = BT_MESH_ADDR_UNASSIGNED;
net_idx1 = 4096;
err = bt_mesh_brg_cfg_tbl_remove(net_idx1, net_idx2, addr1, addr2, &status);
zassert_equal(err, -EINVAL);
net_idx1 = test_vector[TEST_VECT_SZ - 1].net_idx1;
net_idx2 = 4096;
err = bt_mesh_brg_cfg_tbl_remove(net_idx1, net_idx2, addr1, addr2, &status);
zassert_equal(err, -EINVAL);
/* Test remove entries matching netkey1, and netkey2 */
net_idx2 = test_vector[TEST_VECT_SZ - 1].net_idx2;
err = bt_mesh_brg_cfg_tbl_remove(net_idx1, net_idx2, addr1, addr2, &status);
zassert_equal(err, 0);
const struct bt_mesh_brg_cfg_row *brg_tbl;
int n = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
zassert_true(n > 0);
for (int i = 0; i < n; i++) {
zassert_true(brg_tbl[i].net_idx1 != net_idx1);
zassert_true(brg_tbl[i].net_idx2 != net_idx2);
}
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries();
/* Test remove entries matching netkey1, and netkey2, and addr1 */
addr1 = test_vector[TEST_VECT_SZ - 1].addr1;
n = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
zassert_true(n > 0);
for (int i = 0; i < n; i++) {
zassert_true(brg_tbl[i].net_idx1 != net_idx1);
zassert_true(brg_tbl[i].net_idx2 != net_idx2);
zassert_true(brg_tbl[i].addr1 != addr1);
}
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries();
/* Test remove entries matching netkey1, and netkey2, and addr2 */
addr1 = BT_MESH_ADDR_UNASSIGNED;
addr2 = test_vector[TEST_VECT_SZ - 1].addr2;
n = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
zassert_true(n > 0);
for (int i = 0; i < n; i++) {
zassert_true(brg_tbl[i].net_idx1 != net_idx1);
zassert_true(brg_tbl[i].net_idx2 != net_idx2);
zassert_true(brg_tbl[i].addr2 != addr2);
}
}
static void pending_store_enable_create_expectations(bool *enable_val)
{
if (*enable_val) {
ztest_expect_data(settings_save_one, name, "bt/mesh/brg_en");
ztest_expect_value(settings_save_one, val_len, 1);
ztest_expect_data(settings_save_one, value, enable_val);
} else {
ztest_expect_data(settings_delete, name, "bt/mesh/brg_en");
}
}
static void pending_store_tbl_create_expectations(int n,
const struct bt_mesh_brg_cfg_row *tbl_val)
{
if (n > 0) {
ztest_expect_data(settings_save_one, name, "bt/mesh/brg_tbl");
ztest_expect_value(settings_save_one, val_len,
n * sizeof(struct bt_mesh_brg_cfg_row));
ztest_expect_data(settings_save_one, value, tbl_val);
} else {
ztest_expect_data(settings_delete, name, "bt/mesh/brg_tbl");
}
}
/* Test if enable flag is stored correctly. */
ZTEST(bt_mesh_brg_cfg, test_brg_cfg_en)
{
int err;
bool val;
check_bt_mesh_brg_cfg_tbl_reset();
val = bt_mesh_brg_cfg_enable_get();
zassert_equal(val, false, NULL);
/* No changed to the states, nothing to check. */
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_BRG_PENDING);
err = bt_mesh_brg_cfg_enable_set(true);
zassert_equal(err, 0, NULL);
val = bt_mesh_brg_cfg_enable_get();
pending_store_enable_create_expectations(&val);
bt_mesh_brg_cfg_pending_store();
zassert_equal(bt_mesh_brg_cfg_enable_get(), true, NULL);
}
/* Test if pending store works correctly by adding one entry to the table. */
ZTEST(bt_mesh_brg_cfg, test_brg_tbl_pending_store)
{
uint8_t status;
int n, err;
struct bt_mesh_brg_cfg_row test_vec = {
.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 1,
.net_idx2 = 2,
.addr1 = 3,
.addr2 = 4,
};
check_bt_mesh_brg_cfg_tbl_reset();
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_BRG_PENDING);
err = bt_mesh_brg_cfg_tbl_add(test_vec.direction, test_vec.net_idx1,
test_vec.net_idx2, test_vec.addr1, test_vec.addr2, &status);
zassert_equal(err, 0);
zassert_equal(status, STATUS_SUCCESS);
const struct bt_mesh_brg_cfg_row *tbl;
n = bt_mesh_brg_cfg_tbl_get(&tbl);
zassert_equal(n, 1);
zassert_true(tbl);
pending_store_tbl_create_expectations(1, &test_vec);
bt_mesh_brg_cfg_pending_store();
}
/* Test if invalid entries are not added to the table. */
ZTEST(bt_mesh_brg_cfg, test_tbl_add_invalid_ip)
{
uint8_t status;
int err;
/* Create test vector array of test_brg_cfg_row iteams with invalid values.
* Each vector has only one invalid field value, rest all are valid values.
*/
const struct test_brg_cfg_row inv_test_vector[] = {
/* Direction has invalid values */
{.direction = BT_MESH_BRG_CFG_DIR_PROHIBITED,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 2},
{.direction = BT_MESH_BRG_CFG_DIR_MAX,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 2},
/* Out of range netidx values */
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 4096, .net_idx2 = 1, .addr1 = 1, .addr2 = 2},
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 4096, .addr1 = 1, .addr2 = 2},
/* Same netidx values */
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 0, .addr1 = 1, .addr2 = 2},
/* Same addr values */
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 1},
/* Invalid address1 value */
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 0, .addr2 = 1},
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 0x8000, .addr2 = 1},
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 0xC000, .addr2 = 1},
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 0xFFFE, .addr2 = 1},
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 0xFFFF, .addr2 = 1},
/* Invalid address2 values */
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 0},
{.direction = BT_MESH_BRG_CFG_DIR_ONEWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 0xFFFF},
{.direction = BT_MESH_BRG_CFG_DIR_TWOWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 0x8000},
{.direction = BT_MESH_BRG_CFG_DIR_TWOWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 0xC000},
{.direction = BT_MESH_BRG_CFG_DIR_TWOWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 0xFFFE},
{.direction = BT_MESH_BRG_CFG_DIR_TWOWAY,
.net_idx1 = 0, .net_idx2 = 1, .addr1 = 1, .addr2 = 0xFFFF},
};
check_bt_mesh_brg_cfg_tbl_reset();
for (int i = 0; i < ARRAY_SIZE(inv_test_vector); i++) {
err = bt_mesh_brg_cfg_tbl_add(inv_test_vector[i].direction,
inv_test_vector[i].net_idx1, inv_test_vector[i].net_idx2,
inv_test_vector[i].addr1, inv_test_vector[i].addr2,
&status);
zassert_equal(err, -EINVAL, "Test vector index: %zu", i);
}
}
/* Following are helper functions for the test that checks the iteration logic */
#define NUM_MSGS (10000)
static void print_brg_tbl(void)
{
const struct bt_mesh_brg_cfg_row *tbl;
int n = bt_mesh_brg_cfg_tbl_get(&tbl);
zassert_true(n <= CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX);
for (int i = 0; i < n; i++) {
printk("entry: %3d # dir: %d, net_idx1: %3d, addr1: %3d, net_idx2: %3d, addr2: %3d\n",
i, tbl[i].direction, tbl[i].net_idx1, tbl[i].addr1, tbl[i].net_idx2,
tbl[i].addr2);
}
}
static void check_fill_all_bt_entries_reversed(void)
{
uint8_t status;
int err;
for (int i = TEST_VECT_SZ - 2; i >= 0 ; i--) {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_BRG_PENDING);
err = bt_mesh_brg_cfg_tbl_add(test_vector[i].direction, test_vector[i].net_idx1,
test_vector[i].net_idx2, test_vector[i].addr1, test_vector[i].addr2,
&status);
zassert_equal(err, 0);
}
int last = TEST_VECT_SZ - 1;
err = bt_mesh_brg_cfg_tbl_add(test_vector[last].direction, test_vector[last].net_idx1,
test_vector[last].net_idx2, test_vector[last].addr1,
test_vector[last].addr2, &status);
zassert_equal(err, 0);
zassert_equal(status, STATUS_INSUFF_RESOURCES);
}
static struct test_brg_cfg_row test_vector_copy[TEST_VECT_SZ - 1];
static void check_fill_all_bt_entries_randomly(void)
{
uint8_t status;
int err;
int copy_cnt = ARRAY_SIZE(test_vector_copy);
memcpy(test_vector_copy, test_vector, sizeof(test_vector_copy));
for (int i = 0; i < copy_cnt; i++) {
int idx = rand() % copy_cnt;
struct test_brg_cfg_row tmp = test_vector_copy[i];
test_vector_copy[i] = test_vector_copy[idx];
test_vector_copy[idx] = tmp;
}
for (int i = 0; i < copy_cnt; i++) {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_BRG_PENDING);
err = bt_mesh_brg_cfg_tbl_add(test_vector_copy[i].direction,
test_vector_copy[i].net_idx1, test_vector_copy[i].net_idx2,
test_vector_copy[i].addr1, test_vector_copy[i].addr2, &status);
zassert_equal(err, 0);
zassert_equal(status, STATUS_SUCCESS);
}
int last = TEST_VECT_SZ - 1;
err = bt_mesh_brg_cfg_tbl_add(test_vector[last].direction, test_vector[last].net_idx1,
test_vector[last].net_idx2, test_vector[last].addr1, test_vector[last].addr2,
&status);
zassert_equal(err, 0);
zassert_equal(status, STATUS_INSUFF_RESOURCES);
}
static void subnet_relay_cb_check(uint16_t new_net_idx, void *user_data)
{
int idx = *(int *)user_data;
zassert_equal(new_net_idx, test_vector[idx].net_idx2);
}
static void subnet_relay_cb_check_rev(uint16_t new_net_idx, void *user_data)
{
int idx = *(int *)user_data;
if (test_vector[idx].direction == 2) {
zassert_equal(new_net_idx, test_vector[idx].net_idx1);
} else {
/* Should never assert. Test vector created in setup(). */
zassert_true(false);
}
}
static void test_bridging_performance(bool test_one_way)
{
int idx;
uint32_t tick1;
uint32_t ticks = 0;
for (int i = 0; i < NUM_MSGS; i++) {
/* randomly pick an entry from the test vector */
idx = rand() % TEST_VECT_SZ;
/* check src to dst bridging*/
const struct bt_mesh_brg_cfg_row *tbl_row = NULL;
tick1 = k_uptime_ticks();
bt_mesh_brg_cfg_tbl_foreach_subnet(test_vector[idx].addr1, test_vector[idx].addr2,
test_vector[idx].net_idx1, subnet_relay_cb_check, &idx);
ticks += k_uptime_ticks() - tick1;
if (test_one_way) {
continue;
}
/* check dst to src bridging - for the same test vector src-dst pairs
* but now, reverse them and consider packets are arriving on net_idx2
*/
tbl_row = NULL;
tick1 = k_uptime_ticks();
bt_mesh_brg_cfg_tbl_foreach_subnet(test_vector[idx].addr2, test_vector[idx].addr1,
test_vector[idx].net_idx2, subnet_relay_cb_check_rev, &idx);
ticks += k_uptime_ticks() - tick1;
}
printk("ticks: %8u us: %u\n", ticks, k_ticks_to_us_floor32(ticks));
}
/* Test checks iteration logic and performance when run on real devices. */
ZTEST(bt_mesh_brg_cfg, test_zcheck_entry_randomly_sorting)
{
printk("num msgs: %d\n\n", NUM_MSGS);
/* Test performance when packets are flowing in one directions */
/* Fill bridging table in sorted order */
printk("\n\nPackets going only in one direction (from outside towards the subnet)\n");
printk("\nBridging table is pre-filled in sorted order\n");
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries();
print_brg_tbl();
test_bridging_performance(true);
/* Fill bridging table in reversed order */
printk("\nBridging table is pre-filled in reversed order\n");
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries_reversed();
print_brg_tbl();
test_bridging_performance(true);
/* Fill bridging table in random order */
printk("\nBridging table is pre-filled in random order\n");
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries_randomly();
print_brg_tbl();
test_bridging_performance(true);
/* Test performance when packets are flowing in both directions - use same dataset. */
printk("\n\nPackets going in both directions (same data set, flip src and dst pairs)\n");
printk("\nBridging table is pre-filled in sorted order\n");
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries();
print_brg_tbl();
test_bridging_performance(false);
/* Fill bridging table in reversed order */
printk("\nBridging table is pre-filled in reversed order\n");
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries_reversed();
print_brg_tbl();
test_bridging_performance(false);
/* Fill bridging table in random order */
printk("\nBridging table is pre-filled in random order\n");
check_bt_mesh_brg_cfg_tbl_reset();
check_fill_all_bt_entries_randomly();
print_brg_tbl();
test_bridging_performance(false);
}

View File

@@ -0,0 +1,10 @@
tests:
bluetooth.mesh.brg:
platform_allow:
- native_posix
- native_sim
tags:
- bluetooth
- mesh
integration_platforms:
- native_sim

View File

@@ -59,7 +59,7 @@ static void prepare_rpl_and_start_reset(void)
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&msg, NULL));
zassert_false(bt_mesh_rpl_check(&msg, NULL, false));
}
/* settings_save_one() will be triggered for all new entries when
@@ -79,7 +79,7 @@ static void prepare_rpl_and_start_reset(void)
.seq = test_vector[i].seq,
};
zassert_true(bt_mesh_rpl_check(&msg, NULL));
zassert_true(bt_mesh_rpl_check(&msg, NULL, false));
}
/* Simulate IVI Update. This should only flip flags. The actual storing will happen
@@ -108,9 +108,9 @@ static void check_entries_from_test_vector(void)
if (test_vector[i].old_iv) {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&msg, NULL));
zassert_false(bt_mesh_rpl_check(&msg, NULL, false));
} else {
zassert_true(bt_mesh_rpl_check(&msg, NULL));
zassert_true(bt_mesh_rpl_check(&msg, NULL, false));
}
}
}
@@ -128,7 +128,7 @@ static void check_empty_entries(int cnt)
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&msg, NULL));
zassert_false(bt_mesh_rpl_check(&msg, NULL, false));
}
/* Check that there are no more empty entries in RPL. */
@@ -138,7 +138,7 @@ static void check_empty_entries(int cnt)
.old_iv = false,
.seq = 1024,
};
zassert_true(bt_mesh_rpl_check(&msg, NULL));
zassert_true(bt_mesh_rpl_check(&msg, NULL, false));
}
static void check_op(int op)
@@ -149,7 +149,7 @@ static void check_op(int op)
} else {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&recv_msg, NULL));
zassert_false(bt_mesh_rpl_check(&recv_msg, NULL, false));
settings_func_cnt--;
settings_func = 0;
@@ -466,6 +466,6 @@ ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_new_entry)
.old_iv = entry.old_iv,
.seq = entry.seq
};
zassert_true(bt_mesh_rpl_check(&msg, NULL));
zassert_true(bt_mesh_rpl_check(&msg, NULL, false));
check_empty_entries(EMPTY_ENTRIES_CNT - 1);
}

View File

@@ -41,6 +41,10 @@ static struct bt_mesh_od_priv_proxy_cli od_priv_proxy_cli;
struct bt_mesh_large_comp_data_cli large_comp_data_cli;
#endif
#if defined(CONFIG_BT_MESH_BRG_CFG_CLI)
static struct bt_mesh_brg_cfg_cli brg_cfg_cli;
#endif
BT_MESH_SHELL_HEALTH_PUB_DEFINE(health_pub);
static const struct bt_mesh_model root_models[] = {
@@ -106,6 +110,12 @@ static const struct bt_mesh_model root_models[] = {
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
BT_MESH_MODEL_OD_PRIV_PROXY_SRV,
#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
};
static const struct bt_mesh_elem elements[] = {

View File

@@ -2284,6 +2284,14 @@
<table>11</table>
<row>6</row>
</item>
<item>
<table>11</table>
<row>7</row>
</item>
<item>
<table>11</table>
<row>8</row>
</item>
<item>
<table>11</table>
<row>19</row>

View File

@@ -49,4 +49,6 @@ CONFIG_BT_MESH_OD_PRIV_PROXY_SRV=y
CONFIG_BT_MESH_MODEL_EXTENSIONS=y
CONFIG_BT_MESH_COMP_PAGE_1=y
CONFIG_BT_MESH_COMP_PAGE_2=y
CONFIG_BT_MESH_BRG_CFG_SRV=y
CONFIG_BT_MESH_BRG_CFG_CLI=y
CONFIG_SETTINGS=y

View File

@@ -905,6 +905,57 @@ struct btp_rpr_reprov_remote_cmd {
bool comp_change;
} __packed;
#define BTP_MESH_SUBNET_BRIDGE_GET 0x62
struct btp_mesh_subnet_bridge_get_cmd {
uint16_t addr;
} __packed;
#define BTP_MESH_SUBNET_BRIDGE_SET 0x63
struct btp_mesh_subnet_bridge_set_cmd {
uint16_t addr;
uint8_t val;
} __packed;
#define BTP_MESH_BRIDGING_TABLE_ADD 0x64
struct btp_mesh_bridging_table_add_cmd {
uint16_t addr;
uint8_t directions;
uint16_t net_idx1;
uint16_t net_idx2;
uint16_t addr1;
uint16_t addr2;
} __packed;
#define BTP_MESH_BRIDGING_TABLE_REMOVE 0x65
struct btp_mesh_bridging_table_remove_cmd {
uint16_t addr;
uint16_t net_idx1;
uint16_t net_idx2;
uint16_t addr1;
uint16_t addr2;
} __packed;
#define BTP_MESH_BRIDGED_SUBNETS_GET 0x66
struct btp_mesh_bridged_subnets_get_cmd {
uint16_t addr;
uint8_t filter;
uint16_t net_idx;
uint8_t start_idx;
} __packed;
#define BTP_MESH_BRIDGING_TABLE_GET 0x67
struct btp_mesh_bridging_table_get_cmd {
uint16_t addr;
uint16_t net_idx1;
uint16_t net_idx2;
uint16_t start_idx;
} __packed;
#define BTP_MESH_BRIDGING_TABLE_SIZE_GET 0x68
struct btp_mesh_bridging_table_size_get_cmd {
uint16_t addr;
} __packed;
#define BTP_MMDL_DFU_INFO_GET 0x5f
struct btp_mmdl_dfu_info_get_cmd {
uint8_t limit;

View File

@@ -1019,6 +1019,10 @@ static uint8_t proxy_solicit(const void *cmd, uint16_t cmd_len,
}
#endif /* CONFIG_BT_MESH_PROXY_SOLICITATION */
#if defined(CONFIG_BT_MESH_BRG_CFG_CLI)
static struct bt_mesh_brg_cfg_cli brg_cfg_cli;
#endif /* CONFIG_BT_MESH_BRG_CFG_CLI */
static const struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV,
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
@@ -1072,6 +1076,12 @@ static const struct bt_mesh_model root_models[] = {
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
BT_MESH_MODEL_OD_PRIV_PROXY_SRV,
#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
};
@@ -1128,6 +1138,12 @@ static const struct bt_mesh_model root_models_alt[] = {
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
BT_MESH_MODEL_OD_PRIV_PROXY_SRV,
#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
};
struct model_data *lookup_model_bound(uint16_t id)
@@ -2066,6 +2082,171 @@ static uint8_t models_metadata_get(const void *cmd, uint16_t cmd_len,
}
#endif
#if defined(CONFIG_BT_MESH_BRG_CFG_CLI)
static uint8_t subnet_bridge_get(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_subnet_bridge_get_cmd *cp = cmd;
enum bt_mesh_subnet_bridge_state state;
int err;
err = bt_mesh_brg_cfg_cli_subnet_bridge_get(net.net_idx, sys_le16_to_cpu(cp->addr),
&state);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
LOG_DBG("Subnet Bridge state: %u", state);
return BTP_STATUS_SUCCESS;
}
static uint8_t subnet_bridge_set(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_subnet_bridge_set_cmd *cp = cmd;
enum bt_mesh_subnet_bridge_state state;
int err;
state = cp->val;
err = bt_mesh_brg_cfg_cli_subnet_bridge_set(net.net_idx, sys_le16_to_cpu(cp->addr),
state, &state);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
LOG_DBG("Subnet Bridge state: %u", state);
return BTP_STATUS_SUCCESS;
}
static uint8_t bridging_table_add(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_bridging_table_add_cmd *cp = cmd;
struct bt_mesh_bridging_table_entry entry;
struct bt_mesh_bridging_table_status rp;
int err;
LOG_DBG("");
entry.directions = cp->directions;
entry.net_idx1 = sys_le16_to_cpu(cp->net_idx1);
entry.net_idx2 = sys_le16_to_cpu(cp->net_idx2);
entry.addr1 = sys_le16_to_cpu(cp->addr1);
entry.addr2 = sys_le16_to_cpu(cp->addr2);
err = bt_mesh_brg_cfg_cli_bridging_table_add(net_key_idx, sys_le16_to_cpu(cp->addr),
&entry, &rp);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t bridging_table_remove(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_bridging_table_remove_cmd *cp = cmd;
struct bt_mesh_bridging_table_status rp;
int err;
LOG_DBG("");
err = bt_mesh_brg_cfg_cli_bridging_table_remove(net_key_idx, sys_le16_to_cpu(cp->addr),
sys_le16_to_cpu(cp->net_idx1),
sys_le16_to_cpu(cp->net_idx2),
sys_le16_to_cpu(cp->addr1),
sys_le16_to_cpu(cp->addr2), &rp);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t bridged_subnets_get(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_bridged_subnets_get_cmd *cp = cmd;
struct bt_mesh_filter_netkey filter_net_idx;
struct bt_mesh_bridged_subnets_list rp;
int err;
LOG_DBG("");
/* Initialize list ptr to NULL to prevent the client copying response to whatever was
* on the stack where `rp` was allocated.
*/
rp.list = NULL;
filter_net_idx.filter = cp->filter;
filter_net_idx.net_idx = sys_le16_to_cpu(cp->net_idx);
err = bt_mesh_brg_cfg_cli_bridged_subnets_get(net_key_idx, sys_le16_to_cpu(cp->addr),
filter_net_idx, cp->start_idx, &rp);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t bridging_table_get(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_bridging_table_get_cmd *cp = cmd;
struct bt_mesh_bridging_table_list rp;
int err;
LOG_DBG("");
/* Initialize list ptr to NULL to prevent the client copying response to whatever was
* on the stack where `rp` was allocated.
*/
rp.list = NULL;
err = bt_mesh_brg_cfg_cli_bridging_table_get(net_key_idx, sys_le16_to_cpu(cp->addr),
sys_le16_to_cpu(cp->net_idx1),
sys_le16_to_cpu(cp->net_idx2),
sys_le16_to_cpu(cp->start_idx), &rp);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t bridging_table_size_get(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_mesh_bridging_table_size_get_cmd *cp = cmd;
uint16_t size;
int err;
LOG_DBG("");
err = bt_mesh_brg_cfg_cli_bridging_table_size_get(net_key_idx, sys_le16_to_cpu(cp->addr),
&size);
if (err) {
LOG_ERR("err=%d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
#endif
static uint8_t composition_data_get(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
@@ -5142,6 +5323,43 @@ static const struct btp_handler handlers[] = {
.func = srpl_clear
},
#endif
#if defined(CONFIG_BT_MESH_BRG_CFG_CLI)
{
.opcode = BTP_MESH_SUBNET_BRIDGE_GET,
.expect_len = sizeof(struct btp_mesh_subnet_bridge_get_cmd),
.func = subnet_bridge_get
},
{
.opcode = BTP_MESH_SUBNET_BRIDGE_SET,
.expect_len = sizeof(struct btp_mesh_subnet_bridge_set_cmd),
.func = subnet_bridge_set
},
{
.opcode = BTP_MESH_BRIDGING_TABLE_ADD,
.expect_len = sizeof(struct btp_mesh_bridging_table_add_cmd),
.func = bridging_table_add
},
{
.opcode = BTP_MESH_BRIDGING_TABLE_REMOVE,
.expect_len = sizeof(struct btp_mesh_bridging_table_remove_cmd),
.func = bridging_table_remove
},
{
.opcode = BTP_MESH_BRIDGED_SUBNETS_GET,
.expect_len = sizeof(struct btp_mesh_bridged_subnets_get_cmd),
.func = bridged_subnets_get
},
{
.opcode = BTP_MESH_BRIDGING_TABLE_GET,
.expect_len = sizeof(struct btp_mesh_bridging_table_get_cmd),
.func = bridging_table_get
},
{
.opcode = BTP_MESH_BRIDGING_TABLE_SIZE_GET,
.expect_len = sizeof(struct btp_mesh_bridging_table_size_get_cmd),
.func = bridging_table_size_get
},
#endif
#if defined(CONFIG_BT_MESH_PROXY_SOLICITATION)
{
.opcode = BTP_MESH_PROXY_SOLICIT,

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
@@ -64,6 +65,8 @@ CONFIG_BT_MESH_PRIV_BEACON_SRV=y
CONFIG_BT_MESH_PRIV_BEACON_CLI=y
CONFIG_BT_MESH_OD_PRIV_PROXY_SRV=y
CONFIG_BT_MESH_OD_PRIV_PROXY_CLI=y
CONFIG_BT_MESH_BRG_CFG_SRV=y
CONFIG_BT_MESH_BRG_CFG_CLI=y
CONFIG_BT_MESH_COMP_PAGE_1=y
CONFIG_BT_MESH_COMP_PAGE_2=y
CONFIG_BT_TESTING=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