From 50a656ff16791a50b6c149aad5c83240aee8c049 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 10 Nov 2017 10:15:23 +0200 Subject: [PATCH] Bluetooth: Mesh: Implement handling of Friend Clear messages Handling Friend Clear messages and sending the Friend Clear Confirmation responses wasn't so far properly implemented. One of the requirements is to keep sending the reponses even though we no-longer have a friendship. This means that we need to keep the net_idx, frnd and lpn_counter values valid, which in turn requires the introduction of a separate "valid" boolean value. Signed-off-by: Johan Hedberg --- subsys/bluetooth/host/mesh/cfg.c | 2 +- subsys/bluetooth/host/mesh/friend.c | 51 ++++++++++++++++++++++------- subsys/bluetooth/host/mesh/friend.h | 4 +-- subsys/bluetooth/host/mesh/net.h | 1 + 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/subsys/bluetooth/host/mesh/cfg.c b/subsys/bluetooth/host/mesh/cfg.c index be30f5e9e4e..f7505effe2c 100644 --- a/subsys/bluetooth/host/mesh/cfg.c +++ b/subsys/bluetooth/host/mesh/cfg.c @@ -2526,7 +2526,7 @@ static void lpn_timeout_get(struct bt_mesh_model *model, goto send_rsp; } - frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true); + frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true, true); if (!frnd) { timeout = 0; goto send_rsp; diff --git a/subsys/bluetooth/host/mesh/friend.c b/subsys/bluetooth/host/mesh/friend.c index d51835de8c0..933509a019a 100644 --- a/subsys/bluetooth/host/mesh/friend.c +++ b/subsys/bluetooth/host/mesh/friend.c @@ -98,7 +98,7 @@ static struct net_buf *friend_buf_alloc(u16_t src) } struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, - bool established) + bool valid, bool established) { int i; @@ -107,6 +107,10 @@ struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + if (valid && !frnd->valid) { + continue; + } + if (established && !frnd->established) { continue; } @@ -164,12 +168,11 @@ static void friend_clear(struct bt_mesh_friend *frnd) } } + frnd->valid = 0; frnd->established = 0; frnd->pending_buf = 0; frnd->fsn = 0; frnd->queue_size = 0; - frnd->lpn = BT_MESH_ADDR_UNASSIGNED; - frnd->net_idx = BT_MESH_KEY_UNUSED; memset(frnd->sub_list, 0, sizeof(frnd->sub_list)); } @@ -216,6 +219,13 @@ int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) struct bt_mesh_ctl_friend_clear *msg = (void *)buf->data; struct bt_mesh_friend *frnd; u16_t lpn_addr, lpn_counter; + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear_confirm cfm; if (buf->len < sizeof(*msg)) { BT_WARN("Too short Friend Clear"); @@ -227,17 +237,32 @@ int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) BT_DBG("LPN addr 0x%04x counter 0x%04x", lpn_addr, lpn_counter); - frnd = bt_mesh_friend_find(rx->sub->net_idx, lpn_addr, true); + frnd = bt_mesh_friend_find(rx->sub->net_idx, lpn_addr, false, false); if (!frnd) { BT_WARN("No matching LPN addr 0x%04x", lpn_addr); return 0; } - if (frnd->lpn_counter != lpn_counter) { - BT_WARN("LPN Counter mismatch"); + /* A Friend Clear message is considered valid if the result of the + * subtraction of the value of the LPNCounter field of the Friend + * Request message (the one that initiated the friendship) from the + * value of the LPNCounter field of the Friend Clear message, modulo + * 65536, is in the range 0 to 255 inclusive. + */ + if (lpn_counter - frnd->lpn_counter > 255) { + BT_WARN("LPN Counter out of range (old %u new %u)", + frnd->lpn_counter, lpn_counter); return 0; } + tx.ctx->send_ttl = BT_MESH_TTL_MAX; + + cfm.lpn_addr = msg->lpn_addr; + cfm.lpn_counter = msg->lpn_counter; + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR_CFM, &cfm, + sizeof(cfm), NULL, NULL); + friend_clear(frnd); return 0; @@ -418,7 +443,7 @@ int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, return -EINVAL; } - frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true); + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); if (!frnd) { BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); return 0; @@ -455,7 +480,7 @@ int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, return -EINVAL; } - frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true); + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); if (!frnd) { BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); return 0; @@ -511,7 +536,7 @@ int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) return -EINVAL; } - frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, false); + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); if (!frnd) { BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); return 0; @@ -794,10 +819,11 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) old_friend = sys_be16_to_cpu(msg->prev_addr); if (BT_MESH_ADDR_IS_UNICAST(old_friend)) { - frnd = bt_mesh_friend_find(rx->sub->net_idx, old_friend, true); + frnd = bt_mesh_friend_find(rx->sub->net_idx, old_friend, + true, true); } else { frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, - true); + true, true); } if (frnd) { @@ -807,8 +833,9 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) } for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { - if (bt_mesh.frnd[i].lpn == BT_MESH_ADDR_UNASSIGNED) { + if (!bt_mesh.frnd[i].valid) { frnd = &bt_mesh.frnd[i]; + frnd->valid = 1; break; } } diff --git a/subsys/bluetooth/host/mesh/friend.h b/subsys/bluetooth/host/mesh/friend.h index ec4110b1ffe..1e6147f3d3e 100644 --- a/subsys/bluetooth/host/mesh/friend.h +++ b/subsys/bluetooth/host/mesh/friend.h @@ -14,8 +14,8 @@ enum bt_mesh_friend_pdu_type { bool bt_mesh_friend_match(u16_t net_idx, u16_t addr); -struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t addr, - bool established); +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established); void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, enum bt_mesh_friend_pdu_type type, diff --git a/subsys/bluetooth/host/mesh/net.h b/subsys/bluetooth/host/mesh/net.h index 118d40e1ff4..4e8bbabcf8c 100644 --- a/subsys/bluetooth/host/mesh/net.h +++ b/subsys/bluetooth/host/mesh/net.h @@ -94,6 +94,7 @@ struct bt_mesh_friend { send_last:1, sec_update:1, pending_buf:1, + valid:1, established:1; s32_t poll_to; u16_t lpn_counter;