mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-09-27 19:27:23 +00:00
feat(nimble): Added LFU method for device deletion post bond count overflow
This commit is contained in:
@@ -451,8 +451,71 @@ int ble_store_util_delete_all(int type, const union ble_store_key *key);
|
||||
int ble_store_util_delete_peer(const ble_addr_t *peer_id_addr);
|
||||
int ble_store_util_delete_oldest_peer(void);
|
||||
int ble_store_util_count(int type, int *out_count);
|
||||
|
||||
/**
|
||||
* @brief Round-robin status callback for handling store status events.
|
||||
*
|
||||
* This function handles store status events, particularly in cases where there
|
||||
* is insufficient storage capacity for new records.
|
||||
* It attempts to resolve overflow issues by deleting the oldest bond and
|
||||
* proceeds with the persist operation.
|
||||
*
|
||||
* @note This behavior may not be suitable for production use as it may lead to
|
||||
* removal of important bonds by less relevant peers. It is more useful for
|
||||
* demonstration purposes and sample applications.
|
||||
*
|
||||
* @note Retained for backward compatibility. It follows the eviction policy
|
||||
* selected at build time, so it removes the least recently used bond instead
|
||||
* of the oldest one when the least-recently-used policy is enabled. New
|
||||
* applications should use ble_store_util_status_overflow().
|
||||
*
|
||||
* @param event A pointer to the store status event.
|
||||
* @param arg A pointer to additional user-defined arguments.
|
||||
*
|
||||
* @return 0 on success;
|
||||
* Non-zero on error.
|
||||
*/
|
||||
int ble_store_util_status_rr(struct ble_store_status_event *event, void *arg);
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
/**
|
||||
* @brief Least-recently-used status callback for store overflow events.
|
||||
*
|
||||
* Removes the least recently used, disconnected bond when storage is full.
|
||||
* The peer associated with the record being written is never selected.
|
||||
*
|
||||
* @param event A pointer to the store status event.
|
||||
* @param arg A pointer to additional user-defined arguments.
|
||||
*
|
||||
* @return 0 on success;
|
||||
* Non-zero on error.
|
||||
*/
|
||||
int ble_store_util_status_lfu(struct ble_store_status_event *event, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Mark a bonded peer as recently used.
|
||||
*
|
||||
* Updates OUR_SEC recency so least-recently-used overflow eviction preserves
|
||||
* this bond. Safe to call when no bond exists yet (returns 0).
|
||||
*/
|
||||
int ble_store_util_touch_peer(const ble_addr_t *peer_id_addr);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configured status callback for handling store status events.
|
||||
*
|
||||
* Dispatches to the bond eviction policy selected at build time: round-robin
|
||||
* by default, or least recently used when BLE_STORE_OVERFLOW_LFU is enabled.
|
||||
*
|
||||
* @param event A pointer to the store status event.
|
||||
* @param arg A pointer to additional user-defined arguments.
|
||||
*
|
||||
* @return 0 on success;
|
||||
* Non-zero on error.
|
||||
*/
|
||||
int ble_store_util_status_overflow(struct ble_store_status_event *event,
|
||||
void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -41,6 +41,12 @@ struct ble_hs_resolv_entry {
|
||||
#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST)
|
||||
/* Persist peer records in NVS. XXX Need to handle this in `store` module */
|
||||
int ble_store_persist_peer_records(void);
|
||||
#else
|
||||
static inline int
|
||||
ble_store_persist_peer_records(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ble_hs_peer_sec {
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
#include "host/ble_store.h"
|
||||
#include "ble_hs_priv.h"
|
||||
#include "ble_hs_resolv_priv.h"
|
||||
#include "../store/config/src/ble_store_config_priv.h"
|
||||
#include "esp_nimble_mem.h"
|
||||
#include "host/ble_hs_log.h"
|
||||
|
||||
@@ -1449,6 +1448,10 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
|
||||
struct ble_sm_result res;
|
||||
struct ble_sm_proc *proc;
|
||||
bool terminate_conn;
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
#endif
|
||||
int authenticated;
|
||||
int bonded;
|
||||
int key_size;
|
||||
@@ -1560,6 +1563,16 @@ ble_sm_enc_event_rx(uint16_t conn_handle, uint8_t evt_status, int encrypted)
|
||||
ble_gap_terminate(conn_handle, BLE_ERR_AUTH_FAIL);
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
if (evt_status == 0 && encrypted &&
|
||||
ble_gap_conn_find(conn_handle, &desc) == 0) {
|
||||
rc = ble_store_util_touch_peer(&desc.peer_id_addr);
|
||||
if (rc != 0) {
|
||||
BLE_HS_LOG(WARN, "failed to update bond recency; rc=%d\n", rc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
res.bonded = bonded;
|
||||
ble_sm_process_result(conn_handle, &res, true);
|
||||
}
|
||||
|
||||
@@ -452,6 +452,7 @@ ble_store_util_delete_ead_oldest_peer(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !(MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS))
|
||||
/**
|
||||
* Round-robin status callback. If a there is insufficient storage capacity
|
||||
* for a new record, delete the oldest bond and proceed with the persist
|
||||
@@ -461,8 +462,8 @@ ble_store_util_delete_ead_oldest_peer(void)
|
||||
* uninteresting peers could cause important bonds to be deleted. This is
|
||||
* useful for demonstrations and sample apps.
|
||||
*/
|
||||
int
|
||||
ble_store_util_status_rr(struct ble_store_status_event *event, void *arg)
|
||||
static int
|
||||
ble_store_util_status_rr_evict(struct ble_store_status_event *event, void *arg)
|
||||
{
|
||||
switch (event->event_code) {
|
||||
case BLE_STORE_EVENT_OVERFLOW:
|
||||
@@ -496,3 +497,181 @@ ble_store_util_status_rr(struct ble_store_status_event *event, void *arg)
|
||||
return BLE_HS_EUNKNOWN;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
struct ble_store_util_lfu {
|
||||
const ble_addr_t *except_addr;
|
||||
ble_addr_t peer_addr;
|
||||
uint16_t bond_count;
|
||||
bool found;
|
||||
};
|
||||
|
||||
struct ble_store_util_conn_check {
|
||||
const ble_addr_t *peer_id_addr;
|
||||
bool found;
|
||||
};
|
||||
|
||||
static int
|
||||
ble_store_util_conn_check(struct ble_hs_conn *conn, void *arg)
|
||||
{
|
||||
struct ble_store_util_conn_check *check;
|
||||
struct ble_hs_conn_addrs addrs;
|
||||
|
||||
check = arg;
|
||||
ble_hs_conn_addrs(conn, &addrs);
|
||||
if (ble_addr_cmp(&addrs.peer_id_addr, check->peer_id_addr) == 0) {
|
||||
check->found = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
ble_store_util_peer_connected(const ble_addr_t *peer_id_addr)
|
||||
{
|
||||
struct ble_store_util_conn_check check = {
|
||||
.peer_id_addr = peer_id_addr,
|
||||
};
|
||||
|
||||
ble_hs_lock();
|
||||
ble_hs_conn_foreach(ble_store_util_conn_check, &check);
|
||||
ble_hs_unlock();
|
||||
|
||||
return check.found;
|
||||
}
|
||||
|
||||
static int
|
||||
ble_store_util_find_lfu(int obj_type, union ble_store_value *val, void *arg)
|
||||
{
|
||||
struct ble_store_util_lfu *lfu;
|
||||
|
||||
BLE_HS_DBG_ASSERT(obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC);
|
||||
|
||||
lfu = arg;
|
||||
if (lfu->except_addr != NULL &&
|
||||
ble_addr_cmp(&val->sec.peer_addr, lfu->except_addr) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Never remove a bond which belongs to an active connection. */
|
||||
if (ble_store_util_peer_connected(&val->sec.peer_addr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lfu->found || val->sec.bond_count < lfu->bond_count) {
|
||||
lfu->peer_addr = val->sec.peer_addr;
|
||||
lfu->bond_count = val->sec.bond_count;
|
||||
lfu->found = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ble_store_util_unpair_lfu(const ble_addr_t *except_addr)
|
||||
{
|
||||
struct ble_store_util_lfu lfu = {
|
||||
.except_addr = except_addr,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ble_store_iterate(BLE_STORE_OBJ_TYPE_OUR_SEC,
|
||||
ble_store_util_find_lfu, &lfu);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!lfu.found) {
|
||||
return BLE_HS_ESTORE_CAP;
|
||||
}
|
||||
|
||||
return ble_gap_unpair(&lfu.peer_addr);
|
||||
}
|
||||
|
||||
int
|
||||
ble_store_util_status_lfu(struct ble_store_status_event *event, void *arg)
|
||||
{
|
||||
const ble_addr_t *except_addr;
|
||||
|
||||
switch (event->event_code) {
|
||||
case BLE_STORE_EVENT_OVERFLOW:
|
||||
except_addr = NULL;
|
||||
switch (event->overflow.obj_type) {
|
||||
case BLE_STORE_OBJ_TYPE_OUR_SEC:
|
||||
case BLE_STORE_OBJ_TYPE_PEER_SEC:
|
||||
except_addr = &event->overflow.value->sec.peer_addr;
|
||||
break;
|
||||
case BLE_STORE_OBJ_TYPE_PEER_ADDR:
|
||||
except_addr = &event->overflow.value->rpa_rec.peer_addr;
|
||||
break;
|
||||
case BLE_STORE_OBJ_TYPE_CCCD:
|
||||
except_addr = &event->overflow.value->cccd.peer_addr;
|
||||
break;
|
||||
case BLE_STORE_OBJ_TYPE_CSFC:
|
||||
except_addr = &event->overflow.value->csfc.peer_addr;
|
||||
break;
|
||||
#if MYNEWT_VAL(ENC_ADV_DATA)
|
||||
case BLE_STORE_OBJ_TYPE_ENC_ADV_DATA:
|
||||
return ble_store_util_delete_ead_oldest_peer();
|
||||
#endif
|
||||
default:
|
||||
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_EUNKNOWN);
|
||||
return BLE_HS_EUNKNOWN;
|
||||
}
|
||||
|
||||
return ble_store_util_unpair_lfu(except_addr);
|
||||
|
||||
case BLE_STORE_EVENT_FULL:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_EUNKNOWN);
|
||||
return BLE_HS_EUNKNOWN;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
int
|
||||
ble_store_util_touch_peer(const ble_addr_t *peer_id_addr)
|
||||
{
|
||||
struct ble_store_key_sec key_sec = {0};
|
||||
struct ble_store_value_sec value_sec;
|
||||
int rc;
|
||||
|
||||
if (peer_id_addr == NULL ||
|
||||
ble_addr_cmp(peer_id_addr, BLE_ADDR_ANY) == 0) {
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
key_sec.peer_addr = *peer_id_addr;
|
||||
rc = ble_store_read_our_sec(&key_sec, &value_sec);
|
||||
if (rc == BLE_HS_ENOENT) {
|
||||
/* Encryption can complete before bond keys are persisted. */
|
||||
return 0;
|
||||
}
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ble_store_write_our_sec(&value_sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
ble_store_util_status_overflow(struct ble_store_status_event *event, void *arg)
|
||||
{
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
return ble_store_util_status_lfu(event, arg);
|
||||
#else
|
||||
return ble_store_util_status_rr_evict(event, arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
ble_store_util_status_rr(struct ble_store_status_event *event, void *arg)
|
||||
{
|
||||
return ble_store_util_status_overflow(event, arg);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sysinit/sysinit.h"
|
||||
@@ -284,6 +285,10 @@ ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec)
|
||||
{
|
||||
#if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
struct ble_store_key_sec key_sec;
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
|
||||
struct ble_store_value_sec persisted_sec;
|
||||
int updated_existing;
|
||||
#endif
|
||||
int idx;
|
||||
int rc;
|
||||
|
||||
@@ -293,6 +298,9 @@ ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec)
|
||||
ble_store_key_from_value_sec(&key_sec, value_sec);
|
||||
idx = ble_store_config_find_sec(&key_sec, ble_store_config_our_secs,
|
||||
ble_store_config_num_our_secs);
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
|
||||
updated_existing = idx != -1;
|
||||
#endif
|
||||
if (idx == -1) {
|
||||
if (ble_store_config_num_our_secs >= MYNEWT_VAL(BLE_STORE_MAX_BONDS)) {
|
||||
BLE_HS_LOG(DEBUG, "error persisting our sec; too many entries "
|
||||
@@ -308,12 +316,25 @@ ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec)
|
||||
ble_store_config_our_secs[idx] = *value_sec;
|
||||
|
||||
ble_store_config_our_secs[idx].bond_count = ++ble_store_config_our_bond_count;
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
|
||||
persisted_sec = ble_store_config_our_secs[idx];
|
||||
#endif
|
||||
|
||||
/* Ensure entries are sorted at all times */
|
||||
qsort(ble_store_config_our_secs, ble_store_config_num_our_secs,
|
||||
sizeof(struct ble_store_value_sec), ble_store_config_compare_bond_count);
|
||||
|
||||
rc = ble_store_config_persist_our_secs();
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
|
||||
if (updated_existing) {
|
||||
rc = ble_store_config_persist_our_sec_value(&persisted_sec);
|
||||
if (rc == BLE_HS_ENOENT) {
|
||||
rc = ble_store_config_persist_our_secs();
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
rc = ble_store_config_persist_our_secs();
|
||||
}
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -294,6 +294,16 @@ ble_store_config_persist_our_secs(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
int
|
||||
ble_store_config_persist_our_sec_value(
|
||||
const struct ble_store_value_sec *value_sec)
|
||||
{
|
||||
(void)value_sec;
|
||||
return ble_store_config_persist_our_secs();
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
ble_store_config_persist_peer_secs(void)
|
||||
{
|
||||
|
||||
@@ -151,6 +151,10 @@ extern int ble_store_config_num_local_irks;
|
||||
#if MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
int ble_store_config_persist_our_secs(void);
|
||||
int ble_store_config_persist_peer_secs(void);
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
|
||||
int ble_store_config_persist_our_sec_value(
|
||||
const struct ble_store_value_sec *value_sec);
|
||||
#endif
|
||||
#endif
|
||||
#if MYNEWT_VAL(BLE_STORE_MAX_CCCDS)
|
||||
int ble_store_config_persist_cccds(void);
|
||||
@@ -175,6 +179,15 @@ void ble_store_config_deinit(void);
|
||||
|
||||
static inline int ble_store_config_persist_our_secs(void) { return 0; }
|
||||
static inline int ble_store_config_persist_peer_secs(void) { return 0; }
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU)
|
||||
static inline int
|
||||
ble_store_config_persist_our_sec_value(
|
||||
const struct ble_store_value_sec *value_sec)
|
||||
{
|
||||
(void)value_sec;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static inline int ble_store_config_persist_cccds(void) { return 0; }
|
||||
static inline int ble_store_config_persist_csfcs(void) { return 0; }
|
||||
#if MYNEWT_VAL(ENC_ADV_DATA)
|
||||
@@ -184,9 +197,6 @@ static inline int ble_store_config_persist_rpa_recs(void) { return 0; }
|
||||
static inline int ble_store_config_persist_local_irk(void) { return 0; }
|
||||
static inline void ble_store_config_conf_init(void) { }
|
||||
//static inline void ble_store_config_deinit(void) { }
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
static inline int ble_store_persist_peer_records(void) { return 0; }
|
||||
#endif
|
||||
#endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <esp_system.h>
|
||||
#include "sysinit/sysinit.h"
|
||||
@@ -1489,6 +1490,40 @@ end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_STORE_OVERFLOW_LFU) && MYNEWT_VAL(BLE_STORE_MAX_BONDS)
|
||||
int
|
||||
ble_store_config_persist_our_sec_value(
|
||||
const struct ble_store_value_sec *value_sec)
|
||||
{
|
||||
union ble_store_value val;
|
||||
nvs_handle_t nimble_handle;
|
||||
int nvs_idx;
|
||||
int rc;
|
||||
|
||||
rc = nvs_open(NIMBLE_NVS_NAMESPACE, NVS_READWRITE, &nimble_handle);
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS open operation failed");
|
||||
return BLE_HS_ESTORE_FAIL;
|
||||
}
|
||||
|
||||
nvs_idx = get_nvs_sec_identity_index(nimble_handle, value_sec,
|
||||
BLE_STORE_OBJ_TYPE_OUR_SEC);
|
||||
if (nvs_idx == -1) {
|
||||
nvs_close(nimble_handle);
|
||||
return BLE_HS_ENOENT;
|
||||
}
|
||||
|
||||
val.sec = *value_sec;
|
||||
rc = ble_store_nvs_update(nimble_handle, BLE_STORE_OBJ_TYPE_OUR_SEC,
|
||||
nvs_idx, &val);
|
||||
if (rc == 0) {
|
||||
rc = ble_nvs_commit_checked(nimble_handle);
|
||||
}
|
||||
nvs_close(nimble_handle);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ble_store_config_persist_our_secs(void)
|
||||
{
|
||||
int nvs_count, nvs_idx;
|
||||
|
||||
Reference in New Issue
Block a user