Skip to content
Permalink
Browse files

Bluetooth: Mesh: Fix missing protocol error timeout

Provisioning errors shouldn't cause device to close link. Upon error,
device will send Provisioning Failed PDU, and any further PDU's will
be considered as unexpected as per Mesh Profile section 5.4.4.

Also a timer is started every time device sends or receives a PDU.
This affects MESH/NODE/PROV/BV-10-C test case.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
  • Loading branch information...
jhedberg committed Jun 25, 2019
1 parent abaeaed commit 8de784a09d64292bea1795c1154c70441c63c7b3
Showing with 71 additions and 50 deletions.
  1. +71 −50 subsys/bluetooth/host/mesh/prov.c
@@ -109,6 +109,7 @@ enum {
SEND_CONFIRM, /* Waiting to send Confirm value */
WAIT_NUMBER, /* Waiting for number input from user */
WAIT_STRING, /* Waiting for string input from user */
LINK_INVALID, /* Error occurred during provisioning */

NUM_FLAGS,
};
@@ -160,6 +161,8 @@ struct prov_link {
struct k_delayed_work retransmit;
} tx;
#endif

struct k_delayed_work prot_timer;
};

struct prov_rx {
@@ -171,6 +174,7 @@ struct prov_rx {
#define RETRANSMIT_TIMEOUT K_MSEC(500)
#define BUF_TIMEOUT K_MSEC(400)
#define TRANSACTION_TIMEOUT K_SECONDS(30)
#define PROTOCOL_TIMEOUT K_SECONDS(60)

#if defined(CONFIG_BT_MESH_PB_GATT)
#define PROV_BUF_HEADROOM 5
@@ -186,10 +190,10 @@ static struct prov_link link;

static const struct bt_mesh_prov *prov;

static void close_link(u8_t err, u8_t reason);

static void reset_state(void)
{
k_delayed_work_cancel(&link.prot_timer);

/* Disable Attention Timer if it was set */
if (link.conf_inputs[0]) {
bt_mesh_attention(NULL, 0);
@@ -202,7 +206,9 @@ static void reset_state(void)
#endif

#if defined(CONFIG_BT_MESH_PB_ADV)
/* Clear everything except the retransmit delayed work config */
/* 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;

@@ -213,8 +219,9 @@ static void reset_state(void)
link.rx.buf = &rx_buf;
#endif /* PB_GATT */

#else
(void)memset(&link, 0, sizeof(link));
#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 */
}

@@ -471,6 +478,8 @@ static int prov_send_gatt(struct net_buf_simple *msg)

static inline int prov_send(struct net_buf_simple *buf)
{
k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);

#if defined(CONFIG_BT_MESH_PB_GATT)
if (link.conn) {
return prov_send_gatt(buf);
@@ -496,6 +505,8 @@ static void prov_send_fail_msg(u8_t err)
prov_buf_init(&buf, PROV_FAILED);
net_buf_simple_add_u8(&buf, err);
prov_send(&buf);

atomic_set_bit(link.flags, LINK_INVALID);
}

static void prov_invite(const u8_t *data)
@@ -540,7 +551,7 @@ static void prov_invite(const u8_t *data)

if (prov_send(&buf)) {
BT_ERR("Failed to send capabilities");
close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_RESOURCES);
return;
}

@@ -746,23 +757,23 @@ static void send_confirm(void)

if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) {
BT_ERR("Unable to generate confirmation salt");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

BT_DBG("ConfirmationSalt: %s", bt_hex(link.conf_salt, 16));

if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) {
BT_ERR("Unable to generate confirmation key");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

BT_DBG("ConfirmationKey: %s", bt_hex(link.conf_key, 16));

if (bt_rand(link.rand, 16)) {
BT_ERR("Unable to generate random number");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -773,13 +784,13 @@ 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");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

if (prov_send(&cfm)) {
BT_ERR("Failed to send Provisioning Confirm");
close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_RESOURCES);
return;
}

@@ -846,7 +857,7 @@ static void prov_dh_key_cb(const u8_t key[32])

if (!key) {
BT_ERR("DHKey generation failed");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -874,7 +885,7 @@ static void send_pub_key(void)
key = bt_pub_key_get();
if (!key) {
BT_ERR("No public key available");
close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -899,7 +910,7 @@ static void send_pub_key(void)

if (bt_dh_key_gen(buf.data, prov_dh_key_cb)) {
BT_ERR("Failed to generate DHKey");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -969,15 +980,15 @@ 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");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

if (memcmp(conf_verify, link.conf, 16)) {
BT_ERR("Invalid confirmation value");
BT_DBG("Received: %s", bt_hex(link.conf, 16));
BT_DBG("Calculated: %s", bt_hex(conf_verify, 16));
close_link(PROV_ERR_CFM_FAILED, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_CFM_FAILED);
return;
}

@@ -986,14 +997,14 @@ static void prov_random(const u8_t *data)

if (prov_send(&rnd)) {
BT_ERR("Failed to send Provisioning Random");
close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_RESOURCES);
return;
}

if (bt_mesh_prov_salt(link.conf_salt, data, link.rand,
link.prov_salt)) {
BT_ERR("Failed to generate provisioning salt");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -1030,7 +1041,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");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -1039,7 +1050,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");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -1048,14 +1059,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");
close_link(PROV_ERR_DECRYPT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_DECRYPT);
return;
}

err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key);
if (err) {
BT_ERR("Unable to generate device key");
close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
return;
}

@@ -1122,27 +1133,6 @@ static const struct {
{ prov_failed, 1 },
};

static void close_link(u8_t err, u8_t reason)
{
#if defined(CONFIG_BT_MESH_PB_GATT)
if (link.conn) {
bt_mesh_pb_gatt_close(link.conn);
return;
}
#endif

#if defined(CONFIG_BT_MESH_PB_ADV)
if (err) {
prov_send_fail_msg(err);
}

link.rx.seg = 0U;
bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason));
#endif

reset_state();
}

