2017-06-16 12:30:54 +03:00
|
|
|
/* Bluetooth Mesh */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Intel Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zephyr.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <misc/util.h>
|
|
|
|
#include <misc/byteorder.h>
|
|
|
|
|
|
|
|
#include <net/buf.h>
|
|
|
|
|
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/mesh.h>
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_TRANS)
|
2017-06-16 12:30:54 +03:00
|
|
|
#include "common/log.h"
|
|
|
|
|
2018-01-02 11:07:45 +01:00
|
|
|
#include "../testing.h"
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
#include "crypto.h"
|
|
|
|
#include "adv.h"
|
|
|
|
#include "mesh.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "lpn.h"
|
|
|
|
#include "friend.h"
|
|
|
|
#include "access.h"
|
|
|
|
#include "foundation.h"
|
|
|
|
#include "transport.h"
|
|
|
|
|
|
|
|
#define AID_MASK ((u8_t)(BIT_MASK(6)))
|
|
|
|
|
|
|
|
#define SEG(data) ((data)[0] >> 7)
|
|
|
|
#define AKF(data) (((data)[0] >> 6) & 0x01)
|
|
|
|
#define AID(data) ((data)[0] & AID_MASK)
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
#define ASZMIC(data) (((data)[1] >> 7) & 1)
|
2017-06-16 12:30:54 +03:00
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4)
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
#define UNSEG_HDR(akf, aid) ((akf << 6) | (aid & AID_MASK))
|
|
|
|
#define SEG_HDR(akf, aid) (UNSEG_HDR(akf, aid) | 0x80)
|
|
|
|
|
|
|
|
#define BLOCK_COMPLETE(seg_n) (u32_t)(((u64_t)1 << (seg_n + 1)) - 1)
|
|
|
|
|
|
|
|
#define SEQ_AUTH(iv_index, seq) (((u64_t)iv_index) << 24 | (u64_t)seq)
|
|
|
|
|
|
|
|
/* Retransmit timeout after which to retransmit unacked segments */
|
|
|
|
#define SEG_RETRANSMIT_TIMEOUT K_MSEC(400)
|
|
|
|
|
|
|
|
/* Number of retransmit attempts (after the initial transmit) per segment */
|
|
|
|
#define SEG_RETRANSMIT_ATTEMPTS 4
|
|
|
|
|
|
|
|
/* How long to wait for available buffers before giving up */
|
|
|
|
#define BUF_TIMEOUT K_NO_WAIT
|
|
|
|
|
|
|
|
static struct seg_tx {
|
|
|
|
struct bt_mesh_subnet *sub;
|
|
|
|
struct net_buf *seg[BT_MESH_TX_SEG_COUNT];
|
|
|
|
u64_t seq_auth;
|
2017-10-28 21:50:29 +02:00
|
|
|
u16_t dst;
|
2017-11-05 23:16:44 +02:00
|
|
|
u8_t seg_n:5, /* Last segment index */
|
|
|
|
new_key:1; /* New/old key */
|
|
|
|
u8_t nack_count; /* Number of unacked segs */
|
2017-11-17 15:19:51 +02:00
|
|
|
const struct bt_mesh_send_cb *cb;
|
2017-06-16 12:30:54 +03:00
|
|
|
void *cb_data;
|
2017-11-05 23:16:44 +02:00
|
|
|
struct k_delayed_work retransmit; /* Retransmit timer */
|
2017-08-09 09:21:11 +03:00
|
|
|
} seg_tx[CONFIG_BT_MESH_TX_SEG_MSG_COUNT];
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
static struct seg_rx {
|
|
|
|
struct bt_mesh_subnet *sub;
|
|
|
|
u64_t seq_auth;
|
|
|
|
u8_t seg_n:5,
|
|
|
|
ctl:1,
|
2017-10-31 16:16:28 +02:00
|
|
|
in_use:1,
|
|
|
|
obo:1;
|
2017-06-16 12:30:54 +03:00
|
|
|
u8_t hdr;
|
|
|
|
u8_t ttl;
|
|
|
|
u16_t src;
|
|
|
|
u16_t dst;
|
|
|
|
u32_t block;
|
|
|
|
u32_t last;
|
|
|
|
struct k_delayed_work ack;
|
|
|
|
struct net_buf_simple buf;
|
2017-08-09 09:21:11 +03:00
|
|
|
u8_t buf_data[CONFIG_BT_MESH_RX_SDU_MAX];
|
|
|
|
} seg_rx[CONFIG_BT_MESH_RX_SEG_MSG_COUNT] = {
|
|
|
|
[0 ... (CONFIG_BT_MESH_RX_SEG_MSG_COUNT - 1)] = {
|
|
|
|
.buf.size = CONFIG_BT_MESH_RX_SDU_MAX,
|
2017-06-16 12:30:54 +03:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2017-11-02 13:30:46 +02:00
|
|
|
static u16_t hb_sub_dst = BT_MESH_ADDR_UNASSIGNED;
|
|
|
|
|
|
|
|
void bt_mesh_set_hb_sub_dst(u16_t addr)
|
|
|
|
{
|
|
|
|
hb_sub_dst = addr;
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
static int send_unseg(struct bt_mesh_net_tx *tx, struct net_buf_simple *sdu,
|
2017-11-17 15:19:51 +02:00
|
|
|
const struct bt_mesh_send_cb *cb, void *cb_data)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x sdu_len %u",
|
|
|
|
tx->src, tx->ctx->addr, tx->ctx->app_idx, sdu->len);
|
|
|
|
|
2017-11-12 22:19:28 +02:00
|
|
|
buf = bt_mesh_adv_create(BT_MESH_ADV_DATA,
|
|
|
|
BT_MESH_TRANSMIT_COUNT(tx->xmit),
|
|
|
|
BT_MESH_TRANSMIT_INT(tx->xmit), BUF_TIMEOUT);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (!buf) {
|
|
|
|
BT_ERR("Out of network buffers");
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:46:49 +02:00
|
|
|
net_buf_reserve(buf, BT_MESH_NET_HDR_LEN);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
if (tx->ctx->app_idx == BT_MESH_KEY_DEV) {
|
|
|
|
net_buf_add_u8(buf, UNSEG_HDR(0, 0));
|
|
|
|
} else {
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
net_buf_add_u8(buf, UNSEG_HDR(1, tx->aid));
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_add_mem(buf, sdu->data, sdu->len);
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 15:19:51 +02:00
|
|
|
return bt_mesh_net_send(tx, buf, cb, cb_data);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool bt_mesh_tx_in_progress(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
|
|
|
|
if (seg_tx[i].nack_count) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void seg_tx_reset(struct seg_tx *tx)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
k_delayed_work_cancel(&tx->retransmit);
|
|
|
|
|
|
|
|
tx->cb = NULL;
|
|
|
|
tx->cb_data = NULL;
|
|
|
|
tx->seq_auth = 0;
|
|
|
|
tx->sub = NULL;
|
2017-10-28 21:50:29 +02:00
|
|
|
tx->dst = BT_MESH_ADDR_UNASSIGNED;
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
if (!tx->nack_count) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i <= tx->seg_n; i++) {
|
|
|
|
if (!tx->seg[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_unref(tx->seg[i]);
|
|
|
|
tx->seg[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx->nack_count = 0;
|
|
|
|
|
|
|
|
if (bt_mesh.pending_update) {
|
|
|
|
BT_DBG("Proceding with pending IV Update");
|
|
|
|
bt_mesh.pending_update = 0;
|
2017-11-24 21:43:19 +02:00
|
|
|
/* bt_mesh_net_iv_update() will re-enable the flag if this
|
2017-06-16 12:30:54 +03:00
|
|
|
* wasn't the only transfer.
|
|
|
|
*/
|
2017-11-30 13:27:49 +02:00
|
|
|
if (bt_mesh_net_iv_update(bt_mesh.iv_index, false)) {
|
|
|
|
bt_mesh_net_sec_update(NULL);
|
|
|
|
}
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void seg_tx_complete(struct seg_tx *tx, int err)
|
|
|
|
{
|
2017-11-17 15:19:51 +02:00
|
|
|
if (tx->cb && tx->cb->end) {
|
|
|
|
tx->cb->end(err, tx->cb_data);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
seg_tx_reset(tx);
|
|
|
|
}
|
|
|
|
|
2017-11-18 13:10:42 +02:00
|
|
|
static void seg_send_start(u16_t duration, int err, void *user_data)
|
|
|
|
{
|
|
|
|
struct seg_tx *tx = user_data;
|
|
|
|
|
|
|
|
if (tx->cb && tx->cb->start) {
|
|
|
|
tx->cb->start(duration, err, tx->cb_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 12:40:18 +02:00
|
|
|
static void seg_sent(int err, void *user_data)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
2017-11-17 11:58:06 +02:00
|
|
|
struct seg_tx *tx = user_data;
|
2017-06-16 12:30:54 +03:00
|
|
|
|
2017-11-17 12:40:18 +02:00
|
|
|
k_delayed_work_submit(&tx->retransmit, SEG_RETRANSMIT_TIMEOUT);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
2017-11-18 13:10:42 +02:00
|
|
|
static const struct bt_mesh_send_cb first_sent_cb = {
|
|
|
|
.start = seg_send_start,
|
|
|
|
.end = seg_sent,
|
|
|
|
};
|
|
|
|
|
2017-11-17 15:19:51 +02:00
|
|
|
static const struct bt_mesh_send_cb seg_sent_cb = {
|
|
|
|
.end = seg_sent,
|
2017-11-17 12:40:18 +02:00
|
|
|
};
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
static void seg_tx_send_unacked(struct seg_tx *tx)
|
|
|
|
{
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
for (i = 0; i <= tx->seg_n; i++) {
|
|
|
|
struct net_buf *seg = tx->seg[i];
|
|
|
|
|
|
|
|
if (!seg) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BT_MESH_ADV(seg)->busy) {
|
|
|
|
BT_DBG("Skipping segment that's still advertising");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(BT_MESH_ADV(seg)->seg.attempts--)) {
|
|
|
|
BT_ERR("Ran out of retransmit attempts");
|
|
|
|
seg_tx_complete(tx, -ETIMEDOUT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("resending %u/%u", i, tx->seg_n);
|
|
|
|
|
2017-11-17 12:40:18 +02:00
|
|
|
err = bt_mesh_net_resend(tx->sub, seg, tx->new_key,
|
|
|
|
&seg_sent_cb, tx);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Sending segment failed");
|
|
|
|
seg_tx_complete(tx, -EIO);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void seg_retransmit(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct seg_tx *tx = CONTAINER_OF(work, struct seg_tx, retransmit);
|
|
|
|
|
|
|
|
seg_tx_send_unacked(tx);
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
static int send_seg(struct bt_mesh_net_tx *net_tx, struct net_buf_simple *sdu,
|
2017-11-17 15:19:51 +02:00
|
|
|
const struct bt_mesh_send_cb *cb, void *cb_data)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
u8_t seg_hdr, seg_o;
|
|
|
|
u16_t seq_zero;
|
|
|
|
struct seg_tx *tx;
|
|
|
|
int i;
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x aszmic %u sdu_len %u",
|
|
|
|
net_tx->src, net_tx->ctx->addr, net_tx->ctx->app_idx,
|
|
|
|
net_tx->aszmic, sdu->len);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
if (sdu->len < 1) {
|
|
|
|
BT_ERR("Zero-length SDU not allowed");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sdu->len > BT_MESH_TX_SDU_MAX) {
|
|
|
|
BT_ERR("Not enough segment buffers for length %u", sdu->len);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (tx = NULL, i = 0; i < ARRAY_SIZE(seg_tx); i++) {
|
|
|
|
if (!seg_tx[i].nack_count) {
|
|
|
|
tx = &seg_tx[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tx) {
|
|
|
|
BT_ERR("No multi-segment message contexts available");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_tx->ctx->app_idx == BT_MESH_KEY_DEV) {
|
|
|
|
seg_hdr = SEG_HDR(0, 0);
|
|
|
|
} else {
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
seg_hdr = SEG_HDR(1, net_tx->aid);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
seg_o = 0;
|
2017-10-28 21:50:29 +02:00
|
|
|
tx->dst = net_tx->ctx->addr;
|
2017-06-16 12:30:54 +03:00
|
|
|
tx->seg_n = (sdu->len - 1) / 12;
|
|
|
|
tx->nack_count = tx->seg_n + 1;
|
|
|
|
tx->seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_TX, bt_mesh.seq);
|
|
|
|
tx->sub = net_tx->sub;
|
2017-11-05 23:16:44 +02:00
|
|
|
tx->new_key = net_tx->sub->kr_flag;
|
2017-11-12 15:31:19 +02:00
|
|
|
tx->cb = cb;
|
|
|
|
tx->cb_data = cb_data;
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
seq_zero = tx->seq_auth & 0x1fff;
|
|
|
|
|
|
|
|
BT_DBG("SeqZero 0x%04x", seq_zero);
|
|
|
|
|
|
|
|
for (seg_o = 0; sdu->len; seg_o++) {
|
|
|
|
struct net_buf *seg;
|
|
|
|
u16_t len;
|
|
|
|
int err;
|
|
|
|
|
2017-11-09 19:58:25 +02:00
|
|
|
seg = bt_mesh_adv_create(BT_MESH_ADV_DATA,
|
2017-11-12 22:19:28 +02:00
|
|
|
BT_MESH_TRANSMIT_COUNT(net_tx->xmit),
|
|
|
|
BT_MESH_TRANSMIT_INT(net_tx->xmit),
|
2017-11-09 19:58:25 +02:00
|
|
|
BUF_TIMEOUT);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (!seg) {
|
|
|
|
BT_ERR("Out of segment buffers");
|
|
|
|
seg_tx_reset(tx);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_MESH_ADV(seg)->seg.attempts = SEG_RETRANSMIT_ATTEMPTS;
|
|
|
|
|
2017-10-28 21:46:49 +02:00
|
|
|
net_buf_reserve(seg, BT_MESH_NET_HDR_LEN);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
net_buf_add_u8(seg, seg_hdr);
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
net_buf_add_u8(seg, (net_tx->aszmic << 7) | seq_zero >> 6);
|
2017-06-16 12:30:54 +03:00
|
|
|
net_buf_add_u8(seg, (((seq_zero & 0x3f) << 2) |
|
|
|
|
(seg_o >> 3)));
|
|
|
|
net_buf_add_u8(seg, ((seg_o & 0x07) << 5) | tx->seg_n);
|
|
|
|
|
|
|
|
len = min(sdu->len, 12);
|
|
|
|
net_buf_add_mem(seg, sdu->data, len);
|
|
|
|
net_buf_simple_pull(sdu, len);
|
|
|
|
|
|
|
|
tx->seg[seg_o] = net_buf_ref(seg);
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_DBG("Sending %u/%u", seg_o, tx->seg_n);
|
|
|
|
|
2017-11-18 13:10:42 +02:00
|
|
|
err = bt_mesh_net_send(net_tx, seg,
|
|
|
|
seg_o ? &seg_sent_cb : &first_sent_cb,
|
|
|
|
tx);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Sending segment failed");
|
|
|
|
seg_tx_reset(tx);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bt_mesh_lpn_established()) {
|
2017-11-23 09:44:49 +02:00
|
|
|
bt_mesh_lpn_poll();
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct bt_mesh_app_key *bt_mesh_app_key_find(u16_t app_idx)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
|
|
|
|
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
|
|
|
|
|
|
|
|
if (key->net_idx != BT_MESH_KEY_UNUSED &&
|
|
|
|
key->app_idx == app_idx) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
|
2017-11-17 15:19:51 +02:00
|
|
|
const struct bt_mesh_send_cb *cb, void *cb_data)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
const u8_t *key;
|
|
|
|
u8_t *ad;
|
|
|
|
int err;
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
if (net_buf_simple_tailroom(msg) < 4) {
|
|
|
|
BT_ERR("Insufficient tailroom for Transport MIC");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->len > 11) {
|
|
|
|
tx->ctx->send_rel = 1;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->sub->net_idx,
|
|
|
|
tx->ctx->app_idx, tx->ctx->addr);
|
|
|
|
BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len));
|
|
|
|
|
|
|
|
if (tx->ctx->app_idx == BT_MESH_KEY_DEV) {
|
|
|
|
key = bt_mesh.dev_key;
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
tx->aid = 0;
|
2017-06-16 12:30:54 +03:00
|
|
|
} else {
|
|
|
|
struct bt_mesh_app_key *app_key;
|
|
|
|
|
|
|
|
app_key = bt_mesh_app_key_find(tx->ctx->app_idx);
|
|
|
|
if (!app_key) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx->sub->kr_phase == BT_MESH_KR_PHASE_2 &&
|
|
|
|
app_key->updated) {
|
|
|
|
key = app_key->keys[1].val;
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
tx->aid = app_key->keys[1].id;
|
2017-06-16 12:30:54 +03:00
|
|
|
} else {
|
|
|
|
key = app_key->keys[0].val;
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
tx->aid = app_key->keys[0].id;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
if (!tx->ctx->send_rel || net_buf_simple_tailroom(msg) < 8) {
|
|
|
|
tx->aszmic = 0;
|
2017-06-16 12:30:54 +03:00
|
|
|
} else {
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
tx->aszmic = 1;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (BT_MESH_ADDR_IS_VIRTUAL(tx->ctx->addr)) {
|
|
|
|
ad = bt_mesh_label_uuid_get(tx->ctx->addr);
|
|
|
|
} else {
|
|
|
|
ad = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_mesh_app_encrypt(key, tx->ctx->app_idx == BT_MESH_KEY_DEV,
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
tx->aszmic, msg, ad, tx->src,
|
2017-06-16 12:30:54 +03:00
|
|
|
tx->ctx->addr, bt_mesh.seq,
|
|
|
|
BT_MESH_NET_IVI_TX);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
if (tx->ctx->send_rel) {
|
|
|
|
err = send_seg(tx, msg, cb, cb_data);
|
|
|
|
} else {
|
|
|
|
err = send_unseg(tx, msg, cb, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_mesh_trans_resend(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
|
|
|
|
const struct bt_mesh_send_cb *cb, void *cb_data)
|
|
|
|
{
|
|
|
|
struct net_buf_simple_state state;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
net_buf_simple_save(msg, &state);
|
|
|
|
|
|
|
|
if (tx->ctx->send_rel || msg->len > 15) {
|
|
|
|
err = send_seg(tx, msg, cb, cb_data);
|
|
|
|
} else {
|
|
|
|
err = send_unseg(tx, msg, cb, cb_data);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
net_buf_simple_restore(msg, &state);
|
|
|
|
|
|
|
|
return err;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_replay(struct bt_mesh_net_rx *rx)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
|
|
|
|
struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i];
|
|
|
|
|
|
|
|
/* Empty slot */
|
|
|
|
if (!rpl->src) {
|
|
|
|
rpl->src = rx->ctx.addr;
|
|
|
|
rpl->seq = rx->seq;
|
|
|
|
rpl->old_iv = rx->old_iv;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Existing slot for given address */
|
|
|
|
if (rpl->src == rx->ctx.addr) {
|
|
|
|
if (rx->old_iv && !rpl->old_iv) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-29 13:09:51 +02:00
|
|
|
if ((!rx->old_iv && rpl->old_iv) ||
|
|
|
|
rpl->seq < rx->seq) {
|
2017-06-16 12:30:54 +03:00
|
|
|
rpl->seq = rx->seq;
|
|
|
|
rpl->old_iv = rx->old_iv;
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_ERR("RPL is full!");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
static int sdu_recv(struct bt_mesh_net_rx *rx, u8_t hdr, u8_t aszmic,
|
|
|
|
struct net_buf_simple *buf)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
struct net_buf_simple *sdu =
|
2017-08-09 09:21:11 +03:00
|
|
|
NET_BUF_SIMPLE(CONFIG_BT_MESH_RX_SDU_MAX - 4);
|
2017-06-16 12:30:54 +03:00
|
|
|
u8_t *ad;
|
|
|
|
u16_t i;
|
|
|
|
int err;
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr));
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len));
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
if (buf->len < 1 + APP_MIC_LEN(aszmic)) {
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_ERR("Too short SDU + MIC");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
if (BT_MESH_ADDR_IS_VIRTUAL(rx->dst)) {
|
|
|
|
ad = bt_mesh_label_uuid_get(rx->dst);
|
|
|
|
} else {
|
|
|
|
ad = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Adjust the length to not contain the MIC at the end */
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
buf->len -= APP_MIC_LEN(aszmic);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
if (!AKF(&hdr)) {
|
|
|
|
net_buf_simple_init(sdu, 0);
|
|
|
|
err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, aszmic, buf,
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
sdu, ad, rx->ctx.addr, rx->dst,
|
|
|
|
rx->seq, BT_MESH_NET_IVI_RX(rx));
|
2017-06-16 12:30:54 +03:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Unable to decrypt with DevKey");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rx->ctx.app_idx = BT_MESH_KEY_DEV;
|
|
|
|
bt_mesh_model_recv(rx, sdu);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
|
|
|
|
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
|
|
|
|
struct bt_mesh_app_keys *keys;
|
|
|
|
|
|
|
|
/* Check that this AppKey matches received net_idx */
|
|
|
|
if (key->net_idx != rx->sub->net_idx) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx->new_key && key->updated) {
|
|
|
|
keys = &key->keys[1];
|
|
|
|
} else {
|
|
|
|
keys = &key->keys[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that the AppKey ID matches */
|
|
|
|
if (AID(&hdr) != keys->id) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_simple_init(sdu, 0);
|
|
|
|
err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf,
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
sdu, ad, rx->ctx.addr, rx->dst,
|
|
|
|
rx->seq, BT_MESH_NET_IVI_RX(rx));
|
2017-06-16 12:30:54 +03:00
|
|
|
if (err) {
|
|
|
|
BT_WARN("Unable to decrypt with AppKey %u", i);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
rx->ctx.app_idx = key->app_idx;
|
|
|
|
|
|
|
|
bt_mesh_model_recv(rx, sdu);
|
2017-09-07 12:40:57 -06:00
|
|
|
return 0;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
BT_WARN("No matching AppKey");
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:50:29 +02:00
|
|
|
static struct seg_tx *seg_tx_lookup(u16_t seq_zero, u8_t obo, u16_t addr)
|
|
|
|
{
|
|
|
|
struct seg_tx *tx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
|
|
|
|
tx = &seg_tx[i];
|
|
|
|
|
|
|
|
if ((tx->seq_auth & 0x1fff) != seq_zero) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx->dst == addr) {
|
|
|
|
return tx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the expected remote address doesn't match,
|
|
|
|
* but the OBO flag is set and this is the first
|
|
|
|
* acknowledgement, assume it's a Friend that's
|
|
|
|
* responding and therefore accept the message.
|
|
|
|
*/
|
|
|
|
if (obo && tx->nack_count == tx->seg_n + 1) {
|
|
|
|
tx->dst = addr;
|
|
|
|
return tx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr,
|
2017-10-31 16:16:28 +02:00
|
|
|
struct net_buf_simple *buf, u64_t *seq_auth)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
struct seg_tx *tx;
|
|
|
|
unsigned int bit;
|
|
|
|
u32_t ack;
|
|
|
|
u16_t seq_zero;
|
|
|
|
u8_t obo;
|
|
|
|
|
|
|
|
if (buf->len < 6) {
|
|
|
|
BT_ERR("Too short ack message");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
seq_zero = net_buf_simple_pull_be16(buf);
|
|
|
|
obo = seq_zero >> 15;
|
|
|
|
seq_zero = (seq_zero >> 2) & 0x1fff;
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
ack = net_buf_simple_pull_be32(buf);
|
|
|
|
|
|
|
|
BT_DBG("OBO %u seq_zero 0x%04x ack 0x%08x", obo, seq_zero, ack);
|
|
|
|
|
2017-10-28 21:50:29 +02:00
|
|
|
tx = seg_tx_lookup(seq_zero, obo, rx->ctx.addr);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (!tx) {
|
2017-10-28 21:50:29 +02:00
|
|
|
BT_WARN("No matching TX context for ack");
|
2017-06-16 12:30:54 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
*seq_auth = tx->seq_auth;
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
if (!ack) {
|
|
|
|
BT_WARN("SDU canceled");
|
|
|
|
seg_tx_complete(tx, -ECANCELED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (find_msb_set(ack) - 1 > tx->seg_n) {
|
|
|
|
BT_ERR("Too large segment number in ack");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_delayed_work_cancel(&tx->retransmit);
|
|
|
|
|
|
|
|
while ((bit = find_lsb_set(ack))) {
|
|
|
|
if (tx->seg[bit - 1]) {
|
|
|
|
BT_DBG("seg %u/%u acked", bit - 1, tx->seg_n);
|
|
|
|
net_buf_unref(tx->seg[bit - 1]);
|
|
|
|
tx->seg[bit - 1] = NULL;
|
|
|
|
tx->nack_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
ack &= ~BIT(bit - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx->nack_count) {
|
|
|
|
seg_tx_send_unacked(tx);
|
|
|
|
} else {
|
|
|
|
BT_DBG("SDU TX complete");
|
|
|
|
seg_tx_complete(tx, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int trans_heartbeat(struct bt_mesh_net_rx *rx,
|
|
|
|
struct net_buf_simple *buf)
|
|
|
|
{
|
|
|
|
u8_t init_ttl, hops;
|
|
|
|
u16_t feat;
|
|
|
|
|
|
|
|
if (buf->len < 3) {
|
|
|
|
BT_ERR("Too short heartbeat message");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-11-02 13:30:46 +02:00
|
|
|
if (rx->dst != hb_sub_dst) {
|
|
|
|
BT_WARN("Ignoring heartbeat to non-subscribed destination");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
init_ttl = (net_buf_simple_pull_u8(buf) & 0x7f);
|
|
|
|
feat = net_buf_simple_pull_be16(buf);
|
|
|
|
|
|
|
|
hops = (init_ttl - rx->ctx.recv_ttl + 1);
|
|
|
|
|
|
|
|
BT_DBG("src 0x%04x TTL %u InitTTL %u (%u hop%s) feat 0x%04x",
|
|
|
|
rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops,
|
|
|
|
(hops == 1) ? "" : "s", feat);
|
|
|
|
|
|
|
|
bt_mesh_heartbeat(rx->ctx.addr, rx->dst, hops, feat);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr,
|
2017-10-31 16:16:28 +02:00
|
|
|
struct net_buf_simple *buf, u64_t *seq_auth)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
2017-10-30 14:56:16 +02:00
|
|
|
u8_t ctl_op = TRANS_CTL_OP(&hdr);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
BT_DBG("OpCode 0x%02x len %u", ctl_op, buf->len);
|
|
|
|
|
|
|
|
switch (ctl_op) {
|
|
|
|
case TRANS_CTL_OP_ACK:
|
2017-10-31 16:16:28 +02:00
|
|
|
return trans_ack(rx, hdr, buf, seq_auth);
|
2017-11-02 13:30:46 +02:00
|
|
|
case TRANS_CTL_OP_HEARTBEAT:
|
|
|
|
return trans_heartbeat(rx, buf);
|
2017-10-31 16:16:28 +02:00
|
|
|
}
|
|
|
|
|
2017-11-02 13:30:46 +02:00
|
|
|
/* Only acks and heartbeats may need processing without local_match */
|
|
|
|
if (!rx->local_match) {
|
2017-10-31 16:16:28 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-09 23:45:40 +02:00
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !bt_mesh_lpn_established()) {
|
2017-10-31 16:16:28 +02:00
|
|
|
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);
|
2017-11-09 23:45:40 +02:00
|
|
|
case TRANS_CTL_OP_FRIEND_CLEAR_CFM:
|
|
|
|
return bt_mesh_friend_clear_cfm(rx, buf);
|
2017-10-31 16:16:28 +02:00
|
|
|
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);
|
|
|
|
}
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#if defined(CONFIG_BT_MESH_LOW_POWER)
|
2017-06-16 12:30:54 +03:00
|
|
|
if (ctl_op == TRANS_CTL_OP_FRIEND_OFFER) {
|
|
|
|
return bt_mesh_lpn_friend_offer(rx, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx->ctx.addr == bt_mesh.lpn.frnd) {
|
|
|
|
if (ctl_op == TRANS_CTL_OP_FRIEND_CLEAR_CFM) {
|
|
|
|
return bt_mesh_lpn_friend_clear_cfm(rx, buf);
|
|
|
|
}
|
|
|
|
|
2017-11-09 20:35:19 +02:00
|
|
|
if (!rx->friend_cred) {
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_WARN("Message from friend with wrong credentials");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ctl_op) {
|
|
|
|
case TRANS_CTL_OP_FRIEND_UPDATE:
|
|
|
|
return bt_mesh_lpn_friend_update(rx, buf);
|
|
|
|
case TRANS_CTL_OP_FRIEND_SUB_CFM:
|
|
|
|
return bt_mesh_lpn_friend_sub_cfm(rx, buf);
|
|
|
|
}
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
#endif /* CONFIG_BT_MESH_LOW_POWER */
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
BT_WARN("Unhandled TransOpCode 0x%02x", ctl_op);
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
|
|
|
|
u64_t *seq_auth)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
u8_t hdr;
|
|
|
|
|
|
|
|
BT_DBG("AFK %u AID 0x%02x", AKF(buf->data), AID(buf->data));
|
|
|
|
|
|
|
|
if (buf->len < 1) {
|
|
|
|
BT_ERR("Too small unsegmented PDU");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-11-04 22:50:56 +02:00
|
|
|
if (rx->local_match && is_replay(rx)) {
|
|
|
|
BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
|
|
|
|
rx->ctx.addr, rx->dst, rx->seq);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
hdr = net_buf_simple_pull_u8(buf);
|
|
|
|
|
|
|
|
if (rx->ctl) {
|
2017-10-31 16:16:28 +02:00
|
|
|
return ctl_recv(rx, hdr, buf, seq_auth);
|
2017-06-16 12:30:54 +03:00
|
|
|
} else {
|
2017-11-02 13:30:46 +02:00
|
|
|
/* SDUs must match a local element or an LPN of this Friend. */
|
|
|
|
if (!rx->local_match && !rx->friend_match) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
return sdu_recv(rx, hdr, 0, buf);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline s32_t ack_timeout(struct seg_rx *rx)
|
|
|
|
{
|
|
|
|
s32_t to;
|
|
|
|
|
2017-11-05 16:53:53 +02:00
|
|
|
/* The acknowledgment timer shall be set to a minimum of
|
|
|
|
* 150 + 50 * TTL milliseconds.
|
|
|
|
*/
|
|
|
|
to = K_MSEC(150 + (50 * rx->ttl));
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
/* 100 ms for every not yet received segment */
|
2017-11-05 16:53:53 +02:00
|
|
|
to += K_MSEC(((rx->seg_n + 1) - popcount(rx->block)) * 100);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
/* Make sure we don't send more frequently than the duration for
|
|
|
|
* each packet (default is 300ms).
|
|
|
|
*/
|
|
|
|
return max(to, K_MSEC(400));
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data,
|
2017-11-17 12:40:18 +02:00
|
|
|
size_t data_len, u64_t *seq_auth,
|
2017-11-17 15:19:51 +02:00
|
|
|
const struct bt_mesh_send_cb *cb, void *cb_data)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
BT_DBG("src 0x%04x dst 0x%04x ttl 0x%02x ctl 0x%02x", tx->src,
|
|
|
|
tx->ctx->addr, tx->ctx->send_ttl, ctl_op);
|
|
|
|
BT_DBG("len %zu: %s", data_len, bt_hex(data, data_len));
|
|
|
|
|
2017-11-12 22:19:28 +02:00
|
|
|
buf = bt_mesh_adv_create(BT_MESH_ADV_DATA,
|
|
|
|
BT_MESH_TRANSMIT_COUNT(tx->xmit),
|
|
|
|
BT_MESH_TRANSMIT_INT(tx->xmit), BUF_TIMEOUT);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (!buf) {
|
|
|
|
BT_ERR("Out of transport buffers");
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:46:49 +02:00
|
|
|
net_buf_reserve(buf, BT_MESH_NET_HDR_LEN);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
2017-10-30 14:56:16 +02:00
|
|
|
net_buf_add_u8(buf, TRANS_CTL_HDR(ctl_op, 0));
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
net_buf_add_mem(buf, data, data_len);
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 12:40:18 +02:00
|
|
|
return bt_mesh_net_send(tx, buf, cb, cb_data);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst,
|
2017-10-31 16:16:28 +02:00
|
|
|
u8_t ttl, u64_t *seq_auth, u32_t block, u8_t obo)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
struct bt_mesh_msg_ctx ctx = {
|
|
|
|
.net_idx = sub->net_idx,
|
|
|
|
.app_idx = BT_MESH_KEY_UNUSED,
|
|
|
|
.addr = dst,
|
|
|
|
.send_ttl = ttl,
|
|
|
|
};
|
|
|
|
struct bt_mesh_net_tx tx = {
|
|
|
|
.sub = sub,
|
|
|
|
.ctx = &ctx,
|
2017-10-31 16:16:28 +02:00
|
|
|
.src = obo ? bt_mesh_primary_addr() : src,
|
2017-11-09 19:58:25 +02:00
|
|
|
.xmit = bt_mesh_net_transmit_get(),
|
2017-06-16 12:30:54 +03:00
|
|
|
};
|
2017-10-31 16:16:28 +02:00
|
|
|
u16_t seq_zero = *seq_auth & 0x1fff;
|
2017-06-16 12:30:54 +03:00
|
|
|
u8_t buf[6];
|
|
|
|
|
2017-11-28 09:48:48 +02:00
|
|
|
BT_DBG("SeqZero 0x%04x Block 0x%08x OBO %u", seq_zero, block, obo);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
if (bt_mesh_lpn_established()) {
|
|
|
|
BT_WARN("Not sending ack when LPN is enabled");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This can happen if the segmented message was destined for a group
|
|
|
|
* or virtual address.
|
|
|
|
*/
|
|
|
|
if (!BT_MESH_ADDR_IS_UNICAST(src)) {
|
|
|
|
BT_WARN("Not sending ack for non-unicast address");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
sys_put_be16(((seq_zero << 2) & 0x7ffc) | (obo << 15), buf);
|
2017-06-16 12:30:54 +03:00
|
|
|
sys_put_be32(block, &buf[2]);
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_ACK, buf, sizeof(buf),
|
2017-11-17 11:58:06 +02:00
|
|
|
NULL, NULL, NULL);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void seg_rx_reset(struct seg_rx *rx)
|
|
|
|
{
|
|
|
|
BT_DBG("rx %p", rx);
|
|
|
|
|
|
|
|
k_delayed_work_cancel(&rx->ack);
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
rx->in_use = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void seg_ack(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct seg_rx *rx = CONTAINER_OF(work, struct seg_rx, ack);
|
|
|
|
|
|
|
|
BT_DBG("rx %p", rx);
|
|
|
|
|
2018-01-28 12:15:14 -08:00
|
|
|
if (k_uptime_get_32() - rx->last > K_SECONDS(60)) {
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_WARN("Incomplete timer expired");
|
|
|
|
send_ack(rx->sub, rx->dst, rx->src, rx->ttl,
|
2017-10-31 16:16:28 +02:00
|
|
|
&rx->seq_auth, 0, rx->obo);
|
2017-06-16 12:30:54 +03:00
|
|
|
seg_rx_reset(rx);
|
2018-01-02 11:26:08 +01:00
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_BT_TESTING)) {
|
|
|
|
bt_test_mesh_trans_incomp_timer_exp();
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
send_ack(rx->sub, rx->dst, rx->src, rx->ttl, &rx->seq_auth,
|
|
|
|
rx->block, rx->obo);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
k_delayed_work_submit(&rx->ack, ack_timeout(rx));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u8_t seg_len(bool ctl)
|
|
|
|
{
|
|
|
|
if (ctl) {
|
|
|
|
return 8;
|
|
|
|
} else {
|
|
|
|
return 12;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool sdu_len_is_ok(bool ctl, u8_t seg_n)
|
|
|
|
{
|
2017-08-09 09:21:11 +03:00
|
|
|
return ((seg_n * seg_len(ctl) + 1) <= CONFIG_BT_MESH_RX_SDU_MAX);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx,
|
|
|
|
const u64_t *seq_auth)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
|
|
|
|
struct seg_rx *rx = &seg_rx[i];
|
|
|
|
|
|
|
|
if (rx->src != net_rx->ctx.addr || rx->dst != net_rx->dst) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-01-27 11:59:10 -08:00
|
|
|
/* Return newer RX context in addition to an exact match, so
|
|
|
|
* the calling function can properly discard an old SeqAuth.
|
|
|
|
*/
|
|
|
|
if (rx->seq_auth >= *seq_auth) {
|
2017-06-16 12:30:54 +03:00
|
|
|
return rx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx->in_use) {
|
|
|
|
BT_WARN("Duplicate SDU from src 0x%04x",
|
|
|
|
net_rx->ctx.addr);
|
|
|
|
|
|
|
|
/* Clear out the old context since the sender
|
|
|
|
* has apparently started sending a new SDU.
|
|
|
|
*/
|
|
|
|
seg_rx_reset(rx);
|
2017-10-31 16:16:28 +02:00
|
|
|
|
|
|
|
/* Return non-match so caller can re-allocate */
|
|
|
|
return NULL;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool seg_rx_is_valid(struct seg_rx *rx, struct bt_mesh_net_rx *net_rx,
|
|
|
|
const u8_t *hdr, u8_t seg_n)
|
|
|
|
{
|
|
|
|
if (rx->hdr != *hdr || rx->seg_n != seg_n) {
|
|
|
|
BT_ERR("Invalid segment for ongoing session");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx->src != net_rx->ctx.addr || rx->dst != net_rx->dst) {
|
|
|
|
BT_ERR("Invalid source or destination for segment");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx->ctl != net_rx->ctl) {
|
|
|
|
BT_ERR("Inconsistent CTL in segment");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx,
|
|
|
|
const u8_t *hdr, const u64_t *seq_auth,
|
|
|
|
u8_t seg_n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
|
|
|
|
struct seg_rx *rx = &seg_rx[i];
|
|
|
|
|
|
|
|
if (rx->in_use) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
rx->in_use = 1;
|
|
|
|
net_buf_simple_init(&rx->buf, 0);
|
|
|
|
rx->sub = net_rx->sub;
|
|
|
|
rx->ctl = net_rx->ctl;
|
|
|
|
rx->seq_auth = *seq_auth;
|
|
|
|
rx->seg_n = seg_n;
|
|
|
|
rx->hdr = *hdr;
|
|
|
|
rx->ttl = net_rx->ctx.send_ttl;
|
|
|
|
rx->src = net_rx->ctx.addr;
|
|
|
|
rx->dst = net_rx->dst;
|
|
|
|
rx->block = 0;
|
|
|
|
|
|
|
|
BT_DBG("New RX context. Block Complete 0x%08x",
|
|
|
|
BLOCK_COMPLETE(seg_n));
|
|
|
|
|
|
|
|
return rx;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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)
|
2017-06-16 12:30:54 +03:00
|
|
|
{
|
|
|
|
struct seg_rx *rx;
|
|
|
|
u8_t *hdr = buf->data;
|
|
|
|
u16_t seq_zero;
|
|
|
|
u8_t seg_n;
|
|
|
|
u8_t seg_o;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (buf->len < 5) {
|
|
|
|
BT_ERR("Too short segmented message (len %u)", buf->len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
BT_DBG("ASZMIC %u AKF %u AID 0x%02x", ASZMIC(hdr), AKF(hdr), AID(hdr));
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
net_buf_simple_pull(buf, 1);
|
|
|
|
|
|
|
|
seq_zero = net_buf_simple_pull_be16(buf);
|
|
|
|
seg_o = (seq_zero & 0x03) << 3;
|
|
|
|
seq_zero = (seq_zero >> 2) & 0x1fff;
|
|
|
|
seg_n = net_buf_simple_pull_u8(buf);
|
|
|
|
seg_o |= seg_n >> 5;
|
|
|
|
seg_n &= 0x1f;
|
|
|
|
|
|
|
|
BT_DBG("SeqZero 0x%04x SegO %u SegN %u", seq_zero, seg_o, seg_n);
|
|
|
|
|
|
|
|
if (seg_o > seg_n) {
|
|
|
|
BT_ERR("SegO greater than SegN (%u > %u)", seg_o, seg_n);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
*seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_RX(net_rx),
|
|
|
|
(net_rx->seq & 0xffffe000) | seq_zero);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
/* Look for old RX sessions */
|
2017-10-31 16:16:28 +02:00
|
|
|
rx = seg_rx_find(net_rx, seq_auth);
|
2017-06-16 12:30:54 +03:00
|
|
|
if (rx) {
|
2018-01-27 11:59:10 -08:00
|
|
|
/* Discard old SeqAuth packet */
|
|
|
|
if (rx->seq_auth > *seq_auth) {
|
|
|
|
BT_WARN("Ignoring old SeqAuth");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
if (!seg_rx_is_valid(rx, net_rx, hdr, seg_n)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rx->in_use) {
|
|
|
|
BT_DBG("Existing RX context. Block 0x%08x", rx->block);
|
|
|
|
goto found_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,
|
2017-10-31 16:16:28 +02:00
|
|
|
net_rx->ctx.send_ttl, seq_auth, rx->block,
|
|
|
|
rx->obo);
|
|
|
|
return -EALREADY;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We ignore instead of sending block ack 0 since the
|
|
|
|
* ack timer is always smaller than the incomplete
|
|
|
|
* timer, i.e. the sender is misbehaving.
|
|
|
|
*/
|
|
|
|
BT_WARN("Got segment for canceled SDU");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bail out early if we're not ready to receive such a large SDU */
|
2017-09-26 11:26:55 +03:00
|
|
|
if (!sdu_len_is_ok(net_rx->ctl, seg_n)) {
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_ERR("Too big incoming SDU length");
|
|
|
|
send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr,
|
2017-10-31 16:16:28 +02:00
|
|
|
net_rx->ctx.send_ttl, seq_auth, 0,
|
|
|
|
net_rx->friend_match);
|
|
|
|
return -EMSGSIZE;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Look for free slot for a new RX session */
|
2017-10-31 16:16:28 +02:00
|
|
|
rx = seg_rx_alloc(net_rx, hdr, seq_auth, seg_n);
|
2017-06-16 12:30:54 +03:00
|
|
|
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");
|
2017-10-31 16:16:28 +02:00
|
|
|
return -ENOMEM;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
rx->obo = net_rx->friend_match;
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
found_rx:
|
|
|
|
if (BIT(seg_o) & rx->block) {
|
|
|
|
BT_WARN("Received already received fragment");
|
2017-10-31 16:16:28 +02:00
|
|
|
return -EALREADY;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All segments, except the last one, must either have 8 bytes of
|
|
|
|
* payload (for 64bit Net MIC) or 12 bytes of payload (for 32bit
|
|
|
|
* Net MIC).
|
|
|
|
*/
|
|
|
|
if (seg_o == seg_n) {
|
|
|
|
/* Set the expected final buffer length */
|
|
|
|
rx->buf.len = seg_n * seg_len(rx->ctl) + buf->len;
|
|
|
|
BT_DBG("Target len %u * %u + %u = %u", seg_n, seg_len(rx->ctl),
|
|
|
|
buf->len, rx->buf.len);
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
if (rx->buf.len > CONFIG_BT_MESH_RX_SDU_MAX) {
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_ERR("Too large SDU len");
|
|
|
|
send_ack(net_rx->sub, net_rx->dst, net_rx->ctx.addr,
|
2017-10-31 16:16:28 +02:00
|
|
|
net_rx->ctx.send_ttl, seq_auth, 0, rx->obo);
|
2017-06-16 12:30:54 +03:00
|
|
|
seg_rx_reset(rx);
|
2017-10-31 16:16:28 +02:00
|
|
|
return -EMSGSIZE;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (buf->len != seg_len(rx->ctl)) {
|
|
|
|
BT_ERR("Incorrect segment size for message type");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset the Incomplete Timer */
|
|
|
|
rx->last = k_uptime_get_32();
|
|
|
|
|
|
|
|
if (!k_delayed_work_remaining_get(&rx->ack) &&
|
|
|
|
!bt_mesh_lpn_established()) {
|
|
|
|
k_delayed_work_submit(&rx->ack, ack_timeout(rx));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Location in buffer can be calculated based on seg_o & rx->ctl */
|
|
|
|
memcpy(rx->buf_data + (seg_o * seg_len(rx->ctl)), buf->data, buf->len);
|
|
|
|
|
|
|
|
BT_DBG("Received %u/%u", seg_o, seg_n);
|
|
|
|
|
|
|
|
/* Mark segment as received */
|
|
|
|
rx->block |= BIT(seg_o);
|
|
|
|
|
|
|
|
if (rx->block != BLOCK_COMPLETE(seg_n)) {
|
2017-10-31 16:16:28 +02:00
|
|
|
*pdu_type = BT_MESH_FRIEND_PDU_PARTIAL;
|
2017-06-16 12:30:54 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Complete SDU");
|
|
|
|
|
2017-11-04 22:50:56 +02:00
|
|
|
if (net_rx->local_match && is_replay(net_rx)) {
|
|
|
|
BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
|
|
|
|
net_rx->ctx.addr, net_rx->dst, net_rx->seq);
|
|
|
|
/* Clear the segment's bit */
|
|
|
|
rx->block &= ~BIT(seg_o);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
*pdu_type = BT_MESH_FRIEND_PDU_COMPLETE;
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
/* 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,
|
2017-10-31 16:16:28 +02:00
|
|
|
net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo);
|
2017-06-16 12:30:54 +03:00
|
|
|
|
|
|
|
if (net_rx->ctl) {
|
2017-10-31 16:16:28 +02:00
|
|
|
err = ctl_recv(net_rx, *hdr, &rx->buf, seq_auth);
|
2017-06-16 12:30:54 +03:00
|
|
|
} else {
|
Bluetooth: Mesh: Fix model publication
Model publication was broken in a couple of ways:
- The Publish Retransmit State was not taken into account at all
- Health Server used a single publish state for all elements
To implement Publish Retransmit properly, one has to use a callback to
track when the message has been sent. The problem with the transport
layer sending APIs was that giving a callback would cause the
transport layer to assume that segmentation (with acks) is desired,
which is not the case for Model Publication (unless the message itself
is too large, of course). Because of this, the message sending context
receives a new send_rel ("Send Reliable") boolean member that an app
can use to force reliable sending.
Another challenge with the Publish Retransmit state is that a buffer
is needed for storing the AppKey-encrypted SDU once it has been sent
out for the first time.To solve this, a new new net_buf_simple member
is added to the model publication context. The separate 'msg' input
parameter of the bt_mesh_model_publish() API is removed, since the
application is now expected to pre-fill pub->msg instead.
To help with the publishing API change, the Health Server model gets a
new helper macro for initializing the publishing context with a
right-sized publishing message.
The API for creating Health Server instances is also redesigned since
it was so far using a single model publishing state, which would
result in erratic behavior in case of multiple elements with the
Health Server Model. Now, the application needs to provide a unique
publishing context for each Health Server instance.
The changes are heavily intertwined, so it's not easily possible to
split them into multiple patches, hence the large(ish) patch.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2017-11-18 10:24:18 +02:00
|
|
|
err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), &rx->buf);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
seg_rx_reset(rx);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx)
|
|
|
|
{
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
2017-06-16 12:30:54 +03:00
|
|
|
int err;
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-02 13:30:46 +02:00
|
|
|
BT_DBG("src 0x%04x dst 0x%04x seq 0x%08x friend_match %u",
|
|
|
|
rx->ctx.addr, rx->dst, rx->seq, rx->friend_match);
|
2017-10-25 09:46:59 +02:00
|
|
|
|
|
|
|
/* Remove network headers */
|
|
|
|
net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_DBG("Payload %s", bt_hex(buf->data, buf->len));
|
|
|
|
|
2017-12-06 14:59:09 +01:00
|
|
|
if (IS_ENABLED(CONFIG_BT_TESTING)) {
|
|
|
|
bt_test_mesh_net_recv(rx->ctx.recv_ttl, rx->ctl, rx->ctx.addr,
|
|
|
|
rx->dst, buf->data, buf->len);
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
/* If LPN mode is enabled messages are only accepted when we've
|
|
|
|
* requested the Friend to send them. The messages must also
|
|
|
|
* be encrypted using the Friend Credentials.
|
|
|
|
*/
|
2017-08-09 09:21:11 +03:00
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
|
2017-11-18 12:04:25 +02:00
|
|
|
bt_mesh_lpn_established() && rx->net_if == BT_MESH_NET_IF_ADV &&
|
2017-11-09 20:35:19 +02:00
|
|
|
(!bt_mesh_lpn_waiting_update() || !rx->friend_cred)) {
|
2017-06-16 12:30:54 +03:00
|
|
|
BT_WARN("Ignoring unexpected message in Low Power mode");
|
2017-11-02 13:30:46 +02:00
|
|
|
return -EAGAIN;
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
2017-11-13 10:32:04 +02:00
|
|
|
/* Save the app-level state so the buffer can later be placed in
|
|
|
|
* the Friend Queue.
|
2017-10-31 16:16:28 +02:00
|
|
|
*/
|
|
|
|
net_buf_simple_save(buf, &state);
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
if (SEG(buf->data)) {
|
2017-11-02 13:30:46 +02:00
|
|
|
/* Segmented messages must match a local element or an
|
|
|
|
* LPN of this Friend.
|
|
|
|
*/
|
|
|
|
if (!rx->local_match && !rx->friend_match) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
err = trans_seg(buf, rx, &pdu_type, &seq_auth);
|
2017-06-16 12:30:54 +03:00
|
|
|
} else {
|
2017-10-31 16:16:28 +02:00
|
|
|
err = trans_unseg(buf, rx, &seq_auth);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Notify LPN state machine so a Friend Poll will be sent. If the
|
|
|
|
* message was a Friend Update it's possible that a Poll was already
|
|
|
|
* queued for sending, however that's fine since then the
|
|
|
|
* bt_mesh_lpn_waiting_update() function will return false:
|
|
|
|
* we still need to go through the actual sending to the bearer and
|
|
|
|
* wait for ReceiveDelay before transitioning to WAIT_UPDATE state.
|
2017-11-09 10:28:44 +02:00
|
|
|
* Another situation where we want to notify the LPN state machine
|
|
|
|
* is if it's configured to use an automatic Friendship establishment
|
|
|
|
* timer, in which case we want to reset the timer at this point.
|
|
|
|
*
|
2017-06-16 12:30:54 +03:00
|
|
|
*/
|
2018-01-04 14:45:19 +01:00
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
|
2017-11-09 10:28:44 +02:00
|
|
|
(bt_mesh_lpn_timer() ||
|
|
|
|
(bt_mesh_lpn_established() && bt_mesh_lpn_waiting_update()))) {
|
2017-06-16 12:30:54 +03:00
|
|
|
bt_mesh_lpn_msg_received(rx);
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:16:28 +02:00
|
|
|
net_buf_simple_restore(buf, &state);
|
|
|
|
|
2017-11-02 13:30:46 +02:00
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->friend_match && !err) {
|
2017-10-31 16:16:28 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 12:30:54 +03:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_mesh_rx_reset(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BT_DBG("");
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
|
2017-11-20 14:51:32 +02:00
|
|
|
seg_rx_reset(&seg_rx[i]);
|
|
|
|
seg_rx[i].src = BT_MESH_ADDR_UNASSIGNED;
|
|
|
|
seg_rx[i].dst = BT_MESH_ADDR_UNASSIGNED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_mesh_tx_reset(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BT_DBG("");
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
|
|
|
|
seg_tx_reset(&seg_tx[i]);
|
2017-06-16 12:30:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_mesh_trans_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
|
|
|
|
k_delayed_work_init(&seg_tx[i].retransmit, seg_retransmit);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
|
|
|
|
k_delayed_work_init(&seg_rx[i].ack, seg_ack);
|
|
|
|
}
|
|
|
|
}
|
2017-11-29 13:17:27 +02:00
|
|
|
|
|
|
|
void bt_mesh_rpl_clear(void)
|
|
|
|
{
|
|
|
|
BT_DBG("");
|
|
|
|
memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl));
|
|
|
|
}
|