Bluetooth: Mesh: Private Beacons

Adds support for private beacon sending and receiving.

Co-authored-by: Krzysztof Kopyściński <krzysztof.kopyscinski@codecoup.pl>
Co-authored-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2020-06-02 12:04:53 +02:00 committed by Carles Cufí
commit f9b19010ed
26 changed files with 1711 additions and 125 deletions

View file

@ -6,7 +6,7 @@ Mesh models
Foundation models
*****************
The Bluetooth mesh specification defines four foundation models that can be
The Bluetooth mesh specification defines six foundation models that can be
used by network administrators to configure and diagnose mesh nodes.
.. toctree::
@ -16,11 +16,13 @@ used by network administrators to configure and diagnose mesh nodes.
cfg_cli
health_srv
health_cli
priv_beacon_srv
priv_beacon_cli
Model specification models
**************************
In addition to the Foundation models defined in the Bluetooth Mesh specification, the Bluetooth Mesh Model specification defines several models, some of which are implemented in Zephyr:
In addition to the foundation models defined in the Bluetooth mesh specification, the Bluetooth Mesh Model Specification defines several models, some of which are implemented in Zephyr:
.. toctree::
:maxdepth: 1

View file

@ -0,0 +1,36 @@
.. _bluetooth_mesh_models_priv_beacon_cli:
Private Beacon Client
#####################
The Private Beacon Client model is a foundation model defined by the Bluetooth
mesh specification. It is enabled with the
:kconfig:option:`CONFIG_BT_MESH_PRIV_BEACON_CLI` option.
The Private Beacon Client model is introduced in the Bluetooth Mesh Profile
Specification version 1.1, and provides functionality for configuring the
:ref:`bluetooth_mesh_models_priv_beacon_srv` models.
The Private Beacons feature adds privacy to the different Bluetooth mesh
beacons by periodically randomizing the beacon input data. This protects the
mesh node from being tracked by devices outside the mesh network, and hides the
network's IV index, IV update and the Key Refresh state.
The Private Beacon Client model communicates with a
:ref:`bluetooth_mesh_models_priv_beacon_srv` model using the device key of the
target node. The Private Beacon Client model may communicate with servers on
other nodes or self-configure through the local Private Beacon Server model.
All configuration functions in the Private Beacon Client API have ``net_idx``
and ``addr`` as their first parameters. These should be set to the network
index and the primary unicast address the target node was provisioned with.
The Private Beacon Client model is optional, and can be instantiated on any
element.
API reference
*************
.. doxygengroup:: bt_mesh_priv_beacon_cli
:project: Zephyr
:members:

View file

@ -0,0 +1,39 @@
.. _bluetooth_mesh_models_priv_beacon_srv:
Private Beacon Server
#####################
The Private Beacon Server model is a foundation model defined by the Bluetooth
mesh specification. It is enabled with
:kconfig:option:`CONFIG_BT_MESH_PRIV_BEACON_SRV` option.
The Private Beacon Server model is introduced in the Bluetooth Mesh Profile
Specification version 1.1, and controls the mesh node's Private Beacon state,
Private GATT Proxy state and Private Node Identity state.
The Private Beacons feature adds privacy to the different Bluetooth mesh
beacons by periodically randomizing the beacon input data. This protects the
mesh node from being tracked by devices outside the mesh network, and hides the
network's IV index, IV update and the Key Refresh state. The Private Beacon Server
must be instantiated for the device to support sending of the private beacons,
but the node will process received private beacons without it.
The Private Beacon Server does not have an API of its own, but relies on a
:ref:`bluetooth_mesh_models_priv_beacon_cli` to control it. The Private Beacon
Server model only accepts messages encrypted with the node's device key.
The application can configure the initial parameters of the Private Beacon
Server model through the :c:struct:`bt_mesh_priv_beacon_srv` instance passed to
:c:macro:`BT_MESH_MODEL_PRIV_BEACON_SRV`. Note that if the mesh node stored
changes to this configuration in the settings subsystem, the initial values may
be overwritten upon loading.
The Private Beacon Server model is optional, and can only be instantiated in the
node's primary element.
API reference
*************
.. doxygengroup:: bt_mesh_priv_beacon_srv
:project: Zephyr
:members:

View file

@ -25,6 +25,8 @@
#include <zephyr/bluetooth/mesh/health_cli.h>
#include <zephyr/bluetooth/mesh/blob_cli.h>
#include <zephyr/bluetooth/mesh/blob_io_flash.h>
#include <zephyr/bluetooth/mesh/priv_beacon_srv.h>
#include <zephyr/bluetooth/mesh/priv_beacon_cli.h>
#include <zephyr/bluetooth/mesh/dfu_srv.h>
#include <zephyr/bluetooth/mesh/dfd_srv.h>
#include <zephyr/bluetooth/mesh/dfu_cli.h>

View file

