Bluetooth: host: Fix RPA timeout expiration

Fix RPA timeout expiration when BT_EXT_ADV has been enabled.

Always invalidate the device RPA on RPA timeout.
This RPA is used by the scan roles, and the advertiser that was started
using the bt_le_adv_start API.

Call the RPA expire callback only on advertising sets that are enabled
and not being limited by a timeout and not using the identity address.
On RPA timeout always invalidate the RPA of advertising sets that are
disabled.

Fixes: #51208
Fixes: #51228
Fixes: #51247

Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
This commit is contained in:
Joakim Andersson 2022-10-13 11:28:10 +02:00 committed by Carles Cufí
commit d63ae607cc
2 changed files with 31 additions and 36 deletions

View file

@ -87,6 +87,8 @@ enum {
* of the RPA timeout. * of the RPA timeout.
*/ */
BT_ADV_RPA_VALID, BT_ADV_RPA_VALID,
/* The private random address of the advertiser is being updated. */
BT_ADV_RPA_UPDATE,
/* The advertiser set is limited by a timeout, or number of advertising /* The advertiser set is limited by a timeout, or number of advertising
* events, or both. * events, or both.
*/ */

View file

@ -198,9 +198,10 @@ int bt_id_set_adv_random_addr(struct bt_le_ext_adv *adv,
return 0; return 0;
} }
static void adv_rpa_invalidate(struct bt_le_ext_adv *adv, void *data) static void adv_disabled_rpa_invalidate(struct bt_le_ext_adv *adv, void *data)
{ {
if (!atomic_test_bit(adv->flags, BT_ADV_LIMITED)) { /* Don't invalidate RPA of enabled advertising set. */
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID); atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
} }
} }
@ -213,8 +214,13 @@ static void le_rpa_invalidate(void)
atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID);
} }
/* Invalidate RPA of advertising sets that are disabled.
*
* Enabled advertising set have to call rpa_expired callback first to
* determine if the RPA of the advertising set should be rotated.
*/
if (IS_ENABLED(CONFIG_BT_BROADCASTER)) { if (IS_ENABLED(CONFIG_BT_BROADCASTER)) {
bt_le_ext_adv_foreach(adv_rpa_invalidate, NULL); bt_le_ext_adv_foreach(adv_disabled_rpa_invalidate, NULL);
} }
} }
@ -417,19 +423,11 @@ int bt_id_set_adv_private_addr(struct bt_le_ext_adv *adv)
#if defined(CONFIG_BT_EXT_ADV) && defined(CONFIG_BT_PRIVACY) #if defined(CONFIG_BT_EXT_ADV) && defined(CONFIG_BT_PRIVACY)
static void adv_disable_rpa(struct bt_le_ext_adv *adv, void *data) static void adv_disable_rpa(struct bt_le_ext_adv *adv, void *data)
{ {
uint8_t adv_index = bt_le_ext_adv_get_index(adv);
bool *adv_disabled = data;
bool rpa_invalid = true; bool rpa_invalid = true;
adv_disabled[adv_index] = false;
/* Invalidate RPA only for non-limited advertising sets. */
if (atomic_test_bit(adv->flags, BT_ADV_LIMITED)) {
return;
}
/* Disable advertising sets to prepare them for RPA update. */ /* Disable advertising sets to prepare them for RPA update. */
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) && if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
!atomic_test_bit(adv->flags, BT_ADV_LIMITED) &&
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) { !atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
int err; int err;
@ -438,25 +436,27 @@ static void adv_disable_rpa(struct bt_le_ext_adv *adv, void *data)
BT_ERR("Failed to disable advertising (err %d)", err); BT_ERR("Failed to disable advertising (err %d)", err);
} }
adv_disabled[adv_index] = true; atomic_set_bit(adv->flags, BT_ADV_RPA_UPDATE);
}
/* Notify the user about the RPA timeout and set the RPA validity. */ /* Notify the user about the RPA timeout and set the RPA validity. */
if (adv->cb && adv->cb->rpa_expired) { if (adv->cb && adv->cb->rpa_expired) {
rpa_invalid = adv->cb->rpa_expired(adv); rpa_invalid = adv->cb->rpa_expired(adv);
} }
if (rpa_invalid) { if (rpa_invalid) {
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID); atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
} else {
/* Submit the timeout in case no advertising set updates their RPA
* in the current period. This makes sure that the RPA timer is running.
*/
le_rpa_timeout_submit();
}
} }
} }
static void adv_enable_rpa(struct bt_le_ext_adv *adv, void *data) static void adv_enable_rpa(struct bt_le_ext_adv *adv, void *data)
{ {
uint8_t adv_index = bt_le_ext_adv_get_index(adv); if (atomic_test_and_clear_bit(adv->flags, BT_ADV_RPA_UPDATE)) {
bool *adv_disabled = data;
if (adv_disabled[adv_index]) {
int err; int err;
err = bt_id_set_adv_private_addr(adv); err = bt_id_set_adv_private_addr(adv);
@ -476,16 +476,9 @@ static void adv_enable_rpa(struct bt_le_ext_adv *adv, void *data)
static void adv_update_rpa_foreach(void) static void adv_update_rpa_foreach(void)
{ {
#if defined(CONFIG_BT_EXT_ADV) && defined(CONFIG_BT_PRIVACY) #if defined(CONFIG_BT_EXT_ADV) && defined(CONFIG_BT_PRIVACY)
bool adv_disabled[CONFIG_BT_EXT_ADV_MAX_ADV_SET]; bt_le_ext_adv_foreach(adv_disable_rpa, NULL);
bt_le_ext_adv_foreach(adv_disable_rpa, adv_disabled); bt_le_ext_adv_foreach(adv_enable_rpa, NULL);
/* Submit the timeout in case all sets use the same
* RPA for the next rotation period.
*/
le_rpa_timeout_submit();
bt_le_ext_adv_foreach(adv_enable_rpa, adv_disabled);
#endif #endif
} }
@ -500,8 +493,6 @@ static void le_update_private_addr(void)
IS_ENABLED(CONFIG_BT_EXT_ADV) && IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features)) { BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
adv_update_rpa_foreach(); adv_update_rpa_foreach();
} else {
le_rpa_invalidate();
} }
#if defined(CONFIG_BT_OBSERVER) #if defined(CONFIG_BT_OBSERVER)
@ -566,6 +557,7 @@ static void le_force_rpa_timeout(void)
k_work_cancel_delayable_sync(&bt_dev.rpa_update, &sync); k_work_cancel_delayable_sync(&bt_dev.rpa_update, &sync);
#endif #endif
le_rpa_invalidate();
le_update_private_addr(); le_update_private_addr();
} }
@ -587,6 +579,8 @@ static void rpa_timeout(struct k_work *work)
} }
} }
le_rpa_invalidate();
if (IS_ENABLED(CONFIG_BT_BROADCASTER)) { if (IS_ENABLED(CONFIG_BT_BROADCASTER)) {
bt_le_ext_adv_foreach(adv_is_private_enabled, &adv_enabled); bt_le_ext_adv_foreach(adv_is_private_enabled, &adv_enabled);
} }
@ -596,7 +590,6 @@ static void rpa_timeout(struct k_work *work)
atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING) || atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING) ||
(atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) && (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) &&
atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)))) { atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)))) {
le_rpa_invalidate();
return; return;
} }