Removed deprecated native_posix as test platform. Added unique sim_ids for tests with the psa as a overlay. Fixed clang-format issues. Signed-off-by: Ingar Kulbrandstad <ingar.kulbrandstad@nordicsemi.no>
1276 lines
32 KiB
C
1276 lines
32 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <zephyr/sys/atomic.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
|
|
#include <zephyr/net_buf.h>
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
|
|
#include "common/bt_str.h"
|
|
|
|
#include "crypto.h"
|
|
#include "mesh.h"
|
|
#include "net.h"
|
|
#include "rpl.h"
|
|
#include "lpn.h"
|
|
#include "friend.h"
|
|
#include "proxy.h"
|
|
#include "proxy_cli.h"
|
|
#include "transport.h"
|
|
#include "access.h"
|
|
#include "foundation.h"
|
|
#include "beacon.h"
|
|
#include "settings.h"
|
|
#include "prov.h"
|
|
#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>
|
|
LOG_MODULE_REGISTER(bt_mesh_net);
|
|
|
|
#define LOOPBACK_MAX_PDU_LEN (BT_MESH_NET_HDR_LEN + 16)
|
|
|
|
/* Seq limit after IV Update is triggered */
|
|
#define IV_UPDATE_SEQ_LIMIT CONFIG_BT_MESH_IV_UPDATE_SEQ_LIMIT
|
|
|
|
#define IVI(pdu) ((pdu)[0] >> 7)
|
|
#define NID(pdu) ((pdu)[0] & 0x7f)
|
|
#define CTL(pdu) ((pdu)[1] >> 7)
|
|
#define TTL(pdu) ((pdu)[1] & 0x7f)
|
|
#define SEQ(pdu) (sys_get_be24(&pdu[2]))
|
|
#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;
|
|
struct bt_mesh_key dev_key;
|
|
} __packed;
|
|
|
|
/* Sequence number information for persistent storage. */
|
|
struct seq_val {
|
|
uint8_t val[3];
|
|
} __packed;
|
|
|
|
/* IV Index & IV Update information for persistent storage. */
|
|
struct iv_val {
|
|
uint32_t iv_index;
|
|
uint8_t iv_update:1,
|
|
iv_duration:7;
|
|
} __packed;
|
|
|
|
static struct {
|
|
uint32_t src : 15, /* MSb of source is always 0 */
|
|
seq : 17;
|
|
} msg_cache[CONFIG_BT_MESH_MSG_CACHE_SIZE];
|
|
static uint16_t msg_cache_next;
|
|
|
|
/* Singleton network context (the implementation only supports one) */
|
|
struct bt_mesh_net bt_mesh = {
|
|
.local_queue = SYS_SLIST_STATIC_INIT(&bt_mesh.local_queue),
|
|
.sar_tx = BT_MESH_SAR_TX_INIT,
|
|
.sar_rx = BT_MESH_SAR_RX_INIT,
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
.priv_beacon_int = 0x3c,
|
|
#endif
|
|
};
|
|
|
|
/* MshPRTv1.1: 3.11.5:
|
|
* "A node shall not start an IV Update procedure more often than once every 192 hours."
|
|
*
|
|
* Mark that the IV Index Recovery has been done to prevent two recoveries to be
|
|
* done before a normal IV Index update has been completed within 96h+96h.
|
|
*/
|
|
static bool ivi_was_recovered;
|
|
|
|
struct loopback_buf {
|
|
sys_snode_t node;
|
|
struct bt_mesh_subnet *sub;
|
|
uint8_t len;
|
|
uint8_t data[LOOPBACK_MAX_PDU_LEN];
|
|
};
|
|
|
|
K_MEM_SLAB_DEFINE(loopback_buf_pool,
|
|
sizeof(struct loopback_buf),
|
|
CONFIG_BT_MESH_LOOPBACK_BUFS, __alignof__(struct loopback_buf));
|
|
|
|
static uint32_t dup_cache[CONFIG_BT_MESH_MSG_CACHE_SIZE];
|
|
static int dup_cache_next;
|
|
|
|
static bool check_dup(struct net_buf_simple *data)
|
|
{
|
|
const uint8_t *tail = net_buf_simple_tail(data);
|
|
uint32_t val;
|
|
int i;
|
|
|
|
val = sys_get_be32(tail - 4) ^ sys_get_be32(tail - 8);
|
|
|
|
for (i = dup_cache_next; i > 0;) {
|
|
if (dup_cache[--i] == val) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (i = ARRAY_SIZE(dup_cache); i > dup_cache_next;) {
|
|
if (dup_cache[--i] == val) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
dup_cache_next %= ARRAY_SIZE(dup_cache);
|
|
dup_cache[dup_cache_next++] = val;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool msg_cache_match(struct net_buf_simple *pdu)
|
|
{
|
|
uint16_t i;
|
|
|
|
for (i = msg_cache_next; i > 0U;) {
|
|
if (msg_cache[--i].src == SRC(pdu->data) &&
|
|
msg_cache[i].seq == (SEQ(pdu->data) & BIT_MASK(17))) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (i = ARRAY_SIZE(msg_cache); i > msg_cache_next;) {
|
|
if (msg_cache[--i].src == SRC(pdu->data) &&
|
|
msg_cache[i].seq == (SEQ(pdu->data) & BIT_MASK(17))) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void msg_cache_add(struct bt_mesh_net_rx *rx)
|
|
{
|
|
msg_cache_next %= ARRAY_SIZE(msg_cache);
|
|
msg_cache[msg_cache_next].src = rx->ctx.addr;
|
|
msg_cache[msg_cache_next].seq = rx->seq;
|
|
msg_cache_next++;
|
|
}
|
|
|
|
static void store_iv(bool only_duration)
|
|
{
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_IV_PENDING);
|
|
|
|
if (!only_duration) {
|
|
/* Always update Seq whenever IV changes */
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_SEQ_PENDING);
|
|
}
|
|
}
|
|
|
|
void bt_mesh_net_seq_store(bool force)
|
|
{
|
|
if (!force &&
|
|
CONFIG_BT_MESH_SEQ_STORE_RATE > 1 &&
|
|
(bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) {
|
|
return;
|
|
}
|
|
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_SEQ_PENDING);
|
|
}
|
|
|
|
int bt_mesh_net_create(uint16_t idx, uint8_t flags, const struct bt_mesh_key *key,
|
|
uint32_t iv_index)
|
|
{
|
|
int err;
|
|
|
|
LOG_DBG("idx %u flags 0x%02x iv_index %u", idx, flags, iv_index);
|
|
|
|
LOG_DBG("NetKey %s", bt_hex(key, sizeof(struct bt_mesh_key)));
|
|
|
|
if (BT_MESH_KEY_REFRESH(flags)) {
|
|
err = bt_mesh_subnet_set(idx, BT_MESH_KR_PHASE_2, NULL, key);
|
|
} else {
|
|
err = bt_mesh_subnet_set(idx, BT_MESH_KR_NORMAL, key, NULL);
|
|
}
|
|
|
|
if (err) {
|
|
LOG_ERR("Failed creating subnet");
|
|
return err;
|
|
}
|
|
|
|
(void)memset(msg_cache, 0, sizeof(msg_cache));
|
|
msg_cache_next = 0U;
|
|
|
|
bt_mesh.iv_index = iv_index;
|
|
atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS,
|
|
BT_MESH_IV_UPDATE(flags));
|
|
|
|
/* If the node is added to a network when the network is in Normal
|
|
* operation, then it shall operate in Normal operation for at least
|
|
* 96 hours. If a node is added to a network while the network is
|
|
* in the IV Update in Progress state, then the node shall be given
|
|
* the new IV Index value and operate in IV Update in Progress
|
|
* operation without the restriction of being in this state for at
|
|
* least 96 hours.
|
|
*/
|
|
if (BT_MESH_IV_UPDATE(flags)) {
|
|
bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS;
|
|
} else {
|
|
bt_mesh.ivu_duration = 0U;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
LOG_DBG("Storing network information persistently");
|
|
bt_mesh_subnet_store(idx);
|
|
store_iv(false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_MESH_IV_UPDATE_TEST)
|
|
void bt_mesh_iv_update_test(bool enable)
|
|
{
|
|
atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_TEST, enable);
|
|
/* Reset the duration variable - needed for some PTS tests */
|
|
bt_mesh.ivu_duration = 0U;
|
|
}
|
|
|
|
bool bt_mesh_iv_update(void)
|
|
{
|
|
if (!bt_mesh_is_provisioned()) {
|
|
LOG_ERR("Not yet provisioned");
|
|
return false;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
|
|
bt_mesh_net_iv_update(bt_mesh.iv_index, false);
|
|
} else {
|
|
bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
|
|
}
|
|
|
|
return atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
|
|
}
|
|
#endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */
|
|
|
|
bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update)
|
|
{
|
|
/* Check if IV index should to be recovered. */
|
|
if (iv_index < bt_mesh.iv_index ||
|
|
iv_index > bt_mesh.iv_index + 42) {
|
|
LOG_ERR("IV Index out of sync: 0x%08x != 0x%08x", iv_index, bt_mesh.iv_index);
|
|
return false;
|
|
}
|
|
|
|
/* Discard [iv, false] --> [iv, true] */
|
|
if (iv_index == bt_mesh.iv_index && iv_update) {
|
|
LOG_DBG("Ignore previous IV update procedure");
|
|
return false;
|
|
}
|
|
|
|
if ((iv_index > bt_mesh.iv_index + 1) ||
|
|
(iv_index == bt_mesh.iv_index + 1 &&
|
|
(atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) || !iv_update))) {
|
|
if (ivi_was_recovered &&
|
|
(bt_mesh.ivu_duration < (2 * BT_MESH_IVU_MIN_HOURS))) {
|
|
LOG_ERR("IV Index Recovery before minimum delay");
|
|
return false;
|
|
}
|
|
|
|
/* MshPRTv1.1 allows to initiate an
|
|
* IV Index Recovery procedure if previous IV update has
|
|
* been missed. This allows the node to remain
|
|
* functional.
|
|
*
|
|
* Upon receiving and successfully authenticating a
|
|
* Secure Network beacon for a primary subnet whose
|
|
* IV Index is 1 or more higher than the current known IV
|
|
* Index, the node shall set its current IV Index and its
|
|
* current IV Update procedure state from the values in
|
|
* this Secure Network beacon.
|
|
*/
|
|
LOG_WRN("Performing IV Index Recovery");
|
|
ivi_was_recovered = true;
|
|
bt_mesh_rpl_clear();
|
|
bt_mesh.iv_index = iv_index;
|
|
bt_mesh.seq = 0U;
|
|
|
|
goto do_update;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) == iv_update) {
|
|
LOG_DBG("No change for IV Update procedure");
|
|
return false;
|
|
}
|
|
|
|
if (!(IS_ENABLED(CONFIG_BT_MESH_IV_UPDATE_TEST) &&
|
|
atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_TEST))) {
|
|
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
|
|
LOG_WRN("IV Update before minimum duration");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Defer change to Normal Operation if there are pending acks */
|
|
if (!iv_update && bt_mesh_tx_in_progress()) {
|
|
LOG_WRN("IV Update deferred because of pending transfer");
|
|
atomic_set_bit(bt_mesh.flags, BT_MESH_IVU_PENDING);
|
|
return false;
|
|
}
|
|
|
|
if (iv_update) {
|
|
bt_mesh.iv_index = iv_index;
|
|
LOG_DBG("IV Update state entered. New index 0x%08x", bt_mesh.iv_index);
|
|
|
|
bt_mesh_rpl_reset();
|
|
ivi_was_recovered = false;
|
|
} else {
|
|
LOG_DBG("Normal mode entered");
|
|
bt_mesh.seq = 0U;
|
|
}
|
|
|
|
do_update:
|
|
atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv_update);
|
|
bt_mesh.ivu_duration = 0U;
|
|
|
|
k_work_reschedule(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
|
|
|
/* Notify other modules */
|
|
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
|
|
bt_mesh_friend_sec_update(BT_MESH_KEY_ANY);
|
|
}
|
|
|
|
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_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED)) {
|
|
bt_mesh_proxy_beacon_send(NULL);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_CDB)) {
|
|
bt_mesh_cdb_iv_update(iv_index, iv_update);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
store_iv(false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t bt_mesh_next_seq(void)
|
|
{
|
|
uint32_t seq = bt_mesh.seq++;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
bt_mesh_net_seq_store(false);
|
|
}
|
|
|
|
if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) &&
|
|
bt_mesh.seq > IV_UPDATE_SEQ_LIMIT &&
|
|
bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY)) {
|
|
bt_mesh_beacon_ivu_initiator(true);
|
|
bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
|
|
}
|
|
|
|
return seq;
|
|
}
|
|
|
|
static void bt_mesh_net_local(struct k_work *work)
|
|
{
|
|
struct net_buf_simple sbuf;
|
|
sys_snode_t *node;
|
|
|
|
while ((node = sys_slist_get(&bt_mesh.local_queue))) {
|
|
struct loopback_buf *buf = CONTAINER_OF(node, struct loopback_buf, node);
|
|
struct bt_mesh_net_rx rx = {
|
|
.ctx = {
|
|
.net_idx = buf->sub->net_idx,
|
|
/* Initialize AppIdx to a sane value */
|
|
.app_idx = BT_MESH_KEY_UNUSED,
|
|
.recv_ttl = TTL(buf->data),
|
|
/* TTL=1 only goes to local IF */
|
|
.send_ttl = 1U,
|
|
.addr = SRC(buf->data),
|
|
.recv_dst = DST(buf->data),
|
|
.recv_rssi = 0,
|
|
},
|
|
.net_if = BT_MESH_NET_IF_LOCAL,
|
|
.sub = buf->sub,
|
|
.old_iv = (IVI(buf->data) != (bt_mesh.iv_index & 0x01)),
|
|
.ctl = CTL(buf->data),
|
|
.seq = SEQ(buf->data),
|
|
.new_key = SUBNET_KEY_TX_IDX(buf->sub),
|
|
.local_match = 1U,
|
|
.friend_match = 0U,
|
|
};
|
|
|
|
LOG_DBG("src: 0x%04x dst: 0x%04x seq 0x%06x sub %p", rx.ctx.addr, rx.ctx.addr,
|
|
rx.seq, buf->sub);
|
|
|
|
net_buf_simple_init_with_data(&sbuf, buf->data, buf->len);
|
|
(void)bt_mesh_trans_recv(&sbuf, &rx);
|
|
k_mem_slab_free(&loopback_buf_pool, (void *)buf);
|
|
}
|
|
}
|
|
|
|
static const struct bt_mesh_net_cred *net_tx_cred_get(struct bt_mesh_net_tx *tx)
|
|
{
|
|
#if defined(CONFIG_BT_MESH_LOW_POWER)
|
|
if (tx->friend_cred && bt_mesh.lpn.frnd) {
|
|
return &bt_mesh.lpn.cred[SUBNET_KEY_TX_IDX(tx->sub)];
|
|
}
|
|
#endif
|
|
|
|
tx->friend_cred = 0U;
|
|
return &tx->sub->keys[SUBNET_KEY_TX_IDX(tx->sub)].msg;
|
|
}
|
|
|
|
static int net_header_encode(struct bt_mesh_net_tx *tx, uint8_t nid,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
const bool ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED);
|
|
|
|
if (ctl && net_buf_simple_tailroom(buf) < 8) {
|
|
LOG_ERR("Insufficient MIC space for CTL PDU");
|
|
return -EINVAL;
|
|
} else if (net_buf_simple_tailroom(buf) < 4) {
|
|
LOG_ERR("Insufficient MIC space for PDU");
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("src 0x%04x dst 0x%04x ctl %u seq 0x%06x", tx->src, tx->ctx->addr, ctl,
|
|
bt_mesh.seq);
|
|
|
|
net_buf_simple_push_be16(buf, tx->ctx->addr);
|
|
net_buf_simple_push_be16(buf, tx->src);
|
|
net_buf_simple_push_be24(buf, bt_mesh_next_seq());
|
|
|
|
if (ctl) {
|
|
net_buf_simple_push_u8(buf, tx->ctx->send_ttl | 0x80);
|
|
} else {
|
|
net_buf_simple_push_u8(buf, tx->ctx->send_ttl);
|
|
}
|
|
|
|
net_buf_simple_push_u8(buf, (nid | (BT_MESH_NET_IVI_TX & 1) << 7));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int net_encrypt(struct net_buf_simple *buf,
|
|
const struct bt_mesh_net_cred *cred, uint32_t iv_index,
|
|
enum bt_mesh_nonce_type proxy)
|
|
{
|
|
int err;
|
|
|
|
err = bt_mesh_net_encrypt(&cred->enc, buf, iv_index, proxy);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return bt_mesh_net_obfuscate(buf->data, iv_index, &cred->privacy);
|
|
}
|
|
|
|
int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf,
|
|
enum bt_mesh_nonce_type type)
|
|
{
|
|
const struct bt_mesh_net_cred *cred;
|
|
int err;
|
|
|
|
cred = net_tx_cred_get(tx);
|
|
err = net_header_encode(tx, cred->nid, buf);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return net_encrypt(buf, cred, BT_MESH_NET_IVI_TX, type);
|
|
}
|
|
|
|
static int net_loopback(const struct bt_mesh_net_tx *tx, const uint8_t *data,
|
|
size_t len)
|
|
{
|
|
int err;
|
|
struct loopback_buf *buf;
|
|
|
|
err = k_mem_slab_alloc(&loopback_buf_pool, (void **)&buf, K_NO_WAIT);
|
|
if (err) {
|
|
LOG_WRN("Unable to allocate loopback");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
buf->sub = tx->sub;
|
|
|
|
(void)memcpy(buf->data, data, len);
|
|
buf->len = len;
|
|
|
|
sys_slist_append(&bt_mesh.local_queue, &buf->node);
|
|
|
|
k_work_submit(&bt_mesh.local_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct bt_mesh_adv *adv,
|
|
const struct bt_mesh_send_cb *cb, void *cb_data)
|
|
{
|
|
const struct bt_mesh_net_cred *cred;
|
|
int err;
|
|
|
|
LOG_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu", tx->src, tx->ctx->addr,
|
|
adv->b.len, net_buf_simple_headroom(&adv->b), net_buf_simple_tailroom(&adv->b));
|
|
LOG_DBG("Payload len %u: %s", adv->b.len, bt_hex(adv->b.data, adv->b.len));
|
|
LOG_DBG("Seq 0x%06x", bt_mesh.seq);
|
|
|
|
cred = net_tx_cred_get(tx);
|
|
err = net_header_encode(tx, cred->nid, &adv->b);
|
|
if (err) {
|
|
goto done;
|
|
}
|
|
|
|
/* Deliver to local network interface if necessary */
|
|
if (bt_mesh_fixed_group_match(tx->ctx->addr) ||
|
|
bt_mesh_has_addr(tx->ctx->addr)) {
|
|
err = net_loopback(tx, adv->b.data, adv->b.len);
|
|
|
|
/* Local unicast messages should not go out to network */
|
|
if (BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr) ||
|
|
tx->ctx->send_ttl == 1U) {
|
|
if (!err) {
|
|
send_cb_finalize(cb, cb_data);
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* MshPRTv1.1: 3.4.5.2: "The output filter of the interface connected to
|
|
* advertising or GATT bearers shall drop all messages with TTL value
|
|
* set to 1." If a TTL=1 packet wasn't for a local interface, it is
|
|
* invalid.
|
|
*/
|
|
if (tx->ctx->send_ttl == 1U) {
|
|
err = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
err = net_encrypt(&adv->b, cred, BT_MESH_NET_IVI_TX, BT_MESH_NONCE_NETWORK);
|
|
if (err) {
|
|
goto done;
|
|
}
|
|
|
|
adv->ctx.cb = cb;
|
|
adv->ctx.cb_data = cb_data;
|
|
|
|
/* Deliver to GATT Proxy Clients if necessary. */
|
|
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
|
|
(void)bt_mesh_proxy_relay(adv, tx->ctx->addr);
|
|
}
|
|
|
|
/* Deliver to GATT Proxy Servers if necessary. */
|
|
if (IS_ENABLED(CONFIG_BT_MESH_PROXY_CLIENT)) {
|
|
(void)bt_mesh_proxy_cli_relay(adv);
|
|
}
|
|
|
|
bt_mesh_adv_send(adv, cb, cb_data);
|
|
|
|
done:
|
|
bt_mesh_adv_unref(adv);
|
|
return err;
|
|
}
|
|
|
|
void bt_mesh_net_loopback_clear(uint16_t net_idx)
|
|
{
|
|
sys_slist_t new_list;
|
|
sys_snode_t *node;
|
|
|
|
LOG_DBG("0x%04x", net_idx);
|
|
|
|
sys_slist_init(&new_list);
|
|
|
|
while ((node = sys_slist_get(&bt_mesh.local_queue))) {
|
|
struct loopback_buf *buf = CONTAINER_OF(node, struct loopback_buf, node);
|
|
|
|
if (net_idx == BT_MESH_KEY_ANY || net_idx == buf->sub->net_idx) {
|
|
LOG_DBG("Dropped 0x%06x", SEQ(buf->data));
|
|
k_mem_slab_free(&loopback_buf_pool, (void *)buf);
|
|
} else {
|
|
sys_slist_append(&new_list, &buf->node);
|
|
}
|
|
}
|
|
|
|
bt_mesh.local_queue = new_list;
|
|
}
|
|
|
|
static bool net_decrypt(struct bt_mesh_net_rx *rx, struct net_buf_simple *in,
|
|
struct net_buf_simple *out,
|
|
const struct bt_mesh_net_cred *cred)
|
|
{
|
|
bool proxy = (rx->net_if == BT_MESH_NET_IF_PROXY_CFG);
|
|
|
|
if (NID(in->data) != cred->nid) {
|
|
return false;
|
|
}
|
|
|
|
LOG_DBG("NID 0x%02x", NID(in->data));
|
|
LOG_DBG("IVI %u net->iv_index 0x%08x", IVI(in->data), bt_mesh.iv_index);
|
|
|
|
rx->old_iv = (IVI(in->data) != (bt_mesh.iv_index & 0x01));
|
|
|
|
net_buf_simple_reset(out);
|
|
net_buf_simple_add_mem(out, in->data, in->len);
|
|
|
|
if (bt_mesh_net_obfuscate(out->data, BT_MESH_NET_IVI_RX(rx),
|
|
&cred->privacy)) {
|
|
return false;
|
|
}
|
|
|
|
rx->ctx.addr = SRC(out->data);
|
|
if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) {
|
|
LOG_DBG("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr);
|
|
return false;
|
|
}
|
|
|
|
if (bt_mesh_has_addr(rx->ctx.addr)) {
|
|
LOG_DBG("Dropping locally originated packet");
|
|
return false;
|
|
}
|
|
|
|
if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(out)) {
|
|
LOG_DBG("Duplicate found in Network Message Cache");
|
|
return false;
|
|
}
|
|
|
|
LOG_DBG("src 0x%04x", rx->ctx.addr);
|
|
|
|
return bt_mesh_net_decrypt(&cred->enc, out, BT_MESH_NET_IVI_RX(rx),
|
|
proxy) == 0;
|
|
}
|
|
|
|
/* Relaying from advertising to the advertising bearer should only happen
|
|
* if the Relay state is set to enabled. Locally originated packets always
|
|
* get sent to the advertising bearer. If the packet came in through GATT,
|
|
* then we should only relay it if the GATT Proxy state is enabled.
|
|
*/
|
|
static bool relay_to_adv(enum bt_mesh_net_if net_if)
|
|
{
|
|
switch (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) ||
|
|
(bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void bt_mesh_net_relay(struct net_buf_simple *sbuf, struct bt_mesh_net_rx *rx, bool bridge)
|
|
{
|
|
const struct bt_mesh_net_cred *cred;
|
|
struct bt_mesh_adv *adv;
|
|
uint8_t transmit;
|
|
|
|
if (rx->ctx.recv_ttl <= 1U) {
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, rx->ctl, rx->ctx.recv_dst);
|
|
|
|
/* The Relay Retransmit state is only applied to adv-adv relaying.
|
|
* Anything else (like GATT to adv, or locally originated packets)
|
|
* use the Network Transmit state.
|
|
*/
|
|
if (rx->net_if == BT_MESH_NET_IF_ADV && !rx->friend_cred) {
|
|
transmit = bt_mesh_relay_retransmit_get();
|
|
} else {
|
|
transmit = bt_mesh_net_transmit_get();
|
|
}
|
|
|
|
adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_RELAY,
|
|
transmit, K_NO_WAIT);
|
|
if (!adv) {
|
|
LOG_DBG("Out of relay advs");
|
|
return;
|
|
}
|
|
|
|
/* Leave CTL bit intact */
|
|
sbuf->data[1] &= 0x80;
|
|
sbuf->data[1] |= rx->ctx.recv_ttl - 1U;
|
|
|
|
net_buf_simple_add_mem(&adv->b, sbuf->data, sbuf->len);
|
|
|
|
cred = &rx->sub->keys[SUBNET_KEY_TX_IDX(rx->sub)].msg;
|
|
|
|
LOG_DBG("Relaying packet. TTL is now %u", TTL(adv->b.data));
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* We re-encrypt and obfuscate using the received IVI rather than
|
|
* the normal TX IVI (which may be different) since the transport
|
|
* layer nonce includes the IVI.
|
|
*/
|
|
if (net_encrypt(&adv->b, cred, BT_MESH_NET_IVI_RX(rx), BT_MESH_NONCE_NETWORK)) {
|
|
LOG_ERR("Re-encrypting failed");
|
|
goto done;
|
|
}
|
|
|
|
/* When the Friend node relays message for lpn, the message will be
|
|
* retransmitted using the managed flooding security credentials and
|
|
* the Network PDU shall be retransmitted to all network interfaces.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
|
|
(rx->friend_cred ||
|
|
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(adv, rx->ctx.recv_dst);
|
|
}
|
|
|
|
if (relay_to_adv(rx->net_if) || rx->friend_cred || bridge) {
|
|
bt_mesh_adv_send(adv, NULL, NULL);
|
|
}
|
|
|
|
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)
|
|
{
|
|
rx->old_iv = (IVI(buf->data) != (bt_mesh.iv_index & 0x01));
|
|
rx->ctl = CTL(buf->data);
|
|
rx->ctx.recv_ttl = TTL(buf->data);
|
|
rx->seq = SEQ(buf->data);
|
|
rx->ctx.addr = SRC(buf->data);
|
|
rx->ctx.recv_dst = DST(buf->data);
|
|
}
|
|
|
|
int bt_mesh_net_decode(struct net_buf_simple *in, enum bt_mesh_net_if net_if,
|
|
struct bt_mesh_net_rx *rx, struct net_buf_simple *out)
|
|
{
|
|
if (in->len < BT_MESH_NET_MIN_PDU_LEN) {
|
|
LOG_WRN("Dropping too short mesh packet (len %u)", in->len);
|
|
LOG_WRN("%s", bt_hex(in->data, in->len));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (in->len > BT_MESH_NET_MAX_PDU_LEN) {
|
|
LOG_WRN("Dropping too long mesh packet (len %u)", in->len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (net_if == BT_MESH_NET_IF_ADV && check_dup(in)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("%u bytes: %s", in->len, bt_hex(in->data, in->len));
|
|
|
|
rx->net_if = net_if;
|
|
|
|
if (!bt_mesh_net_cred_find(rx, in, out, net_decrypt)) {
|
|
LOG_DBG("Unable to find matching net for packet");
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Initialize AppIdx to a sane value */
|
|
rx->ctx.app_idx = BT_MESH_KEY_UNUSED;
|
|
|
|
rx->ctx.recv_ttl = TTL(out->data);
|
|
|
|
/* Default to responding with TTL 0 for non-routed messages */
|
|
if (rx->ctx.recv_ttl == 0U) {
|
|
rx->ctx.send_ttl = 0U;
|
|
} else {
|
|
rx->ctx.send_ttl = BT_MESH_TTL_DEFAULT;
|
|
}
|
|
|
|
rx->ctl = CTL(out->data);
|
|
rx->seq = SEQ(out->data);
|
|
rx->ctx.recv_dst = DST(out->data);
|
|
|
|
LOG_DBG("Decryption successful. Payload len %u", out->len);
|
|
|
|
if (net_if != BT_MESH_NET_IF_PROXY_CFG &&
|
|
rx->ctx.recv_dst == BT_MESH_ADDR_UNASSIGNED) {
|
|
LOG_ERR("Destination address is unassigned; dropping packet");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
LOG_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst, rx->ctx.recv_ttl);
|
|
LOG_DBG("PDU: %s", bt_hex(out->data, out->len));
|
|
|
|
msg_cache_add(rx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bt_mesh_net_recv(struct net_buf_simple *data, int8_t rssi,
|
|
enum bt_mesh_net_if net_if)
|
|
{
|
|
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_NET_MAX_PDU_LEN);
|
|
struct bt_mesh_net_rx rx = { .ctx.recv_rssi = rssi };
|
|
struct net_buf_simple_state state;
|
|
int err;
|
|
|
|
LOG_DBG("rssi %d net_if %u", rssi, net_if);
|
|
|
|
if (!bt_mesh_is_provisioned()) {
|
|
return;
|
|
}
|
|
|
|
if (bt_mesh_net_decode(data, net_if, &rx, &buf)) {
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_STATISTIC)) {
|
|
bt_mesh_stat_rx(net_if);
|
|
}
|
|
|
|
/* Save the state so the buffer can later be relayed */
|
|
net_buf_simple_save(&buf, &state);
|
|
|
|
rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) ||
|
|
bt_mesh_has_addr(rx.ctx.recv_dst));
|
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
|
|
net_if == BT_MESH_NET_IF_PROXY) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
err = bt_mesh_trans_recv(&buf, &rx);
|
|
if (err == -EAGAIN) {
|
|
/* The transport layer has indicated that it has rejected the message,
|
|
* but would like to see it again if it is received in the future.
|
|
* This can happen if a message is received when the device is in
|
|
* Low Power mode, but the message was not encrypted with the friend
|
|
* credentials. Remove it from the message cache so that we accept
|
|
* it again in the future.
|
|
*/
|
|
LOG_WRN("Removing rejected message from Network Message Cache");
|
|
/* Rewind the next index now that we're not using this entry */
|
|
msg_cache[--msg_cache_next].src = BT_MESH_ADDR_UNASSIGNED;
|
|
dup_cache[--dup_cache_next] = 0;
|
|
return;
|
|
} else if (err == -EBADMSG) {
|
|
LOG_DBG("Not relaying message rejected by the Transport layer");
|
|
return;
|
|
}
|
|
|
|
/* Relay if this was a group/virtual address, or if the destination
|
|
* was neither a local element nor an LPN we're Friends for.
|
|
*/
|
|
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, 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)
|
|
{
|
|
if (!bt_mesh_is_provisioned()) {
|
|
return;
|
|
}
|
|
|
|
bt_mesh.ivu_duration = MIN(UINT8_MAX,
|
|
bt_mesh.ivu_duration + BT_MESH_IVU_HOURS);
|
|
|
|
LOG_DBG("%s for %u hour%s",
|
|
atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ? "IVU in Progress"
|
|
: "IVU Normal mode",
|
|
bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1U ? "" : "s");
|
|
|
|
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
store_iv(true);
|
|
}
|
|
|
|
goto end;
|
|
}
|
|
|
|
/* Because the beacon may be cached, iv update or iv recovery
|
|
* cannot be performed after 96 hours or 192 hours.
|
|
* So we need clear beacon cache.
|
|
*/
|
|
if (!(bt_mesh.ivu_duration % BT_MESH_IVU_MIN_HOURS)) {
|
|
bt_mesh_subnet_foreach(bt_mesh_beacon_cache_clear);
|
|
}
|
|
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
|
|
bt_mesh_beacon_ivu_initiator(true);
|
|
bt_mesh_net_iv_update(bt_mesh.iv_index, false);
|
|
} else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
store_iv(true);
|
|
}
|
|
|
|
end:
|
|
k_work_reschedule(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
|
}
|
|
|
|
void bt_mesh_net_init(void)
|
|
{
|
|
k_work_init_delayable(&bt_mesh.ivu_timer, ivu_refresh);
|
|
|
|
k_work_init(&bt_mesh.local_work, bt_mesh_net_local);
|
|
}
|
|
|
|
static int net_set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
|
void *cb_arg)
|
|
{
|
|
struct net_val net;
|
|
struct bt_mesh_key key;
|
|
int err;
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("val (null)");
|
|
|
|
bt_mesh_comp_unprovision();
|
|
bt_mesh_key_destroy(&bt_mesh.dev_key);
|
|
memset(&bt_mesh.dev_key, 0, sizeof(struct bt_mesh_key));
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &net, sizeof(net));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'net\'");
|
|
return err;
|
|
}
|
|
|
|
/* One extra copying since net.dev_key is from packed structure
|
|
* and might be unaligned.
|
|
*/
|
|
memcpy(&key, &net.dev_key, sizeof(struct bt_mesh_key));
|
|
|
|
bt_mesh_key_assign(&bt_mesh.dev_key, &key);
|
|
bt_mesh_comp_provision(net.primary_addr);
|
|
|
|
LOG_DBG("Provisioned with primary address 0x%04x", net.primary_addr);
|
|
LOG_DBG("Recovered DevKey %s", bt_hex(&bt_mesh.dev_key, sizeof(struct bt_mesh_key)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
BT_MESH_SETTINGS_DEFINE(net, "Net", net_set);
|
|
|
|
static int iv_set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
|
void *cb_arg)
|
|
{
|
|
struct iv_val iv;
|
|
int err;
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("IV deleted");
|
|
|
|
bt_mesh.iv_index = 0U;
|
|
atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &iv, sizeof(iv));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'iv\'");
|
|
return err;
|
|
}
|
|
|
|
bt_mesh.iv_index = iv.iv_index;
|
|
atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update);
|
|
bt_mesh.ivu_duration = iv.iv_duration;
|
|
|
|
LOG_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours", iv.iv_index, iv.iv_update,
|
|
iv.iv_duration);
|
|
|
|
return 0;
|
|
}
|
|
|
|
BT_MESH_SETTINGS_DEFINE(iv, "IV", iv_set);
|
|
|
|
static int seq_set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
|
void *cb_arg)
|
|
{
|
|
struct seq_val seq;
|
|
int err;
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("val (null)");
|
|
|
|
bt_mesh.seq = 0U;
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &seq, sizeof(seq));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'seq\'");
|
|
return err;
|
|
}
|
|
|
|
bt_mesh.seq = sys_get_le24(seq.val);
|
|
|
|
if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) {
|
|
/* Make sure we have a large enough sequence number. We
|
|
* subtract 1 so that the first transmission causes a write
|
|
* to the settings storage.
|
|
*/
|
|
bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE -
|
|
(bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE));
|
|
bt_mesh.seq--;
|
|
}
|
|
|
|
LOG_DBG("Sequence Number 0x%06x", bt_mesh.seq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
BT_MESH_SETTINGS_DEFINE(seq, "Seq", seq_set);
|
|
|
|
#if defined(CONFIG_BT_MESH_RPR_SRV)
|
|
static int dev_key_cand_set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
|
void *cb_arg)
|
|
{ int err;
|
|
|
|
if (len_rd < 16) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &bt_mesh.dev_key_cand,
|
|
sizeof(struct bt_mesh_key));
|
|
if (!err) {
|
|
LOG_DBG("DevKey candidate recovered from storage");
|
|
atomic_set_bit(bt_mesh.flags, BT_MESH_DEVKEY_CAND);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
BT_MESH_SETTINGS_DEFINE(dev_key, "DevKeyC", dev_key_cand_set);
|
|
#endif
|
|
|
|
void bt_mesh_net_pending_dev_key_cand_store(void)
|
|
{
|
|
#if defined(CONFIG_BT_MESH_RPR_SRV)
|
|
int err;
|
|
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_DEVKEY_CAND)) {
|
|
err = settings_save_one("bt/mesh/DevKeyC", &bt_mesh.dev_key_cand,
|
|
sizeof(struct bt_mesh_key));
|
|
} else {
|
|
err = settings_delete("bt/mesh/DevKeyC");
|
|
}
|
|
|
|
if (err) {
|
|
LOG_ERR("Failed to update DevKey candidate value");
|
|
} else {
|
|
LOG_DBG("Stored DevKey candidate value");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void bt_mesh_net_dev_key_cand_store(void)
|
|
{
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_DEV_KEY_CAND_PENDING);
|
|
}
|
|
|
|
static void clear_iv(void)
|
|
{
|
|
int err;
|
|
|
|
err = settings_delete("bt/mesh/IV");
|
|
if (err) {
|
|
LOG_ERR("Failed to clear IV");
|
|
} else {
|
|
LOG_DBG("Cleared IV");
|
|
}
|
|
}
|
|
|
|
static void store_pending_iv(void)
|
|
{
|
|
struct iv_val iv;
|
|
int err;
|
|
|
|
iv.iv_index = bt_mesh.iv_index;
|
|
iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
|
|
iv.iv_duration = bt_mesh.ivu_duration;
|
|
|
|
err = settings_save_one("bt/mesh/IV", &iv, sizeof(iv));
|
|
if (err) {
|
|
LOG_ERR("Failed to store IV value");
|
|
} else {
|
|
LOG_DBG("Stored IV value");
|
|
}
|
|
}
|
|
|
|
void bt_mesh_net_pending_iv_store(void)
|
|
{
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
|
|
store_pending_iv();
|
|
} else {
|
|
clear_iv();
|
|
}
|
|
}
|
|
|
|
static void clear_net(void)
|
|
{
|
|
int err;
|
|
|
|
err = settings_delete("bt/mesh/Net");
|
|
if (err) {
|
|
LOG_ERR("Failed to clear Network");
|
|
} else {
|
|
LOG_DBG("Cleared Network");
|
|
}
|
|
}
|
|
|
|
static void store_pending_net(void)
|
|
{
|
|
struct net_val net;
|
|
int err;
|
|
|
|
LOG_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(),
|
|
bt_hex(&bt_mesh.dev_key, sizeof(struct bt_mesh_key)));
|
|
|
|
net.primary_addr = bt_mesh_primary_addr();
|
|
memcpy(&net.dev_key, &bt_mesh.dev_key, sizeof(struct bt_mesh_key));
|
|
|
|
err = settings_save_one("bt/mesh/Net", &net, sizeof(net));
|
|
if (err) {
|
|
LOG_ERR("Failed to store Network value");
|
|
} else {
|
|
LOG_DBG("Stored Network value");
|
|
}
|
|
}
|
|
|
|
void bt_mesh_net_pending_net_store(void)
|
|
{
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
|
|
store_pending_net();
|
|
} else {
|
|
clear_net();
|
|
}
|
|
}
|
|
|
|
void bt_mesh_net_pending_seq_store(void)
|
|
{
|
|
struct seq_val seq;
|
|
int err;
|
|
|
|
if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
|
|
sys_put_le24(bt_mesh.seq, seq.val);
|
|
|
|
err = settings_save_one("bt/mesh/Seq", &seq, sizeof(seq));
|
|
if (err) {
|
|
LOG_ERR("Failed to stor Seq value");
|
|
} else {
|
|
LOG_DBG("Stored Seq value");
|
|
}
|
|
} else {
|
|
err = settings_delete("bt/mesh/Seq");
|
|
if (err) {
|
|
LOG_ERR("Failed to clear Seq value");
|
|
} else {
|
|
LOG_DBG("Cleared Seq value");
|
|
}
|
|
}
|
|
}
|
|
|
|
void bt_mesh_net_store(void)
|
|
{
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_NET_PENDING);
|
|
}
|
|
|
|
void bt_mesh_net_clear(void)
|
|
{
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_NET_PENDING);
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_IV_PENDING);
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_SEQ_PENDING);
|
|
}
|
|
|
|
void bt_mesh_net_settings_commit(void)
|
|
{
|
|
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
|
|
k_work_reschedule(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
|
}
|
|
}
|