Skip to content

Commit

Permalink
Bluetooth: Mesh: Fix segmented message RPL behavior
Browse files Browse the repository at this point in the history
Update the Replay Protection List handling for segmented messages to
be more in line with Figure 3.43 in Mesh Profile Specification 1.0.
This means that the RPL check and update need to be split into two
independent steps rather than always doing these together.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
  • Loading branch information
jhedberg committed Jun 25, 2019
1 parent 69406e0 commit 4857cb8
Showing 1 changed file with 46 additions and 19 deletions.
65 changes: 46 additions & 19 deletions subsys/bluetooth/host/mesh/transport.c
Expand Up @@ -531,7 +531,23 @@ int bt_mesh_trans_resend(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
return err;
}

static bool is_replay(struct bt_mesh_net_rx *rx)
static void update_rpl(struct bt_mesh_rpl *rpl, struct bt_mesh_net_rx *rx)
{
rpl->src = rx->ctx.addr;
rpl->seq = rx->seq;
rpl->old_iv = rx->old_iv;

if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_rpl(rpl);
}
}

/* Check the Replay Protection List for a replay attempt. If non-NULL match
* parameter is given the RPL slot is returned but it is not immediately
* updated (needed for segmented messages), whereas if a NULL match is given
* the RPL is immediately updated (used for unsegmented messages).
*/
static bool is_replay(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match)
{
int i;

Expand All @@ -540,17 +556,20 @@ static bool is_replay(struct bt_mesh_net_rx *rx)
return false;
}

/* The RPL is used only for the local node */
if (!rx->local_match) {
return false;
}

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;

if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_rpl(rpl);
if (match) {
*match = rpl;
} else {
update_rpl(rpl, rx);
}

return false;
Expand All @@ -564,11 +583,10 @@ static bool is_replay(struct bt_mesh_net_rx *rx)

if ((!rx->old_iv && rpl->old_iv) ||
rpl->seq < rx->seq) {
rpl->seq = rx->seq;
rpl->old_iv = rx->old_iv;

if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_rpl(rpl);
if (match) {
*match = rpl;
} else {
update_rpl(rpl, rx);
}

return false;
Expand Down Expand Up @@ -879,7 +897,7 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
return -EINVAL;
}

if (rx->local_match && is_replay(rx)) {
if (is_replay(rx, NULL)) {
BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
rx->ctx.addr, rx->ctx.recv_dst, rx->seq);
return -EINVAL;
Expand Down Expand Up @@ -1160,6 +1178,7 @@ static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx,
static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx,
enum bt_mesh_friend_pdu_type *pdu_type, u64_t *seq_auth)
{
struct bt_mesh_rpl *rpl = NULL;
struct seg_rx *rx;
u8_t *hdr = buf->data;
u16_t seq_zero;
Expand All @@ -1172,6 +1191,12 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx,
return -EINVAL;
}

if (is_replay(net_rx, &rpl)) {
BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
net_rx->ctx.addr, net_rx->ctx.recv_dst, net_rx->seq);
return -EINVAL;
}

BT_DBG("ASZMIC %u AKF %u AID 0x%02x", ASZMIC(hdr), AKF(hdr), AID(hdr));

net_buf_simple_pull(buf, 1);
Expand Down Expand Up @@ -1228,9 +1253,15 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx,

if (rx->block == BLOCK_COMPLETE(rx->seg_n)) {
BT_WARN("Got segment for already complete SDU");

send_ack(net_rx->sub, net_rx->ctx.recv_dst,
net_rx->ctx.addr, net_rx->ctx.send_ttl,
seq_auth, rx->block, rx->obo);

if (rpl) {
update_rpl(rpl, net_rx);
}

return -EALREADY;
}

Expand Down Expand Up @@ -1318,12 +1349,8 @@ static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx,

BT_DBG("Complete SDU");

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->ctx.recv_dst, net_rx->seq);
/* Clear the segment's bit */
rx->block &= ~BIT(seg_o);
return -EINVAL;
if (rpl) {
update_rpl(rpl, net_rx);
}

*pdu_type = BT_MESH_FRIEND_PDU_COMPLETE;
Expand Down

0 comments on commit 4857cb8

Please sign in to comment.