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;