Compare commits
24 Commits
v4.1.0
...
collab-mes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5e7142b3d | ||
|
|
955b1f677b | ||
|
|
dab117c85e | ||
|
|
aeeb2053c6 | ||
|
|
1b44e607a8 | ||
|
|
c1f5ae30c6 | ||
|
|
bbd0498803 | ||
|
|
85a856d79a | ||
|
|
ef43b94495 | ||
|
|
62f08da47b | ||
|
|
81dbc697f4 | ||
|
|
dfdce2c122 | ||
|
|
6724345372 | ||
|
|
0bb34284d9 | ||
|
|
55175d63e4 | ||
|
|
d7b4497054 | ||
|
|
9d61afa758 | ||
|
|
cddbafe374 | ||
|
|
71b29f4796 | ||
|
|
563aa9be92 | ||
|
|
6874a4a719 | ||
|
|
380bd5483e | ||
|
|
988ad5b0c2 | ||
|
|
470c0d13a0 |
@@ -24,3 +24,4 @@ Read more about Bluetooth Mesh on the
|
||||
mesh/cfg.rst
|
||||
mesh/statistic.rst
|
||||
mesh/shell.rst
|
||||
mesh/brg_cfg.rst
|
||||
|
||||
160
doc/connectivity/bluetooth/api/mesh/brg_cfg.rst
Normal file
160
doc/connectivity/bluetooth/api/mesh/brg_cfg.rst
Normal 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
|
||||
21
doc/connectivity/bluetooth/api/mesh/brg_cfg_cli.rst
Normal file
21
doc/connectivity/bluetooth/api/mesh/brg_cfg_cli.rst
Normal 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
|
||||
31
doc/connectivity/bluetooth/api/mesh/brg_cfg_srv.rst
Normal file
31
doc/connectivity/bluetooth/api/mesh/brg_cfg_srv.rst
Normal 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 node’s
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
======================
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
97
include/zephyr/bluetooth/mesh/brg_cfg.h
Normal file
97
include/zephyr/bluetooth/mesh/brg_cfg.h
Normal 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__ */
|
||||
323
include/zephyr/bluetooth/mesh/brg_cfg_cli.h
Normal file
323
include/zephyr/bluetooth/mesh/brg_cfg_cli.h
Normal 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__ */
|
||||
47
include/zephyr/bluetooth/mesh/brg_cfg_srv.h
Normal file
47
include/zephyr/bluetooth/mesh/brg_cfg_srv.h
Normal 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__ */
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
367
subsys/bluetooth/mesh/brg_cfg.c
Normal file
367
subsys/bluetooth/mesh/brg_cfg.c
Normal 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;
|
||||
}
|
||||
79
subsys/bluetooth/mesh/brg_cfg.h
Normal file
79
subsys/bluetooth/mesh/brg_cfg.h
Normal 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_ */
|
||||
373
subsys/bluetooth/mesh/brg_cfg_cli.c
Normal file
373
subsys/bluetooth/mesh/brg_cfg_cli.c
Normal 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;
|
||||
}
|
||||
323
subsys/bluetooth/mesh/brg_cfg_srv.c
Normal file
323
subsys/bluetooth/mesh/brg_cfg_srv.c
Normal 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
249
subsys/bluetooth/mesh/shell/brg_cfg.c
Normal file
249
subsys/bluetooth/mesh/shell/brg_cfg.c
Normal 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);
|
||||
@@ -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;
|
||||
|
||||
22
tests/bluetooth/mesh/brg/CMakeLists.txt
Normal file
22
tests/bluetooth/mesh/brg/CMakeLists.txt
Normal 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)
|
||||
3
tests/bluetooth/mesh/brg/prj.conf
Normal file
3
tests/bluetooth/mesh/brg/prj.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
CONFIG_ZTEST=y
|
||||
CONFIG_ZTEST_MOCKING=y
|
||||
CONFIG_BT_MESH_BRG_CFG_SRV=y
|
||||
549
tests/bluetooth/mesh/brg/src/main.c
Normal file
549
tests/bluetooth/mesh/brg/src/main.c
Normal 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);
|
||||
}
|
||||
10
tests/bluetooth/mesh/brg/testcase.yaml
Normal file
10
tests/bluetooth/mesh/brg/testcase.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
tests:
|
||||
bluetooth.mesh.brg:
|
||||
platform_allow:
|
||||
- native_posix
|
||||
- native_sim
|
||||
tags:
|
||||
- bluetooth
|
||||
- mesh
|
||||
integration_platforms:
|
||||
- native_sim
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
|
||||
978
tests/bsim/bluetooth/mesh/src/test_brg.c
Normal file
978
tests/bsim/bluetooth/mesh/src/test_brg.c
Normal 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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
22
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_ivu.sh
Executable file
22
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_ivu.sh
Executable 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
|
||||
25
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_net_key_update.sh
Executable file
25
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_net_key_update.sh
Executable 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
|
||||
30
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_persistent_storage.sh
Executable file
30
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_persistent_storage.sh
Executable 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
|
||||
45
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_simple.sh
Executable file
45
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_simple.sh
Executable 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
|
||||
32
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_table_state_change.sh
Executable file
32
tests/bsim/bluetooth/mesh/tests_scripts/bridge/brg_table_state_change.sh
Executable 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
|
||||
Reference in New Issue
Block a user