diff --git a/nimble/host/include/host/ble_hs_pvcy.h b/nimble/host/include/host/ble_hs_pvcy.h index 0ff32b80b..19087f220 100644 --- a/nimble/host/include/host/ble_hs_pvcy.h +++ b/nimble/host/include/host/ble_hs_pvcy.h @@ -19,6 +19,9 @@ * under the License. */ +#ifndef H_BLE_HS_PVCY_ +#define H_BLE_HS_PVCY_ + #include "host/ble_hs.h" #ifdef __cplusplus @@ -26,15 +29,45 @@ extern "C" { #endif #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) -/* Called to configure local(own) privacy (RPA) when using host based privacy. In - * Host based privacy as controller is not aware of RPA, we do it via - * 'BLE_ADDR_RANDOM' addr_type route. + +#define NIMBLE_HOST_DISABLE_PRIVACY 0x00 +#define NIMBLE_HOST_ENABLE_RPA 0x01 +#define NIMBLE_HOST_ENABLE_NRPA 0x02 + +/* Called to configure local(own) privacy (RPA/NRPA) when using Host based privacy. + * In Host based privacy, as controller is not aware of RPA/NRPA address is in use, + * we do it through 'BLE_ADDR_RANDOM (0x01)' addr_type route. This is necessary + * so as to set the private address as random address in controller. + * Remember to configure `BLE_SM_PAIR_KEY_DIST_ID` in our & their + * key distributions for using RPA. For NRPA part of privacy it is not + * necessary to configure key distributions in host, as anyway NRPA is non-resolvable. + * Please call this API once host-controller are synced as we set the private + * (RPA/NRPA) address using host-controller HCI commands. * - * @param enable RPA when enable is not 0 - * disable RPA otherwise + * To give brief information on how to use this feature, + * please refer to following steps while using RPA feature: + * + * 1. Include "host/ble_hs_pvcy.h". + * 2. Set own_addr_type to `BLE_OWN_ADDR_RANDOM`. + * 3. Add `BLE_SM_PAIR_KEY_DIST_ID` to key distribution in + * `ble_hs_cfg.sm_our_key_dist` & `ble_hs_cfg.sm_their_key_dist`. + * 4. Call `ble_hs_pvcy_rpa_config(1)` in Host-Controller sync callback. + * + * In case of NRPA, steps 1, 2 and calling ble_hs_pvcy_rpa_config(2) will + * suffice. + * + * @param enable RPA when param = 1 (NIMBLE_HOST_ENABLE_RPA) + * enable NRPA when param = 2 (NIMBLE_HOST_ENABLE_NRPA) + * disable privacy when param = 0 (NIMBLE_HOST_DISABLE_PRIVACY) * * @return return 0 when successful. * return appropriate error code otherwise */ int ble_hs_pvcy_rpa_config(uint8_t enable); #endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/nimble/host/src/ble_eddystone.c b/nimble/host/src/ble_eddystone.c index 7d80d134d..eccb3e988 100644 --- a/nimble/host/src/ble_eddystone.c +++ b/nimble/host/src/ble_eddystone.c @@ -76,7 +76,7 @@ ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields, if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) { return BLE_HS_EINVAL; } - if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) { + if (svc_data_len > (BLE_EDDYSTONE_MAX_SVC_DATA_LEN - BLE_EDDYSTONE_SVC_DATA_BASE_SZ)) { return BLE_HS_EINVAL; } if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) { diff --git a/nimble/host/src/ble_hs_conn.c b/nimble/host/src/ble_hs_conn.c index eb65e3288..b45128e23 100644 --- a/nimble/host/src/ble_hs_conn.c +++ b/nimble/host/src/ble_hs_conn.c @@ -470,29 +470,52 @@ ble_hs_conn_timer(void) int32_t time_diff; uint16_t conn_handle; - conn_handle = BLE_HS_CONN_HANDLE_NONE; - next_exp_in = BLE_HS_FOREVER; - now = ble_npl_time_get(); + for (;;) { + conn_handle = BLE_HS_CONN_HANDLE_NONE; + next_exp_in = BLE_HS_FOREVER; + now = ble_npl_time_get(); - ble_hs_lock(); + ble_hs_lock(); - /* This loop performs one of two tasks: - * 1. Determine if any connections need to be terminated due to timeout. - * If so, break out of the loop and terminate the connection. This - * function will need to be executed again. - * 2. Otherwise, determine when the next timeout will occur. - */ - SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) { - if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) { + /* This loop performs one of two tasks: + * 1. Determine if any connections need to be terminated due to timeout. + * If so, break out of the loop and terminate the connection. This + * function will need to be executed again. + * 2. Otherwise, determine when the next timeout will occur. + */ + SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) { + if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) { #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0 - /* Check each connection's rx fragment timer. If too much time - * passes after a partial packet is received, the connection is - * terminated. - */ - if (conn->bhc_rx_chan != NULL) { - time_diff = conn->bhc_rx_timeout - now; + /* Check each connection's rx fragment timer. If too much time + * passes after a partial packet is received, the connection is + * terminated. + */ + if (conn->bhc_rx_chan != NULL) { + time_diff = conn->bhc_rx_timeout - now; + if (time_diff <= 0) { + /* ACL reassembly has timed out. Remember the connection + * handle so it can be terminated after the mutex is + * unlocked. + */ + conn_handle = conn->bhc_handle; + break; + } + + /* Determine if this connection is the soonest to time out. */ + if (time_diff < next_exp_in) { + next_exp_in = time_diff; + } + } +#endif + +#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO + /* Check each connection's rx queued write timer. If too much + * time passes after a prep write is received, the queue is + * cleared. + */ + time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now); if (time_diff <= 0) { /* ACL reassembly has timed out. Remember the connection * handle so it can be terminated after the mutex is @@ -506,45 +529,22 @@ ble_hs_conn_timer(void) if (time_diff < next_exp_in) { next_exp_in = time_diff; } - } #endif - -#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO - /* Check each connection's rx queued write timer. If too much - * time passes after a prep write is received, the queue is - * cleared. - */ - time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now); - if (time_diff <= 0) { - /* ACL reassembly has timed out. Remember the connection - * handle so it can be terminated after the mutex is - * unlocked. - */ - conn_handle = conn->bhc_handle; - break; } - - /* Determine if this connection is the soonest to time out. */ - if (time_diff < next_exp_in) { - next_exp_in = time_diff; - } -#endif } + + ble_hs_unlock(); + + /* If a connection has timed out, terminate it. We need to repeatedly + * call this function again to determine when the next timeout is. + */ + if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { + ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); + continue; + } + + return next_exp_in; } - - ble_hs_unlock(); - - /* If a connection has timed out, terminate it. We need to recursively - * call this function again to determine when the next timeout is. This - * is a tail-recursive call, so it should be optimized to execute in the - * same stack frame. - */ - if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { - ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); - return ble_hs_conn_timer(); - } - - return next_exp_in; } int diff --git a/nimble/host/src/ble_hs_id.c b/nimble/host/src/ble_hs_id.c index 43380d1f6..db32f90df 100644 --- a/nimble/host/src/ble_hs_id.c +++ b/nimble/host/src/ble_hs_id.c @@ -67,6 +67,37 @@ ble_hs_id_gen_rnd(int nrpa, ble_addr_t *out_addr) } #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +/** + * Sets the device's pseudo Non Resolvable Private Address when 'Host based + * privacy' is in use. + * + * @return 0 on success; + * Appropriate error code if failure. + */ +int +ble_hs_id_set_nrpa_rnd() +{ + + ble_addr_t nrpa_addr; + int rc; + + ble_hs_id_gen_rnd(1, &nrpa_addr); + + ble_hs_lock(); + + /* set the NRPA address as pseudo random address in controller */ + rc = ble_hs_hci_util_set_random_addr(nrpa_addr.val); + if (rc != 0) { + goto done; + } + + memcpy(ble_hs_id_rnd, nrpa_addr.val, BLE_DEV_ADDR_LEN); + +done: + ble_hs_unlock(); + return rc; +} + /** * Sets the device's pseudo RPA address when 'Host based privacy' is in use. * The address type (RPA) is inferred from the most-significant bits. The diff --git a/nimble/host/src/ble_hs_id_priv.h b/nimble/host/src/ble_hs_id_priv.h index c031b9511..85260ec48 100644 --- a/nimble/host/src/ble_hs_id_priv.h +++ b/nimble/host/src/ble_hs_id_priv.h @@ -36,6 +36,7 @@ void ble_hs_id_rnd_reset(void); #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) bool ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type); int ble_hs_id_set_pseudo_rnd(const uint8_t *); +int ble_hs_id_set_nrpa_rnd(void); #endif #ifdef __cplusplus } diff --git a/nimble/host/src/ble_hs_pvcy.c b/nimble/host/src/ble_hs_pvcy.c index 32f15974b..69f3da543 100644 --- a/nimble/host/src/ble_hs_pvcy.c +++ b/nimble/host/src/ble_hs_pvcy.c @@ -22,6 +22,7 @@ #include "stats/stats.h" #include "ble_hs_priv.h" #include "ble_hs_resolv_priv.h" +#include "host/ble_hs_pvcy.h" static uint8_t ble_hs_pvcy_started; static uint8_t ble_hs_pvcy_irk[16]; @@ -329,7 +330,7 @@ ble_hs_pvcy_rpa_config(uint8_t enable) { int rc = 0; - if (enable != 0) { + if (enable != NIMBLE_HOST_DISABLE_PRIVACY) { rc = ble_hs_pvcy_ensure_started(); if (rc != 0) { return rc; @@ -337,8 +338,15 @@ ble_hs_pvcy_rpa_config(uint8_t enable) ble_hs_resolv_enable(true); + /* Configure NRPA address related flags according to input parameter */ + if (enable == NIMBLE_HOST_ENABLE_NRPA) { + ble_hs_resolv_nrpa_enable(); + } else { + ble_hs_resolv_nrpa_disable(); + } + /* Generate local RPA address and set it in controller */ - rc = ble_hs_gen_own_rpa_random(); + rc = ble_hs_gen_own_private_rnd(); } else { ble_hs_resolv_enable(false); } diff --git a/nimble/host/src/ble_hs_resolv.c b/nimble/host/src/ble_hs_resolv.c index 7f2413898..46e5577f6 100644 --- a/nimble/host/src/ble_hs_resolv.c +++ b/nimble/host/src/ble_hs_resolv.c @@ -50,6 +50,9 @@ struct ble_hs_resolv_data { struct ble_npl_callout rpa_timer; }; +/* NRPA bit: Enables NRPA as private address. */ +static bool nrpa_pvcy; + /*** APIs for Peer Device Records. * * These Peer records are necessary to take care of Peers with RPA address when @@ -178,6 +181,44 @@ is_rpa_resolvable_by_peer_rec(struct ble_hs_dev_records *p_dev_rec, uint8_t *pee return false; } +static void +ble_rpa_replace_id_with_rand_addr(uint8_t *addr_type, uint8_t *peer_addr) +{ + struct ble_hs_dev_records *p_dev_rec; + ble_addr_t p_addr = {0}; + struct ble_hs_conn *conn = NULL; + + p_dev_rec = ble_rpa_find_peer_dev_rec(peer_addr); + + if (p_dev_rec != NULL) { + if (memcmp(p_dev_rec->rand_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN)) { + /* OTA address (before resolving) gets saved in RAND_ADDR when the peer + * record is fetched from resolving list. Replace peer address + * with rand_addr to maintain status quo for new pairing/encryption request. */ + p_addr.type = *addr_type; + memcpy(&p_addr.val[0], peer_addr, BLE_DEV_ADDR_LEN); + + ble_hs_lock(); + + conn = ble_hs_conn_find_by_addr(&p_addr); + /* Rewrite the peer address history in ble_hs_conn. Need to take + * this step to avoid taking wrong address during re-pairing + * process */ + if (conn != NULL) { + conn->bhc_peer_rpa_addr.type = p_dev_rec->rand_addr_type; + memcpy(&conn->bhc_peer_rpa_addr.val[0], p_dev_rec->rand_addr, BLE_DEV_ADDR_LEN); + conn->bhc_peer_addr.type = p_dev_rec->rand_addr_type; + memcpy(&conn->bhc_peer_addr.val[0], p_dev_rec->rand_addr, BLE_DEV_ADDR_LEN); + BLE_HS_LOG(DEBUG, "\n Replace Identity addr with random addr received at" + " start of the connection\n"); + } + + ble_hs_unlock(); + } + } + return; +} + /* Add peer to peer device records. * * @return 0 if added successfully, @@ -224,6 +265,7 @@ ble_rpa_find_rl_from_peer_records(uint8_t *peer_addr, uint8_t *peer_addr_type) if (is_rpa_resolvable_by_peer_rec(p_dev_rec, peer_addr)) { memcpy(p_dev_rec->rand_addr, peer_addr, BLE_DEV_ADDR_LEN); + p_dev_rec->rand_addr_type = *peer_addr_type; rl = ble_hs_resolv_list_find(p_dev_rec->identity_addr); if (rl) { memcpy(peer_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN); @@ -274,13 +316,10 @@ ble_rpa_replace_peer_params_with_rl(uint8_t *peer_addr, uint8_t *addr_type, if (is_rpa) { ble_hs_log_flat_buf(peer_addr, BLE_DEV_ADDR_LEN); - rl_tmp = ble_hs_resolv_list_find(peer_addr); + BLE_HS_LOG(DEBUG, "\n"); - /* Try to find from your peer_device records, if RL doesn't - * exist */ - if (rl_tmp == NULL) { - rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type); - } + /* Try to find RL from your peer_device records */ + rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type); } if (rl != NULL) { @@ -305,6 +344,10 @@ is_ble_hs_resolv_enabled(void) bool ble_host_rpa_enabled(void) { + if (nrpa_pvcy) { + return false; + } + if (is_ble_hs_resolv_enabled() && ble_hs_pvcy_enabled()) { return true; } @@ -385,12 +428,16 @@ ble_hs_resolv_gen_priv_addr(struct ble_hs_resolv_entry *rl, int local) addr[2] = ecb.cipher_text[13]; } -/* Called to generate RPA address and this address is set in controller as - * Random address. This is necessary in Host based privacy because controller is unaware of RPA - * address is being used */ +/* Called to generate private (RPA/NRPA) address and this address is set in controller as + * Random address. This is necessary in Host based privacy because controller + * is unaware of private address is being used */ int -ble_hs_gen_own_rpa_random(void) +ble_hs_gen_own_private_rnd(void) { + if (nrpa_pvcy) { + return ble_hs_id_set_nrpa_rnd(); + } + struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[0]; ble_hs_resolv_gen_priv_addr(rl, 1); @@ -412,12 +459,11 @@ ble_hs_get_rpa_local(void) static void ble_hs_resolv_rpa_timer_cb(struct ble_npl_event *ev) { - if (ble_host_rpa_enabled()) { - BLE_HS_LOG(DEBUG, "RPA Timeout; start active adv & scan with new RPA\n"); - + if (ble_host_rpa_enabled() || (nrpa_pvcy)) { + BLE_HS_LOG(DEBUG, "RPA/NRPA Timeout; start active adv & scan with new Private address \n"); ble_gap_preempt(); - /* Generate local RPA */ - ble_hs_gen_own_rpa_random(); + /* Generate local private address */ + ble_hs_gen_own_private_rnd(); ble_npl_callout_reset(&g_ble_hs_resolv_data.rpa_timer, (int32_t)g_ble_hs_resolv_data.rpa_tmo); ble_gap_preempt_done(); @@ -561,7 +607,7 @@ ble_hs_resolv_list_add(uint8_t *cmdbuf) int ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr) { - int position; + int position, rc = BLE_HS_ENOENT; /* Remove from IRK records */ position = ble_hs_is_on_resolv_list(ident_addr, addr_type); @@ -573,10 +619,15 @@ ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr) ble_hs_resolv_entry)); --g_ble_hs_resolv_data.rl_cnt; - return 0; + rc = 0; } - return BLE_HS_ENOENT; + /* As we are removing the RL record, it is needed to change + * peer_address to its latest received OTA address, this helps when existing bond at + * peer side is removed */ + ble_rpa_replace_id_with_rand_addr(&addr_type, ident_addr); + + return rc; } /** @@ -595,6 +646,23 @@ ble_hs_resolv_list_clear_all(void) return; } +/** +* Called by host stack to enable NRPA privacy flag for future reference +*/ +void +ble_hs_resolv_nrpa_enable(void) +{ + nrpa_pvcy = true; +} + +/** +* Called by host stack to disable NRPA privacy flag +*/ +void +ble_hs_resolv_nrpa_disable(void) +{ + nrpa_pvcy = false; +} /** * Called to enable or disable address resolution in the host * diff --git a/nimble/host/src/ble_hs_resolv_priv.h b/nimble/host/src/ble_hs_resolv_priv.h index 568aa89ab..7bc8a2fe6 100644 --- a/nimble/host/src/ble_hs_resolv_priv.h +++ b/nimble/host/src/ble_hs_resolv_priv.h @@ -53,6 +53,7 @@ struct ble_hs_peer_sec { */ struct ble_hs_dev_records { bool rec_used; + uint8_t rand_addr_type; uint8_t pseudo_addr[BLE_DEV_ADDR_LEN]; uint8_t rand_addr[BLE_DEV_ADDR_LEN]; uint8_t identity_addr[BLE_DEV_ADDR_LEN]; @@ -61,7 +62,7 @@ struct ble_hs_dev_records { /* Add a device to the resolving list */ int ble_hs_resolv_list_add(uint8_t *cmdbuf); -int ble_hs_gen_own_rpa_random(void); +int ble_hs_gen_own_private_rnd(void); uint8_t *ble_hs_get_rpa_local(void); /* Remove a device from the resolving list */ @@ -71,6 +72,8 @@ void ble_hs_resolv_list_clear_all(void); /* Address resolution enable command */ void ble_hs_resolv_enable(bool); +void ble_hs_resolv_nrpa_enable(void); +void ble_hs_resolv_nrpa_disable(void); /* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */ struct ble_hs_resolv_entry * diff --git a/nimble/host/store/config/src/ble_store_nvs.c b/nimble/host/store/config/src/ble_store_nvs.c index 6a72e667a..6ee027b43 100644 --- a/nimble/host/store/config/src/ble_store_nvs.c +++ b/nimble/host/store/config/src/ble_store_nvs.c @@ -490,6 +490,11 @@ int ble_store_config_persist_cccds(void) union ble_store_value val; nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, 0, NULL, 0); + if (nvs_count == -1) { + ESP_LOGE(TAG, "NVS operation failed while persisting CCCD"); + return BLE_HS_ESTORE_FAIL; + } + if (nvs_count < ble_store_config_num_cccds) { /* NVS db count less than RAM count, write operation */ @@ -516,6 +521,11 @@ int ble_store_config_persist_peer_secs(void) union ble_store_value val; nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, 0, NULL, 0); + if (nvs_count == -1) { + ESP_LOGE(TAG, "NVS operation failed while persisting peer sec"); + return BLE_HS_ESTORE_FAIL; + } + if (nvs_count < ble_store_config_num_peer_secs) { /* NVS db count less than RAM count, write operation */ @@ -542,6 +552,11 @@ int ble_store_config_persist_our_secs(void) union ble_store_value val; nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, 0, NULL, 0); + if (nvs_count == -1) { + ESP_LOGE(TAG, "NVS operation failed while persisting our sec"); + return BLE_HS_ESTORE_FAIL; + } + if (nvs_count < ble_store_config_num_our_secs) { /* NVS db count less than RAM count, write operation */ @@ -571,7 +586,13 @@ int ble_store_persist_peer_records(void) struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records(); nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, NULL, 0); + if (nvs_count == -1) { + ESP_LOGE(TAG, "NVS operation failed while persisting peer_dev_rec"); + return BLE_HS_ESTORE_FAIL; + } + if (nvs_count < ble_store_num_peer_dev_rec) { + /* NVS db count less than RAM count, write operation */ ESP_LOGD(TAG, "Persisting peer dev record to NVS..."); peer_rec = peer_dev_rec[ble_store_num_peer_dev_rec - 1]; diff --git a/porting/nimble/src/os_mempool.c b/porting/nimble/src/os_mempool.c index f4c712ff1..ba837e7d6 100644 --- a/porting/nimble/src/os_mempool.c +++ b/porting/nimble/src/os_mempool.c @@ -346,7 +346,8 @@ os_mempool_info_get_next(struct os_mempool *mp, struct os_mempool_info *omi) omi->omi_num_blocks = cur->mp_num_blocks; omi->omi_num_free = cur->mp_num_free; omi->omi_min_free = cur->mp_min_free; - strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name)); + strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name) - 1); + omi->omi_name[sizeof(omi->omi_name) - 1] = '\0'; return (cur); }