@ -128,6 +128,8 @@ struct bt_mesh_elem {
#define BT_MESH_MODEL_ID_HEALTH_CLI 0x0003
#define BT_MESH_MODEL_ID_REMOTE_PROV_SRV 0x0004
#define BT_MESH_MODEL_ID_REMOTE_PROV_CLI 0x0005
#define BT_MESH_MODEL_ID_PRIV_BEACON_SRV 0x000a
#define BT_MESH_MODEL_ID_PRIV_BEACON_CLI 0x000b
#define BT_MESH_MODEL_ID_SAR_CFG_SRV 0x000e
#define BT_MESH_MODEL_ID_SAR_CFG_CLI 0x000f
#define BT_MESH_MODEL_ID_LARGE_COMP_DATA_SRV 0x0012

View file

@ -49,10 +49,17 @@ enum bt_mesh_feat_state {
#define BT_MESH_BEACON_DISABLED BT_MESH_FEATURE_DISABLED
#define BT_MESH_BEACON_ENABLED BT_MESH_FEATURE_ENABLED
#define BT_MESH_PRIV_BEACON_DISABLED BT_MESH_FEATURE_DISABLED
#define BT_MESH_PRIV_BEACON_ENABLED BT_MESH_FEATURE_ENABLED
#define BT_MESH_GATT_PROXY_DISABLED BT_MESH_FEATURE_DISABLED
#define BT_MESH_GATT_PROXY_ENABLED BT_MESH_FEATURE_ENABLED
#define BT_MESH_GATT_PROXY_NOT_SUPPORTED BT_MESH_FEATURE_NOT_SUPPORTED
#define BT_MESH_PRIV_GATT_PROXY_DISABLED BT_MESH_FEATURE_DISABLED
#define BT_MESH_PRIV_GATT_PROXY_ENABLED BT_MESH_FEATURE_ENABLED
#define BT_MESH_PRIV_GATT_PROXY_NOT_SUPPORTED BT_MESH_FEATURE_NOT_SUPPORTED
#define BT_MESH_FRIEND_DISABLED BT_MESH_FEATURE_DISABLED
#define BT_MESH_FRIEND_ENABLED BT_MESH_FEATURE_ENABLED
#define BT_MESH_FRIEND_NOT_SUPPORTED BT_MESH_FEATURE_NOT_SUPPORTED
@ -73,6 +80,50 @@ void bt_mesh_beacon_set(bool beacon);
*/
bool bt_mesh_beacon_enabled(void);
/** @brief Enable or disable sending of the Mesh Private beacon.
*
* Support for the Private beacon state must be enabled with @c
* CONFIG_BT_MESH_PRIV_BEACONS.
*
* @param priv_beacon New Mesh Private beacon state. Must be one of
* @ref BT_MESH_FEATURE_ENABLED and
* @ref BT_MESH_FEATURE_DISABLED.
*
* @retval 0 Successfully changed the Mesh Private beacon feature state.
* @retval -ENOTSUP The Mesh Private beacon feature is not supported.
* @retval -EINVAL Invalid parameter.
* @retval -EALREADY Already in the given state.
*/
int bt_mesh_priv_beacon_set(enum bt_mesh_feat_state priv_beacon);
/** @brief Get the current Mesh Private beacon state.
*
* @returns The Mesh Private beacon feature state.
*/
enum bt_mesh_feat_state bt_mesh_priv_beacon_get(void);
/** @brief Set the current Mesh Private beacon update interval.
*
* The Mesh Private beacon's randomization value is updated regularly to
* maintain the node's privacy. The update interval controls how often
* the beacon is updated, in 10 second increments.
*
* @param interval Private beacon update interval in 10 second steps, or 0 to
* update on every beacon transmission.
*/
void bt_mesh_priv_beacon_update_interval_set(uint8_t interval);
/** @brief Get the current Mesh Private beacon update interval.
*
* The Mesh Private beacon's randomization value is updated regularly to
* maintain the node's privacy. The update interval controls how often
* the beacon is updated, in 10 second increments.
*
* @returns The Private beacon update interval in 10 second steps, or 0 if
* the beacon is updated every time it's transmitted.
*/
uint8_t bt_mesh_priv_beacon_update_interval_get(void);
/** @brief Set the default TTL value.
*
* The default TTL value is used when no explicit TTL value is set. Models will
@ -181,6 +232,29 @@ int bt_mesh_gatt_proxy_set(enum bt_mesh_feat_state gatt_proxy);
*/
enum bt_mesh_feat_state bt_mesh_gatt_proxy_get(void);
/** @brief Enable or disable the Private GATT Proxy feature.
*
* Support for the Private GATT Proxy feature must be enabled through the
* @kconfig{CONFIG_BT_MESH_PRIV_BEACONS} and @kconfig{CONFIG_BT_MESH_GATT_PROXY}
* configuration options.
*
* @param priv_gatt_proxy New Private GATT Proxy state. Must be one of
* @ref BT_MESH_FEATURE_ENABLED and
* @ref BT_MESH_FEATURE_DISABLED.
*
* @retval 0 Successfully changed the Private GATT Proxy feature state.
* @retval -ENOTSUP The Private GATT Proxy feature is not supported.
* @retval -EINVAL Invalid parameter.
* @retval -EALREADY Already in the given state.
*/
int bt_mesh_priv_gatt_proxy_set(enum bt_mesh_feat_state priv_gatt_proxy);
/** @brief Get the current Private GATT Proxy state.
*
* @returns The Private GATT Proxy feature state.
*/
enum bt_mesh_feat_state bt_mesh_priv_gatt_proxy_get(void);
/** @brief Enable or disable the Friend feature.
*
* Any active friendships will be terminated immediately if the Friend feature
@ -341,6 +415,45 @@ uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx,
uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx,
enum bt_mesh_feat_state *node_id);
/** @brief Set the Private Node Identity state of the Subnet.
*
* The Private Node Identity state of a Subnet determines whether the Subnet
* advertises connectable Private Node Identity beacons for Proxy Clients to
* connect to. Once started, the Node Identity beacon runs for 60 seconds,
* or until it is stopped.
*
* Private Node Identity can only be enabled if regular Node Identity is not
* enabled for any subnet.
*
* GATT Proxy and Private Beacon support must be enabled through
* @kconfig{CONFIG_BT_MESH_GATT_PROXY} and
* @kconfig{CONFIG_BT_MESH_PRIV_BEACONS}.
*
* @param net_idx Network index.
* @param priv_node_id New Private Node Identity state, must be either @ref
* BT_MESH_FEATURE_ENABLED or @ref BT_MESH_FEATURE_DISABLED.
*
* @retval STATUS_SUCCESS Successfully set the Private Node Identity state of the Subnet.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_FEAT_NOT_SUPP The Private Node Identity feature is not supported.
* @retval STATUS_TEMP_STATE_CHG_FAIL The Private Node Identity state cannot be enabled, because
* Node Identity state is already enabled.
* @retval STATUS_CANNOT_SET Couldn't set the Private Node Identity state.
*/
uint8_t bt_mesh_subnet_priv_node_id_set(uint16_t net_idx,
enum bt_mesh_feat_state priv_node_id);
/** @brief Get the Private Node Identity state of the Subnet.
*
* @param net_idx Network index.
* @param priv_node_id Private Node Identity variable to fill.
*
* @retval STATUS_SUCCESS Successfully populated the @c priv_node_id variable.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
*/
uint8_t bt_mesh_subnet_priv_node_id_get(uint16_t net_idx,
enum bt_mesh_feat_state *priv_node_id);
/** @brief Get a list of all known Subnet indexes.
*
* Builds a list of all known Subnet indexes in the @c net_idxs array.

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @defgroup bt_mesh_priv_beacon_cli Bluetooth Mesh Private Beacon Client
* @{
* @brief
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_CLI_H__
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_CLI_H__
#include <zephyr/bluetooth/mesh.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
*
* @brief Private Beacon Client model composition data entry.
*
* @param cli_data Pointer to a @ref bt_mesh_priv_beacon_cli instance.
*/
#define BT_MESH_MODEL_PRIV_BEACON_CLI(cli_data) \
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_PRIV_BEACON_CLI, \
bt_mesh_priv_beacon_cli_op, NULL, cli_data, \
&bt_mesh_priv_beacon_cli_cb)
/** Mesh Private Beacon Client model */
struct bt_mesh_priv_beacon_cli {
/** Timeout value in milliseconds. */
int32_t timeout;
struct bt_mesh_model *mod;
/* Internal parameters for tracking message responses. */
struct bt_mesh_msg_ack_ctx ack_ctx;
};
/** Private Beacon */
struct bt_mesh_priv_beacon {
/** Private beacon is enabled */
uint8_t enabled;
/** Random refresh interval (in 10 second steps), or 0 to keep current
* value.
*/
uint8_t rand_interval;
};
/** Private Node Identity */
struct bt_mesh_priv_node_id {
/** Index of the NetKey. */
uint16_t net_idx;
/** Private Node Identity state */
uint8_t state;
/** Response status code. */
uint8_t status;
};
/** @brief Set the target's Private Beacon state.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param val New Private Beacon value. Returns response status on success.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_priv_beacon_cli_set(uint16_t net_idx, uint16_t addr,
struct bt_mesh_priv_beacon *val);
/** @brief Get the target's Private Beacon state.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param val Response buffer for Private Beacon value.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_priv_beacon_cli_get(uint16_t net_idx, uint16_t addr,
struct bt_mesh_priv_beacon *val);
/** @brief Set the target's Private GATT Proxy state.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param val New Private GATT Proxy value. Returns response status on
* success.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_priv_beacon_cli_gatt_proxy_set(uint16_t net_idx, uint16_t addr,
uint8_t *val);
/** @brief Get the target's Private GATT Proxy state.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param val Response buffer for Private GATT Proxy value.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_priv_beacon_cli_gatt_proxy_get(uint16_t net_idx, uint16_t addr,
uint8_t *val);
/** @brief Set the target's Private Node Identity state.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param val New Private Node Identity value. Returns response status on
* success.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_priv_beacon_cli_node_id_set(uint16_t net_idx, uint16_t addr,
struct bt_mesh_priv_node_id *val);
/** @brief Get the target's Private Node Identity state.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param key_net_idx Network index to get the Private Node Identity state of.
* @param val Response buffer for Private Node Identity value.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_priv_beacon_cli_node_id_get(uint16_t net_idx, uint16_t addr,
uint16_t key_net_idx,
struct bt_mesh_priv_node_id *val);
/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op bt_mesh_priv_beacon_cli_op[];
extern const struct bt_mesh_model_cb bt_mesh_priv_beacon_cli_cb;
/** @endcond */
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_CLI_H__ */
/** @} */

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @defgroup bt_mesh_priv_beacon_srv Bluetooth Mesh Private Beacon Server
* @{
* @brief
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_SRV_H__
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_SRV_H__
#include <zephyr/bluetooth/mesh.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
*
* @brief Private Beacon Server model composition data entry.
*/
#define BT_MESH_MODEL_PRIV_BEACON_SRV \
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_PRIV_BEACON_SRV, \
bt_mesh_priv_beacon_srv_op, NULL, NULL, \
&bt_mesh_priv_beacon_srv_cb)
/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op bt_mesh_priv_beacon_srv_op[];
extern const struct bt_mesh_model_cb bt_mesh_priv_beacon_srv_cb;
/** @endcond */
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_SRV_H__ */
/** @} */

View file

@ -62,6 +62,16 @@ struct bt_mesh_proxy_cb {
*/
int bt_mesh_proxy_identity_enable(void);
/** @brief Enable advertising with Private Node Identity.
*
* This API requires that GATT Proxy support has been enabled. Once called
* each subnet will start advertising using Private Node Identity for the next
* 60 seconds.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_proxy_private_identity_enable(void);
/** @brief Allow Proxy Client to auto connect to a network.
*
* This API allows a proxy client to auto-connect a given network.

View file

@ -73,6 +73,10 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV large_comp_data
zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI large_comp_data_cli.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_PRIV_BEACON_SRV priv_beacon_srv.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_PRIV_BEACON_CLI priv_beacon_cli.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_SELF_TEST test.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_CDB cdb.c)

View file

@ -446,6 +446,7 @@ config BT_MESH_ADV_STACK_SIZE
int "Mesh advertiser thread stack size"
depends on BT_MESH_ADV_LEGACY
default 1024 if BT_HOST_CRYPTO
default 776 if BT_MESH_PRIV_BEACONS
default 768
help
Size of bt mesh adv thread stack.
@ -1212,6 +1213,26 @@ config BT_MESH_LARGE_COMP_DATA_CLI
help
Enable support for the Large Composition Data Client model.
config BT_MESH_PRIV_BEACONS
bool "Support for private beacons"
default y
help
Enable support for private beacons.
if BT_MESH_PRIV_BEACONS
config BT_MESH_PRIV_BEACON_SRV
bool "Support for Private Beacon Server Model"
help
Enable support for the Private Beacon Server model.
config BT_MESH_PRIV_BEACON_CLI
bool "Support for Private Beacon Client Model"
help
Enable support for the Private Beacon Client model.
endif # BT_MESH_PRIV_BEACONS
menu "Transport SAR configuration"
config BT_MESH_SAR_TX_SEG_INT_STEP

View file

@ -7,6 +7,7 @@
#include <zephyr/kernel.h>
#include <errno.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/bluetooth.h>
@ -22,7 +23,7 @@
#include "prov.h"
#include "crypto.h"
#include "beacon.h"
#include "foundation.h"
#include "cfg.h"
#define LOG_LEVEL CONFIG_BT_MESH_BEACON_LOG_LEVEL
#include <zephyr/logging/log.h>
@ -32,6 +33,7 @@ LOG_MODULE_REGISTER(bt_mesh_beacon);
#define BEACON_TYPE_UNPROVISIONED 0x00
#define BEACON_TYPE_SECURE 0x01
#define BEACON_TYPE_PRIVATE 0x02
/* 3 transmissions, 20ms interval */
#define UNPROV_XMIT BT_MESH_TRANSMIT(2, 20)
@ -40,20 +42,39 @@ LOG_MODULE_REGISTER(bt_mesh_beacon);
#define PROV_XMIT BT_MESH_TRANSMIT(0, 20)
static struct k_work_delayable beacon_timer;
static struct {
/**
* Identifier for the current Private beacon random-value.
* Each time we regenerate the random-value, we'll update this idx.
* Whenever it's time for a subnet to create a beacon, it'll compare
* the subnet's beacon idx to determine whether the random value has
* changed since the last beacon was sent. If this is the case, we'll
* regenerate the beacon based on the new random value.
*/
uint16_t idx;
uint8_t val[13];
uint64_t timestamp;
} priv_random;
static bool beacon_cache_match(struct bt_mesh_subnet *sub, void *beacon_data)
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
static int private_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf);
static int private_beacon_update(struct bt_mesh_subnet *sub);
#endif
static bool beacon_cache_match(struct bt_mesh_subnet *sub, void *auth)
{
return !memcmp(sub->beacon_cache, beacon_data, 21);
return !memcmp(sub->beacon_cache, auth, sizeof(sub->beacon_cache));
}
static void cache_add(uint8_t data[21], struct bt_mesh_subnet *sub)
static void cache_add(const uint8_t auth[8], struct bt_mesh_subnet *sub)
{
memcpy(sub->beacon_cache, data, 21);
memcpy(sub->beacon_cache, auth, sizeof(sub->beacon_cache));
}
void bt_mesh_beacon_cache_clear(struct bt_mesh_subnet *sub)
{
(void)memset(sub->beacon_cache, 0, 21);
(void)memset(sub->beacon_cache, 0, sizeof(sub->beacon_cache));
}
static void beacon_complete(int err, void *user_data)
@ -65,8 +86,8 @@ static void beacon_complete(int err, void *user_data)
sub->beacon_sent = k_uptime_get_32();
}
void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf)
static void secure_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf)
{
uint8_t flags = bt_mesh_net_flags(sub);
struct bt_mesh_subnet_keys *keys;
@ -90,11 +111,99 @@ void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
LOG_DBG("IV Index 0x%08x Auth %s", bt_mesh.iv_index, bt_hex(sub->auth, 8));
}
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
static int private_random_update(void)
{
uint8_t interval = bt_mesh_priv_beacon_update_interval_get();
uint64_t uptime = k_uptime_get();
int err;
/* The Private beacon random value should change every N seconds to maintain privacy.
* N = (10 * interval) seconds, or on every beacon creation, if the interval is 0.
*/
if (interval &&
uptime - priv_random.timestamp < (10 * interval * MSEC_PER_SEC)) {
/* Not time yet */
return 0;
}
err = bt_rand(priv_random.val, sizeof(priv_random.val));
if (err) {
return err;
}
/* Update the index to indicate to all subnets that the private beacon must be regenerated.
* Each subnet maintains the random index their private beacon data was generated with.
*/
priv_random.idx++;
priv_random.timestamp = uptime;
return 0;
}
static int private_beacon_update(struct bt_mesh_subnet *sub)
{
struct bt_mesh_subnet_keys *keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
uint8_t flags = bt_mesh_net_flags(sub);
int err;
err = bt_mesh_beacon_encrypt(keys->priv_beacon, flags, bt_mesh.iv_index,
priv_random.val, sub->priv_beacon.data,
sub->auth);
if (err) {
return err;
}
sub->priv_beacon.idx = priv_random.idx;
return 0;
}
static int private_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf)
{
int err;
/* Refresh beacon data */
err = private_random_update();
if (err) {
return err;
}
if (sub->priv_beacon.idx != priv_random.idx) {
err = private_beacon_update(sub);
if (err) {
return err;
}
}
net_buf_simple_add_u8(buf, BEACON_TYPE_PRIVATE);
net_buf_simple_add_mem(buf, priv_random.val, 13);
net_buf_simple_add_mem(buf, sub->priv_beacon.data, 5);
net_buf_simple_add_mem(buf, sub->auth, 8);
LOG_DBG("0x%03x", sub->net_idx);
return 0;
}
#endif
int bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf)
{
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
if (bt_mesh_priv_beacon_get() == BT_MESH_FEATURE_ENABLED) {
return private_beacon_create(sub, buf);
}
#endif
secure_beacon_create(sub, buf);
return 0;
}
/* If the interval has passed or is within 5 seconds from now send a beacon */
#define BEACON_THRESHOLD(sub) \
((10 * ((sub)->beacons_last + 1)) * MSEC_PER_SEC - (5 * MSEC_PER_SEC))
static bool secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data)
static bool net_beacon_send(struct bt_mesh_subnet *sub, void *cb_data)
{
static const struct bt_mesh_send_cb send_cb = {
.end = beacon_complete,
@ -103,6 +212,7 @@ static bool secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data)
struct net_buf *buf;
uint32_t time_diff;
uint32_t time_since_last_recv;
int err;
LOG_DBG("");
@ -121,7 +231,10 @@ static bool secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data)
return true; /* Bail out */
}
bt_mesh_beacon_create(sub, &buf->b);
err = bt_mesh_beacon_create(sub, &buf->b);
if (err) {
return true; /* Bail out */
}
bt_mesh_adv_send(buf, &send_cb, sub);
net_buf_unref(buf);
@ -242,18 +355,24 @@ static void update_beacon_observation(void)
bt_mesh_subnet_foreach(sub_update_beacon_observation);
}
static bool net_beacon_is_running(void)
{
return bt_mesh_beacon_enabled() ||
atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) ||
(bt_mesh_priv_beacon_get() == BT_MESH_FEATURE_ENABLED);
}
static void beacon_send(struct k_work *work)
{
LOG_DBG("");
if (bt_mesh_is_provisioned()) {
if (!bt_mesh_beacon_enabled() &&
!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
if (!net_beacon_is_running()) {
return;
}
update_beacon_observation();
(void)bt_mesh_subnet_find(secure_beacon_send, NULL);
(void)bt_mesh_subnet_find(net_beacon_send, NULL);
k_work_schedule(&beacon_timer, PROVISIONED_INTERVAL);
return;
@ -271,7 +390,13 @@ static void beacon_send(struct k_work *work)
}
struct beacon_params {
const uint8_t *net_id;
union {
const uint8_t *net_id;
struct {
const uint8_t *data;
const uint8_t *random;
} private;
};
const uint8_t *auth;
uint32_t iv_index;
uint8_t flags;
@ -300,7 +425,7 @@ static bool auth_match(struct bt_mesh_subnet_keys *keys,
return true;
}
static bool subnet_by_id(struct bt_mesh_subnet *sub, void *cb_data)
static bool secure_beacon_authenticate(struct bt_mesh_subnet *sub, void *cb_data)
{
struct beacon_params *params = cb_data;
@ -314,74 +439,128 @@ static bool subnet_by_id(struct bt_mesh_subnet *sub, void *cb_data)
return false;
}
static bool priv_beacon_decrypt(struct bt_mesh_subnet *sub, void *cb_data)
{
struct beacon_params *params = cb_data;
uint8_t out[5];
int err;
for (int i = 0; i < ARRAY_SIZE(sub->keys); i++) {
if (!sub->keys[i].valid) {
continue;
}
err = bt_mesh_beacon_decrypt(sub->keys[i].priv_beacon,
params->private.random,
params->private.data, params->auth,
out);
if (!err) {
params->new_key = (i > 0);
params->flags = out[0];
params->iv_index = sys_get_be32(&out[1]);
return true;
}
}
return false;
}
static void net_beacon_register(struct bt_mesh_subnet *sub)
{
if (bt_mesh_beacon_enabled() && sub->beacons_cur < 0xff) {
sub->beacons_cur++;
sub->beacon_recv = k_uptime_get_32();
}
}
static void net_beacon_recv(struct bt_mesh_subnet *sub,
const struct beacon_params *params)
{
bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(params->flags),
params->new_key);
/* If we have NetKey0 accept IV index initiation only from it */
if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
sub->net_idx != BT_MESH_KEY_PRIMARY) {
LOG_WRN("Ignoring secure beacon on non-primary subnet");
return;
}
LOG_DBG("net_idx 0x%04x flags %u iv_index 0x%08x, "
"current iv_index 0x%08x",
sub->net_idx, params->flags, params->iv_index, bt_mesh.iv_index);
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
(atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
BT_MESH_IV_UPDATE(params->flags))) {
bt_mesh_beacon_ivu_initiator(false);
}
bt_mesh_net_iv_update(params->iv_index,
BT_MESH_IV_UPDATE(params->flags));
}
static void net_beacon_resolve(struct beacon_params *params,
bool (*matcher)(struct bt_mesh_subnet *sub,
void *cb_data))
{
struct bt_mesh_subnet *sub;
sub = bt_mesh_subnet_find(beacon_cache_match, (void *)params->auth);
if (sub) {
/* We've seen this beacon before - just update the stats */
net_beacon_register(sub);
return;
}
sub = bt_mesh_subnet_find(matcher, params);
if (!sub) {
LOG_DBG("No subnet that matched beacon");
return;
}
if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !params->new_key) {
LOG_WRN("Ignoring Phase 2 KR Update secured using old key");
return;
}
cache_add(params->auth, sub);
net_beacon_recv(sub, params);
net_beacon_register(sub);
}
static void secure_beacon_recv(struct net_buf_simple *buf)
{
struct beacon_params params;
struct bt_mesh_subnet *sub;
uint8_t *data;
if (buf->len < 21) {
LOG_ERR("Too short secure beacon (len %u)", buf->len);
return;
}
sub = bt_mesh_subnet_find(beacon_cache_match, buf->data);
if (sub) {
/* We've seen this beacon before - just update the stats */
goto update_stats;
}
/* So we can add to the cache if auth matches */
data = buf->data;
params.flags = net_buf_simple_pull_u8(buf);
params.net_id = net_buf_simple_pull_mem(buf, 8);
params.iv_index = net_buf_simple_pull_be32(buf);
params.auth = buf->data;
LOG_DBG("flags 0x%02x id %s iv_index 0x%08x", params.flags, bt_hex(params.net_id, 8),
params.iv_index);
net_beacon_resolve(&params, secure_beacon_authenticate);
}
sub = bt_mesh_subnet_find(subnet_by_id, &params);
if (!sub) {
LOG_DBG("No subnet that matched beacon");
static void private_beacon_recv(struct net_buf_simple *buf)
{
struct beacon_params params;
if (buf->len < 26) {
LOG_ERR("Too short private beacon (len %u)", buf->len);
return;
}
if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !params.new_key) {
LOG_WRN("Ignoring Phase 2 KR Update secured using old key");
return;
}
params.private.random = net_buf_simple_pull_mem(buf, 13);
params.private.data = net_buf_simple_pull_mem(buf, 5);
params.auth = buf->data;
cache_add(data, sub);
bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(params.flags),
params.new_key);
/* If we have NetKey0 accept initiation only from it */
if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
sub->net_idx != BT_MESH_KEY_PRIMARY) {
LOG_WRN("Ignoring secure beacon on non-primary subnet");
goto update_stats;
}
LOG_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x", sub->net_idx,
params.iv_index, bt_mesh.iv_index);
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
(atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
BT_MESH_IV_UPDATE(params.flags))) {
bt_mesh_beacon_ivu_initiator(false);
}
bt_mesh_net_iv_update(params.iv_index, BT_MESH_IV_UPDATE(params.flags));
update_stats:
if (bt_mesh_beacon_enabled() &&
sub->beacons_cur < 0xff) {
sub->beacons_cur++;
sub->beacon_recv = k_uptime_get_32();
}
net_beacon_resolve(&params, priv_beacon_decrypt);
}
void bt_mesh_beacon_recv(struct net_buf_simple *buf)
@ -405,6 +584,9 @@ void bt_mesh_beacon_recv(struct net_buf_simple *buf)
case BEACON_TYPE_SECURE:
secure_beacon_recv(buf);
break;
case BEACON_TYPE_PRIVATE:
private_beacon_recv(buf);
break;
default:
LOG_WRN("Unknown beacon type 0x%02x", type);
break;
@ -415,7 +597,6 @@ void bt_mesh_beacon_update(struct bt_mesh_subnet *sub)
{
uint8_t flags = bt_mesh_net_flags(sub);
struct bt_mesh_subnet_keys *keys;
int err;
keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
@ -423,11 +604,13 @@ void bt_mesh_beacon_update(struct bt_mesh_subnet *sub)
SUBNET_KEY_TX_IDX(sub) ? "new" : "current");
LOG_DBG("flags 0x%02x, IVI 0x%08x", flags, bt_mesh.iv_index);
err = bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id,
bt_mesh.iv_index, sub->auth);
if (err) {
LOG_ERR("Failed updating net beacon for 0x%03x", sub->net_idx);
}
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
/* Invalidate private beacon to force regeneration: */
sub->priv_beacon.idx = priv_random.idx - 1;
#endif
bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, bt_mesh.iv_index,
sub->auth);
}
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
@ -444,6 +627,10 @@ BT_MESH_SUBNET_CB_DEFINE(beacon) = {
void bt_mesh_beacon_init(void)
{
k_work_init_delayable(&beacon_timer, beacon_send);
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
private_random_update();
#endif
}
void bt_mesh_beacon_ivu_initiator(bool enable)
@ -481,8 +668,12 @@ void bt_mesh_beacon_enable(void)
void bt_mesh_beacon_disable(void)
{
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
/* If this fails, we'll do an early exit in the work handler. */
(void)k_work_cancel_delayable(&beacon_timer);
}
/* If this fails, we'll do an early exit in the work handler. */
(void)k_work_cancel_delayable(&beacon_timer);
}
void bt_mesh_beacon_priv_random_get(uint8_t *random, size_t size)
{
__ASSERT(size <= sizeof(priv_random.val), "Invalid random value size %u", size);
memcpy(random, priv_random.val, size);
}

