Bluetooth: Mesh: Add complete Friend support

Add all the missing pieces of Friend node support.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2017-10-31 16:16:28 +02:00 committed by Johan Hedberg
commit 7248e4b7c3
11 changed files with 1326 additions and 235 deletions

View file

@ -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

View file

@ -1,8 +1,6 @@
Bluetooth Mesh implementation tasks
===================================
* Friend support (work ongoing)
* PB-GATT (Node)
* Any generally useful models from the Model Specification

View file

@ -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) {

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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);