diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h index 92a42dbcf..a84c53e2c 100644 --- a/nimble/host/mesh/include/mesh/glue.h +++ b/nimble/host/mesh/include/mesh/glue.h @@ -242,6 +242,12 @@ static inline void net_buf_simple_init(struct os_mbuf *buf, buf->om_len = 0; } +static inline void net_buf_simple_reset(struct os_mbuf *om) +{ + om->om_len = 0; + om->om_data = om->om_databuf; +} + void net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *buf); void * net_buf_ref(struct os_mbuf *om); void net_buf_unref(struct os_mbuf *om); @@ -285,6 +291,7 @@ void net_buf_reserve(struct os_mbuf *om, size_t reserve); #define BT_GATT_CCC_NOTIFY BLE_GATT_CHR_PROP_NOTIFY +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) /** Description of different data types that can be encoded into * advertising data. Used to form arrays that are passed to the * bt_le_adv_start() function. diff --git a/nimble/host/mesh/src/mesh.c b/nimble/host/mesh/src/mesh.c index e00bb1a7e..1d2184b06 100644 --- a/nimble/host/mesh/src/mesh.c +++ b/nimble/host/mesh/src/mesh.c @@ -209,55 +209,6 @@ bool bt_mesh_is_provisioned(void) return atomic_test_bit(bt_mesh.flags, BT_MESH_VALID); } -int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers) -{ - if (bt_mesh_is_provisioned()) { - return -EALREADY; - } - - char uuid_buf[BLE_UUID_STR_LEN]; - const struct bt_mesh_prov *prov = bt_mesh_prov_get(); - ble_uuid_t *uuid = BLE_UUID128_DECLARE(); - - memcpy(BLE_UUID128(uuid)->value, prov->uuid, 16); - BT_INFO("Device UUID: %s", ble_uuid_to_str(uuid, uuid_buf)); - - if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && - (bearers & BT_MESH_PROV_ADV)) { - /* Make sure we're scanning for provisioning inviations */ - bt_mesh_scan_enable(); - /* Enable unprovisioned beacon sending */ - bt_mesh_beacon_enable(); - } - - if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && - (bearers & BT_MESH_PROV_GATT)) { - bt_mesh_proxy_prov_enable(); - bt_mesh_adv_update(); - } - - return 0; -} - -int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers) -{ - if (bt_mesh_is_provisioned()) { - return -EALREADY; - } - - if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && - (bearers & BT_MESH_PROV_ADV)) { - bt_mesh_beacon_disable(); - bt_mesh_scan_disable(); - } - - if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && - (bearers & BT_MESH_PROV_GATT)) { - bt_mesh_proxy_prov_disable(true); - } - - return 0; -} static int bt_mesh_gap_event(struct ble_gap_event *event, void *arg) { @@ -375,11 +326,6 @@ int bt_mesh_init(uint8_t own_addr_type, const struct bt_mesh_prov *prov, } #endif -#if (MYNEWT_VAL(BLE_MESH_PROV)) - /* Need this to proper link.rx.buf allocation */ - bt_mesh_prov_reset_link(); -#endif - bt_mesh_net_init(); bt_mesh_trans_init(); bt_mesh_beacon_init(); diff --git a/nimble/host/mesh/src/pb_adv.c b/nimble/host/mesh/src/pb_adv.c new file mode 100644 index 000000000..ce2aebcbd --- /dev/null +++ b/nimble/host/mesh/src/pb_adv.c @@ -0,0 +1,839 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "mesh/mesh.h" +#include +#include "testing.h" +#include "net.h" +#include "prov.h" +#include "adv.h" +#include "crypto.h" +#include "beacon.h" +#include "prov_bearer.h" +#include "mesh/glue.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_PROV) +#define LOG_MODULE_NAME bt_mesh_pb_adv +#include "log/log.h" + +#define GPCF(gpc) (gpc & 0x03) +#define GPC_START(last_seg) (((last_seg) << 2) | 0x00) +#define GPC_ACK 0x01 +#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) +#define GPC_CTL(op) (((op) << 2) | 0x03) + +#define START_PAYLOAD_MAX 20 +#define CONT_PAYLOAD_MAX 23 + +#define START_LAST_SEG(gpc) (gpc >> 2) +#define CONT_SEG_INDEX(gpc) (gpc >> 2) + +#define BEARER_CTL(gpc) (gpc >> 2) +#define LINK_OPEN 0x00 +#define LINK_ACK 0x01 +#define LINK_CLOSE 0x02 + +#define XACT_SEG_DATA(_seg) (&link.rx.buf->om_data[20 + ((_seg - 1) * 23)]) +#define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) + +#define XACT_NVAL 0xff + +#define RETRANSMIT_TIMEOUT K_MSEC(500) +#define BUF_TIMEOUT K_MSEC(400) +#define CLOSING_TIMEOUT K_SECONDS(3) +#define TRANSACTION_TIMEOUT K_SECONDS(30) + +/* Acked messages, will do retransmissions manually, taking acks into account: + */ +#define RETRANSMITS_RELIABLE 0 +/* Unacked messages: */ +#define RETRANSMITS_UNRELIABLE 2 +/* PDU acks: */ +#define RETRANSMITS_ACK 2 + +enum { + LINK_ACTIVE, /* Link has been opened */ + LINK_ACK_RECVD, /* Ack for link has been received */ + LINK_CLOSING, /* Link is closing down */ + LINK_INVALID, /* Error occurred during provisioning */ + ACK_PENDING, /* An acknowledgment is being sent */ + PROVISIONER, /* The link was opened as provisioner */ + + NUM_FLAGS, +}; + +struct pb_adv { + u32_t id; /* Link ID */ + + ATOMIC_DEFINE(flags, NUM_FLAGS); + + const struct prov_bearer_cb *cb; + void *cb_data; + + struct { + u8_t id; /* Most recent transaction ID */ + u8_t seg; /* Bit-field of unreceived segments */ + u8_t last_seg; /* Last segment (to check length) */ + u8_t fcs; /* Expected FCS value */ + struct os_mbuf *buf; + } rx; + + struct { + /* Start timestamp of the transaction */ + s64_t start; + + /* Transaction id */ + u8_t id; + + /* Current ack id */ + u8_t pending_ack; + + /* Pending outgoing buffer(s) */ + struct os_mbuf *buf[3]; + + prov_bearer_send_complete_t cb; + + void *cb_data; + + /* Retransmit timer */ + struct k_delayed_work retransmit; + } tx; + + /* Protocol timeout */ + struct k_delayed_work prot_timer; +}; + +struct prov_rx { + u32_t link_id; + u8_t xact_id; + u8_t gpc; +}; + +static struct os_mbuf rx_buf = { .om_len = 65 }; + +static struct pb_adv link = { .rx = { .buf = &rx_buf } }; + +static void gen_prov_ack_send(u8_t xact_id); +static void link_open(struct prov_rx *rx, struct os_mbuf *buf); +static void link_ack(struct prov_rx *rx, struct os_mbuf *buf); +static void link_close(struct prov_rx *rx, struct os_mbuf *buf); + +static void buf_sent(int err, void *user_data) +{ + if (!link.tx.buf[0]) { + return; + } + + k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); +} + +static struct bt_mesh_send_cb buf_sent_cb = { + .end = buf_sent, +}; + +static u8_t last_seg(u8_t len) +{ + if (len <= START_PAYLOAD_MAX) { + return 0; + } + + len -= START_PAYLOAD_MAX; + + return 1 + (len / CONT_PAYLOAD_MAX); +} + +static void free_segments(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + link.tx.buf[i] = NULL; + /* Mark as canceled */ + BT_MESH_ADV(buf)->busy = 0U; + net_buf_unref(buf); + } +} + +static u8_t next_transaction_id(u8_t id) +{ + return (((id + 1) & 0x7f) | (id & 0x80)); +} + +static void prov_clear_tx(void) +{ + BT_DBG(""); + + k_delayed_work_cancel(&link.tx.retransmit); + + free_segments(); +} + +static void reset_adv_link(void) +{ + BT_DBG(""); + prov_clear_tx(); + + k_delayed_work_cancel(&link.prot_timer); + + /* Clear everything except the retransmit and protocol timer + * delayed work objects. + */ + (void)memset(&link, 0, offsetof(struct pb_adv, tx.retransmit)); + link.rx.id = XACT_NVAL; + link.tx.pending_ack = XACT_NVAL; + link.rx.buf = &rx_buf; + net_buf_simple_reset(link.rx.buf); +} + +static void close_link(enum prov_bearer_link_status reason) +{ + const struct prov_bearer_cb *cb = link.cb; + void *cb_data = link.cb_data; + + reset_adv_link(); + cb->link_closed(&pb_adv, cb_data, reason); +} + +static struct os_mbuf *adv_buf_create(u8_t retransmits) +{ + struct os_mbuf *buf; + + buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, + BT_MESH_TRANSMIT(retransmits, 20), + BUF_TIMEOUT); + if (!buf) { + BT_ERR("Out of provisioning buffers"); + return NULL; + } + + return buf; +} + +static void ack_complete(u16_t duration, int err, void *user_data) +{ + BT_DBG("xact 0x%x complete", (u8_t)link.tx.pending_ack); + atomic_clear_bit(link.flags, ACK_PENDING); +} + +static bool ack_pending(void) +{ + return atomic_test_bit(link.flags, ACK_PENDING); +} + +static void prov_failed(u8_t err) +{ + BT_DBG("%u", err); + link.cb->error(&pb_adv, link.cb_data, err); + atomic_set_bit(link.flags, LINK_INVALID); +} + +static void prov_msg_recv(void) +{ + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { + BT_ERR("Incorrect FCS"); + return; + } + + gen_prov_ack_send(link.rx.id); + + if (atomic_test_bit(link.flags, LINK_INVALID)) { + BT_WARN("Unexpected msg 0x%02x on invalidated link", + link.rx.buf->om_data[0]); + prov_failed(PROV_ERR_UNEXP_PDU); + return; + } + + link.cb->recv(&pb_adv, link.cb_data, link.rx.buf); +} + +static void protocol_timeout(struct ble_npl_event *work) +{ + BT_DBG(""); + + link.rx.seg = 0U; + close_link(PROV_BEARER_LINK_STATUS_TIMEOUT); +} +/******************************************************************************* + * Generic provisioning + ******************************************************************************/ + +static void gen_prov_ack_send(u8_t xact_id) +{ + static const struct bt_mesh_send_cb cb = { + .start = ack_complete, + }; + const struct bt_mesh_send_cb *complete; + struct os_mbuf *buf; + bool pending = atomic_test_and_set_bit(link.flags, ACK_PENDING); + + BT_DBG("xact_id 0x%x", xact_id); + + if (pending && link.tx.pending_ack == xact_id) { + BT_DBG("Not sending duplicate ack"); + return; + } + + buf = adv_buf_create(RETRANSMITS_ACK); + if (!buf) { + atomic_clear_bit(link.flags, ACK_PENDING); + return; + } + + if (pending) { + complete = NULL; + } else { + link.tx.pending_ack = xact_id; + complete = &cb; + } + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_ACK); + + bt_mesh_adv_send(buf, complete, NULL); + net_buf_unref(buf); +} + +static void gen_prov_cont(struct prov_rx *rx, struct os_mbuf *buf) +{ + u8_t seg = CONT_SEG_INDEX(rx->gpc); + + BT_DBG("len %u, seg_index %u", buf->om_len, seg); + + if (!link.rx.seg && link.rx.id == rx->xact_id) { + if (!ack_pending()) { + BT_DBG("Resending ack"); + gen_prov_ack_send(rx->xact_id); + } + + return; + } + + if (rx->xact_id != link.rx.id) { + BT_WARN("Data for unknown transaction (0x%x != 0x%x)", + rx->xact_id, link.rx.id); + return; + } + + if (seg > link.rx.last_seg) { + BT_ERR("Invalid segment index %u", seg); + prov_failed(PROV_ERR_NVAL_FMT); + return; + } else if (seg == link.rx.last_seg) { + u8_t expect_len; + + expect_len = (link.rx.buf->om_len - 20U - + ((link.rx.last_seg - 1) * 23U)); + if (expect_len != buf->om_len) { + BT_ERR("Incorrect last seg len: %u != %u", expect_len, + buf->om_len); + prov_failed(PROV_ERR_NVAL_FMT); + return; + } + } + + if (!(link.rx.seg & BIT(seg))) { + BT_DBG("Ignoring already received segment"); + return; + } + + memcpy(XACT_SEG_DATA(seg), buf->om_data, buf->om_len); + XACT_SEG_RECV(seg); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static void gen_prov_ack(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("len %u", buf->om_len); + + if (!link.tx.buf[0]) { + return; + } + + if (rx->xact_id == link.tx.id) { + /* Don't clear resending of LINK_CLOSE messages */ + if (!atomic_test_bit(link.flags, LINK_CLOSING)) { + prov_clear_tx(); + } + + if (link.tx.cb && link.tx.cb) { + link.tx.cb(0, link.tx.cb_data); + } + } +} + +static void gen_prov_start(struct prov_rx *rx, struct os_mbuf *buf) +{ + u8_t expected_id = next_transaction_id(link.rx.id); + + if (link.rx.seg) { + if (rx->xact_id != link.rx.id) { + BT_WARN("Got Start while there are unreceived " + "segments"); + } + + return; + } + + if (rx->xact_id == link.rx.id) { + if (!ack_pending()) { + BT_DBG("Resending ack"); + gen_prov_ack_send(rx->xact_id); + } + + return; + } + + if (rx->xact_id != expected_id) { + BT_WARN("Unexpected xact 0x%x, expected 0x%x", rx->xact_id, + expected_id); + return; + } + + net_buf_simple_reset(link.rx.buf); + link.rx.buf->om_len = net_buf_simple_pull_be16(buf); + link.rx.id = rx->xact_id; + link.rx.fcs = net_buf_simple_pull_u8(buf); + + BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->om_len, + START_LAST_SEG(rx->gpc), link.rx.buf->om_len, link.rx.fcs); + + if (link.rx.buf->om_len < 1) { + BT_ERR("Ignoring zero-length provisioning PDU"); + prov_failed(PROV_ERR_NVAL_FMT); + return; + } + + if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->om_len <= 20U) { + BT_ERR("Too small total length for multi-segment PDU"); + prov_failed(PROV_ERR_NVAL_FMT); + return; + } + + prov_clear_tx(); + + link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; + link.rx.last_seg = START_LAST_SEG(rx->gpc); + memcpy(link.rx.buf->om_data, buf->om_data, buf->om_len); + XACT_SEG_RECV(0); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static void gen_prov_ctl(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->om_len); + + switch (BEARER_CTL(rx->gpc)) { + case LINK_OPEN: + link_open(rx, buf); + break; + case LINK_ACK: + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_ack(rx, buf); + break; + case LINK_CLOSE: + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_close(rx, buf); + break; + default: + BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc)); + } + + return; + } +} + +static const struct { + void (*func)(struct prov_rx *rx, struct os_mbuf *buf); + bool require_link; + u8_t min_len; +} gen_prov[] = { + { gen_prov_start, true, 3 }, + { gen_prov_ack, true, 0 }, + { gen_prov_cont, true, 0 }, + { gen_prov_ctl, false, 0 }, +}; + +static void gen_prov_recv(struct prov_rx *rx, struct os_mbuf *buf) +{ + if (buf->om_len < gen_prov[GPCF(rx->gpc)].min_len) { + BT_ERR("Too short GPC message type %u", GPCF(rx->gpc)); + return; + } + + if (!atomic_test_bit(link.flags, LINK_ACTIVE) && + gen_prov[GPCF(rx->gpc)].require_link) { + BT_DBG("Ignoring message that requires active link"); + return; + } + + gen_prov[GPCF(rx->gpc)].func(rx, buf); +} + +/******************************************************************************* + * TX + ******************************************************************************/ + +static void send_reliable(void) +{ + int i; + + link.tx.start = k_uptime_get(); + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + } +} + +static void prov_retransmit(struct ble_npl_event *work) +{ + int i, timeout; + + BT_DBG(""); + + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + BT_WARN("Link not active"); + return; + } + + /* + * According to mesh profile spec (5.3.1.4.3), the close message should + * be restransmitted at least three times. Retransmit the LINK_CLOSE + * message until CLOSING_TIMEOUT has elapsed. + */ + if (atomic_test_bit(link.flags, LINK_CLOSING)) { + timeout = CLOSING_TIMEOUT; + } else { + timeout = TRANSACTION_TIMEOUT; + } + + if (k_uptime_get() - link.tx.start > timeout) { + if (atomic_test_bit(link.flags, LINK_CLOSING)) { + close_link(PROV_BEARER_LINK_STATUS_SUCCESS); + } else { + BT_WARN("Giving up transaction"); + close_link(PROV_BEARER_LINK_STATUS_TIMEOUT); + } + + return; + } + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (BT_MESH_ADV(buf)->busy) { + continue; + } + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + } +} + +static int bearer_ctl_send(u8_t op, const void *data, u8_t data_len, + bool reliable) +{ + struct os_mbuf *buf; + + BT_DBG("op 0x%02x data_len %u", op, data_len); + + prov_clear_tx(); + + buf = adv_buf_create(reliable ? RETRANSMITS_RELIABLE : + RETRANSMITS_UNRELIABLE); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_be32(buf, link.id); + /* Transaction ID, always 0 for Bearer messages */ + net_buf_add_u8(buf, 0x00); + net_buf_add_u8(buf, GPC_CTL(op)); + net_buf_add_mem(buf, data, data_len); + + if (reliable) { + link.tx.buf[0] = buf; + send_reliable(); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + + return 0; +} + +static int prov_send_adv(struct os_mbuf *msg, + prov_bearer_send_complete_t cb, void *cb_data) +{ + struct os_mbuf *start, *buf; + u8_t seg_len, seg_id; + + prov_clear_tx(); + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + start = adv_buf_create(RETRANSMITS_RELIABLE); + if (!start) { + return -ENOBUFS; + } + + link.tx.id = next_transaction_id(link.tx.id); + net_buf_add_be32(start, link.id); + net_buf_add_u8(start, link.tx.id); + + net_buf_add_u8(start, GPC_START(last_seg(msg->om_len))); + net_buf_add_be16(start, msg->om_len); + net_buf_add_u8(start, bt_mesh_fcs_calc(msg->om_data, msg->om_len)); + + link.tx.buf[0] = start; + link.tx.cb = cb; + link.tx.cb_data = cb_data; + + BT_DBG("xact_id: 0x%x len: %u", link.tx.id, msg->om_len); + + seg_len = MIN(msg->om_len, START_PAYLOAD_MAX); + BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->om_data, seg_len)); + net_buf_add_mem(start, msg->om_data, seg_len); + net_buf_simple_pull(msg, seg_len); + + buf = start; + for (seg_id = 1U; msg->om_len > 0; seg_id++) { + if (seg_id >= ARRAY_SIZE(link.tx.buf)) { + BT_ERR("Too big message"); + free_segments(); + return -E2BIG; + } + + buf = adv_buf_create(RETRANSMITS_RELIABLE); + if (!buf) { + free_segments(); + return -ENOBUFS; + } + + link.tx.buf[seg_id] = buf; + + seg_len = MIN(msg->om_len, CONT_PAYLOAD_MAX); + + BT_DBG("seg %u len %u: %s", seg_id, seg_len, + bt_hex(msg->om_data, seg_len)); + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, link.tx.id); + net_buf_add_u8(buf, GPC_CONT(seg_id)); + net_buf_add_mem(buf, msg->om_data, seg_len); + net_buf_simple_pull(msg, seg_len); + } + + send_reliable(); + + return 0; +} + +/******************************************************************************* + * Link management rx + ******************************************************************************/ + +static void link_open(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("len %u", buf->om_len); + + if (buf->om_len < 16) { + BT_ERR("Too short bearer open message (len %u)", buf->om_len); + return; + } + + if (atomic_test_bit(link.flags, LINK_ACTIVE)) { + /* Send another link ack if the provisioner missed the last */ + if (link.id == rx->link_id && link.tx.id == 0x7F) { + BT_DBG("Resending link ack"); + bearer_ctl_send(LINK_ACK, NULL, 0, false); + } else { + BT_DBG("Ignoring bearer open: link already active"); + } + + return; + } + + if (memcmp(buf->om_data, bt_mesh_prov_get()->uuid, 16)) { + BT_DBG("Bearer open message not for us"); + return; + } + + link.id = rx->link_id; + atomic_set_bit(link.flags, LINK_ACTIVE); + net_buf_simple_reset(link.rx.buf); + + bearer_ctl_send(LINK_ACK, NULL, 0, false); + + link.cb->link_opened(&pb_adv, link.cb_data); +} + +static void link_ack(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("len %u", buf->om_len); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + if (atomic_test_and_set_bit(link.flags, LINK_ACK_RECVD)) { + return; + } + + prov_clear_tx(); + + link.cb->link_opened(&pb_adv, link.cb_data); + } +} + +static void link_close(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("len %u", buf->om_len); + + if (buf->om_len != 1) { + return; + } + + close_link(net_buf_simple_pull_u8(buf)); +} + +/******************************************************************************* + * Higher level functionality + ******************************************************************************/ + +void bt_mesh_pb_adv_recv(struct os_mbuf *buf) +{ + struct prov_rx rx; + + if (!link.cb) { + return; + } + + if (buf->om_len < 6) { + BT_WARN("Too short provisioning packet (len %u)", buf->om_len); + return; + } + + rx.link_id = net_buf_simple_pull_be32(buf); + rx.xact_id = net_buf_simple_pull_u8(buf); + rx.gpc = net_buf_simple_pull_u8(buf); + + if (atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) { + return; + } + + BT_DBG("link_id 0x%08x xact_id 0x%x", rx.link_id, rx.xact_id); + + gen_prov_recv(&rx, buf); +} + +static int prov_link_open(const u8_t uuid[16], s32_t timeout, + const struct prov_bearer_cb *cb, void *cb_data) +{ + BT_DBG("uuid %s", bt_hex(uuid, 16)); + + if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { + return -EBUSY; + } + + atomic_set_bit(link.flags, PROVISIONER); + + bt_rand(&link.id, sizeof(link.id)); + link.tx.id = 0x7F; + link.rx.id = 0xFF; + link.cb = cb; + link.cb_data = cb_data; + + net_buf_simple_reset(link.rx.buf); + + bearer_ctl_send(LINK_OPEN, uuid, 16, true); + + return 0; +} + +static int prov_link_accept(const struct prov_bearer_cb *cb, void *cb_data) +{ + if (atomic_test_bit(link.flags, LINK_ACTIVE)) { + return -EBUSY; + } + + link.rx.id = 0x7F; + link.tx.id = 0xFF; + link.cb = cb; + link.cb_data = cb_data; + + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + bt_mesh_beacon_enable(); + + return 0; +} + +static void prov_link_close(enum prov_bearer_link_status status) +{ + if (atomic_test_and_set_bit(link.flags, LINK_CLOSING)) { + return; + } + + bearer_ctl_send(LINK_CLOSE, &status, 1, true); +} + +void pb_adv_init(void) +{ + k_delayed_work_init(&link.prot_timer, protocol_timeout); + k_delayed_work_init(&link.tx.retransmit, prov_retransmit); +} + +const struct prov_bearer pb_adv = { + .type = BT_MESH_PROV_ADV, + .link_open = prov_link_open, + .link_accept = prov_link_accept, + .link_close = prov_link_close, + .send = prov_send_adv, + .clear_tx = prov_clear_tx, +}; \ No newline at end of file diff --git a/nimble/host/mesh/src/pb_gatt.c b/nimble/host/mesh/src/pb_gatt.c new file mode 100644 index 000000000..ca02317e3 --- /dev/null +++ b/nimble/host/mesh/src/pb_gatt.c @@ -0,0 +1,155 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "mesh/mesh.h" +#include "prov.h" +#include "net.h" +#include "proxy.h" +#include "adv.h" +#include "prov_bearer.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_PROV) +#define LOG_MODULE_NAME bt_mesh_pb_gatt +#include "log/log.h" + +struct prov_link { + uint16_t conn_handle; + const struct prov_bearer_cb *cb; + void *cb_data; + struct { + u8_t id; /* Transaction ID */ + u8_t prev_id; /* Previous Transaction ID */ + u8_t seg; /* Bit-field of unreceived segments */ + u8_t last_seg; /* Last segment (to check length) */ + u8_t fcs; /* Expected FCS value */ + struct os_mbuf *buf; + } rx; + struct k_delayed_work prot_timer; +}; + +static struct prov_link link; + +static void reset_state(void) +{ + link.conn_handle = BLE_HS_CONN_HANDLE_NONE; + + k_delayed_work_cancel(&link.prot_timer); + memset(&link, 0, offsetof(struct prov_link, prot_timer)); + + link.rx.buf = bt_mesh_proxy_get_buf(); +} + +static void protocol_timeout(struct ble_npl_event *work) +{ + const struct prov_bearer_cb *cb = link.cb; + + BT_DBG("Protocol timeout"); + + if (link.conn_handle) { + bt_mesh_pb_gatt_close(link.conn_handle); + } + + reset_state(); + + cb->link_closed(&pb_gatt, link.cb_data, + PROV_BEARER_LINK_STATUS_TIMEOUT); +} + +int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf) +{ + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (link.conn_handle != conn_handle || !link.cb) { + BT_WARN("Data for unexpected connection"); + return -ENOTCONN; + } + + if (buf->om_len < 1) { + BT_WARN("Too short provisioning packet (len %u)", buf->om_len); + return -EINVAL; + } + + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + link.cb->recv(&pb_gatt, link.cb_data, buf); + + return 0; +} + +int bt_mesh_pb_gatt_open(uint16_t conn_handle) +{ + BT_DBG("conn %p", conn_handle); + + if (link.conn_handle) { + return -EBUSY; + } + + link.conn_handle = conn_handle; + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + link.cb->link_opened(&pb_gatt, link.cb_data); + + return 0; +} + +int bt_mesh_pb_gatt_close(uint16_t conn_handle) +{ + BT_DBG("conn %p", conn_handle); + + if (link.conn_handle != conn_handle) { + BT_ERR("Not connected"); + return -ENOTCONN; + } + + link.cb->link_closed(&pb_gatt, link.cb_data, + PROV_BEARER_LINK_STATUS_SUCCESS); + + reset_state(); + + return 0; +} + +static int link_accept(const struct prov_bearer_cb *cb, void *cb_data) +{ + bt_mesh_proxy_prov_enable(); + bt_mesh_adv_update(); + + link.cb = cb; + link.cb_data = cb_data; + + return 0; +} + +static int buf_send(struct os_mbuf *buf, prov_bearer_send_complete_t cb, + void *cb_data) +{ + if (!link.conn_handle) { + return -ENOTCONN; + } + + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + return bt_mesh_proxy_send(link.conn_handle, BT_MESH_PROXY_PROV, buf); +} + +static void clear_tx(void) +{ + /* No action */ +} + +void pb_gatt_init(void) +{ + k_delayed_work_init(&link.prot_timer, protocol_timeout); +} + +const struct prov_bearer pb_gatt = { + .type = BT_MESH_PROV_GATT, + .link_accept = link_accept, + .send = buf_send, + .clear_tx = clear_tx, +}; \ No newline at end of file diff --git a/nimble/host/mesh/src/prov.c b/nimble/host/mesh/src/prov.c index c2d2307fa..f751cd87f 100644 --- a/nimble/host/mesh/src/prov.c +++ b/nimble/host/mesh/src/prov.c @@ -9,8 +9,6 @@ #include "syscfg/syscfg.h" #define MESH_LOG_MODULE BLE_MESH_PROV_LOG -#if MYNEWT_VAL(BLE_MESH_PROV) - #include #include "mesh/mesh.h" @@ -20,15 +18,14 @@ #include "atomic.h" #include "adv.h" #include "net.h" +#include "beacon.h" #include "access.h" #include "foundation.h" #include "proxy.h" #include "prov.h" #include "testing.h" #include "settings.h" - -/* 3 transmissions, 20ms interval */ -#define PROV_XMIT BT_MESH_TRANSMIT(2, 20) +#include "prov_bearer.h" #define AUTH_METHOD_NO_OOB 0x00 #define AUTH_METHOD_STATIC 0x01 @@ -49,16 +46,6 @@ #define PUB_KEY_NO_OOB 0x00 #define PUB_KEY_OOB 0x01 -#define PROV_ERR_NONE 0x00 -#define PROV_ERR_NVAL_PDU 0x01 -#define PROV_ERR_NVAL_FMT 0x02 -#define PROV_ERR_UNEXP_PDU 0x03 -#define PROV_ERR_CFM_FAILED 0x04 -#define PROV_ERR_RESOURCES 0x05 -#define PROV_ERR_DECRYPT 0x06 -#define PROV_ERR_UNEXP_ERR 0x07 -#define PROV_ERR_ADDR 0x08 - #define PROV_INVITE 0x00 #define PROV_CAPABILITIES 0x01 #define PROV_START 0x02 @@ -74,42 +61,12 @@ #define PROV_ALG_P256 0x00 -#define GPCF(gpc) (gpc & 0x03) -#define GPC_START(last_seg) (((last_seg) << 2) | 0x00) -#define GPC_ACK 0x01 -#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) -#define GPC_CTL(op) (((op) << 2) | 0x03) - -#define START_PAYLOAD_MAX 20 -#define CONT_PAYLOAD_MAX 23 - -#define START_LAST_SEG(gpc) (gpc >> 2) -#define CONT_SEG_INDEX(gpc) (gpc >> 2) - -#define BEARER_CTL(gpc) (gpc >> 2) -#define LINK_OPEN 0x00 -#define LINK_ACK 0x01 -#define LINK_CLOSE 0x02 - -#define CLOSE_REASON_SUCCESS 0x00 -#define CLOSE_REASON_TIMEOUT 0x01 -#define CLOSE_REASON_FAILED 0x02 - -#define XACT_SEG_DATA(_seg) (&link.rx.buf->om_data[20 + ((_seg - 1) * 23)]) -#define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) - -#define XACT_NVAL 0xff - enum { WAIT_PUB_KEY, /* Waiting for local PubKey to be generated */ LINK_ACTIVE, /* Link has been opened */ - LINK_ACK_RECVD, /* Ack for link has been received */ - LINK_CLOSING, /* Link is closing down */ - SEND_PUB_KEY, /* Waiting to send PubKey */ WAIT_NUMBER, /* Waiting for number input from user */ WAIT_STRING, /* Waiting for string input from user */ NOTIFY_INPUT_COMPLETE, /* Notify that input has been completed. */ - LINK_INVALID, /* Error occurred during provisioning */ PROVISIONER, /* The link was opened as provisioner */ NUM_FLAGS, @@ -131,9 +88,6 @@ struct provisioner_link { struct prov_link { ATOMIC_DEFINE(flags, NUM_FLAGS); -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) - uint16_t conn_handle; /* GATT connection */ -#endif struct provisioner_link provisioner[PROVISIONER_LINK]; u8_t dhkey[32]; /* Calculated DHKey */ @@ -152,64 +106,18 @@ struct prov_link { u8_t conf_inputs[145]; /* ConfirmationInputs */ u8_t prov_salt[16]; /* Provisioning Salt */ -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) - u32_t id; /* Link ID */ - - struct { - u8_t id; /* Transaction ID */ - u8_t prev_id; /* Previous Transaction ID */ - u8_t seg; /* Bit-field of unreceived segments */ - u8_t last_seg; /* Last segment (to check length) */ - u8_t fcs; /* Expected FCS value */ - struct os_mbuf *buf; - } rx; - - struct { - /* Start timestamp of the transaction */ - s64_t start; - - /* Transaction id*/ - u8_t id; - - /* Pending outgoing buffer(s) */ - struct os_mbuf *buf[3]; - - /* Retransmit timer */ - struct k_delayed_work retransmit; - } tx; -#endif - - struct k_delayed_work prot_timer; + const struct prov_bearer *bearer; }; -struct prov_rx { - u32_t link_id; - u8_t xact_id; - u8_t gpc; -}; - -#define RETRANSMIT_TIMEOUT K_MSEC(500) -#define BUF_TIMEOUT K_MSEC(400) -#define CLOSING_TIMEOUT K_SECONDS(3) -#define TRANSACTION_TIMEOUT K_SECONDS(30) -#define PROTOCOL_TIMEOUT K_SECONDS(60) - -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) -#define PROV_BUF_HEADROOM 5 -#else -#define PROV_BUF_HEADROOM 0 -#ifdef BLE_MESH_PB_ADV -static struct os_mbuf *rx_buf; -#endif -#endif - -#define PROV_BUF(len) NET_BUF_SIMPLE(PROV_BUF_HEADROOM + len) +#define PROV_BUF(len) NET_BUF_SIMPLE(PROV_BEARER_BUF_HEADROOM + len) static struct prov_link link; static const struct bt_mesh_prov *prov; +static void send_pub_key(void); static void pub_key_ready(const u8_t *pkey); +static void start_sent(int err, void *cb_data); static int reset_state(void) { @@ -218,8 +126,6 @@ static int reset_state(void) }; int err; - k_delayed_work_cancel(&link.prot_timer); - /* Disable Attention Timer if it was set */ if (link.conf_inputs[0]) { bt_mesh_attention(NULL, 0); @@ -230,31 +136,7 @@ static int reset_state(void) bt_mesh_cdb_node_del(link.provisioner->node, false); } -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) - /* Clear everything except the retransmit and protocol timer - * delayed work objects. - */ - (void)memset(&link, 0, offsetof(struct prov_link, tx.retransmit)); - link.rx.prev_id = XACT_NVAL; - -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) - link.rx.buf = bt_mesh_proxy_get_buf(); -#else - if (!rx_buf) { - rx_buf = NET_BUF_SIMPLE(65); - } - net_buf_simple_init(rx_buf, 0); - link.rx.buf = rx_buf; -#endif /* PB_GATT */ - -#else /* !PB_ADV */ - /* Clear everything except the protocol timer (k_delayed_work) */ - (void)memset(&link, 0, offsetof(struct prov_link, prot_timer)); -#endif /* PB_ADV */ - -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) - link.conn_handle = BLE_HS_CONN_HANDLE_NONE; -#endif + memset(&link, 0, sizeof(link)); err = bt_pub_key_gen(&pub_key_cb); if (err) { @@ -265,309 +147,58 @@ static int reset_state(void) return 0; } -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) -static void buf_sent(int err, void *user_data) +static inline int prov_send(struct os_mbuf *buf, + prov_bearer_send_complete_t cb) { - BT_DBG("buf_sent"); - - if (!link.tx.buf[0]) { - return; - } - - k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); -} - -static struct bt_mesh_send_cb buf_sent_cb = { - .end = buf_sent, -}; - -static void free_segments(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { - struct os_mbuf *buf = link.tx.buf[i]; - - if (!buf) { - break; - } - - link.tx.buf[i] = NULL; - /* Mark as canceled */ - BT_MESH_ADV(buf)->busy = 0; - net_buf_unref(buf); - } -} - -static void prov_clear_tx(void) -{ - BT_DBG(""); - - k_delayed_work_cancel(&link.tx.retransmit); - - free_segments(); -} - -static void reset_adv_link(void) -{ - prov_clear_tx(); - - if (prov->link_close) { - prov->link_close(BT_MESH_PROV_ADV); - } - - reset_state(); -} - -static struct os_mbuf *adv_buf_create(void) -{ - struct os_mbuf *buf; - - buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT); - if (!buf) { - BT_ERR("Out of provisioning buffers"); - assert(0); - return NULL; - } - - return buf; -} - -static u8_t pending_ack = XACT_NVAL; - -static void ack_complete(u16_t duration, int err, void *user_data) -{ - BT_DBG("xact %u complete", (u8_t)pending_ack); - pending_ack = XACT_NVAL; -} - -static void gen_prov_ack_send(u8_t xact_id) -{ - static const struct bt_mesh_send_cb cb = { - .start = ack_complete, - }; - const struct bt_mesh_send_cb *complete; - struct os_mbuf *buf; - - BT_DBG("xact_id %u", xact_id); - - if (pending_ack == xact_id) { - BT_DBG("Not sending duplicate ack"); - return; - } - - buf = adv_buf_create(); - if (!buf) { - return; - } - - if (pending_ack == XACT_NVAL) { - pending_ack = xact_id; - complete = &cb; - } else { - complete = NULL; - } - - net_buf_add_be32(buf, link.id); - net_buf_add_u8(buf, xact_id); - net_buf_add_u8(buf, GPC_ACK); - - bt_mesh_adv_send(buf, complete, NULL); - net_buf_unref(buf); -} - -static void send_reliable(void) -{ - int i; - - link.tx.start = k_uptime_get(); - - for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { - struct os_mbuf *buf = link.tx.buf[i]; - - if (!buf) { - break; - } - - if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { - bt_mesh_adv_send(buf, NULL, NULL); - } else { - bt_mesh_adv_send(buf, &buf_sent_cb, NULL); - } - } -} - -static int bearer_ctl_send(u8_t op, const void *data, u8_t data_len) -{ - struct os_mbuf *buf; - - BT_DBG("op 0x%02x data_len %u", op, data_len); - - prov_clear_tx(); - - buf = adv_buf_create(); - if (!buf) { - return -ENOBUFS; - } - - net_buf_add_be32(buf, link.id); - /* Transaction ID, always 0 for Bearer messages */ - net_buf_add_u8(buf, 0x00); - net_buf_add_u8(buf, GPC_CTL(op)); - net_buf_add_mem(buf, data, data_len); - - link.tx.buf[0] = buf; - send_reliable(); - - return 0; -} - -static u8_t last_seg(u8_t len) -{ - if (len <= START_PAYLOAD_MAX) { - return 0; - } - - len -= START_PAYLOAD_MAX; - - return 1 + (len / CONT_PAYLOAD_MAX); -} - -static inline u8_t next_transaction_id(void) -{ - if (atomic_test_bit(link.flags, PROVISIONER)) { - if (link.tx.id != 0x7F) { - link.tx.id++; - } else { - link.tx.id = 0; - } - } else { - if (link.tx.id != 0U && link.tx.id != 0xFF) { - link.tx.id++; - } else { - link.tx.id = 0x80; - } - } - - return link.tx.id; -} - -static int prov_send_adv(struct os_mbuf *msg) -{ - struct os_mbuf *start, *buf; - u8_t seg_len, seg_id; - u8_t xact_id; - - BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len)); - - prov_clear_tx(); - - start = adv_buf_create(); - if (!start) { - return -ENOBUFS; - } - - xact_id = next_transaction_id(); - net_buf_add_be32(start, link.id); - net_buf_add_u8(start, xact_id); - - net_buf_add_u8(start, GPC_START(last_seg(msg->om_len))); - net_buf_add_be16(start, msg->om_len); - net_buf_add_u8(start, bt_mesh_fcs_calc(msg->om_data, msg->om_len)); - - link.tx.buf[0] = start; - - seg_len = min(msg->om_len, START_PAYLOAD_MAX); - BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->om_data, seg_len)); - net_buf_add_mem(start, msg->om_data, seg_len); - net_buf_simple_pull(msg, seg_len); - - buf = start; - for (seg_id = 1; msg->om_len > 0; seg_id++) { - if (seg_id >= ARRAY_SIZE(link.tx.buf)) { - BT_ERR("Too big message"); - free_segments(); - return -E2BIG; - } - - buf = adv_buf_create(); - if (!buf) { - free_segments(); - return -ENOBUFS; - } - - link.tx.buf[seg_id] = buf; - - seg_len = min(msg->om_len, CONT_PAYLOAD_MAX); - - BT_DBG("seg_id %u len %u: %s", seg_id, seg_len, - bt_hex(msg->om_data, seg_len)); - - net_buf_add_be32(buf, link.id); - net_buf_add_u8(buf, xact_id); - net_buf_add_u8(buf, GPC_CONT(seg_id)); - net_buf_add_mem(buf, msg->om_data, seg_len); - net_buf_simple_pull(msg, seg_len); - } - - send_reliable(); - - return 0; -} - -#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ - -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) -static int prov_send_gatt(struct os_mbuf *msg) -{ - if (link.conn_handle == BLE_HS_CONN_HANDLE_NONE) { - BT_ERR("No connection handle!?"); - return -ENOTCONN; - } - - return bt_mesh_proxy_send(link.conn_handle, BT_MESH_PROXY_PROV, msg); -} -#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ - -static inline int prov_send(struct os_mbuf *buf) -{ - k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); - -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) - if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) { - return prov_send_gatt(buf); - } -#endif -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) - return prov_send_adv(buf); -#else - return 0; -#endif + return link.bearer->send(buf, cb, NULL); } static void prov_buf_init(struct os_mbuf *buf, u8_t type) { - net_buf_simple_init(buf, PROV_BUF_HEADROOM); + net_buf_simple_init(buf, PROV_BEARER_BUF_HEADROOM); net_buf_simple_add_u8(buf, type); } +static void prov_link_close(enum prov_bearer_link_status status) +{ + BT_DBG("%u", status); + link.expect = PROV_NO_PDU; + + link.bearer->link_close(status); +} + static void prov_send_fail_msg(u8_t err) { struct os_mbuf *buf = PROV_BUF(2); + BT_DBG("%u", err); + + link.expect = PROV_NO_PDU; + prov_buf_init(buf, PROV_FAILED); net_buf_simple_add_u8(buf, err); - if (prov_send(buf)) { + if (prov_send(buf, NULL)) { BT_ERR("Failed to send Provisioning Failed message"); } - atomic_set_bit(link.flags, LINK_INVALID); - os_mbuf_free_chain(buf); } -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static void prov_fail(u8_t reason) +{ + /* According to Bluetooth Mesh Specification v1.0.1, Section 5.4.4, the + * provisioner just closes the link when something fails, while the + * provisionee sends the fail message, and waits for the provisioner to + * close the link. + */ + if (atomic_test_bit(link.flags, PROVISIONER)) { + prov_link_close(PROV_BEARER_LINK_STATUS_FAIL); + } else { + prov_send_fail_msg(reason); + } +} + static void prov_invite(const u8_t *data) { struct os_mbuf *buf = PROV_BUF(12); @@ -608,7 +239,7 @@ static void prov_invite(const u8_t *data) memcpy(&link.conf_inputs[1], &buf->om_data[1], 11); - if (prov_send(buf)) { + if (prov_send(buf, NULL)) { BT_ERR("Failed to send capabilities"); goto done; } @@ -630,7 +261,7 @@ static void send_invite(void) link.conf_inputs[0] = link.provisioner->attention_duration; - if (prov_send(inv)) { + if (prov_send(inv, NULL)) { BT_ERR("Failed to send invite"); goto done; } @@ -640,9 +271,7 @@ static void send_invite(void) done: os_mbuf_free_chain(inv); } -#endif -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) static void send_start(void) { struct os_mbuf *start = PROV_BUF(6); @@ -661,13 +290,25 @@ static void send_start(void) memcpy(&link.conf_inputs[12], &start->om_data[1], 5); - if (prov_send(start)) { + if (prov_send(start, start_sent)) { BT_ERR("Failed to send start"); } os_mbuf_free_chain(start); } +static void start_sent(int err, void *cb_data) +{ + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + if (!bt_pub_key_get()) { + atomic_set_bit(link.flags, WAIT_PUB_KEY); + BT_WARN("Waiting for local public key"); + } else { + send_pub_key(); + } + } +} + static void prov_capabilities(const u8_t *data) { u16_t algorithms, output_action, input_action; @@ -695,7 +336,7 @@ static void prov_capabilities(const u8_t *data) if (data[0] == 0) { BT_ERR("Invalid number of elements"); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); + prov_fail(PROV_ERR_NVAL_FMT); return; } @@ -704,14 +345,13 @@ static void prov_capabilities(const u8_t *data) link.provisioner->addr, data[0], link.provisioner->net_idx); if (link.provisioner->node == NULL) { - prov_send_fail_msg(PROV_ERR_RESOURCES); + BT_ERR("Failed allocating node 0x%04x", link.provisioner->addr); + prov_fail(PROV_ERR_RESOURCES); return; } memcpy(&link.conf_inputs[1], data, 11); - atomic_set_bit(link.flags, SEND_PUB_KEY); - send_start(); } @@ -860,13 +500,13 @@ static void prov_start(const u8_t *data) if (data[0] != PROV_ALG_P256) { BT_ERR("Unknown algorithm 0x%02x", data[0]); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); + prov_fail(PROV_ERR_NVAL_FMT); return; } if (data[1] != PUB_KEY_NO_OOB) { BT_ERR("Invalid public key type: 0x%02x", data[1]); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); + prov_fail(PROV_ERR_NVAL_FMT); return; } @@ -879,10 +519,9 @@ static void prov_start(const u8_t *data) BT_ERR("Invalid authentication method: 0x%02x; " "action: 0x%02x; size: 0x%02x", data[2], data[3], data[4]); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); + prov_fail(PROV_ERR_NVAL_FMT); } } -#endif static void send_confirm(void) { @@ -894,7 +533,7 @@ static void send_confirm(void) if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) { BT_ERR("Unable to generate confirmation salt"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -902,7 +541,7 @@ static void send_confirm(void) if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) { BT_ERR("Unable to generate confirmation key"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -910,7 +549,7 @@ static void send_confirm(void) if (bt_rand(link.rand, 16)) { BT_ERR("Unable to generate random number"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -921,11 +560,11 @@ static void send_confirm(void) if (bt_mesh_prov_conf(link.conf_key, link.rand, link.auth, net_buf_simple_add(cfm, 16))) { BT_ERR("Unable to generate confirmation value"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } - if (prov_send(cfm)) { + if (prov_send(cfm, NULL)) { BT_ERR("Failed to send Provisioning Confirm"); goto done; } @@ -945,7 +584,7 @@ static void send_input_complete(void) struct os_mbuf *buf = PROV_BUF(1); prov_buf_init(buf, PROV_INPUT_COMPLETE); - if (prov_send(buf)) { + if (prov_send(buf, NULL)) { BT_ERR("Failed to send Provisioning Input Complete"); } link.expect = PROV_CONFIRM; @@ -991,7 +630,7 @@ static void send_pub_key(void) key = bt_pub_key_get(); if (!key) { BT_ERR("No public key available"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1011,7 +650,7 @@ static void send_pub_key(void) memcpy(&link.conf_inputs[81], &buf->om_data[1], 64); } - if (prov_send(buf)) { + if (prov_send(buf, NULL)) { BT_ERR("Failed to send Public Key"); goto done; } @@ -1037,7 +676,7 @@ static void prov_dh_key_cb(const u8_t dhkey[32]) if (!dhkey) { BT_ERR("DHKey generation failed"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); return; } @@ -1071,11 +710,10 @@ static void prov_dh_key_gen(void) if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) { BT_ERR("Failed to generate DHKey"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); } } -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) static void prov_pub_key(const u8_t *data) { BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); @@ -1084,19 +722,15 @@ static void prov_pub_key(const u8_t *data) /* PublicKeyDevice */ memcpy(&link.conf_inputs[81], data, 64); -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) - prov_clear_tx(); -#endif + link.bearer->clear_tx(); } else { /* PublicKeyProvisioner */ memcpy(&link.conf_inputs[17], data, 64); if (!bt_pub_key_get()) { /* Clear retransmit timer */ -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) - prov_clear_tx(); -#endif + link.bearer->clear_tx(); atomic_set_bit(link.flags, WAIT_PUB_KEY); BT_WARN("Waiting for local public key"); return; @@ -1105,7 +739,7 @@ static void prov_pub_key(const u8_t *data) prov_dh_key_gen(); } -#endif + static void pub_key_ready(const u8_t *pkey) { @@ -1125,7 +759,6 @@ static void pub_key_ready(const u8_t *pkey) } } -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) static void notify_input_complete(void) { if (atomic_test_and_clear_bit(link.flags, NOTIFY_INPUT_COMPLETE) && @@ -1151,7 +784,7 @@ static void send_prov_data(void) err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); if (err) { BT_ERR("Unable to generate session key"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1160,7 +793,7 @@ static void send_prov_data(void) err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); if (err) { BT_ERR("Unable to generate session nonce"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1170,7 +803,7 @@ static void send_prov_data(void) link.provisioner->node->dev_key); if (err) { BT_ERR("Unable to generate device key"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1180,7 +813,7 @@ static void send_prov_data(void) if (sub == NULL) { BT_ERR("No subnet with net_idx %u", link.provisioner->node->net_idx); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1200,11 +833,11 @@ static void send_prov_data(void) &pdu->om_data[1]); if (err) { BT_ERR("Unable to encrypt provisioning data"); - prov_send_fail_msg(PROV_ERR_DECRYPT); + prov_fail(PROV_ERR_DECRYPT); goto done; } - if (prov_send(pdu)) { + if (prov_send(pdu, NULL)) { BT_ERR("Failed to send Provisioning Data"); goto done; } @@ -1222,9 +855,6 @@ static void prov_complete(const u8_t *data) } struct bt_mesh_cdb_node *node = link.provisioner->node; -#if MYNEWT_VAL(BLE_MESH_PB_ADV) - u8_t reason = CLOSE_REASON_SUCCESS; -#endif BT_DBG("key %s, net_idx %u, num_elem %u, addr 0x%04x", bt_hex(node->dev_key, 16), node->net_idx, node->num_elem, @@ -1235,24 +865,11 @@ static void prov_complete(const u8_t *data) } link.provisioner->node = NULL; - link.expect = PROV_NO_PDU; - atomic_set_bit(link.flags, LINK_CLOSING); - -#if MYNEWT_VAL(BLE_MESH_PB_ADV) - bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); -#endif - + prov_link_close(PROV_BEARER_LINK_STATUS_SUCCESS); if (prov->node_added) { prov->node_added(node->net_idx, node->uuid, node->addr, node->num_elem); } - - /* - * According to mesh profile spec (5.3.1.4.3), the close message should - * be restransmitted at least three times. Retransmit the LINK_CLOSE - * message until CLOSING_TIMEOUT has elapsed instead of resetting the - * link here. - */ } static void send_random(void) @@ -1262,7 +879,7 @@ static void send_random(void) prov_buf_init(rnd, PROV_RANDOM); net_buf_simple_add_mem(rnd, link.rand, 16); - if (prov_send(rnd)) { + if (prov_send(rnd, NULL)) { BT_ERR("Failed to send Provisioning Random"); goto done; } @@ -1286,7 +903,7 @@ static void prov_random(const u8_t *data) if (bt_mesh_prov_conf(link.conf_key, data, link.auth, conf_verify)) { BT_ERR("Unable to calculate confirmation verification"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); return; } @@ -1294,7 +911,7 @@ static void prov_random(const u8_t *data) BT_ERR("Invalid confirmation value"); BT_DBG("Received: %s", bt_hex(link.conf, 16)); BT_DBG("Calculated: %s", bt_hex(conf_verify, 16)); - prov_send_fail_msg(PROV_ERR_CFM_FAILED); + prov_fail(PROV_ERR_CFM_FAILED); return; } @@ -1309,7 +926,7 @@ static void prov_random(const u8_t *data) if (bt_mesh_prov_salt(link.conf_salt, prov_rand, dev_rand, link.prov_salt)) { BT_ERR("Failed to generate provisioning salt"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); return; } @@ -1322,9 +939,7 @@ static void prov_random(const u8_t *data) send_random(); } } -#endif -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) static void prov_confirm(const u8_t *data) { BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); @@ -1339,18 +954,12 @@ static void prov_confirm(const u8_t *data) send_confirm(); } } -#endif static inline bool is_pb_gatt(void) { -#if MYNEWT_VAL(BLE_MESH_PB_GATT) - return (link.conn_handle != BLE_HS_CONN_HANDLE_NONE); -#else - return false; -#endif + return link.bearer && link.bearer->type == BT_MESH_PROV_GATT; } -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) static void prov_data(const u8_t *data) { struct os_mbuf *msg = PROV_BUF(1); @@ -1370,7 +979,7 @@ static void prov_data(const u8_t *data) err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); if (err) { BT_ERR("Unable to generate session key"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1379,7 +988,7 @@ static void prov_data(const u8_t *data) err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); if (err) { BT_ERR("Unable to generate session nonce"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1388,14 +997,14 @@ static void prov_data(const u8_t *data) err = bt_mesh_prov_decrypt(session_key, nonce, data, pdu); if (err) { BT_ERR("Unable to decrypt provisioning data"); - prov_send_fail_msg(PROV_ERR_DECRYPT); + prov_fail(PROV_ERR_DECRYPT); goto done; } err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key); if (err) { BT_ERR("Unable to generate device key"); - prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + prov_fail(PROV_ERR_UNEXP_ERR); goto done; } @@ -1410,7 +1019,7 @@ static void prov_data(const u8_t *data) net_idx, (unsigned) iv_index, addr); prov_buf_init(msg, PROV_COMPLETE); - if (prov_send(msg)) { + if (prov_send(msg, NULL)) { BT_ERR("Failed to send Provisioning Complete"); goto done; } @@ -1441,12 +1050,11 @@ static void prov_data(const u8_t *data) done: os_mbuf_free_chain(msg); } -#endif -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) static void prov_failed(const u8_t *data) { BT_WARN("Error: 0x%02x", data[0]); + reset_state(); } static const struct { @@ -1464,483 +1072,103 @@ static const struct { { prov_complete, 0 }, { prov_failed, 1 }, }; -#endif -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) -static void prov_retransmit(struct ble_npl_event *work) +static void prov_recv(const struct prov_bearer *bearer, void *cb_data, + struct os_mbuf *buf) { - int i, timeout; + u8_t type = buf->om_data[0]; + BT_DBG("type 0x%02x len %u", type, buf->om_len); - BT_DBG(""); - - if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { - BT_WARN("Link not active"); - return; - } - - if (atomic_test_bit(link.flags, LINK_CLOSING)) { - timeout = CLOSING_TIMEOUT; - } else { - timeout = TRANSACTION_TIMEOUT; - } - - if (k_uptime_get() - link.tx.start > timeout) { - BT_WARN("Giving up transaction"); - reset_adv_link(); - return; - } - - for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { - struct os_mbuf *buf = link.tx.buf[i]; - - if (!buf) { - break; - } - - if (BT_MESH_ADV(buf)->busy) { - continue; - } - - BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); - - if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { - bt_mesh_adv_send(buf, NULL, NULL); - } else { - bt_mesh_adv_send(buf, &buf_sent_cb, NULL); - } - - } -} - -static void link_open(struct prov_rx *rx, struct os_mbuf *buf) -{ - BT_DBG("link open: len %u", buf->om_len); - - if (buf->om_len < 16) { - BT_ERR("Too short bearer open message (len %u)", buf->om_len); - return; - } - - if (atomic_test_bit(link.flags, LINK_ACTIVE)) { - /* Send another link ack if the provisioner missed the last */ - if (link.id == rx->link_id) { - BT_DBG("Resending link ack"); - bearer_ctl_send(LINK_ACK, NULL, 0); - } else { - BT_WARN("Ignoring bearer open: link already active"); - } - - return; - } - - if (memcmp(buf->om_data, prov->uuid, 16)) { - BT_DBG("Bearer open message not for us"); - return; - } - - if (prov->link_open) { - prov->link_open(BT_MESH_PROV_ADV); - } - - link.id = rx->link_id; - atomic_set_bit(link.flags, LINK_ACTIVE); - net_buf_simple_init(link.rx.buf, 0); - - bearer_ctl_send(LINK_ACK, NULL, 0); - - link.expect = PROV_INVITE; -} - -static void link_ack(struct prov_rx *rx, struct os_mbuf *buf) -{ - BT_DBG("Link ack: len %u", buf->om_len); - - if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && - atomic_test_bit(link.flags, PROVISIONER)) { - if (atomic_test_and_set_bit(link.flags, LINK_ACK_RECVD)) { - return; - } - - prov_clear_tx(); - - if (prov->link_open) { - prov->link_open(BT_MESH_PROV_ADV); - } - - send_invite(); - } -} - -static void link_close(struct prov_rx *rx, struct os_mbuf *buf) -{ - BT_DBG("Link close: len %u", buf->om_len); - - reset_adv_link(); -} - -static void gen_prov_ctl(struct prov_rx *rx, struct os_mbuf *buf) -{ - BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->om_len); - - switch (BEARER_CTL(rx->gpc)) { - case LINK_OPEN: - link_open(rx, buf); - break; - case LINK_ACK: - if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { - return; - } - - link_ack(rx, buf); - break; - case LINK_CLOSE: - if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { - return; - } - - link_close(rx, buf); - break; - default: - BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); - - if (IS_ENABLED(CONFIG_BT_TESTING)) { - bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc)); - } - - return; - } -} - -static void prov_msg_recv(void) -{ - u8_t type = link.rx.buf->om_data[0]; - - BT_DBG("type 0x%02x len %u", type, link.rx.buf->om_len); - - k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); - - if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { - BT_ERR("Incorrect FCS"); - return; - } - - gen_prov_ack_send(link.rx.id); - link.rx.prev_id = link.rx.id; - link.rx.id = 0; - - if (atomic_test_bit(link.flags, LINK_INVALID)) { - BT_WARN("Unexpected msg 0x%02x on invalidated link", type); - prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_fail(PROV_ERR_UNEXP_PDU); return; } if (type >= ARRAY_SIZE(prov_handlers)) { BT_ERR("Unknown provisioning PDU type 0x%02x", type); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); + prov_fail(PROV_ERR_NVAL_FMT); return; } - if (type != PROV_FAILED && type != link.expect) { - BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); - prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + if (1 + prov_handlers[type].len != buf->om_len) { + BT_ERR("Invalid length %u for type 0x%02x", buf->om_len, type); + prov_fail(PROV_ERR_NVAL_FMT); return; } - if (1 + prov_handlers[type].len != link.rx.buf->om_len) { - BT_ERR("Invalid length %u for type 0x%02x", - link.rx.buf->om_len, type); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); - return; - } - - prov_handlers[type].func(&link.rx.buf->om_data[1]); + prov_handlers[type].func(&buf->om_data[1]); } -static void gen_prov_cont(struct prov_rx *rx, struct os_mbuf *buf) +static void prov_link_opened(const struct prov_bearer *bearer, void *cb_data) { - u8_t seg = CONT_SEG_INDEX(rx->gpc); + atomic_set_bit(link.flags, LINK_ACTIVE); - BT_DBG("len %u, seg_index %u", buf->om_len, seg); - - if (!link.rx.seg && link.rx.prev_id == rx->xact_id) { - BT_WARN("Resending ack"); - gen_prov_ack_send(rx->xact_id); - return; + if (prov->link_open) { + prov->link_open(bearer->type); } - if (rx->xact_id != link.rx.id) { - BT_WARN("Data for unknown transaction (%u != %u)", - rx->xact_id, link.rx.id); - return; - } + link.bearer = bearer; - if (seg > link.rx.last_seg) { - BT_ERR("Invalid segment index %u", seg); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); - return; - } else if (seg == link.rx.last_seg) { - u8_t expect_len; - - expect_len = (link.rx.buf->om_len - 20 - - ((link.rx.last_seg - 1) * 23)); - if (expect_len != buf->om_len) { - BT_ERR("Incorrect last seg len: %u != %u", - expect_len, buf->om_len); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); - return; - } - } - - if (!(link.rx.seg & BIT(seg))) { - BT_WARN("Ignoring already received segment"); - return; - } - - memcpy(XACT_SEG_DATA(seg), buf->om_data, buf->om_len); - XACT_SEG_RECV(seg); - - if (!link.rx.seg) { - prov_msg_recv(); + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_invite(); + } else { + link.expect = PROV_INVITE; } } -static void gen_prov_ack(struct prov_rx *rx, struct os_mbuf *buf) +static void prov_link_closed(const struct prov_bearer *bearer, void *cb_data, + enum prov_bearer_link_status reason) { - BT_DBG("len %u", buf->om_len); + /* Reset state before calling link_close, so a new provisioning + * procedure can be started from the callback. + */ + reset_state(); - if (!link.tx.buf[0]) { - return; - } - - if (rx->xact_id == link.tx.id) { - /* Don't clear resending of LINK_CLOSE messages */ - if (!atomic_test_bit(link.flags, LINK_CLOSING)) { - prov_clear_tx(); - } - - /* Send the PubKey when the the Start message is ACK'ed */ - if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && - atomic_test_and_clear_bit(link.flags, SEND_PUB_KEY)) { - if (!bt_pub_key_get()) { - atomic_set_bit(link.flags, WAIT_PUB_KEY); - BT_WARN("Waiting for local public key"); - } else { - send_pub_key(); - } - } + if (prov->link_close) { + prov->link_close(bearer->type); } } -static void gen_prov_start(struct prov_rx *rx, struct os_mbuf *buf) +static void prov_bearer_error(const struct prov_bearer *bearer, void *cb_data, + u8_t err) { - u16_t trailing_space = 0; - - if (link.rx.seg) { - BT_WARN("Got Start while there are unreceived segments"); - return; - } - - if (link.rx.prev_id == rx->xact_id) { - BT_WARN("Resending ack"); - gen_prov_ack_send(rx->xact_id); - return; - } - - trailing_space = OS_MBUF_TRAILINGSPACE(link.rx.buf); - - link.rx.buf->om_len = net_buf_simple_pull_be16(buf); - link.rx.id = rx->xact_id; - link.rx.fcs = net_buf_simple_pull_u8(buf); - - BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->om_len, - START_LAST_SEG(rx->gpc), link.rx.buf->om_len, link.rx.fcs); - - if (link.rx.buf->om_len < 1) { - BT_ERR("Ignoring zero-length provisioning PDU"); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); - return; - } - - if (link.rx.buf->om_len > trailing_space) { - BT_ERR("Too large provisioning PDU (%u bytes)", - link.rx.buf->om_len); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); - return; - } - - if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->om_len <= 20) { - BT_ERR("Too small total length for multi-segment PDU"); - prov_send_fail_msg(PROV_ERR_NVAL_FMT); - return; - } - - link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; - link.rx.last_seg = START_LAST_SEG(rx->gpc); - memcpy(link.rx.buf->om_data, buf->om_data, buf->om_len); - XACT_SEG_RECV(0); - - if (!link.rx.seg) { - prov_msg_recv(); - } + prov_fail(err); } -static const struct { - void (*func)(struct prov_rx *rx, struct os_mbuf *buf); - bool require_link; - u8_t min_len; -} gen_prov[] = { - { gen_prov_start, true, 3 }, - { gen_prov_ack, true, 0 }, - { gen_prov_cont, true, 0 }, - { gen_prov_ctl, false, 0 }, +static const struct prov_bearer_cb prov_bearer_cb = { + .link_opened = prov_link_opened, + .link_closed = prov_link_closed, + .error = prov_bearer_error, + .recv = prov_recv, }; -static void gen_prov_recv(struct prov_rx *rx, struct os_mbuf *buf) -{ - if (buf->om_len < gen_prov[GPCF(rx->gpc)].min_len) { - BT_ERR("Too short GPC message type %u", GPCF(rx->gpc)); - return; - } - - if (!atomic_test_bit(link.flags, LINK_ACTIVE) && - gen_prov[GPCF(rx->gpc)].require_link) { - BT_DBG("Ignoring message that requires active link"); - return; - } - - BT_DBG("prov_action: %d", GPCF(rx->gpc)); - gen_prov[GPCF(rx->gpc)].func(rx, buf); -} - -void bt_mesh_pb_adv_recv(struct os_mbuf *buf) -{ - struct prov_rx rx; - - if (!bt_prov_active() && bt_mesh_is_provisioned()) { - BT_DBG("Ignoring provisioning PDU - already provisioned"); - return; - } - - if (buf->om_len < 6) { - BT_WARN("Too short provisioning packet (len %u)", buf->om_len); - return; - } - - rx.link_id = net_buf_simple_pull_be32(buf); - rx.xact_id = net_buf_simple_pull_u8(buf); - rx.gpc = net_buf_simple_pull_u8(buf); - - BT_DBG("link_id 0x%08x xact_id %u", (unsigned) rx.link_id, rx.xact_id); - - if (atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) { - BT_DBG("Ignoring mesh beacon for unknown link"); - return; - } - - gen_prov_recv(&rx, buf); -} - +#if defined(CONFIG_BT_MESH_PB_ADV) int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, u8_t attention_duration) { - BT_DBG("uuid %s", bt_hex(uuid, 16)); + int err; if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { return -EBUSY; } atomic_set_bit(link.flags, PROVISIONER); - - bt_rand(&link.id, sizeof(link.id)); - link.tx.id = 0x7F; memcpy(link.provisioner->uuid, uuid, 16); link.provisioner->addr = addr; link.provisioner->net_idx = net_idx; link.provisioner->attention_duration = attention_duration; + link.bearer = &pb_adv; - net_buf_simple_init(link.rx.buf, 0); + err = link.bearer->link_open(link.provisioner->uuid, PROTOCOL_TIMEOUT, + &prov_bearer_cb, NULL); + if (err) { + atomic_clear_bit(link.flags, LINK_ACTIVE); + } - bearer_ctl_send(LINK_OPEN, uuid, 16); - - return 0; + return err; } - -#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ - -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) -int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf) -{ - u8_t type; - - BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); - - if (link.conn_handle != conn_handle) { - BT_WARN("Data for unexpected connection"); - return -ENOTCONN; - } - - if (buf->om_len < 1) { - BT_WARN("Too short provisioning packet (len %u)", buf->om_len); - return -EINVAL; - } - - type = net_buf_simple_pull_u8(buf); - if (type != PROV_FAILED && type != link.expect) { - BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); - prov_send_fail_msg(PROV_ERR_UNEXP_PDU); - return -EINVAL; - } - - if (type >= ARRAY_SIZE(prov_handlers)) { - BT_ERR("Unknown provisioning PDU type 0x%02x", type); - return -EINVAL; - } - - if (prov_handlers[type].len != buf->om_len) { - BT_ERR("Invalid length %u for type 0x%02x", buf->om_len, type); - return -EINVAL; - } - - prov_handlers[type].func(buf->om_data); - - return 0; -} - -int bt_mesh_pb_gatt_open(uint16_t conn_handle) -{ - BT_DBG("conn_handle %d", conn_handle); - - if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { - BT_ERR("Link already opened?"); - return -EBUSY; - } - - link.conn_handle = conn_handle; - link.expect = PROV_INVITE; - - if (prov->link_open) { - prov->link_open(BT_MESH_PROV_GATT); - } - - return 0; -} - -int bt_mesh_pb_gatt_close(uint16_t conn_handle) -{ - BT_DBG("conn_handle %d", conn_handle); - - if (link.conn_handle != conn_handle) { - BT_ERR("Not connected"); - return -ENOTCONN; - } - - if (prov->link_close) { - prov->link_close(BT_MESH_PROV_GATT); - } - - return reset_state(); -} -#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ +#endif const struct bt_mesh_prov *bt_mesh_prov_get(void) { @@ -1952,27 +1180,6 @@ bool bt_prov_active(void) return atomic_test_bit(link.flags, LINK_ACTIVE); } -static void protocol_timeout(struct ble_npl_event *work) -{ - BT_DBG("Protocol timeout"); - -#if MYNEWT_VAL(BLE_MESH_PB_GATT) - if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) { - bt_mesh_pb_gatt_close(link.conn_handle); - return; - } -#endif - -#if MYNEWT_VAL(BLE_MESH_PB_ADV) - u8_t reason = CLOSE_REASON_TIMEOUT; - - link.rx.seg = 0U; - bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); - - reset_state(); -#endif -} - int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info) { if (!prov_info) { @@ -1980,27 +1187,63 @@ int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info) return -EINVAL; } - k_delayed_work_init(&link.prot_timer, protocol_timeout); - prov = prov_info; -#if MYNEWT_VAL(BLE_MESH_PB_ADV) - k_delayed_work_init(&link.tx.retransmit, prov_retransmit); -#endif + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) { + pb_adv_init(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) { + pb_gatt_init(); + } return reset_state(); } -void bt_mesh_prov_reset_link(void) +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers) { -#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) -#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) - link.rx.buf = bt_mesh_proxy_get_buf(); -#else - net_buf_simple_init(rx_buf, 0); - link.rx.buf = rx_buf; -#endif -#endif + char uuid_buf[BLE_UUID_STR_LEN]; + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + ble_uuid_t *uuid = BLE_UUID128_DECLARE(); + + memcpy(BLE_UUID128(uuid)->value, prov->uuid, 16); + BT_INFO("Device UUID: %s", ble_uuid_to_str(uuid, uuid_buf)); + + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && + (bearers & BT_MESH_PROV_ADV)) { + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + bt_mesh_beacon_enable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && + (bearers & BT_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_enable(); + bt_mesh_adv_update(); + } + + return 0; +} + +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && + (bearers & BT_MESH_PROV_ADV)) { + bt_mesh_beacon_disable(); + bt_mesh_scan_disable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && + (bearers & BT_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_disable(true); + } + + return 0; } void bt_mesh_prov_complete(u16_t net_idx, u16_t addr) @@ -2016,5 +1259,3 @@ void bt_mesh_prov_reset(void) prov->reset(); } } - -#endif /* MYNEWT_VAL(BLE_MESH_PROV) */ diff --git a/nimble/host/mesh/src/prov.h b/nimble/host/mesh/src/prov.h index 96e5a447c..22560a749 100644 --- a/nimble/host/mesh/src/prov.h +++ b/nimble/host/mesh/src/prov.h @@ -13,6 +13,16 @@ #include "mesh/mesh.h" #include "../src/ble_hs_conn_priv.h" +#define PROV_ERR_NONE 0x00 +#define PROV_ERR_NVAL_PDU 0x01 +#define PROV_ERR_NVAL_FMT 0x02 +#define PROV_ERR_UNEXP_PDU 0x03 +#define PROV_ERR_CFM_FAILED 0x04 +#define PROV_ERR_RESOURCES 0x05 +#define PROV_ERR_DECRYPT 0x06 +#define PROV_ERR_UNEXP_ERR 0x07 +#define PROV_ERR_ADDR 0x08 + int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, u8_t attention_duration); diff --git a/nimble/host/mesh/src/prov_bearer.h b/nimble/host/mesh/src/prov_bearer.h new file mode 100644 index 000000000..699c3880b --- /dev/null +++ b/nimble/host/mesh/src/prov_bearer.h @@ -0,0 +1,113 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define PROTOCOL_TIMEOUT K_SECONDS(60) + +/** @def PROV_BEARER_BUF_HEADROOM + * + * @brief Required headroom for the bearer packet buffers. + */ +#if defined(CONFIG_BT_MESH_PB_GATT) +#define PROV_BEARER_BUF_HEADROOM 5 +#else +#define PROV_BEARER_BUF_HEADROOM 0 +#endif + +enum prov_bearer_link_status { + PROV_BEARER_LINK_STATUS_SUCCESS, + PROV_BEARER_LINK_STATUS_TIMEOUT, + PROV_BEARER_LINK_STATUS_FAIL, +}; + +struct prov_bearer; + +/** Callbacks from bearer to host */ +struct prov_bearer_cb { + + void (*link_opened)(const struct prov_bearer *bearer, void *cb_data); + + void (*link_closed)(const struct prov_bearer *bearer, void *cb_data, + enum prov_bearer_link_status reason); + + void (*error)(const struct prov_bearer *bearer, void *cb_data, + u8_t err); + + void (*recv)(const struct prov_bearer *bearer, void *cb_data, + struct os_mbuf *buf); +}; + +typedef void (*prov_bearer_send_complete_t)(int err, void *cb_data); + +/** Provisioning bearer API */ +struct prov_bearer { + /** Provisioning bearer type. */ + bt_mesh_prov_bearer_t type; + + /** @brief Enable link establishment as a provisionee. + * + * Prompts the bearer to make itself visible to provisioners, and + * start accepting link open messages. + * + * @param cb Bearer event callbacks used for the duration of the link. + * @param cb_data Context parameter to pass to the bearer callbacks. + * + * @return Zero on success, or (negative) error code otherwise. + */ + int (*link_accept)(const struct prov_bearer_cb *cb, void *cb_data); + + /** @brief Send a packet on an established link. + * + * @param buf Payload buffer. Requires @ref + * PROV_BEARER_BUF_HEADROOM bytes of headroom. + * @param cb Callback to call when sending is complete. + * @param cb_data Callback data. + * + * @return Zero on success, or (negative) error code otherwise. + */ + int (*send)(struct os_mbuf *buf, prov_bearer_send_complete_t cb, + void *cb_data); + + /** @brief Clear any ongoing transmissions, if possible. + * + * Bearers that don't support tx clearing must implement this callback + * and leave it empty. + */ + void (*clear_tx)(void); + + /* Only available in provisioners: */ + + /** @brief Open a new link as a provisioner. + * + * Only available in provisioners. Bearers that don't support the + * provisioner role should leave this as NULL. + * + * @param uuid UUID of the node to establish a link to. + * @param timeout Protocol timeout. + * @param cb Bearer event callbacks used for the duration of the link. + * @param cb_data Context parameter to pass to the bearer callbacks. + * + * @return Zero on success, or (negative) error code otherwise. + */ + int (*link_open)(const u8_t uuid[16], s32_t timeout, + const struct prov_bearer_cb *cb, void *cb_data); + + /** @brief Close the current link. + * + * Only available in provisioners. Bearers that don't support the + * provisioner role should leave this as NULL. + * + * @param status Link status for the link close message. + */ + void (*link_close)(enum prov_bearer_link_status status); +}; + +extern const struct prov_bearer pb_adv; +extern const struct prov_bearer pb_gatt; + +void pb_adv_init(void); +void pb_gatt_init(void); \ No newline at end of file