View file

@ -11,8 +11,9 @@ void bt_mesh_beacon_ivu_initiator(bool enable);
void bt_mesh_beacon_recv(struct net_buf_simple *buf);
void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf);
int bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf);
void bt_mesh_beacon_init(void);
void bt_mesh_beacon_update(struct bt_mesh_subnet *sub);
void bt_mesh_beacon_priv_random_get(uint8_t *random, size_t size);

View file

@ -14,8 +14,8 @@
#include "settings.h"
#include "heartbeat.h"
#include "friend.h"
#include "cfg.h"
#include "adv.h"
#include "cfg.h"
#define LOG_LEVEL CONFIG_BT_MESH_CFG_LOG_LEVEL
#include <zephyr/logging/log.h>
@ -30,6 +30,10 @@ struct cfg_val {
uint8_t gatt_proxy;
uint8_t frnd;
uint8_t default_ttl;
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
uint8_t priv_beacon;
uint8_t priv_beacon_int;
#endif
};
void bt_mesh_beacon_set(bool beacon)
@ -82,6 +86,58 @@ static enum bt_mesh_feat_state feature_get(int feature_flag)
BT_MESH_FEATURE_DISABLED;
}
int bt_mesh_priv_beacon_set(enum bt_mesh_feat_state priv_beacon)
{
int err;
if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return -ENOTSUP;
}
err = feature_set(BT_MESH_PRIV_BEACON, priv_beacon);
if (err) {
return err;
}
if (priv_beacon == BT_MESH_FEATURE_ENABLED) {
bt_mesh_beacon_enable();
} else if (bt_mesh_beacon_enabled()) {
bt_mesh_beacon_disable();
}
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
}
return 0;
}
enum bt_mesh_feat_state bt_mesh_priv_beacon_get(void)
{
if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return BT_MESH_FEATURE_NOT_SUPPORTED;
}
return feature_get(BT_MESH_PRIV_BEACON);
}
void bt_mesh_priv_beacon_update_interval_set(uint8_t interval)
{
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
bt_mesh.priv_beacon_int = interval;
#endif
}
uint8_t bt_mesh_priv_beacon_update_interval_get(void)
{
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
return bt_mesh.priv_beacon_int;
#else
return 0;
#endif
}
static bool node_id_is_running(struct bt_mesh_subnet *sub, void *cb_data)
{
return sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING;
@ -125,6 +181,42 @@ enum bt_mesh_feat_state bt_mesh_gatt_proxy_get(void)
return feature_get(BT_MESH_GATT_PROXY);
}
int bt_mesh_priv_gatt_proxy_set(enum bt_mesh_feat_state priv_gatt_proxy)
{
int err;
if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) || !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return BT_MESH_FEATURE_NOT_SUPPORTED;
}
err = feature_set(BT_MESH_PRIV_GATT_PROXY, priv_gatt_proxy);
if (err) {
return err;
}
if (priv_gatt_proxy == BT_MESH_FEATURE_ENABLED) {
/* Re-generate proxy beacon */
bt_mesh_adv_gatt_update();
}
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
}
return 0;
}
enum bt_mesh_feat_state bt_mesh_priv_gatt_proxy_get(void)
{
if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) || !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return BT_MESH_FEATURE_NOT_SUPPORTED;
}
return feature_get(BT_MESH_PRIV_GATT_PROXY);
}
int bt_mesh_default_ttl_set(uint8_t default_ttl)
{
if (default_ttl == 1 || default_ttl > BT_MESH_TTL_MAX) {
@ -349,6 +441,11 @@ static void store_pending_cfg(void)
val.gatt_proxy = bt_mesh_gatt_proxy_get();
val.frnd = bt_mesh_friend_get();
val.default_ttl = bt_mesh_default_ttl_get();
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
val.priv_beacon = bt_mesh_priv_beacon_get();
val.priv_beacon_int = bt_mesh_priv_beacon_update_interval_get();
#endif
err = settings_save_one("bt/mesh/Cfg", &val, sizeof(val));
if (err) {

View file

@ -546,6 +546,13 @@ static int gatt_proxy_set(struct bt_mesh_model *model,
(void)bt_mesh_gatt_proxy_set(buf->data[0]);
/** 4.2.46.1: If the value of the Node Identity state of the node for any subnet is 0x01,
* then the value of the Private Node Identity state shall be Disable (0x00).
*/
if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) && buf->data[0]) {
(void)bt_mesh_priv_gatt_proxy_set(BT_MESH_FEATURE_DISABLED);
}
return send_gatt_proxy_status(model, ctx);
}

View file

@ -702,3 +702,126 @@ int bt_mesh_dhkey_gen(const uint8_t *pub_key, const uint8_t *priv_key, uint8_t *
return 0;
}
static int private_beacon_obf(const uint8_t pbk[16], const uint8_t data[5],
const uint8_t random[13], uint8_t out[5])
{
uint8_t salt[16];
int i, err;
/* C1 = 0x01 | random | 0x0001 */
salt[0] = 0x01;
memcpy(&salt[1], random, 13);
sys_put_be16(0x0001, &salt[14]);
/* ObfData = e(pbk, C1) ^ (flags | iv_index) */
err = bt_encrypt_be(pbk, salt, salt);
if (err) {
return err;
}
for (i = 0; i < 5; i++) {
out[i] = data[i] ^ salt[i];
}
return 0;
}
static int private_beacon_auth(const uint8_t pbk[16],
const uint8_t beacon_data[5],
const uint8_t random[13], uint8_t auth[8])
{
uint8_t salt[16], tmp[16];
int i, err;
/* B0 = 0x19 | random | 0x0005 */
salt[0] = 0x19;
memcpy(&salt[1], random, 13);
sys_put_be16(0x0005, &salt[14]);
/* T0 = e(PBK, b0) */
err = bt_encrypt_be(pbk, salt, tmp);
if (err) {
return err;
}
/* P = flags | iv_index | 000 */
/* T1 = e(PBK, P ^ T0) */
for (i = 0; i < 5; i++) {
tmp[i] ^= beacon_data[i];
}
err = bt_encrypt_be(pbk, tmp, tmp);
if (err) {
return err;
}
/* C0 = 0x01 | random | 0x0000 */
salt[0] = 0x01;
sys_put_be16(0x0000, &salt[14]);
/* T2 = T1 ^ e(PBK, C0) */
memcpy(auth, tmp, 8);
err = bt_encrypt_be(pbk, salt, tmp);
if (err) {
return err;
}
/* Auth = T2[0..7] */
for (i = 0; i < 8; i++) {
auth[i] ^= tmp[i];
}
return 0;
}
int bt_mesh_beacon_decrypt(const uint8_t pbk[16], const uint8_t random[13],
const uint8_t data[5],
const uint8_t expected_auth[8], uint8_t out[5])
{
uint8_t auth[8];
int err;
LOG_DBG("");
err = private_beacon_obf(pbk, data, random, out);
if (err) {
return err;
}
err = private_beacon_auth(pbk, out, random, auth);
if (err) {
return err;
}
LOG_DBG("0x%02x, 0x%08x", out[0], sys_get_be32(&out[1]));
if (memcmp(auth, expected_auth, 8)) {
LOG_DBG("Invalid auth: %s expected %s", bt_hex(auth, 8),
bt_hex(expected_auth, 8));
return -EBADMSG;
}
return 0;
}
int bt_mesh_beacon_encrypt(const uint8_t pbk[16], uint8_t flags,
uint32_t iv_index, const uint8_t random[13],
uint8_t data[5], uint8_t auth[8])
{
int err;
LOG_WRN("Enc beacon: 0x%02x, 0x%08x", flags, iv_index);
data[0] = flags;
sys_put_be32(iv_index, &data[1]);
err = private_beacon_auth(pbk, data, random, auth);
if (err) {
return err;
}
return private_beacon_obf(pbk, data, random, data);
}

View file

@ -56,6 +56,12 @@ static inline int bt_mesh_beacon_key(const uint8_t net_key[16],
return bt_mesh_id128(net_key, "nkbk", beacon_key);
}
static inline int bt_mesh_private_beacon_key(const uint8_t net_key[16],
uint8_t private_beacon_key[16])
{
return bt_mesh_id128(net_key, "nkpk", private_beacon_key);
}
int bt_mesh_beacon_auth(const uint8_t beacon_key[16], uint8_t flags,
const uint8_t net_id[8], uint32_t iv_index,
uint8_t auth[8]);
@ -150,3 +156,10 @@ int bt_mesh_prov_encrypt(const uint8_t key[16], uint8_t nonce[13],
const uint8_t data[25], uint8_t out[25 + 8]);
int bt_mesh_dhkey_gen(const uint8_t *pub_key, const uint8_t *priv_key, uint8_t *dhkey);
int bt_mesh_beacon_decrypt(const uint8_t pbk[16], const uint8_t random[13],
const uint8_t data[5], const uint8_t expected_auth[8],
uint8_t out[5]);
int bt_mesh_beacon_encrypt(const uint8_t pbk[16], uint8_t flags, uint32_t iv_index,
const uint8_t random[13], uint8_t data[5], uint8_t auth[8]);

View file

@ -102,6 +102,16 @@
#define OP_MODELS_METADATA_GET BT_MESH_MODEL_OP_2(0x80, 0x76)
#define OP_MODELS_METADATA_STATUS BT_MESH_MODEL_OP_2(0x80, 0x77)
#define OP_PRIV_BEACON_GET BT_MESH_MODEL_OP_2(0x80, 0x60)
#define OP_PRIV_BEACON_SET BT_MESH_MODEL_OP_2(0x80, 0x61)
#define OP_PRIV_BEACON_STATUS BT_MESH_MODEL_OP_2(0x80, 0x62)
#define OP_PRIV_GATT_PROXY_GET BT_MESH_MODEL_OP_2(0x80, 0x63)
#define OP_PRIV_GATT_PROXY_SET BT_MESH_MODEL_OP_2(0x80, 0x64)
#define OP_PRIV_GATT_PROXY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x65)
#define OP_PRIV_NODE_ID_GET BT_MESH_MODEL_OP_2(0x80, 0x66)
#define OP_PRIV_NODE_ID_SET BT_MESH_MODEL_OP_2(0x80, 0x67)
#define OP_PRIV_NODE_ID_STATUS BT_MESH_MODEL_OP_2(0x80, 0x68)
#define STATUS_SUCCESS 0x00
#define STATUS_INVALID_ADDRESS 0x01
#define STATUS_INVALID_MODEL 0x02

View file

@ -96,6 +96,10 @@ struct bt_mesh_net bt_mesh = {
.sar_tx = BT_MESH_SAR_TX_INIT,
.sar_rx = BT_MESH_SAR_RX_INIT,
#endif
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
.priv_beacon_int = 0x3c,
#endif
};
/* Mesh Profile Specification 3.10.6
@ -355,7 +359,8 @@ do_update:
bt_mesh_subnet_foreach(bt_mesh_beacon_update);
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
(bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED ||
bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED)) {
bt_mesh_proxy_beacon_send(NULL);
}
@ -673,7 +678,8 @@ static bool relay_to_adv(enum bt_mesh_net_if net_if)
case BT_MESH_NET_IF_ADV:
return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED);
case BT_MESH_NET_IF_PROXY:
return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) ||
(bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED);
default:
return false;
}
@ -693,7 +699,8 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
if (rx->net_if == BT_MESH_NET_IF_ADV &&
!rx->friend_cred &&
bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED &&
bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED) {
bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED &&
bt_mesh_priv_gatt_proxy_get() != BT_MESH_PRIV_GATT_PROXY_ENABLED) {
return;
}
@ -747,7 +754,8 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
*/
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
(rx->friend_cred ||
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED)) {
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED ||
bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED)) {
bt_mesh_proxy_relay(buf, rx->ctx.recv_dst);
}
@ -858,6 +866,7 @@ void bt_mesh_net_recv(struct net_buf_simple *data, int8_t rssi,
bt_mesh_proxy_addr_add(data, rx.ctx.addr);
if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_DISABLED &&
bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_DISABLED &&
!rx.local_match) {
LOG_INF("Proxy is disabled; ignoring message");
return;

View file

@ -5,6 +5,7 @@
*/
#include "subnet.h"
#include <zephyr/bluetooth/mesh/sar_cfg.h>
#define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01)
#define BT_MESH_KEY_REFRESH(flags) (flags & 0x01)
@ -185,6 +186,8 @@ enum {
BT_MESH_BEACON,
BT_MESH_GATT_PROXY,
BT_MESH_FRIEND,
BT_MESH_PRIV_BEACON,
BT_MESH_PRIV_GATT_PROXY,
/* Don't touch - intentionally last */
BT_MESH_FLAG_COUNT,
@ -216,6 +219,10 @@ struct bt_mesh_net {
uint8_t relay_xmit;
uint8_t default_ttl;
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
uint8_t priv_beacon_int;
#endif
/* Timer to track duration in current IV Update state */
struct k_work_delayable ivu_timer;

View file

@ -0,0 +1,303 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#include "net.h"
#include "foundation.h"
#include "access.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_priv_beacon_cli);
static struct bt_mesh_priv_beacon_cli *cli;
static int handle_beacon_status(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_priv_beacon *rsp;
uint8_t beacon, rand_int;
beacon = net_buf_simple_pull_u8(buf);
rand_int = net_buf_simple_pull_u8(buf);
if (beacon != BT_MESH_BEACON_DISABLED &&
beacon != BT_MESH_BEACON_ENABLED) {
LOG_WRN("Invalid beacon value 0x%02x", beacon);
return -EINVAL;
}
LOG_DBG("0x%02x (%u s)", beacon, 10U * rand_int);
if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_PRIV_BEACON_STATUS, ctx->addr,
(void **)&rsp)) {
LOG_WRN("Unexpected beacon status from 0x%04x", ctx->addr);
return -EINVAL;
}
rsp->enabled = beacon;
rsp->rand_interval = rand_int;
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
return 0;
}
static int handle_gatt_proxy_status(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t *rsp;
uint8_t proxy;
proxy = net_buf_simple_pull_u8(buf);
if (proxy != BT_MESH_GATT_PROXY_DISABLED &&
proxy != BT_MESH_GATT_PROXY_ENABLED &&
proxy != BT_MESH_GATT_PROXY_NOT_SUPPORTED) {
LOG_WRN("Invalid GATT proxy value 0x%02x", proxy);
return -EINVAL;
}
if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_PRIV_GATT_PROXY_STATUS, ctx->addr,
(void **)&rsp)) {
LOG_WRN("Unexpected proxy status from 0x%04x", ctx->addr);
return -EINVAL;
}
*rsp = proxy;
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
return 0;
}
static int handle_node_id_status(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_priv_node_id *rsp;
uint8_t status, node_id;
uint16_t net_idx;
status = net_buf_simple_pull_u8(buf);
net_idx = net_buf_simple_pull_le16(buf);
node_id = net_buf_simple_pull_u8(buf);
if (node_id != BT_MESH_NODE_IDENTITY_STOPPED &&
node_id != BT_MESH_NODE_IDENTITY_RUNNING &&
node_id != BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) {
LOG_WRN("Invalid node ID value 0x%02x", node_id);
return -EINVAL;
}
if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_PRIV_NODE_ID_STATUS, ctx->addr,
(void **)&rsp)) {
LOG_WRN("Unexpected node ID status from 0x%04x", ctx->addr);
return -EINVAL;
}
rsp->status = status;
rsp->state = node_id;
bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
return 0;
}
const struct bt_mesh_model_op bt_mesh_priv_beacon_cli_op[] = {
{ OP_PRIV_BEACON_STATUS, BT_MESH_LEN_EXACT(2), handle_beacon_status },
{ OP_PRIV_GATT_PROXY_STATUS, BT_MESH_LEN_EXACT(1), handle_gatt_proxy_status },
{ OP_PRIV_NODE_ID_STATUS, BT_MESH_LEN_EXACT(4), handle_node_id_status },
BT_MESH_MODEL_OP_END,
};
static int priv_beacon_cli_init(struct bt_mesh_model *mod)
{
if (!bt_mesh_model_in_primary(mod)) {
LOG_ERR("Private Beacon Client only allowed in primary element");
return -EINVAL;
}
cli = mod->user_data;
cli->mod = mod;
cli->timeout = 2 * MSEC_PER_SEC;
mod->keys[0] = BT_MESH_KEY_DEV_ANY;
mod->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_priv_beacon_cli_cb = {
.init = priv_beacon_cli_init,
};
static int send(struct bt_mesh_priv_beacon_cli *cli, uint16_t net_idx,
uint16_t addr, struct net_buf_simple *buf)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = net_idx,
.app_idx = BT_MESH_KEY_DEV_REMOTE,
.addr = addr,
.send_ttl = BT_MESH_TTL_DEFAULT,
};
return bt_mesh_model_send(cli->mod, &ctx, buf, NULL, NULL);
}
int bt_mesh_priv_beacon_cli_set(uint16_t net_idx, uint16_t addr, struct bt_mesh_priv_beacon *val)
{
int err;
err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_BEACON_STATUS, addr, val);
if (err) {
return err;
}
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_BEACON_SET, 2);
bt_mesh_model_msg_init(&buf, OP_PRIV_BEACON_SET);
net_buf_simple_add_u8(&buf, val->enabled);
if (val->rand_interval) {
net_buf_simple_add_u8(&buf, val->rand_interval);
}
err = send(cli, net_idx, addr, &buf);
if (err) {
bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
return err;
}
return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout));
}
int bt_mesh_priv_beacon_cli_get(uint16_t net_idx, uint16_t addr, struct bt_mesh_priv_beacon *val)
{
int err;
err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_BEACON_STATUS, addr, val);
if (err) {
return err;
}
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_BEACON_GET, 0);
bt_mesh_model_msg_init(&buf, OP_PRIV_BEACON_GET);
err = send(cli, net_idx, addr, &buf);
if (err) {
bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
return err;
}
return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout));
}
int bt_mesh_priv_beacon_cli_gatt_proxy_set(uint16_t net_idx, uint16_t addr, uint8_t *val)
{
int err;
if (!val || (*val != BT_MESH_GATT_PROXY_DISABLED &&
*val != BT_MESH_GATT_PROXY_ENABLED)) {
return -EINVAL;
}
err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_GATT_PROXY_STATUS, addr, val);
if (err) {
return err;
}
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_GATT_PROXY_SET, 1);
bt_mesh_model_msg_init(&buf, OP_PRIV_GATT_PROXY_SET);
net_buf_simple_add_u8(&buf, *val);
err = send(cli, net_idx, addr, &buf);
if (err) {
bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
return err;
}
return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout));
}
int bt_mesh_priv_beacon_cli_gatt_proxy_get(uint16_t net_idx, uint16_t addr, uint8_t *val)
{
int err;
err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_GATT_PROXY_STATUS, addr, val);
if (err) {
return err;
}
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_GATT_PROXY_GET, 0);
bt_mesh_model_msg_init(&buf, OP_PRIV_GATT_PROXY_GET);
err = send(cli, net_idx, addr, &buf);
if (err) {
bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
return err;
}
return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout));
}
int bt_mesh_priv_beacon_cli_node_id_set(uint16_t net_idx, uint16_t addr,
struct bt_mesh_priv_node_id *val)
{
int err;
if (!val || val->net_idx > 0xfff ||
(val->state != BT_MESH_NODE_IDENTITY_STOPPED &&
val->state != BT_MESH_NODE_IDENTITY_RUNNING)) {
return -EINVAL;
}
err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_NODE_ID_STATUS, addr, val);
if (err) {
return err;
}
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_NODE_ID_SET, 3);
bt_mesh_model_msg_init(&buf, OP_PRIV_NODE_ID_SET);
net_buf_simple_add_le16(&buf, val->net_idx);
net_buf_simple_add_u8(&buf, val->state);
err = send(cli, net_idx, addr, &buf);
if (err) {
bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
return err;
}
return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout));
}
int bt_mesh_priv_beacon_cli_node_id_get(uint16_t net_idx, uint16_t addr, uint16_t key_net_idx,
struct bt_mesh_priv_node_id *val)
{
int err;
err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_NODE_ID_STATUS, addr, val);
if (err) {
return err;
}
val->net_idx = key_net_idx;
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_NODE_ID_GET, 2);
bt_mesh_model_msg_init(&buf, OP_PRIV_NODE_ID_GET);
net_buf_simple_add_le16(&buf, key_net_idx);
err = send(cli, net_idx, addr, &buf);
if (err) {
bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
return err;
}
return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout));
}

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#include "net.h"
#include "adv.h"
#include <zephyr/bluetooth/conn.h>
#include "proxy.h"
#include "foundation.h"
#include "beacon.h"
#include "cfg.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_priv_beacon_srv);
static int beacon_status_rsp(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx)
{
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_BEACON_STATUS, 2);
bt_mesh_model_msg_init(&buf, OP_PRIV_BEACON_STATUS);
net_buf_simple_add_u8(&buf, bt_mesh_priv_beacon_get());
net_buf_simple_add_u8(&buf, bt_mesh_priv_beacon_update_interval_get());
bt_mesh_model_send(mod, ctx, &buf, NULL, NULL);
return 0;
}
static int handle_beacon_get(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_DBG("");
beacon_status_rsp(mod, ctx);
return 0;
}
static int handle_beacon_set(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t beacon;
if (buf->len > 2U) {
return -EMSGSIZE;
}
beacon = net_buf_simple_pull_u8(buf);
if (beacon != BT_MESH_BEACON_DISABLED &&
beacon != BT_MESH_BEACON_ENABLED) {
LOG_WRN("Invalid beacon value %u", beacon);
return -EINVAL;
}
if (buf->len == 1U) {
bt_mesh_priv_beacon_update_interval_set(net_buf_simple_pull_u8(buf));
}
(void)bt_mesh_priv_beacon_set(beacon);
beacon_status_rsp(mod, ctx);
return 0;
}
static void gatt_proxy_status_rsp(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx)
{
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_GATT_PROXY_STATUS, 1);
bt_mesh_model_msg_init(&buf, OP_PRIV_GATT_PROXY_STATUS);
net_buf_simple_add_u8(&buf, bt_mesh_priv_gatt_proxy_get());
bt_mesh_model_send(mod, ctx, &buf, NULL, NULL);
}
static int handle_gatt_proxy_get(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_DBG("");
gatt_proxy_status_rsp(mod, ctx);
return 0;
}
static int handle_gatt_proxy_set(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t gatt_proxy;
gatt_proxy = net_buf_simple_pull_u8(buf);
if (gatt_proxy != BT_MESH_GATT_PROXY_DISABLED &&
gatt_proxy != BT_MESH_GATT_PROXY_ENABLED) {
LOG_WRN("Invalid GATT proxy value %u", gatt_proxy);
return -EINVAL;
}
LOG_DBG("%u", gatt_proxy);
bt_mesh_priv_gatt_proxy_set(gatt_proxy);
gatt_proxy_status_rsp(mod, ctx);
return 0;
}
static void node_id_status_rsp(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx, uint8_t status,
uint16_t net_idx, uint8_t node_id)
{
BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_NODE_ID_STATUS, 4);
bt_mesh_model_msg_init(&buf, OP_PRIV_NODE_ID_STATUS);
net_buf_simple_add_u8(&buf, status);
net_buf_simple_add_le16(&buf, net_idx);
net_buf_simple_add_u8(&buf, node_id);
bt_mesh_model_send(mod, ctx, &buf, NULL, NULL);
}
static int handle_node_id_get(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t node_id, status;
uint16_t net_idx;
net_idx = net_buf_simple_pull_le16(buf) & 0xfff;
status = bt_mesh_subnet_priv_node_id_get(net_idx, (enum bt_mesh_feat_state *)&node_id);
node_id_status_rsp(mod, ctx, status, net_idx, node_id);
return 0;
}
static int handle_node_id_set(struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t node_id, status;
uint16_t net_idx;
net_idx = net_buf_simple_pull_le16(buf) & 0xfff;
node_id = net_buf_simple_pull_u8(buf);
if (node_id != BT_MESH_NODE_IDENTITY_RUNNING &&
node_id != BT_MESH_NODE_IDENTITY_STOPPED) {
LOG_ERR("Invalid node ID value 0x%02x", node_id);
return -EINVAL;
}
status = bt_mesh_subnet_priv_node_id_set(net_idx, node_id);
node_id_status_rsp(mod, ctx, status, net_idx, node_id);
return 0;
}
const struct bt_mesh_model_op bt_mesh_priv_beacon_srv_op[] = {
{ OP_PRIV_BEACON_GET, BT_MESH_LEN_EXACT(0), handle_beacon_get },
{ OP_PRIV_BEACON_SET, BT_MESH_LEN_MIN(1), handle_beacon_set },
{ OP_PRIV_GATT_PROXY_GET, BT_MESH_LEN_EXACT(0), handle_gatt_proxy_get },
{ OP_PRIV_GATT_PROXY_SET, BT_MESH_LEN_EXACT(1), handle_gatt_proxy_set },
{ OP_PRIV_NODE_ID_GET, BT_MESH_LEN_EXACT(2), handle_node_id_get },
{ OP_PRIV_NODE_ID_SET, BT_MESH_LEN_EXACT(3), handle_node_id_set },
BT_MESH_MODEL_OP_END
};
static int priv_beacon_srv_init(struct bt_mesh_model *mod)
{
if (!bt_mesh_model_in_primary(mod)) {
LOG_ERR("Priv beacon server not in primary element");
return -EINVAL;
}
mod->keys[0] = BT_MESH_KEY_DEV_LOCAL;
return 0;
}
const struct bt_mesh_model_cb bt_mesh_priv_beacon_srv_cb = {
.init = priv_beacon_srv_init,
};

