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:
parent
9bb07ff69a
commit
7248e4b7c3
11 changed files with 1326 additions and 235 deletions
|
@ -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
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
Bluetooth Mesh implementation tasks
|
||||
===================================
|
||||
|
||||
* Friend support (work ongoing)
|
||||
|
||||
* PB-GATT (Node)
|
||||
|
||||
* Any generally useful models from the Model Specification
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue