zephyr/subsys/bluetooth/mesh/adv.c
Krzysztof Kopyściński d0995541fb Bluetooth: Mesh: add implementation for Proxy Solicitation
This is initial implementation of Proxy solicitation procedure.
This includes:
- support for sending and receiving Solicitation PDUs
- On-Demand Private Proxy functionality (Server and Client) controlling
  behaviour of node after receiving Solicitation PDU
- Solicitation PDU RPL Configuration (Server and Client), which manages
  Replay Protection List for Solicitation PDUs.

Proxy Solicitation allows to enable advertising of Proxy service on node
by sending Solicitation PDUs. These PDUs are not part of Mesh messages;
instead, these are non-connectable, undirected advertising PDUs with
their own format, containing Proxy Solicitation UUID.

Signed-off-by: Krzysztof Kopyściński <krzysztof.kopyscinski@codecoup.pl>
2023-03-06 13:52:15 +01:00

374 lines
8.9 KiB
C

/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/debug/stack.h>
#include <zephyr/sys/util.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/mesh.h>
#include "common/bt_str.h"
#include "adv.h"
#include "net.h"
#include "foundation.h"
#include "beacon.h"
#include "host/ecc.h"
#include "prov.h"
#include "proxy.h"
#include "pb_gatt_srv.h"
#include "solicitation.h"
#define LOG_LEVEL CONFIG_BT_MESH_ADV_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_adv);
/* Window and Interval are equal for continuous scanning */
#define MESH_SCAN_INTERVAL BT_MESH_ADV_SCAN_UNIT(BT_MESH_SCAN_INTERVAL_MS)
#define MESH_SCAN_WINDOW BT_MESH_ADV_SCAN_UNIT(BT_MESH_SCAN_WINDOW_MS)
const uint8_t bt_mesh_adv_type[BT_MESH_ADV_TYPES] = {
[BT_MESH_ADV_PROV] = BT_DATA_MESH_PROV,
[BT_MESH_ADV_DATA] = BT_DATA_MESH_MESSAGE,
[BT_MESH_ADV_BEACON] = BT_DATA_MESH_BEACON,
[BT_MESH_ADV_URI] = BT_DATA_URI,
};
static bool active_scanning;
static K_FIFO_DEFINE(bt_mesh_adv_queue);
static K_FIFO_DEFINE(bt_mesh_relay_queue);
static K_FIFO_DEFINE(bt_mesh_friend_queue);
static void adv_buf_destroy(struct net_buf *buf)
{
struct bt_mesh_adv adv = *BT_MESH_ADV(buf);
net_buf_destroy(buf);
bt_mesh_adv_send_end(0, &adv);
}
NET_BUF_POOL_DEFINE(adv_buf_pool, CONFIG_BT_MESH_ADV_BUF_COUNT,
BT_MESH_ADV_DATA_SIZE, BT_MESH_ADV_USER_DATA_SIZE,
adv_buf_destroy);
static struct bt_mesh_adv adv_local_pool[CONFIG_BT_MESH_ADV_BUF_COUNT];
#if defined(CONFIG_BT_MESH_RELAY)
NET_BUF_POOL_DEFINE(relay_buf_pool, CONFIG_BT_MESH_RELAY_BUF_COUNT,
BT_MESH_ADV_DATA_SIZE, BT_MESH_ADV_USER_DATA_SIZE,
adv_buf_destroy);
static struct bt_mesh_adv adv_relay_pool[CONFIG_BT_MESH_RELAY_BUF_COUNT];
#endif
#if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)
NET_BUF_POOL_DEFINE(friend_buf_pool, CONFIG_BT_MESH_FRIEND_LPN_COUNT,
BT_MESH_ADV_DATA_SIZE, BT_MESH_ADV_USER_DATA_SIZE,
adv_buf_destroy);
static struct bt_mesh_adv adv_friend_pool[CONFIG_BT_MESH_FRIEND_LPN_COUNT];
#endif
static struct net_buf *bt_mesh_adv_create_from_pool(struct net_buf_pool *buf_pool,
struct bt_mesh_adv *adv_pool,
enum bt_mesh_adv_type type,
enum bt_mesh_adv_tag tag,
uint8_t xmit, k_timeout_t timeout)
{
struct bt_mesh_adv *adv;
struct net_buf *buf;
if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
LOG_WRN("Refusing to allocate buffer while suspended");
return NULL;
}
buf = net_buf_alloc(buf_pool, timeout);
if (!buf) {
return NULL;
}
adv = &adv_pool[net_buf_id(buf)];
BT_MESH_ADV(buf) = adv;
(void)memset(adv, 0, sizeof(*adv));
adv->type = type;
adv->tag = tag;
adv->xmit = xmit;
return buf;
}
struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type,
enum bt_mesh_adv_tag tag,
uint8_t xmit, k_timeout_t timeout)
{
#if defined(CONFIG_BT_MESH_RELAY)
if (tag & BT_MESH_RELAY_ADV) {
return bt_mesh_adv_create_from_pool(&relay_buf_pool,
adv_relay_pool, type,
tag, xmit, timeout);
}
#endif
#if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)
if (tag & BT_MESH_FRIEND_ADV) {
return bt_mesh_adv_create_from_pool(&friend_buf_pool,
adv_friend_pool, type,
tag, xmit, timeout);
}
#endif
return bt_mesh_adv_create_from_pool(&adv_buf_pool, adv_local_pool, type,
tag, xmit, timeout);
}
#if CONFIG_BT_MESH_RELAY_ADV_SETS || CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE
static struct net_buf *process_events(struct k_poll_event *ev, int count)
{
for (; count; ev++, count--) {
LOG_DBG("ev->state %u", ev->state);
switch (ev->state) {
case K_POLL_STATE_FIFO_DATA_AVAILABLE:
return net_buf_get(ev->fifo, K_NO_WAIT);
case K_POLL_STATE_NOT_READY:
case K_POLL_STATE_CANCELLED:
break;
default:
LOG_WRN("Unexpected k_poll event state %u", ev->state);
break;
}
}
return NULL;
}
struct net_buf *bt_mesh_adv_buf_get(k_timeout_t timeout)
{
int err;
struct k_poll_event events[] = {
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&bt_mesh_adv_queue,
0),
#if defined(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET)
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&bt_mesh_relay_queue,
0),
#endif /* CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET */
};
err = k_poll(events, ARRAY_SIZE(events), timeout);
if (err) {
return NULL;
}
return process_events(events, ARRAY_SIZE(events));
}
struct net_buf *bt_mesh_adv_buf_get_by_tag(uint8_t tag, k_timeout_t timeout)
{
if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && tag & BT_MESH_FRIEND_ADV) {
return net_buf_get(&bt_mesh_friend_queue, timeout);
}
#if CONFIG_BT_MESH_RELAY_ADV_SETS
if (tag & BT_MESH_RELAY_ADV) {
return net_buf_get(&bt_mesh_relay_queue, timeout);
}
#endif
return bt_mesh_adv_buf_get(timeout);
}
#else /* !(CONFIG_BT_MESH_RELAY_ADV_SETS || CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) */
struct net_buf *bt_mesh_adv_buf_get(k_timeout_t timeout)
{
return net_buf_get(&bt_mesh_adv_queue, timeout);
}
struct net_buf *bt_mesh_adv_buf_get_by_tag(uint8_t tag, k_timeout_t timeout)
{
ARG_UNUSED(tag);
return bt_mesh_adv_buf_get(timeout);
}
#endif /* CONFIG_BT_MESH_RELAY_ADV_SETS || CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE */
void bt_mesh_adv_buf_get_cancel(void)
{
LOG_DBG("");
k_fifo_cancel_wait(&bt_mesh_adv_queue);
#if CONFIG_BT_MESH_RELAY_ADV_SETS
k_fifo_cancel_wait(&bt_mesh_relay_queue);
#endif /* CONFIG_BT_MESH_RELAY_ADV_SETS */
if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)) {
k_fifo_cancel_wait(&bt_mesh_friend_queue);
}
}
void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb,
void *cb_data)
{
LOG_DBG("type 0x%02x len %u: %s", BT_MESH_ADV(buf)->type, buf->len,
bt_hex(buf->data, buf->len));
BT_MESH_ADV(buf)->cb = cb;
BT_MESH_ADV(buf)->cb_data = cb_data;
BT_MESH_ADV(buf)->busy = 1U;
if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) &&
BT_MESH_ADV(buf)->tag == BT_MESH_FRIEND_ADV) {
net_buf_put(&bt_mesh_friend_queue, net_buf_ref(buf));
bt_mesh_adv_buf_friend_ready();
return;
}
#if CONFIG_BT_MESH_RELAY_ADV_SETS
if (BT_MESH_ADV(buf)->tag == BT_MESH_RELAY_ADV) {
net_buf_put(&bt_mesh_relay_queue, net_buf_ref(buf));
bt_mesh_adv_buf_relay_ready();
return;
}
#endif
net_buf_put(&bt_mesh_adv_queue, net_buf_ref(buf));
bt_mesh_adv_buf_local_ready();
}
int bt_mesh_adv_gatt_send(void)
{
if (bt_mesh_is_provisioned()) {
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
LOG_DBG("Proxy Advertising");
return bt_mesh_proxy_adv_start();
}
} else if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
LOG_DBG("PB-GATT Advertising");
return bt_mesh_pb_gatt_srv_adv_start();
}
return -ENOTSUP;
}
static void bt_mesh_scan_cb(const bt_addr_le_t *addr, int8_t rssi,
uint8_t adv_type, struct net_buf_simple *buf)
{
if (adv_type != BT_GAP_ADV_TYPE_ADV_NONCONN_IND) {
return;
}
LOG_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len));
while (buf->len > 1) {
struct net_buf_simple_state state;
uint8_t len, type;
len = net_buf_simple_pull_u8(buf);
/* Check for early termination */
if (len == 0U) {
return;
}
if (len > buf->len) {
LOG_WRN("AD malformed");
return;
}
net_buf_simple_save(buf, &state);
type = net_buf_simple_pull_u8(buf);
buf->len = len - 1;
switch (type) {
case BT_DATA_MESH_MESSAGE:
bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV);
break;
#if defined(CONFIG_BT_MESH_PB_ADV)
case BT_DATA_MESH_PROV:
bt_mesh_pb_adv_recv(buf);
break;
#endif
case BT_DATA_MESH_BEACON:
bt_mesh_beacon_recv(buf);
break;
case BT_DATA_UUID16_SOME:
/* Fall through */
case BT_DATA_UUID16_ALL:
if (IS_ENABLED(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)) {
/* Restore buffer with Solicitation PDU */
net_buf_simple_restore(buf, &state);
bt_mesh_sol_recv(buf, len - 1);
}
break;
default:
break;
}
net_buf_simple_restore(buf, &state);
net_buf_simple_pull(buf, len);
}
}
int bt_mesh_scan_active_set(bool active)
{
if (active_scanning == active) {
return 0;
}
active_scanning = active;
bt_mesh_scan_disable();
return bt_mesh_scan_enable();
}
int bt_mesh_scan_enable(void)
{
struct bt_le_scan_param scan_param = {
.type = active_scanning ? BT_HCI_LE_SCAN_ACTIVE :
BT_HCI_LE_SCAN_PASSIVE,
.interval = MESH_SCAN_INTERVAL,
.window = MESH_SCAN_WINDOW
};
int err;
LOG_DBG("");
err = bt_le_scan_start(&scan_param, bt_mesh_scan_cb);
if (err && err != -EALREADY) {
LOG_ERR("starting scan failed (err %d)", err);
return err;
}
return 0;
}
int bt_mesh_scan_disable(void)
{
int err;
LOG_DBG("");
err = bt_le_scan_stop();
if (err && err != -EALREADY) {
LOG_ERR("stopping scan failed (err %d)", err);
return err;
}
return 0;
}