View file

@ -18,8 +18,10 @@
.interval_min = BT_GAP_ADV_FAST_INT_MIN_2, \
.interval_max = BT_GAP_ADV_FAST_INT_MAX_2
#define BT_MESH_ID_TYPE_NET 0x00
#define BT_MESH_ID_TYPE_NODE 0x01
#define BT_MESH_ID_TYPE_NET 0x00
#define BT_MESH_ID_TYPE_NODE 0x01
#define BT_MESH_ID_TYPE_PRIV_NET 0x02
#define BT_MESH_ID_TYPE_PRIV_NODE 0x03
int bt_mesh_proxy_gatt_enable(void);
int bt_mesh_proxy_gatt_disable(void);
@ -29,7 +31,7 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub);
int bt_mesh_proxy_adv_start(void);
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub);
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub, bool private);
void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub);
bool bt_mesh_proxy_relay(struct net_buf *buf, uint16_t dst);

View file

@ -13,6 +13,7 @@
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/sys/util.h>
#include <zephyr/bluetooth/hci.h>
@ -35,6 +36,12 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_gatt);
/* Interval to update random value in (10 minutes).
*
* Defined in the Bluetooth Mesh Specification v1.1, Section 7.2.2.2.4.
*/
#define PROXY_RANDOM_UPDATE_INTERVAL (10 * 60 * MSEC_PER_SEC)
#if defined(CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME)
#define ADV_OPT_USE_NAME BT_LE_ADV_OPT_USE_NAME
#else
@ -312,10 +319,15 @@ static void proxy_msg_recv(struct bt_mesh_proxy_role *role)
static int beacon_send(struct bt_mesh_proxy_client *client,
struct bt_mesh_subnet *sub)
{
NET_BUF_SIMPLE_DEFINE(buf, 23);
int err;
NET_BUF_SIMPLE_DEFINE(buf, 28);
net_buf_simple_reserve(&buf, 1);
bt_mesh_beacon_create(sub, &buf);
err = bt_mesh_beacon_create(sub, &buf);
if (err) {
return err;
}
return bt_mesh_proxy_msg_send(client->cli->conn, BT_MESH_PROXY_BEACON,
&buf, NULL, NULL);
@ -354,7 +366,7 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
}
}
static void node_id_start(struct bt_mesh_subnet *sub)
static void identity_enabled(struct bt_mesh_subnet *sub)
{
sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
sub->node_id_start = k_uptime_get_32();
@ -366,9 +378,31 @@ static void node_id_start(struct bt_mesh_subnet *sub)
}
}
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
static void node_id_start(struct bt_mesh_subnet *sub)
{
node_id_start(sub);
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
sub->priv_beacon.node_id = false;
#endif
identity_enabled(sub);
}
static void private_node_id_start(struct bt_mesh_subnet *sub)
{
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
sub->priv_beacon.node_id = true;
#endif
identity_enabled(sub);
}
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub, bool private)
{
if (private) {
private_node_id_start(sub);
} else {
node_id_start(sub);
}
/* Prioritize the recently enabled subnet */
beacon_sub = sub;
@ -401,20 +435,39 @@ int bt_mesh_proxy_identity_enable(void)
return 0;
}
#define NODE_ID_LEN 19
int bt_mesh_proxy_private_identity_enable(void)
{
LOG_DBG("");
if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return -ENOTSUP;
}
if (!bt_mesh_is_provisioned()) {
return -EAGAIN;
}
if (bt_mesh_subnet_foreach(private_node_id_start)) {
bt_mesh_adv_gatt_update();
}
return 0;
}
#define ENC_ID_LEN 19
#define NET_ID_LEN 11
#define NODE_ID_TIMEOUT (CONFIG_BT_MESH_NODE_ID_TIMEOUT * MSEC_PER_SEC)
static uint8_t proxy_svc_data[NODE_ID_LEN] = {
static uint8_t proxy_svc_data[ENC_ID_LEN] = {
BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL),
};
static const struct bt_data node_id_ad[] = {
static const struct bt_data enc_id_ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)),
BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN),
BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, ENC_ID_LEN),
};
static const struct bt_data net_id_ad[] = {
@ -424,46 +477,113 @@ static const struct bt_data net_id_ad[] = {
BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN),
};
static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
static int randomize_bt_addr(void)
{
/* TODO: There appears to be no way to force an RPA/NRPA refresh. */
return 0;
}
static int enc_id_adv(struct bt_mesh_subnet *sub, uint8_t type,
uint8_t hash[16], int32_t duration)
{
struct bt_le_adv_param slow_adv_param = {
.options = ADV_OPT_PROXY,
ADV_SLOW_INT,
};
struct bt_le_adv_param fast_adv_param = {
.options = ADV_OPT_PROXY,
ADV_FAST_INT,
};
uint8_t tmp[16];
int err;
LOG_DBG("");
proxy_svc_data[2] = BT_MESH_ID_TYPE_NODE;
err = bt_rand(proxy_svc_data + 11, 8);
err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, hash, hash);
if (err) {
return err;
}
(void)memset(tmp, 0, 6);
memcpy(tmp + 6, proxy_svc_data + 11, 8);
sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp,
tmp);
/* Section 7.2.2.2.4: The AdvA field shall be regenerated whenever the Random field is
* regenerated.
*/
err = randomize_bt_addr();
if (err) {
LOG_ERR("AdvA refresh failed: %d", err);
return err;
}
memcpy(proxy_svc_data + 3, tmp + 8, 8);
proxy_svc_data[2] = type;
memcpy(&proxy_svc_data[3], &hash[8], 8);
err = bt_mesh_adv_gatt_start(&fast_adv_param, duration, node_id_ad,
ARRAY_SIZE(node_id_ad), NULL, 0);
err = bt_mesh_adv_gatt_start(
type == BT_MESH_ID_TYPE_PRIV_NET ? &slow_adv_param : &fast_adv_param,
duration, enc_id_ad, ARRAY_SIZE(enc_id_ad), NULL, 0);
if (err) {
LOG_WRN("Failed to advertise using Node ID (err %d)", err);
LOG_WRN("Failed to advertise using type 0x%02x (err %d)", type, err);
return err;
}
return 0;
}
static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
uint8_t *random = &proxy_svc_data[11];
uint8_t tmp[16];
int err;
LOG_DBG("0x%03x", sub->net_idx);
err = bt_rand(random, 8);
if (err) {
return err;
}
memset(&tmp[0], 0x00, 6);
memcpy(&tmp[6], random, 8);
sys_put_be16(bt_mesh_primary_addr(), &tmp[14]);
return enc_id_adv(sub, BT_MESH_ID_TYPE_NODE, tmp, duration);
}
static int priv_node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
uint8_t *random = &proxy_svc_data[11];
uint8_t tmp[16];
int err;
LOG_DBG("0x%03x", sub->net_idx);
err = bt_rand(random, 8);
if (err) {
return err;
}
memset(&tmp[0], 0x00, 5);
tmp[5] = 0x03;
memcpy(&tmp[6], random, 8);
sys_put_be16(bt_mesh_primary_addr(), &tmp[14]);
return enc_id_adv(sub, BT_MESH_ID_TYPE_PRIV_NODE, tmp, duration);
}
static int priv_net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
uint8_t *random = &proxy_svc_data[11];
uint8_t tmp[16];
int err;
LOG_DBG("0x%03x", sub->net_idx);
err = bt_rand(random, 8);
if (err) {
return err;
}
memcpy(&tmp[0], sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8);
memcpy(&tmp[8], random, 8);
return enc_id_adv(sub, BT_MESH_ID_TYPE_PRIV_NET, tmp, duration);
}
static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
{
struct bt_le_adv_param slow_adv_param = {
@ -472,8 +592,6 @@ static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
};
int err;
LOG_DBG("");
proxy_svc_data[2] = BT_MESH_ID_TYPE_NET;
LOG_DBG("Advertising with NetId %s", bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8));
@ -497,7 +615,8 @@ static bool advertise_subnet(struct bt_mesh_subnet *sub)
}
return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING ||
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED ||
bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
}
static struct bt_mesh_subnet *next_sub(void)
@ -591,12 +710,20 @@ static int gatt_proxy_advertise(struct bt_mesh_subnet *sub)
if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
uint32_t active = k_uptime_get_32() - sub->node_id_start;
bool priv_node_id = false;
if (active < NODE_ID_TIMEOUT) {
remaining = MIN(remaining, NODE_ID_TIMEOUT - active);
LOG_DBG("Node ID active for %u ms, %d ms remaining", active,
remaining);
err = node_id_adv(sub, remaining);
LOG_DBG("Node ID active for %u ms, %d ms remaining",
active, remaining);
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
priv_node_id = sub->priv_beacon.node_id;
#endif
if (priv_node_id) {
err = priv_node_id_adv(sub, remaining);
} else {
err = node_id_adv(sub, remaining);
}
planned = true;
} else {
bt_mesh_proxy_identity_stop(sub);
@ -608,10 +735,25 @@ static int gatt_proxy_advertise(struct bt_mesh_subnet *sub)
* A node that does not support the Proxy feature or
* has the GATT Proxy state disabled shall not advertise with Network ID.
*/
if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED &&
bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
err = net_id_adv(sub, remaining);
planned = true;
if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) {
if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) &&
(bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED)) {
/* Bluetooth mesh specification v1.1, section 7.2.2.2.4: The Random
* field should be updated every 10 minutes. Limit advertising to
* 10 minutes to ensure regeneration of a new random value at least
* that often.
*/
if (remaining == SYS_FOREVER_MS ||
remaining > PROXY_RANDOM_UPDATE_INTERVAL) {
remaining = PROXY_RANDOM_UPDATE_INTERVAL;
}
err = priv_net_id_adv(sub, remaining);
planned = true;
} else if (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) {
err = net_id_adv(sub, remaining);
planned = true;
}
}
beacon_sub = bt_mesh_subnet_next(sub);