#if defined(CONFIG_BT_MESH_PB_ADV)
static void prov_retransmit(struct k_work *work)
{
@@ -1273,6 +1263,8 @@ static void prov_msg_recv(void)

BT_DBG("type 0x%02x len %u", type, link.rx.buf->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;
@@ -1282,6 +1274,12 @@ static void prov_msg_recv(void)
link.rx.prev_id = link.rx.id;
link.rx.id = 0U;

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);
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);
@@ -1290,14 +1288,14 @@ static void prov_msg_recv(void)

if (type >= ARRAY_SIZE(prov_handlers)) {
BT_ERR("Unknown provisioning PDU type 0x%02x", type);
close_link(PROV_ERR_NVAL_PDU, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_PDU);
return;
}

if (1 + prov_handlers[type].len != link.rx.buf->len) {
BT_ERR("Invalid length %u for type 0x%02x",
link.rx.buf->len, type);
close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_FMT);
return;
}

@@ -1324,7 +1322,7 @@ static void gen_prov_cont(struct prov_rx *rx, struct net_buf_simple *buf)

if (seg > link.rx.last_seg) {
BT_ERR("Invalid segment index %u", seg);
close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_FMT);
return;
} else if (seg == link.rx.last_seg) {
u8_t expect_len;
@@ -1334,7 +1332,7 @@ static void gen_prov_cont(struct prov_rx *rx, struct net_buf_simple *buf)
if (expect_len != buf->len) {
BT_ERR("Incorrect last seg len: %u != %u",
expect_len, buf->len);
close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_FMT);
return;
}
}
@@ -1387,20 +1385,20 @@ static void gen_prov_start(struct prov_rx *rx, struct net_buf_simple *buf)

if (link.rx.buf->len < 1) {
BT_ERR("Ignoring zero-length provisioning PDU");
close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_FMT);
return;
}

if (link.rx.buf->len > link.rx.buf->size) {
BT_ERR("Too large provisioning PDU (%u bytes)",
link.rx.buf->len);
close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_FMT);
return;
}

if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->len <= 20U) {
BT_ERR("Too small total length for multi-segment PDU");
close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED);
prov_send_fail_msg(PROV_ERR_NVAL_FMT);
return;
}

@@ -1556,6 +1554,27 @@ bool bt_prov_active(void)
return atomic_test_bit(link.flags, LINK_ACTIVE);
}

static void protocol_timeout(struct k_work *work)
{
BT_DBG("Protocol timeout");

#if defined(CONFIG_BT_MESH_PB_GATT)
if (link.conn) {
bt_mesh_pb_gatt_close(link.conn);
return;
}
#endif

#if defined(CONFIG_BT_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)
{
static struct bt_pub_key_cb pub_key_cb = {
@@ -1568,6 +1587,8 @@ int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info)
return -EINVAL;
}

k_delayed_work_init(&link.prot_timer, protocol_timeout);

err = bt_pub_key_gen(&pub_key_cb);
if (err) {
BT_ERR("Failed to generate public key (%d)", err);

0 comments on commit 8de784a

Please sign in to comment.
You can’t perform that action at this time.