diff --git a/subsys/bluetooth/host/mesh/Kconfig b/subsys/bluetooth/host/mesh/Kconfig index 8a79523a788..39a6fe86343 100644 --- a/subsys/bluetooth/host/mesh/Kconfig +++ b/subsys/bluetooth/host/mesh/Kconfig @@ -265,16 +265,17 @@ config BT_MESH_FRIEND_RECV_WIN Receive Window in milliseconds supported by the Friend node. config BT_MESH_FRIEND_QUEUE_SIZE - int "Friend Queue Size" - range 1 BT_MESH_ADV_BUF_COUNT - default 16 + int "Minimum number of buffers supported per Friend Queue" + range 2 65536 + default 4 help - Queue Size available on the Friend node. + Minimum number of buffers available to be stored for each + local Friend Queue. config BT_MESH_FRIEND_SUB_LIST_SIZE int "Friend Subscription List Size" - range 1 255 - default 16 + range 0 1023 + default 3 help Size of the Subscription List that can be supported by a Friend node for a Low Power node. @@ -287,6 +288,16 @@ config BT_MESH_FRIEND_LPN_COUNT Number of Low Power Nodes the Friend can have a Friendship with simultaneously. +config BT_MESH_FRIEND_SEG_RX + int "Number of incomplete segment lists per LPN" + range 1 1000 + default 1 + help + Number of incomplete segment lists that we track for each LPN + that we are Friends for. In other words, this determines how + many elements we can simultaneously be receiving segmented + messages from when the messages are going into the Friend queue. + endif # BT_MESH_FRIEND config BT_MESH_DEBUG diff --git a/subsys/bluetooth/host/mesh/TODO b/subsys/bluetooth/host/mesh/TODO index 0db4f075d4e..a56176749e5 100644 --- a/subsys/bluetooth/host/mesh/TODO +++ b/subsys/bluetooth/host/mesh/TODO @@ -1,8 +1,6 @@ Bluetooth Mesh implementation tasks =================================== - * Friend support (work ongoing) - * PB-GATT (Node) * Any generally useful models from the Model Specification diff --git a/subsys/bluetooth/host/mesh/beacon.c b/subsys/bluetooth/host/mesh/beacon.c index 317f58764e2..38bf1b2459d 100644 --- a/subsys/bluetooth/host/mesh/beacon.c +++ b/subsys/bluetooth/host/mesh/beacon.c @@ -25,6 +25,7 @@ #include "crypto.h" #include "beacon.h" #include "foundation.h" +#include "friend.h" #define UNPROVISIONED_INTERVAL K_SECONDS(5) #define PROVISIONED_INTERVAL K_SECONDS(10) @@ -241,8 +242,8 @@ static void secure_beacon_recv(struct net_buf_simple *buf) u8_t *data, *net_id, *auth; struct bt_mesh_subnet *sub; u32_t iv_index; + bool new_key, kr_change, iv_change; u8_t flags; - bool new_key; if (buf->len < 21) { BT_ERR("Too short secure beacon (len %u)", buf->len); @@ -295,12 +296,21 @@ static void secure_beacon_recv(struct net_buf_simple *buf) bt_mesh_beacon_ivu_initiator(false); } - bt_mesh_iv_update(iv_index, BT_MESH_IV_UPDATE(flags)); + iv_change = bt_mesh_iv_update(iv_index, BT_MESH_IV_UPDATE(flags)); - if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key)) { + kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key); + if (kr_change) { bt_mesh_net_beacon_update(sub); } + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && (iv_change || kr_change)) { + if (iv_change) { + bt_mesh_friend_sec_update(BT_MESH_KEY_ANY); + } else { + bt_mesh_friend_sec_update(sub->net_idx); + } + } + update_stats: if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED && sub->beacons_cur < 0xff) { diff --git a/subsys/bluetooth/host/mesh/cfg.c b/subsys/bluetooth/host/mesh/cfg.c index 4034a3d9c57..b30257c5b4d 100644 --- a/subsys/bluetooth/host/mesh/cfg.c +++ b/subsys/bluetooth/host/mesh/cfg.c @@ -31,6 +31,7 @@ #include "beacon.h" #include "proxy.h" #include "foundation.h" +#include "friend.h" #define DEFAULT_TTL 7 @@ -101,7 +102,8 @@ static void hb_send(struct bt_mesh_model *model) BT_DBG("InitTTL %u feat 0x%04x", cfg->hb_pub.ttl, feat); - bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb), NULL); + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb), + NULL, NULL); } static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, @@ -2069,6 +2071,10 @@ static void net_key_del(struct bt_mesh_model *model, } } + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_clear_net_idx(del_idx); + } + memset(sub, 0, sizeof(*sub)); sub->net_idx = BT_MESH_KEY_UNUSED; @@ -2476,6 +2482,10 @@ static void friend_set(struct bt_mesh_model *model, if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { cfg->frnd = buf->data[0]; + + if (cfg->frnd == BT_MESH_FRIEND_DISABLED) { + bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY); + } } sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); diff --git a/subsys/bluetooth/host/mesh/friend.c b/subsys/bluetooth/host/mesh/friend.c index 18b896a690e..c8ab32004e1 100644 --- a/subsys/bluetooth/host/mesh/friend.c +++ b/subsys/bluetooth/host/mesh/friend.c @@ -23,190 +23,1108 @@ #include "net.h" #include "transport.h" #include "access.h" +#include "foundation.h" #include "friend.h" -static int send_friend_update(void) +#define FRIEND_BUF_SIZE (BT_MESH_ADV_DATA_SIZE - BT_MESH_NET_HDR_LEN) + +/* We reserve one extra buffer for each friendship, since we need to be able + * to resend the last sent PDU, which sits separately outside of the queue. + */ +#define FRIEND_BUF_COUNT ((CONFIG_BT_MESH_FRIEND_QUEUE_SIZE + 1) * \ + CONFIG_BT_MESH_FRIEND_LPN_COUNT) + +struct friend_pdu_info { + u16_t src; + u16_t dst; + + u8_t seq[3]; + + u8_t ttl:7, + ctl:1; + + u32_t iv_index; +}; + +NET_BUF_POOL_DEFINE(friend_buf_pool, FRIEND_BUF_COUNT, + BT_MESH_ADV_DATA_SIZE, sizeof(struct bt_mesh_adv), NULL); + +static u64_t buf_seq_auth[FRIEND_BUF_COUNT]; + +static void discard_buffer(void) { - struct bt_mesh_msg_ctx ctx = { - .net_idx = bt_mesh.sub[0].net_idx, - .app_idx = BT_MESH_KEY_UNUSED, - .addr = bt_mesh.frnd.lpn, - .send_ttl = 0, - .friend_cred = 1, - }; - struct bt_mesh_net_tx tx = { - .sub = &bt_mesh.sub[0], - .ctx = &ctx, - .src = bt_mesh_primary_addr(), - }; - struct bt_mesh_ctl_friend_update upd = { - .flags = 0, - .iv_index = sys_cpu_to_be32(bt_mesh.iv_index), - .md = !k_fifo_is_empty(&bt_mesh.frnd.queue), - }; + struct bt_mesh_friend *frnd = &bt_mesh.frnd[0]; + struct net_buf *buf; + int i; - BT_DBG(""); + /* Find the Friend context with the most queued buffers */ + for (i = 1; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (bt_mesh.frnd[i].queue_size > frnd->queue_size) { + frnd = &bt_mesh.frnd[i]; + } + } - return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_UPDATE, &upd, - sizeof(upd), NULL); + BT_WARN("Discarding buffer for LPN 0x%04x", frnd->lpn); + + buf = net_buf_slist_get(&frnd->queue); + __ASSERT_NO_MSG(buf != NULL); + net_buf_unref(buf); +} + +static struct net_buf *friend_buf_alloc(u16_t src) +{ + struct net_buf *buf; + + BT_DBG("src 0x%04x", src); + + do { + buf = bt_mesh_adv_create_from_pool(&friend_buf_pool, + BT_MESH_ADV_DATA, + 0, 0, K_NO_WAIT); + if (!buf) { + discard_buffer(); + } + } while (!buf); + + BT_MESH_ADV(buf)->addr = src; + buf_seq_auth[net_buf_id(buf)] = TRANS_SEQ_AUTH_NVAL; + + BT_DBG("allocated buf %p", buf); + + return buf; +} + +static struct bt_mesh_friend *friend_lpn_find(u16_t net_idx, u16_t addr, + bool established) +{ + int i; + + BT_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (established && !frnd->established) { + continue; + } + + if (frnd->net_idx != net_idx) { + continue; + } + + if (frnd->lpn == addr) { + return frnd; + } + } + + return NULL; +} + +/* Intentionally start a little bit late into the ReceiveWindow when + * it's large enough. This may improve reliability with some platforms, + * like the PTS, where the receiver might not have sufficiently compensated + * for internal latencies required to start scanning. + */ +static s32_t recv_delay(struct bt_mesh_friend *frnd) +{ +#if CONFIG_BT_MESH_FRIEND_RECV_WIN > 50 + return (s32_t)frnd->recv_delay + (CONFIG_BT_MESH_FRIEND_RECV_WIN / 5); +#else + return frnd->recv_delay; +#endif +} + +static void friend_clear(struct bt_mesh_friend *frnd) +{ + int i; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + k_delayed_work_cancel(&frnd->timer); + + bt_mesh_friend_cred_del(bt_mesh.sub[0].net_idx, frnd->lpn); + + if (frnd->last) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + while (!sys_slist_is_empty(&frnd->queue)) { + net_buf_unref(net_buf_slist_get(&frnd->queue)); + } + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + + while (!sys_slist_is_empty(&seg->queue)) { + net_buf_unref(net_buf_slist_get(&seg->queue)); + } + } + + 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)); +} + +void bt_mesh_friend_clear_net_idx(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) { + friend_clear(frnd); + } + } +} + +void bt_mesh_friend_sec_update(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) { + frnd->sec_update = 1; + } + } +} + +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; + + if (buf->len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear"); + return -EINVAL; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPN addr 0x%04x counter 0x%04x", lpn_addr, lpn_counter); + + frnd = friend_lpn_find(rx->sub->net_idx, lpn_addr, true); + 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"); + return 0; + } + + friend_clear(frnd); + + return 0; +} + +static void friend_sub_add(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == BT_MESH_ADDR_UNASSIGNED) { + frnd->sub_list[i] = addr; + return; + } + } + + BT_WARN("No space in friend subscription list"); +} + +static void friend_sub_rem(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + frnd->sub_list[i] = BT_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static struct net_buf *create_friend_pdu(struct bt_mesh_friend *frnd, + struct friend_pdu_info *info, + struct net_buf_simple *sdu) +{ + struct bt_mesh_subnet *sub; + const u8_t *enc, *priv; + struct net_buf *buf; + u8_t nid, key_idx; + + sub = bt_mesh_subnet_get(frnd->net_idx); + __ASSERT_NO_MSG(sub != NULL); + + buf = friend_buf_alloc(info->src); + + key_idx = (sub->kr_phase == BT_MESH_KR_PHASE_2); + + /* Friend Offer needs master security credentials */ + if (info->ctl && TRANS_CTL_OP(sdu->data) == TRANS_CTL_OP_FRIEND_OFFER) { + enc = sub->keys[key_idx].enc; + priv = sub->keys[key_idx].privacy; + nid = sub->keys[key_idx].nid; + } else { + if (bt_mesh_friend_cred_get(sub->net_idx, frnd->lpn, key_idx, + &nid, &enc, &priv)) { + BT_ERR("bt_mesh_friend_cred_get failed"); + goto failed; + } + } + + net_buf_add_u8(buf, (nid | (info->iv_index & 1) << 7)); + + if (info->ctl) { + net_buf_add_u8(buf, info->ttl | 0x80); + } else { + net_buf_add_u8(buf, info->ttl); + } + + net_buf_add_mem(buf, info->seq, sizeof(info->seq)); + + net_buf_add_be16(buf, info->src); + net_buf_add_be16(buf, info->dst); + + net_buf_add_mem(buf, sdu->data, sdu->len); + + /* We re-encrypt and obfuscate using the received IVI rather than + * the normal TX IVI (which may be different) since the transport + * layer nonce includes the IVI. + */ + if (bt_mesh_net_encrypt(enc, &buf->b, info->iv_index, false)) { + BT_ERR("Re-encrypting failed"); + goto failed; + } + + if (bt_mesh_net_obfuscate(buf->data, info->iv_index, priv)) { + BT_ERR("Re-obfuscating failed"); + goto failed; + } + + return buf; + +failed: + net_buf_unref(buf); + return NULL; +} + +static struct net_buf *encode_friend_ctl(struct bt_mesh_friend *frnd, + u8_t ctl_op, + struct net_buf_simple *sdu) +{ + struct friend_pdu_info info; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + net_buf_simple_push_u8(sdu, TRANS_CTL_HDR(ctl_op, 0)); + + info.src = bt_mesh_primary_addr(); + info.dst = frnd->lpn; + + info.ctl = 1; + info.ttl = 0; + + info.seq[0] = (bt_mesh.seq >> 16); + info.seq[1] = (bt_mesh.seq >> 8); + info.seq[2] = bt_mesh.seq++; + + info.iv_index = BT_MESH_NET_IVI_TX; + + return create_friend_pdu(frnd, &info, sdu); +} + +static struct net_buf *encode_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct bt_mesh_ctl_friend_update *upd; + struct net_buf_simple *sdu = NET_BUF_SIMPLE(1 + sizeof(*upd)); + struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx); + + __ASSERT_NO_MSG(sub != NULL); + + BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md); + + net_buf_simple_init(sdu, 1); + + upd = net_buf_simple_add(sdu, sizeof(*upd)); + upd->flags = bt_mesh_net_flags(sub); + upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index); + upd->md = md; + + return encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_UPDATE, sdu); +} + +static void enqueue_sub_cfm(struct bt_mesh_friend *frnd, u8_t xact) +{ + struct bt_mesh_ctl_friend_sub_confirm *cfm; + struct net_buf_simple *sdu = NET_BUF_SIMPLE(1 + sizeof(*cfm)); + struct net_buf *buf; + + BT_DBG("lpn 0x%04x xact 0x%02x", frnd->lpn, xact); + + net_buf_simple_init(sdu, 1); + + cfm = net_buf_simple_add(sdu, sizeof(*cfm)); + cfm->xact = xact; + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_SUB_CFM, sdu); + if (!buf) { + BT_ERR("Unable to encode Subscription List Confirmation"); + return; + } + + if (frnd->last) { + BT_DBG("Discarding last PDU"); + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1; +} + +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->len < BT_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("Too short Friend Subscription Add"); + return -EINVAL; + } + + frnd = friend_lpn_find(rx->sub->net_idx, rx->ctx.addr, true); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent!"); + return 0; + } + + k_delayed_work_submit(&frnd->timer, recv_delay(frnd)); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->len >= 2) { + friend_sub_add(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + BT_DBG("Waiting RecvDelay of %d ms", recv_delay(frnd)); + + return 0; +} + +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->len < BT_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("Too short Friend Subscription Remove"); + return -EINVAL; + } + + frnd = friend_lpn_find(rx->sub->net_idx, rx->ctx.addr, true); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent!"); + return 0; + } + + k_delayed_work_submit(&frnd->timer, recv_delay(frnd)); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->len >= 2) { + friend_sub_rem(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + BT_DBG("Waiting RecvDelay of %d ms", recv_delay(frnd)); + + return 0; +} + +static void enqueue_buf(struct bt_mesh_friend *frnd, struct net_buf *buf) +{ + net_buf_slist_put(&frnd->queue, buf); + frnd->queue_size++; +} + +static void enqueue_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct net_buf *buf; + + buf = encode_update(frnd, md); + if (!buf) { + BT_ERR("Unable to encode Friend Update"); + return; + } + + frnd->sec_update = 0; + enqueue_buf(frnd, buf); } int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) { struct bt_mesh_ctl_friend_poll *msg = (void *)buf->data; - struct bt_mesh_friend *frnd = &bt_mesh.frnd; + struct bt_mesh_friend *frnd; if (buf->len < sizeof(*msg)) { - BT_WARN("Too short Friend Update"); + BT_WARN("Too short Friend Poll"); return -EINVAL; } - BT_DBG("msg->fsn 0x%02x frnd->fsn 0x%02x", msg->fsn, frnd->fsn); - - if (msg->fsn != frnd->fsn) { - frnd->send_last = 1; + frnd = friend_lpn_find(rx->sub->net_idx, rx->ctx.addr, false); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; } - frnd->fsn++; - frnd->send_update = 1; + if (msg->fsn & ~1) { + BT_WARN("Prohibited (non-zero) padding bits"); + return -EINVAL; + } - k_delayed_work_submit(&frnd->timer, frnd->recv_delay); + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent"); + return 0; + } + + k_delayed_work_submit(&frnd->timer, recv_delay(frnd)); + + if (!frnd->established) { + BT_DBG("Friendship established with 0x%04x", frnd->lpn); + frnd->established = 1; + } + + BT_DBG("msg->fsn %u frnd->fsn %u", (msg->fsn & 1), frnd->fsn); + + if (msg->fsn == frnd->fsn && frnd->last) { + BT_DBG("Re-sending last PDU"); + frnd->send_last = 1; + } else { + frnd->fsn = msg->fsn; + + if (sys_slist_is_empty(&frnd->queue)) { + enqueue_update(frnd, 0); + BT_DBG("Enqueued Friend Update to empty queue"); + } + } + + BT_DBG("Waiting RecvDelay of %d ms", recv_delay(frnd)); return 0; } -static int send_friend_offer(s8_t rssi) +static void send_friend_clear(u16_t addr) { - struct bt_mesh_msg_ctx ctx = { - .net_idx = bt_mesh.sub[0].net_idx, - .app_idx = BT_MESH_KEY_UNUSED, - .addr = bt_mesh.frnd.lpn, - .send_ttl = 0, - }; - struct bt_mesh_net_tx tx = { - .sub = &bt_mesh.sub[0], - .ctx = &ctx, - .src = bt_mesh_primary_addr(), - }; - struct bt_mesh_ctl_friend_offer off = { - .recv_win = CONFIG_BT_MESH_FRIEND_RECV_WIN, - .queue_size = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, - .rssi = rssi, - .frnd_counter = bt_mesh.frnd.counter++, - }; + /* TODO: Send Friend Clear message to the old Friend */ + BT_WARN("Sending Friend Clear not yet implemented"); +} + +static void enqueue_offer(struct bt_mesh_friend *frnd, s8_t rssi) +{ + struct bt_mesh_ctl_friend_offer *off; + struct net_buf_simple *sdu = NET_BUF_SIMPLE(1 + sizeof(*off)); + struct net_buf *buf; BT_DBG(""); - return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_OFFER, &off, - sizeof(off), NULL); + net_buf_simple_init(sdu, 1); + + off = net_buf_simple_add(sdu, sizeof(*off)); + + off->recv_win = CONFIG_BT_MESH_FRIEND_RECV_WIN, + off->queue_size = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, + off->sub_list_size = ARRAY_SIZE(frnd->sub_list), + off->rssi = rssi, + off->frnd_counter = sys_cpu_to_be16(frnd->counter); + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_OFFER, sdu); + if (!buf) { + BT_ERR("Unable to encode Friend Offer"); + return; + } + + frnd->counter++; + + enqueue_buf(frnd, buf); +} + +#define RECV_WIN CONFIG_BT_MESH_FRIEND_RECV_WIN +#define RSSI_FACT(crit) (((crit) >> 5) & (u8_t)BIT_MASK(2)) +#define RECV_WIN_FACT(crit) (((crit) >> 3) & (u8_t)BIT_MASK(2)) +#define MIN_QUEUE_SIZE_LOG(crit) ((crit) & (u8_t)BIT_MASK(3)) +#define MIN_QUEUE_SIZE(crit) ((u32_t)BIT(MIN_QUEUE_SIZE_LOG(crit))) + +static s32_t offer_delay(struct bt_mesh_friend *frnd, s8_t rssi, u8_t crit) +{ + /* Scaling factors. The actual values are 1, 1.5, 2 & 2.5, but we + * want to avoid floating-point arithmetic. + */ + static const u8_t fact[] = { 10, 15, 20, 25 }; + s32_t delay; + + BT_DBG("ReceiveWindowFactor %u ReceiveWindow %u RSSIFactor %u RSSI %d", + fact[RECV_WIN_FACT(crit)], RECV_WIN, + fact[RSSI_FACT(crit)], rssi); + + /* Delay = ReceiveWindowFactor * ReceiveWindow - RSSIFactor * RSSI */ + delay = (s32_t)fact[RECV_WIN_FACT(crit)] * RECV_WIN; + delay -= (s32_t)fact[RSSI_FACT(crit)] * rssi; + delay /= 10; + + BT_DBG("Local Delay calculated as %d ms", delay); + + if (delay < 100) { + return K_MSEC(100); + } + + return K_MSEC(delay); } int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) { struct bt_mesh_ctl_friend_req *msg = (void *)buf->data; - struct bt_mesh_friend *frnd = &bt_mesh.frnd; struct bt_mesh_subnet *sub = rx->sub; + struct bt_mesh_friend *frnd = NULL; + u16_t old_friend; + u16_t poll_to; + int i; if (buf->len < sizeof(*msg)) { BT_WARN("Too short Friend Request"); return -EINVAL; } + if (msg->recv_delay <= 0x09) { + BT_WARN("Prohibited ReceiveDelay (0x%02x)", msg->recv_delay); + return -EINVAL; + } + + poll_to = (((u32_t)msg->poll_to[0] << 16) | + ((u32_t)msg->poll_to[1] << 8) | + ((u32_t)msg->poll_to[2])); + + if (poll_to <= 0x000009 || poll_to >= 0x34bc00) { + BT_WARN("Prohibited PollTimeout (0x%06x)", poll_to); + return -EINVAL; + } + + if (msg->num_elem == 0x00) { + BT_WARN("Prohibited NumElements value (0x00)"); + return -EINVAL; + } + + if (!MIN_QUEUE_SIZE_LOG(msg->criteria)) { + BT_WARN("Prohibited Minimum Queue Size in Friend Request"); + return -EINVAL; + } + + if (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE < MIN_QUEUE_SIZE(msg->criteria)) { + BT_WARN("We have a too small Friend Queue size (%u < %u)", + CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, + MIN_QUEUE_SIZE(msg->criteria)); + return 0; + } + + old_friend = sys_be16_to_cpu(msg->prev_addr); + if (BT_MESH_ADDR_IS_UNICAST(old_friend)) { + frnd = friend_lpn_find(rx->sub->net_idx, old_friend, true); + } else { + frnd = friend_lpn_find(rx->sub->net_idx, rx->ctx.addr, true); + } + + if (frnd) { + BT_WARN("Existing LPN re-requesting Friendship"); + friend_clear(frnd); + goto init_friend; + } else if (BT_MESH_ADDR_IS_UNICAST(old_friend)) { + send_friend_clear(old_friend); + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (bt_mesh.frnd[i].lpn == BT_MESH_ADDR_UNASSIGNED) { + frnd = &bt_mesh.frnd[i]; + break; + } + } + + if (!frnd) { + BT_WARN("No free Friend contexts for new LPN"); + return -ENOMEM; + } + +init_friend: frnd->lpn = rx->ctx.addr; - frnd->rssi = rx->rssi; + frnd->net_idx = rx->sub->net_idx; frnd->recv_delay = msg->recv_delay; - frnd->poll_to = (((u32_t)msg->poll_to[0] << 16) | - ((u32_t)msg->poll_to[1] << 8) | - ((u32_t)msg->poll_to[2])); - frnd->poll_to *= 100; + frnd->poll_to = poll_to * 100; frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter); BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums", - frnd->lpn, frnd->rssi, frnd->recv_delay, frnd->poll_to); + frnd->lpn, rx->rssi, frnd->recv_delay, frnd->poll_to); + + k_delayed_work_submit(&frnd->timer, + offer_delay(frnd, rx->rssi, msg->criteria)); bt_mesh_friend_cred_add(sub->net_idx, sub->keys[0].net, 0, frnd->lpn, frnd->lpn_counter, frnd->counter); - frnd->send_offer = 1; - - k_delayed_work_submit(&frnd->timer, K_MSEC(100)); + enqueue_offer(frnd, rx->rssi); return 0; } +static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd, + u16_t src, u64_t *seq_auth) +{ + struct bt_mesh_friend_seg *unassigned = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + struct net_buf *buf = (void *)sys_slist_peek_head(&seg->queue); + + if (buf && BT_MESH_ADV(buf)->addr == src && + buf_seq_auth[net_buf_id(buf)] == *seq_auth) { + return seg; + } + + if (!unassigned && !buf) { + unassigned = seg; + } + } + + return unassigned; +} + +static void enqueue_friend_pdu(struct bt_mesh_friend *frnd, + enum bt_mesh_friend_pdu_type type, + struct net_buf *buf) +{ + struct bt_mesh_friend_seg *seg; + + BT_DBG("type %u", type); + + if (type == BT_MESH_FRIEND_PDU_SINGLE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + enqueue_buf(frnd, buf); + return; + } + + seg = get_seg(frnd, BT_MESH_ADV(buf)->addr, + &buf_seq_auth[net_buf_id(buf)]); + if (!seg) { + BT_ERR("No free friend segment RX contexts for 0x%04x", + BT_MESH_ADV(buf)->addr); + net_buf_unref(buf); + return; + } + + net_buf_slist_put(&seg->queue, buf); + + if (type == BT_MESH_FRIEND_PDU_COMPLETE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + /* Only acks should have a valid SeqAuth in the Friend queue + * (otherwise we can't easily detect them there), so clear + * the SeqAuth information from the segments before merging. + */ + SYS_SLIST_FOR_EACH_CONTAINER(&seg->queue, buf, node) { + buf_seq_auth[net_buf_id(buf)] = TRANS_SEQ_AUTH_NVAL; + frnd->queue_size++; + } + + sys_slist_merge_slist(&frnd->queue, &seg->queue); + } +} + +static void buf_sent(struct net_buf *buf, int err) +{ + struct bt_mesh_friend *frnd = NULL; + int i; + + BT_DBG("buf %p err %d", buf, err); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (bt_mesh.frnd[i].last == buf) { + frnd = &bt_mesh.frnd[i]; + break; + } + } + + if (!frnd) { + return; + } + + frnd->pending_buf = 0; + + if (frnd->established) { + k_delayed_work_submit(&frnd->timer, frnd->poll_to); + BT_DBG("Waiting %u ms for next poll", frnd->poll_to); + } else { + /* Friend offer timeout is 1 second */ + k_delayed_work_submit(&frnd->timer, K_SECONDS(1)); + BT_DBG("Waiting for first poll"); + + /* Friend Offer doesn't follow the re-sending semantics */ + net_buf_unref(frnd->last); + frnd->last = NULL; + } +} + static void friend_timeout(struct k_work *work) { - struct bt_mesh_friend *frnd = &bt_mesh.frnd; + struct bt_mesh_friend *frnd = CONTAINER_OF(work, struct bt_mesh_friend, + timer.work); - BT_DBG("send_offer %u send_update %u", frnd->send_offer, - frnd->send_update); + __ASSERT_NO_MSG(frnd->pending_buf == 0); - if (frnd->send_offer) { - frnd->send_offer = 0; - send_friend_offer(frnd->rssi); - return; - } - - if (!frnd->send_update) { - BT_WARN("Friendship lost"); - bt_mesh_friend_cred_del(bt_mesh.sub[0].net_idx, frnd->lpn); - return; - } - - frnd->send_update = 0; + BT_DBG("lpn 0x%04x send_last %u last %p", frnd->lpn, + frnd->send_last, frnd->last); if (frnd->send_last && frnd->last) { + BT_DBG("Sending frnd->last"); frnd->send_last = 0; - bt_mesh_adv_send(frnd->last, NULL); - return; + goto send_last; } if (frnd->last) { net_buf_unref(frnd->last); } - frnd->last = net_buf_get(&frnd->queue, K_NO_WAIT); - if (frnd->last) { - bt_mesh_adv_send(frnd->last, NULL); - } else { - send_friend_update(); + /* If the timeout is triggered without anything in the queue, it + * means that there was no poll/req from the LPN and the timeout + * occured because we hit the poll timeout. + */ + frnd->last = net_buf_slist_get(&frnd->queue); + if (!frnd->last) { + BT_WARN("Friendship %s", + frnd->established ? "lost" : "not established"); + friend_clear(frnd); + return; } - k_delayed_work_submit(&frnd->timer, frnd->poll_to); + BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x", + frnd->last, frnd->lpn); + +send_last: + bt_mesh_adv_send(frnd->last, buf_sent); } int bt_mesh_friend_init(void) { - struct bt_mesh_friend *frnd = &bt_mesh.frnd; + int i; - k_fifo_init(&frnd->queue); + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; - k_delayed_work_init(&frnd->timer, friend_timeout); + frnd->net_idx = BT_MESH_KEY_UNUSED; + + sys_slist_init(&frnd->queue); + + k_delayed_work_init(&frnd->timer, friend_timeout); + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + sys_slist_init(&frnd->seg[j].queue); + } + } return 0; } -bool bt_mesh_friend_enqueue(struct net_buf *buf, u16_t dst) +static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth, + u16_t src) { - /* FIXME: Add support for multiple LPNs and group addresses */ - if (!bt_mesh_friend_dst_is_lpn(dst)) { + sys_snode_t *cur, *prev = NULL; + + BT_DBG("SeqAuth %llx src 0x%04x", *seq_auth, src); + + for (cur = sys_slist_peek_head(&frnd->queue); + cur != NULL; prev = cur, cur = sys_slist_peek_next(cur)) { + struct net_buf *buf = (void *)cur; + + if (BT_MESH_ADV(buf)->addr == src && + buf_seq_auth[net_buf_id(buf)] == *seq_auth) { + BT_DBG("Removing old ack from Friend Queue"); + + sys_slist_remove(&frnd->queue, prev, cur); + /* Make sure old slist entry state doesn't remain */ + buf->frags = NULL; + + net_buf_unref(buf); + break; + } + } +} + +static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + struct friend_pdu_info info; + struct net_buf *buf; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, rx->ctx.addr); + } + + info.src = rx->ctx.addr; + info.dst = rx->dst; + + if (rx->net_if == BT_MESH_NET_IF_LOCAL) { + info.ttl = rx->ctx.recv_ttl; + } else { + info.ttl = rx->ctx.recv_ttl - 1; + } + + info.ctl = rx->ctl; + + info.seq[0] = (rx->seq >> 16); + info.seq[1] = (rx->seq >> 8); + info.seq[2] = rx->seq; + + info.iv_index = BT_MESH_NET_IVI_RX(rx); + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("Failed to encode Friend buffer"); + return; + } + + if (seq_auth) { + buf_seq_auth[net_buf_id(buf)] = *seq_auth; + } + + enqueue_friend_pdu(frnd, type, buf); + + BT_DBG("Queued message for LPN 0x%04x", frnd->lpn); +} + +static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + struct friend_pdu_info info; + struct net_buf *buf; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, tx->src); + } + + info.src = tx->src; + info.dst = tx->ctx->addr; + + info.ttl = tx->ctx->send_ttl; + info.ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED); + + info.seq[0] = (bt_mesh.seq >> 16); + info.seq[1] = (bt_mesh.seq >> 8); + info.seq[2] = bt_mesh.seq++; + + info.iv_index = BT_MESH_NET_IVI_TX; + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("Failed to encode Friend buffer"); + return; + } + + if (seq_auth) { + buf_seq_auth[net_buf_id(buf)] = *seq_auth; + } + + enqueue_friend_pdu(frnd, type, buf); + + BT_DBG("Queued message for LPN 0x%04x", frnd->lpn); +} + +static bool friend_lpn_matches(struct bt_mesh_friend *frnd, u16_t net_idx, + u16_t addr) +{ + int i; + + BT_DBG("frnd->established %u frnd->lpn 0x%04x frnd->net_idx 0x%04x", + frnd->established, frnd->lpn, frnd->net_idx); + BT_DBG("addr 0x%04x net_idx 0x%04x", addr, net_idx); + + if (!frnd->established) { return false; } - if (BT_MESH_ADDR_IS_UNICAST(dst)) { - net_buf_put(&bt_mesh.frnd.queue, net_buf_ref(buf)); - } else { - struct net_buf *clone = net_buf_clone(buf, K_NO_WAIT); + if (net_idx != frnd->net_idx) { + return false; + } - if (clone) { - net_buf_put(&bt_mesh.frnd.queue, clone); - } else { - BT_WARN("Unable to allocate buffer for friend queue"); - return false; + if (BT_MESH_ADDR_IS_UNICAST(addr)) { + if (addr == frnd->lpn) { + return true; + } + + return false; + } + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + return true; } } - BT_DBG("Queued message for LPN"); - - return true; + return false; +} + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr) +{ + int i; + + BT_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, net_idx, addr)) { + return true; + } + } + + return false; +} + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + int i; + + if (!rx->friend_match || + (rx->ctx.recv_ttl <= 1 && rx->net_if != BT_MESH_NET_IF_LOCAL) || + bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) { + return; + } + + BT_DBG("recv_ttl %u net_idx 0x%04x src 0x%04x dst 0x%04x", + rx->ctx.recv_ttl, rx->sub->net_idx, rx->ctx.addr, rx->dst); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, rx->sub->net_idx, rx->dst)) { + friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, sbuf); + } + } +} + +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + bool matched = false; + int i; + + if (!bt_mesh_friend_match(tx->sub->net_idx, tx->ctx->addr) || + bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) { + return matched; + } + + BT_DBG("net_idx 0x%04x dst 0x%04x src 0x%04x", tx->sub->net_idx, + tx->ctx->addr, tx->src); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, tx->sub->net_idx, tx->ctx->addr)) { + friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, sbuf); + matched = true; + } + } + + return matched; +} + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth) +{ + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + if (!friend_lpn_matches(frnd, sub->net_idx, dst)) { + continue; + } + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[j]; + struct net_buf *buf; + + buf = (void *)sys_slist_peek_head(&seg->queue); + if (!buf) { + continue; + } + + if (BT_MESH_ADV(buf)->addr != src) { + continue; + } + + if (buf_seq_auth[net_buf_id(buf)] != *seq_auth) { + continue; + } + + BT_WARN("Clearing incomplete segments for 0x%04x", src); + + while (!sys_slist_is_empty(&seg->queue)) { + net_buf_unref(net_buf_slist_get(&seg->queue)); + } + } + } } diff --git a/subsys/bluetooth/host/mesh/friend.h b/subsys/bluetooth/host/mesh/friend.h index 2c639b206df..b5a66a20d3c 100644 --- a/subsys/bluetooth/host/mesh/friend.h +++ b/subsys/bluetooth/host/mesh/friend.h @@ -6,18 +6,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -static inline bool bt_mesh_friend_dst_is_lpn(u16_t dst) -{ -#if defined(CONFIG_BT_MESH_FRIEND) - return (dst == bt_mesh.frnd.lpn); -#else - return false; -#endif -} +enum bt_mesh_friend_pdu_type { + BT_MESH_FRIEND_PDU_SINGLE, + BT_MESH_FRIEND_PDU_PARTIAL, + BT_MESH_FRIEND_PDU_COMPLETE, +}; -bool bt_mesh_friend_enqueue(struct net_buf *buf, u16_t dst); +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr); + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf); +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf); + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth); + +void bt_mesh_friend_sec_update(u16_t net_idx); + +void bt_mesh_friend_clear_net_idx(u16_t net_idx); int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); int bt_mesh_friend_init(void); diff --git a/subsys/bluetooth/host/mesh/lpn.c b/subsys/bluetooth/host/mesh/lpn.c index 4ba90b9b256..9f5789d8cf5 100644 --- a/subsys/bluetooth/host/mesh/lpn.c +++ b/subsys/bluetooth/host/mesh/lpn.c @@ -126,7 +126,7 @@ static int send_friend_clear(void) BT_DBG(""); return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, - sizeof(req), friend_clear_sent); + sizeof(req), NULL, friend_clear_sent); } static void clear_friendship(bool disable) @@ -205,7 +205,7 @@ static int send_friend_req(void) BT_DBG(""); return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req, - sizeof(req), friend_req_sent); + sizeof(req), NULL, friend_req_sent); } static inline void group_zero(atomic_t *target) @@ -302,7 +302,7 @@ static int send_friend_poll(void) } err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1, - req_sent); + NULL, req_sent); if (err == 0) { lpn->pending_poll = 0; lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL; @@ -595,7 +595,7 @@ static bool sub_update(u8_t op) req.xact = lpn->xact_next++; - if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, req_sent) < 0) { + if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL, req_sent) < 0) { group_zero(lpn->pending); return false; } diff --git a/subsys/bluetooth/host/mesh/net.c b/subsys/bluetooth/host/mesh/net.c index 0e5a4d281d7..5de86592410 100644 --- a/subsys/bluetooth/host/mesh/net.c +++ b/subsys/bluetooth/host/mesh/net.c @@ -309,6 +309,7 @@ struct bt_mesh_friend_cred *bt_mesh_friend_cred_add(u16_t net_idx, } if (!cred) { + BT_WARN("No free friend credential slots"); return NULL; } @@ -716,6 +717,10 @@ int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct net_buf *buf, if (!bt_mesh.iv_update && bt_mesh.seq > IV_UPDATE_SEQ_LIMIT) { bt_mesh_beacon_ivu_initiator(true); bt_mesh_iv_update(bt_mesh.iv_index + 1, true); + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_sec_update(BT_MESH_KEY_ANY); + } } return 0; @@ -1021,13 +1026,18 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, struct net_buf *buf; u8_t nid, transmit; - BT_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, CTL(sbuf->data), - rx->dst); - if (rx->net_if != BT_MESH_NET_IF_LOCAL && rx->ctx.recv_ttl <= 1) { return; } + if (rx->net_if == BT_MESH_NET_IF_ADV && + bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED && + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED) { + return; + } + + BT_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, rx->ctl, rx->dst); + transmit = bt_mesh_relay_retransmit_get(); buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, TRANSMIT_COUNT(transmit), TRANSMIT_INT(transmit), K_NO_WAIT); @@ -1036,47 +1046,29 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, return; } - net_buf_add_mem(buf, sbuf->data, sbuf->len); - /* Only decrement TTL for non-locally originated packets */ if (rx->net_if != BT_MESH_NET_IF_LOCAL) { /* Leave CTL bit intact */ - buf->data[1] &= 0x80; - buf->data[1] |= rx->ctx.recv_ttl - 1; + sbuf->data[1] &= 0x80; + sbuf->data[1] |= rx->ctx.recv_ttl - 1; } + net_buf_add_mem(buf, sbuf->data, sbuf->len); + if (rx->sub->kr_phase == BT_MESH_KR_PHASE_2) { - if (bt_mesh_friend_dst_is_lpn(rx->dst)) { - if (bt_mesh_friend_cred_get(rx->sub->net_idx, - BT_MESH_ADDR_UNASSIGNED, - 1, &nid, &enc, &priv)) { - BT_ERR("friend_cred_get failed"); - goto done; - } - } else { - enc = rx->sub->keys[1].enc; - priv = rx->sub->keys[1].privacy; - nid = rx->sub->keys[1].nid; - } + enc = rx->sub->keys[1].enc; + priv = rx->sub->keys[1].privacy; + nid = rx->sub->keys[1].nid; } else { - if (bt_mesh_friend_dst_is_lpn(rx->dst)) { - if (bt_mesh_friend_cred_get(rx->sub->net_idx, - BT_MESH_ADDR_UNASSIGNED, - 0, &nid, &enc, &priv)) { - BT_ERR("friend_cred_get failed"); - goto done; - } - } else { - enc = rx->sub->keys[0].enc; - priv = rx->sub->keys[0].privacy; - nid = rx->sub->keys[0].nid; - } + enc = rx->sub->keys[0].enc; + priv = rx->sub->keys[0].privacy; + nid = rx->sub->keys[0].nid; } BT_DBG("Relaying packet. TTL is now %u", TTL(buf->data)); - /* Update NID if RX or TX is with friend credentials */ - if (rx->ctx.friend_cred || bt_mesh_friend_dst_is_lpn(rx->dst)) { + /* Update NID if RX or RX was with friend credentials */ + if (rx->ctx.friend_cred) { buf->data[0] &= 0x80; /* Clear everything except IVI */ buf->data[0] |= nid; } @@ -1095,13 +1087,6 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, goto done; } - if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { - if (bt_mesh_friend_enqueue(buf, rx->dst) && - BT_MESH_ADDR_IS_UNICAST(rx->dst)) { - goto done; - } - } - if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { if (bt_mesh_proxy_relay(&buf->b, rx->dst) && BT_MESH_ADDR_IS_UNICAST(rx->dst)) { @@ -1109,10 +1094,7 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, } } - if (rx->net_if != BT_MESH_NET_IF_ADV || - bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) { - bt_mesh_adv_send(buf, NULL); - } + bt_mesh_adv_send(buf, NULL); done: net_buf_unref(buf); @@ -1185,7 +1167,6 @@ void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi, enum bt_mesh_net_if net_if) { struct net_buf_simple *buf = NET_BUF_SIMPLE(29); - struct net_buf_simple_state state; struct bt_mesh_net_rx rx; BT_DBG("rssi %d net_if %u", rssi, net_if); @@ -1203,19 +1184,18 @@ void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi, bt_mesh_proxy_addr_add(data, rx.ctx.addr); } - /* Save parsing state so the buffer can later be relayed */ - net_buf_simple_save(buf, &state); + rx.local_match = (bt_mesh_fixed_group_match(rx.dst) || + bt_mesh_elem_find(rx.dst)); - if (bt_mesh_fixed_group_match(rx.dst) || bt_mesh_elem_find(rx.dst)) { - bt_mesh_trans_recv(buf, &rx); + bt_mesh_trans_recv(buf, &rx); - if (BT_MESH_ADDR_IS_UNICAST(rx.dst)) { - return; - } + /* Relay if this was a group/virtual address, or if the destination + * was neither a local element nor an LPN we're Friends for. + */ + if (!BT_MESH_ADDR_IS_UNICAST(rx.dst) || + (!rx.local_match && !rx.friend_match)) { + bt_mesh_net_relay(buf, &rx); } - - net_buf_simple_restore(buf, &state); - bt_mesh_net_relay(buf, &rx); } static void ivu_complete(struct k_work *work) diff --git a/subsys/bluetooth/host/mesh/net.h b/subsys/bluetooth/host/mesh/net.h index 26ccaee29f8..4e2c1d13612 100644 --- a/subsys/bluetooth/host/mesh/net.h +++ b/subsys/bluetooth/host/mesh/net.h @@ -79,22 +79,40 @@ struct bt_mesh_rpl { u32_t seq; }; +#if defined(CONFIG_BT_MESH_FRIEND) +#define FRIEND_SEG_RX CONFIG_BT_MESH_FRIEND_SEG_RX +#define FRIEND_SUB_LIST_SIZE CONFIG_BT_MESH_FRIEND_SUB_LIST_SIZE +#else +#define FRIEND_SEG_RX 0 +#define FRIEND_SUB_LIST_SIZE 0 +#endif + struct bt_mesh_friend { u16_t lpn; u8_t recv_delay; u8_t fsn:1, send_last:1, - send_offer:1, - send_update:1; + sec_update:1, + pending_buf:1, + established:1; s32_t poll_to; u16_t lpn_counter; u16_t counter; - s8_t rssi; + + u16_t net_idx; + + u16_t sub_list[FRIEND_SUB_LIST_SIZE]; struct k_delayed_work timer; + struct bt_mesh_friend_seg { + sys_slist_t queue; + } seg[FRIEND_SEG_RX]; + struct net_buf *last; - struct k_fifo queue; + + sys_slist_t queue; + u32_t queue_size; }; #if defined(CONFIG_BT_MESH_LOW_POWER) @@ -171,7 +189,8 @@ struct bt_mesh_net { struct k_fifo local_queue; #if defined(CONFIG_BT_MESH_FRIEND) - struct bt_mesh_friend frnd; /* Friend state */ + /* Friend state, unique for each LPN that we're Friends for */ + struct bt_mesh_friend frnd[CONFIG_BT_MESH_FRIEND_LPN_COUNT]; #endif #if defined(CONFIG_BT_MESH_LOW_POWER) @@ -202,12 +221,14 @@ enum bt_mesh_net_if { struct bt_mesh_net_rx { struct bt_mesh_subnet *sub; struct bt_mesh_msg_ctx ctx; - u32_t seq; /* Sequence Number */ - u16_t dst; /* Destination address */ - u8_t old_iv:1, /* iv_index - 1 was used */ - new_key:1, /* Data was encrypted with updated key */ - ctl:1, /* Network Control */ - net_if:2; /* Network interface */ + u32_t seq; /* Sequence Number */ + u16_t dst; /* Destination address */ + u8_t old_iv:1, /* iv_index - 1 was used */ + new_key:1, /* Data was encrypted with updated key */ + ctl:1, /* Network Control */ + net_if:2, /* Network interface */ + local_match:1, /* Matched a local element */ + friend_match:1; /* Matched an LPN we're friends for */ s8_t rssi; }; diff --git a/subsys/bluetooth/host/mesh/transport.c b/subsys/bluetooth/host/mesh/transport.c index 89b2b160432..0041e089a53 100644 --- a/subsys/bluetooth/host/mesh/transport.c +++ b/subsys/bluetooth/host/mesh/transport.c @@ -73,7 +73,8 @@ static struct seg_rx { u64_t seq_auth; u8_t seg_n:5, ctl:1, - in_use:1; + in_use:1, + obo:1; u8_t hdr; u8_t ttl; u16_t src; @@ -116,6 +117,18 @@ static int send_unseg(struct bt_mesh_net_tx *tx, u8_t aid, net_buf_add_mem(buf, sdu->data, sdu->len); + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + if (bt_mesh_friend_enqueue_tx(tx, BT_MESH_FRIEND_PDU_SINGLE, + NULL, &buf->b) && + BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* PDUs for a specific Friend should only go + * out through the Friend Queue. + */ + net_buf_unref(buf); + return 0; + } + } + return bt_mesh_net_send(tx, buf, NULL); } @@ -314,6 +327,27 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, u8_t aid, tx->seg[seg_o] = net_buf_ref(seg); + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + enum bt_mesh_friend_pdu_type type; + + if (seg_o == tx->seg_n) { + type = BT_MESH_FRIEND_PDU_COMPLETE; + } else { + type = BT_MESH_FRIEND_PDU_PARTIAL; + } + + if (bt_mesh_friend_enqueue_tx(net_tx, type, + &tx->seq_auth, + &seg->b) && + BT_MESH_ADDR_IS_UNICAST(net_tx->ctx->addr)) { + /* PDUs for a specific Friend should only go + * out through the Friend Queue. + */ + net_buf_unref(seg); + return 0; + } + } + BT_DBG("Sending %u/%u", seg_o, tx->seg_n); err = bt_mesh_net_send(net_tx, seg, seg_sent); @@ -463,6 +497,11 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, u8_t hdr, u8_t mic_size, return -EINVAL; } + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) { + BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend", rx->dst); + return 0; + } + if (BT_MESH_ADDR_IS_VIRTUAL(rx->dst)) { ad = bt_mesh_label_uuid_get(rx->dst); } else { @@ -567,7 +606,7 @@ static struct seg_tx *seg_tx_lookup(u16_t seq_zero, u8_t obo, u16_t addr) } static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr, - struct net_buf_simple *buf) + struct net_buf_simple *buf, u64_t *seq_auth) { struct seg_tx *tx; unsigned int bit; @@ -584,6 +623,13 @@ static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr, obo = seq_zero >> 15; seq_zero = (seq_zero >> 2) & 0x1fff; + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->friend_match) { + BT_DBG("Ack for LPN 0x%04x of this Friend", rx->dst); + /* Best effort - we don't have enough info for true SeqAuth */ + *seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_RX(rx), seq_zero); + return 0; + } + ack = net_buf_simple_pull_be32(buf); BT_DBG("OBO %u seq_zero 0x%04x ack 0x%08x", obo, seq_zero, ack); @@ -594,6 +640,8 @@ static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr, return -EINVAL; } + *seq_auth = tx->seq_auth; + if (!ack) { BT_WARN("SDU canceled"); seg_tx_complete(tx, -ECANCELED); @@ -654,7 +702,7 @@ static int trans_heartbeat(struct bt_mesh_net_rx *rx, } static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr, - struct net_buf_simple *buf) + struct net_buf_simple *buf, u64_t *seq_auth) { u8_t ctl_op = TRANS_CTL_OP(&hdr); @@ -662,19 +710,33 @@ static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr, switch (ctl_op) { case TRANS_CTL_OP_ACK: - return trans_ack(rx, hdr, buf); + return trans_ack(rx, hdr, buf, seq_auth); + } + + /* Only acks need processing for Friend behavior */ + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) { + return 0; + } + + switch (ctl_op) { case TRANS_CTL_OP_HEARTBEAT: return trans_heartbeat(rx, buf); } -#if defined(CONFIG_BT_MESH_FRIEND) - switch (ctl_op) { - case TRANS_CTL_OP_FRIEND_POLL: - return bt_mesh_friend_poll(rx, buf); - case TRANS_CTL_OP_FRIEND_REQ: - return bt_mesh_friend_req(rx, buf); + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + switch (ctl_op) { + case TRANS_CTL_OP_FRIEND_POLL: + return bt_mesh_friend_poll(rx, buf); + case TRANS_CTL_OP_FRIEND_REQ: + return bt_mesh_friend_req(rx, buf); + case TRANS_CTL_OP_FRIEND_CLEAR: + return bt_mesh_friend_clear(rx, buf); + case TRANS_CTL_OP_FRIEND_SUB_ADD: + return bt_mesh_friend_sub_add(rx, buf); + case TRANS_CTL_OP_FRIEND_SUB_REM: + return bt_mesh_friend_sub_rem(rx, buf); + } } -#endif #if defined(CONFIG_BT_MESH_LOW_POWER) if (ctl_op == TRANS_CTL_OP_FRIEND_OFFER) { @@ -705,7 +767,8 @@ static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr, return -ENOENT; } -static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx) +static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx, + u64_t *seq_auth) { u8_t hdr; @@ -719,7 +782,7 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx) hdr = net_buf_simple_pull_u8(buf); if (rx->ctl) { - return ctl_recv(rx, hdr, buf); + return ctl_recv(rx, hdr, buf, seq_auth); } else { return sdu_recv(rx, hdr, 4, 0, buf); } @@ -739,7 +802,7 @@ static inline s32_t ack_timeout(struct seg_rx *rx) } int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, - size_t data_len, bt_mesh_adv_func_t cb) + size_t data_len, u64_t *seq_auth, bt_mesh_adv_func_t cb) { struct net_buf *buf; u8_t xmit; @@ -762,11 +825,23 @@ int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, net_buf_add_mem(buf, data, data_len); + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + if (bt_mesh_friend_enqueue_tx(tx, BT_MESH_FRIEND_PDU_SINGLE, + seq_auth, &buf->b) && + BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* PDUs for a specific Friend should only go + * out through the Friend Queue. + */ + net_buf_unref(buf); + return 0; + } + } + return bt_mesh_net_send(tx, buf, cb); } static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst, - u8_t ttl, u16_t seq_zero, u32_t block) + u8_t ttl, u64_t *seq_auth, u32_t block, u8_t obo) { struct bt_mesh_msg_ctx ctx = { .net_idx = sub->net_idx, @@ -777,8 +852,9 @@ static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst, struct bt_mesh_net_tx tx = { .sub = sub, .ctx = &ctx, - .src = src, + .src = obo ? bt_mesh_primary_addr() : src, }; + u16_t seq_zero = *seq_auth & 0x1fff; u8_t buf[6]; BT_DBG("SeqZero 0x%04x Block 0x%08x", seq_zero, block); @@ -796,10 +872,11 @@ static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst, return 0; } - sys_put_be16(((seq_zero << 2) & 0x7ffc), buf); + sys_put_be16(((seq_zero << 2) & 0x7ffc) | (obo << 15), buf); sys_put_be32(block, &buf[2]); - return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_ACK, buf, sizeof(buf), NULL); + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_ACK, buf, sizeof(buf), + NULL, NULL); } static void seg_rx_reset(struct seg_rx *rx) @@ -808,6 +885,13 @@ static void seg_rx_reset(struct seg_rx *rx) k_delayed_work_cancel(&rx->ack); + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->obo && + rx->block != BLOCK_COMPLETE(rx->seg_n)) { + BT_WARN("Clearing incomplete buffers from Friend queue"); + bt_mesh_friend_clear_incomplete(rx->sub, rx->src, rx->dst, + &rx->seq_auth); + } + /* We don't reset rx->net and rx->seq_auth here since we need to * be able to send an ack if we receive a segment after we've * already received the full SDU. @@ -825,13 +909,13 @@ static void seg_ack(struct k_work *work) if (k_uptime_get_32() - rx->last > (60 * MSEC_PER_SEC)) { BT_WARN("Incomplete timer expired"); send_ack(rx->sub, rx->dst, rx->src, rx->ttl, - rx->seq_auth & 0x1fff, 0); + &rx->seq_auth, 0, rx->obo); seg_rx_reset(rx); return; } - send_ack(rx->sub, rx->dst, rx->src, rx->ttl, rx->seq_auth & 0x1fff, - rx->block); + send_ack(rx->sub, rx->dst, rx->src, rx->ttl, &rx->seq_auth, + rx->block, rx->obo); k_delayed_work_submit(&rx->ack, ack_timeout(rx)); } @@ -874,7 +958,9 @@ static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx, * has apparently started sending a new SDU. */ seg_rx_reset(rx); - return rx; + + /* Return non-match so caller can re-allocate */ + return NULL; } } @@ -936,10 +1022,10 @@ static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx, return NULL; } -static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx) +static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx, + enum bt_mesh_friend_pdu_type *pdu_type, u64_t *seq_auth) { struct seg_rx *rx; - u64_t seq_auth; u8_t *hdr = buf->data; u16_t seq_zero; u8_t seg_n; @@ -970,11 +1056,11 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx) return -EINVAL; } - seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_RX(net_rx), - (net_rx->seq & 0xffffe000) | seq_zero); + *seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_RX(net_rx), + (net_rx->seq & 0xffffe000) | seq_zero); /* Look for old RX sessions */ - rx = seg_rx_find(net_rx, &seq_auth); + rx = seg_rx_find(net_rx, seq_auth); if (rx) { if (!seg_rx_is_valid(rx, net_rx, hdr, seg_n)) { return -EINVAL; @@ -988,8 +1074,9 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx) if (rx->block == BLOCK_COMPLETE(rx->seg_n)) { BT_WARN("Got segment for already complete SDU"); send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr, - net_rx->ctx.send_ttl, seq_zero, rx->block); - return 0; + net_rx->ctx.send_ttl, seq_auth, rx->block, + rx->obo); + return -EALREADY; } /* We ignore instead of sending block ack 0 since the @@ -1004,25 +1091,28 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx) if (!sdu_len_is_ok(net_rx->ctl, seg_n)) { BT_ERR("Too big incoming SDU length"); send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr, - net_rx->ctx.send_ttl, seq_zero, 0); - return 0; + net_rx->ctx.send_ttl, seq_auth, 0, + net_rx->friend_match); + return -EMSGSIZE; } /* Look for free slot for a new RX session */ - rx = seg_rx_alloc(net_rx, hdr, &seq_auth, seg_n); + rx = seg_rx_alloc(net_rx, hdr, seq_auth, seg_n); if (!rx) { /* Warn but don't cancel since the existing slots willl * eventually be freed up and we'll be able to process * this one. */ BT_WARN("No free slots for new incoming segmented messages"); - return 0; + return -ENOMEM; } + rx->obo = net_rx->friend_match; + found_rx: if (BIT(seg_o) & rx->block) { BT_WARN("Received already received fragment"); - return 0; + return -EALREADY; } /* All segments, except the last one, must either have 8 bytes of @@ -1038,9 +1128,9 @@ found_rx: if (rx->buf.len > CONFIG_BT_MESH_RX_SDU_MAX) { BT_ERR("Too large SDU len"); send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr, - net_rx->ctx.send_ttl, seq_zero, 0); + net_rx->ctx.send_ttl, seq_auth, 0, rx->obo); seg_rx_reset(rx); - return 0; + return -EMSGSIZE; } } else { if (buf->len != seg_len(rx->ctl)) { @@ -1066,20 +1156,23 @@ found_rx: rx->block |= BIT(seg_o); if (rx->block != BLOCK_COMPLETE(seg_n)) { + *pdu_type = BT_MESH_FRIEND_PDU_PARTIAL; return 0; } BT_DBG("Complete SDU"); + *pdu_type = BT_MESH_FRIEND_PDU_COMPLETE; + /* Set the correct sequence number to be used with the App Nonce */ net_rx->seq = (rx->seq_auth & 0xffffff); k_delayed_work_cancel(&rx->ack); send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr, - net_rx->ctx.send_ttl, seq_zero, rx->block); + net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo); if (net_rx->ctl) { - err = ctl_recv(net_rx, *hdr, &rx->buf); + err = ctl_recv(net_rx, *hdr, &rx->buf, seq_auth); } else { err = sdu_recv(net_rx, *hdr, MIC_SIZE(hdr), SZMIC(MIC_SIZE(hdr)), &rx->buf); @@ -1092,8 +1185,22 @@ found_rx: int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx) { + u64_t seq_auth = TRANS_SEQ_AUTH_NVAL; + enum bt_mesh_friend_pdu_type pdu_type = BT_MESH_FRIEND_PDU_SINGLE; + struct net_buf_simple_state state; int err; + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + rx->friend_match = bt_mesh_friend_match(rx->sub->net_idx, + rx->dst); + } else { + rx->friend_match = false; + } + + if (!rx->local_match && !rx->friend_match) { + return 0; + } + BT_DBG("src 0x%04x dst 0x%04x seq 0x%08x", rx->ctx.addr, rx->dst, rx->seq); @@ -1110,13 +1217,19 @@ int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx) bt_mesh_lpn_established() && (!bt_mesh_lpn_waiting_update() || !rx->ctx.friend_cred)) { BT_WARN("Ignoring unexpected message in Low Power mode"); - return -EAGAIN; + err = -EAGAIN; + goto done; } + /* Save the parsing state so the buffer can later be relayed or + * placed in the Friend Queue. + */ + net_buf_simple_save(buf, &state); + if (SEG(buf->data)) { - err = trans_seg(buf, rx); + err = trans_seg(buf, rx, &pdu_type, &seq_auth); } else { - err = trans_unseg(buf, rx); + err = trans_unseg(buf, rx, &seq_auth); } /* Notify LPN state machine so a Friend Poll will be sent. If the @@ -1131,6 +1244,17 @@ int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx) bt_mesh_lpn_msg_received(rx); } +done: + net_buf_simple_restore(buf, &state); + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !err) { + if (seq_auth == TRANS_SEQ_AUTH_NVAL) { + bt_mesh_friend_enqueue_rx(rx, pdu_type, NULL, buf); + } else { + bt_mesh_friend_enqueue_rx(rx, pdu_type, &seq_auth, buf); + } + } + return err; } diff --git a/subsys/bluetooth/host/mesh/transport.h b/subsys/bluetooth/host/mesh/transport.h index 326977217d9..8642531c31b 100644 --- a/subsys/bluetooth/host/mesh/transport.h +++ b/subsys/bluetooth/host/mesh/transport.h @@ -6,6 +6,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#define TRANS_SEQ_AUTH_NVAL 0xffffffffffffffff + #define BT_MESH_TX_SEG_COUNT (CONFIG_BT_MESH_ADV_BUF_COUNT - 3) #define BT_MESH_TX_SDU_MAX (BT_MESH_TX_SEG_COUNT * 12) @@ -62,6 +64,7 @@ struct bt_mesh_ctl_friend_clear_confirm { u16_t lpn_counter; } __packed; +#define BT_MESH_FRIEND_SUB_MIN_LEN (1 + 2) struct bt_mesh_ctl_friend_sub { u8_t xact; u16_t addr_list[5]; @@ -78,7 +81,7 @@ bool bt_mesh_tx_in_progress(void); void bt_mesh_rx_reset(void); int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, - size_t data_len, bt_mesh_adv_func_t cb); + size_t data_len, u64_t *seq_auth, bt_mesh_adv_func_t cb); int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg, bt_mesh_cb_t cb, void *cb_data);