View file

@ -328,6 +328,16 @@ static int net_keys_create(struct bt_mesh_subnet_keys *keys,
LOG_DBG("BeaconKey %s", bt_hex(keys->beacon, 16));
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
err = bt_mesh_private_beacon_key(key, keys->priv_beacon);
if (err) {
LOG_ERR("Unable to generate private beacon key");
return err;
}
LOG_DBG("PrivateBeaconKey %s", bt_hex(keys->priv_beacon, 16));
#endif
keys->valid = 1U;
return 0;
@ -533,7 +543,7 @@ uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx,
}
if (node_id) {
bt_mesh_proxy_identity_start(sub);
bt_mesh_proxy_identity_start(sub, false);
} else {
bt_mesh_proxy_identity_stop(sub);
}
@ -559,6 +569,61 @@ uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx,
return STATUS_SUCCESS;
}
uint8_t bt_mesh_subnet_priv_node_id_set(uint16_t net_idx,
enum bt_mesh_feat_state priv_node_id)
{
struct bt_mesh_subnet *sub;
if (priv_node_id == BT_MESH_FEATURE_NOT_SUPPORTED) {
return STATUS_CANNOT_SET;
}
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
return STATUS_INVALID_NETKEY;
}
if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) ||
!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) {
return STATUS_FEAT_NOT_SUPP;
}
if (priv_node_id) {
bt_mesh_proxy_identity_start(sub, true);
} else {
bt_mesh_proxy_identity_stop(sub);
}
bt_mesh_adv_gatt_update();
return STATUS_SUCCESS;
}
uint8_t bt_mesh_subnet_priv_node_id_get(uint16_t net_idx,
enum bt_mesh_feat_state *priv_node_id)
{
struct bt_mesh_subnet *sub;
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
*priv_node_id = 0x00;
return STATUS_INVALID_NETKEY;
}
#if CONFIG_BT_MESH_GATT_PROXY && CONFIG_BT_MESH_PRIV_BEACONS
if (sub->node_id == BT_MESH_FEATURE_ENABLED && sub->priv_beacon.node_id) {
*priv_node_id = sub->node_id;
} else {
*priv_node_id = BT_MESH_FEATURE_DISABLED;
}
#else
*priv_node_id = BT_MESH_FEATURE_NOT_SUPPORTED;
#endif
return STATUS_SUCCESS;
}
ssize_t bt_mesh_subnets_get(uint16_t net_idxs[], size_t max, off_t skip)
{
size_t count = 0;

View file

@ -44,7 +44,7 @@ struct bt_mesh_subnet {
* currently ongoing window.
*/
uint8_t beacon_cache[21]; /* Cached last authenticated beacon */
uint8_t beacon_cache[8]; /* Cached last beacon auth value */
uint16_t net_idx; /* NetKeyIndex */
@ -53,6 +53,13 @@ struct bt_mesh_subnet {
uint8_t node_id; /* Node Identity State */
uint32_t node_id_start; /* Node Identity started timestamp */
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
struct {
uint16_t idx; /* Private beacon random index */
bool node_id; /* Private Node Identity enabled */
uint8_t data[5]; /* Private Beacon data */
} priv_beacon;
#endif
uint8_t auth[8]; /* Beacon Authentication Value */
struct bt_mesh_subnet_keys {
@ -63,7 +70,8 @@ struct bt_mesh_subnet {
#if defined(CONFIG_BT_MESH_GATT_PROXY)
uint8_t identity[16]; /* IdentityKey */
#endif
uint8_t beacon[16]; /* BeaconKey */
uint8_t beacon[16]; /* BeaconKey */
uint8_t priv_beacon[16]; /* PrivateBeaconKey */
} keys[2];
};
@ -183,6 +191,8 @@ uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);
* @param kr_flag Key Refresh flag.
* @param new_key Whether the Key Refresh event was received on the new key
* set.
*
* @returns Whether the Key Refresh event caused a change.
*/
void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key);