From 21eafe6b914bfaaa8503760ed4910f2c56606d11 Mon Sep 17 00:00:00 2001 From: Aleksandr Khromykh Date: Tue, 19 Mar 2024 14:42:34 +0100 Subject: [PATCH] Bluetooth: Mesh: store rpl only if incoming data is accepted Commit prevents updating replay protection list on any frame came from the network layer. Replay protection cache is updated only if - transport handles transport command without errors - access layer handles model message without errors - transport frame has correct length Signed-off-by: Aleksandr Khromykh --- subsys/bluetooth/mesh/lpn.c | 3 +- subsys/bluetooth/mesh/rpl.c | 10 +++--- subsys/bluetooth/mesh/transport.c | 55 +++++++++++++++++-------------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/subsys/bluetooth/mesh/lpn.c b/subsys/bluetooth/mesh/lpn.c index f6f906b12fe..0d14c9c55e5 100644 --- a/subsys/bluetooth/mesh/lpn.c +++ b/subsys/bluetooth/mesh/lpn.c @@ -675,7 +675,7 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, err = send_friend_poll(); if (err) { - /* Will retry sending later */ + LOG_WRN("LPN didn't succeed poll sending (err %d)", err); for (int i = 0; i < ARRAY_SIZE(lpn->cred); i++) { if (lpn->sub->keys[i].valid) { bt_mesh_friend_cred_destroy(&lpn->cred[i]); @@ -686,7 +686,6 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, lpn->frnd = BT_MESH_ADDR_UNASSIGNED; lpn->recv_win = 0U; lpn->queue_size = 0U; - return err; } return 0; diff --git a/subsys/bluetooth/mesh/rpl.c b/subsys/bluetooth/mesh/rpl.c index df67ddf9d2e..2170a946671 100644 --- a/subsys/bluetooth/mesh/rpl.c +++ b/subsys/bluetooth/mesh/rpl.c @@ -102,12 +102,12 @@ void bt_mesh_rpl_update(struct bt_mesh_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). + * parameter is given the RPL slot is returned, but it is not immediately + * updated. This is used to prevent storing data in RPL that has been rejected + * by upper logic (access, transport commands) and for receiving the segmented messages. + * If a NULL match is given the RPL is immediately updated (used for proxy configuration). */ -bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, - struct bt_mesh_rpl **match) +bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match) { struct bt_mesh_rpl *rpl; int i; diff --git a/subsys/bluetooth/mesh/transport.c b/subsys/bluetooth/mesh/transport.c index a0364dceb25..66678911f1b 100644 --- a/subsys/bluetooth/mesh/transport.c +++ b/subsys/bluetooth/mesh/transport.c @@ -796,23 +796,22 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, uint8_t hdr, uint8_t aszmic, LOG_DBG("AKF %u AID 0x%02x", !ctx.crypto.dev_key, AID(&hdr)); if (!rx->local_match) { - return 0; + /* if friend_match was set the frame is for LPN which we are friends. */ + return rx->friend_match ? 0 : -ENXIO; } rx->ctx.app_idx = bt_mesh_app_key_find(ctx.crypto.dev_key, AID(&hdr), rx, sdu_try_decrypt, &ctx); if (rx->ctx.app_idx == BT_MESH_KEY_UNUSED) { LOG_DBG("No matching AppKey"); - return 0; + return -EACCES; } rx->ctx.uuid = ctx.crypto.ad; LOG_DBG("Decrypted (AppIdx: 0x%03x)", rx->ctx.app_idx); - (void)bt_mesh_model_recv(&rx->ctx, sdu); - - return 0; + return bt_mesh_access_recv(&rx->ctx, sdu); } static struct seg_tx *seg_tx_lookup(uint16_t seq_zero, uint8_t obo, uint16_t addr) @@ -1027,6 +1026,8 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx, { NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_SDU_UNSEG_MAX); uint8_t hdr; + struct bt_mesh_rpl *rpl = NULL; + int err; LOG_DBG("AFK %u AID 0x%02x", AKF(buf->data), AID(buf->data)); @@ -1035,7 +1036,7 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx, return -EBADMSG; } - if (bt_mesh_rpl_check(rx, NULL)) { + if (bt_mesh_rpl_check(rx, &rpl)) { LOG_WRN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", rx->ctx.addr, rx->ctx.recv_dst, rx->seq); return -EINVAL; @@ -1044,18 +1045,22 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx, hdr = net_buf_simple_pull_u8(buf); if (rx->ctl) { - return ctl_recv(rx, hdr, buf, seq_auth); - } - - if (buf->len < 1 + APP_MIC_LEN(0)) { + err = ctl_recv(rx, hdr, buf, seq_auth); + } else if (buf->len < 1 + APP_MIC_LEN(0)) { LOG_ERR("Too short SDU + MIC"); - return -EINVAL; + err = -EINVAL; + } else { + /* Adjust the length to not contain the MIC at the end */ + buf->len -= APP_MIC_LEN(0); + err = sdu_recv(rx, hdr, 0, buf, &sdu, NULL); } - /* Adjust the length to not contain the MIC at the end */ - buf->len -= APP_MIC_LEN(0); + /* Update rpl only if there is place and upper logic accepted incoming data. */ + if (err == 0 && rpl != NULL) { + bt_mesh_rpl_update(rpl, rx); + } - return sdu_recv(rx, hdr, 0, buf, &sdu, NULL); + return err; } int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, uint8_t ctl_op, void *data, @@ -1558,17 +1563,6 @@ found_rx: } LOG_DBG("Complete SDU"); - - if (rpl) { - bt_mesh_rpl_update(rpl, net_rx); - /* Update the seg, unless it has already been surpassed: - * This needs to happen after rpl_update to ensure that the IV - * update reset logic inside rpl_update doesn't overwrite the - * change. - */ - rpl->seg = MAX(rpl->seg, auth_seqnum); - } - *pdu_type = BT_MESH_FRIEND_PDU_COMPLETE; /* If this fails, the work handler will either exit early because the @@ -1602,6 +1596,17 @@ found_rx: err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), &seg_buf, &sdu, rx); } + /* Update rpl only if there is place and upper logic accepted incoming data. */ + if (err == 0 && rpl != NULL) { + bt_mesh_rpl_update(rpl, net_rx); + /* Update the seg, unless it has already been surpassed: + * This needs to happen after rpl_update to ensure that the IV + * update reset logic inside rpl_update doesn't overwrite the + * change. + */ + rpl->seg = MAX(rpl->seg, auth_seqnum); + } + seg_rx_reset(rx, false); return err;