From 03b919e9aea1fbb2f5e81c59350623af09314cac Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 14 Mar 2025 13:27:10 +0100 Subject: [PATCH] att_server: cache GATT Database Hash in TLV and discard stored CCCs upon change --- CHANGELOG.md | 1 + src/ble/att_server.c | 63 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 608f5abb5..ecaada63c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - GAP: simulate HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE if HCI Remote Name Request fails - AVRCP Controller: Added send generic PASS THROUGH command +- GATT Server: store Databse Hash in TLV and discard stored CCCs if database changes - HID Host: storage for HID Descriptors is now optional - HCI: support newer AIROC Controller that require Download Mode with ENABLE_AIROC_DOWNLOAD_MODE - Zephyr: provide hal_flash_bank implementation for native flash driver diff --git a/src/ble/att_server.c b/src/ble/att_server.c index 580d05bb1..0fb7942ed 100644 --- a/src/ble/att_server.c +++ b/src/ble/att_server.c @@ -1030,6 +1030,10 @@ static uint32_t att_server_persistent_ccc_tag_for_index(uint8_t index){ return (((uint8_t)'B') << 24u) | (((uint8_t)'T') << 16u) | (((uint8_t)'C') << 8u) | index; } +static uint32_t att_server_tag_for_hash(void){ + return (((uint8_t)'B') << 24u) | (((uint8_t)'T') << 16u) | (((uint8_t)'D') << 8u) | ((uint8_t)'B'); +} + static void att_server_persistent_ccc_write(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t value){ // lookup att_server instance hci_connection_t * hci_connection = hci_connection_for_handle(con_handle); @@ -1149,16 +1153,67 @@ static void att_server_persistent_ccc_clear(att_server_t * att_server){ } } +// @returns true if cccs are valid +static bool att_server_persistent_ccc_validate_database(const btstack_tlv_t * tlv_impl, void * tlv_context) { + // we skip validation if there's no GATT Database Hash Characteristic + uint8_t hash_from_db[16]; + bool have_db_hash = gatt_server_get_database_hash(hash_from_db); + if (!have_db_hash) return true; + log_info("Database Hash:"); + log_info_hexdump(hash_from_db, 16); + + // otherwise, compare to stored hash if available + uint8_t hash_from_tlv[16]; + uint32_t hash_tag = att_server_tag_for_hash(); + bool store_hash = false; + bool stored_cccs_valid = true; + int len = tlv_impl->get_tag(tlv_context, hash_tag, hash_from_tlv, sizeof(hash_from_tlv)); + if (len == (int) sizeof(hash_from_tlv)) { + log_info("TLV Hash:"); + log_info_hexdump(hash_from_db, 16); + if (memcmp(hash_from_db, hash_from_tlv, 16) != 0) { + log_info("Database Hash changed -> clear persistent CCCs"); + stored_cccs_valid = false; + store_hash = true; + } + } else { + // optimistic update: store hash without clearing CCCs + store_hash = true; + } + + // clear stored CCCs + if (stored_cccs_valid == false) { + for (int index=0;indexdelete_tag(tlv_context, ccc_tag); + } + } + // store updated database hash + if (store_hash) { + tlv_impl->store_tag(tlv_context, hash_tag, hash_from_db, sizeof(hash_from_db)); + } + return stored_cccs_valid; +} + static void att_server_persistent_ccc_restore(att_server_t * att_server, att_connection_t * att_connection){ - int le_device_index = att_server->ir_le_device_db_index; - log_info("Restore CCC values of remote %s, le device id %d", bd_addr_to_str(att_server->peer_address), le_device_index); - // check if bonded - if (le_device_index < 0) return; // get btstack_tlv const btstack_tlv_t * tlv_impl = NULL; void * tlv_context; btstack_tlv_get_instance(&tlv_impl, &tlv_context); if (!tlv_impl) return; + + // validate database hash + if ((att_server_flags & ATT_SERVER_FLAGS_VALIDATE_DATABASE_HASH) != 0) { + att_server_flags &= ~ATT_SERVER_FLAGS_VALIDATE_DATABASE_HASH; + if (att_server_persistent_ccc_validate_database(tlv_impl, tlv_context) == false) { + return; + } + } + + // check if bonded + int le_device_index = att_server->ir_le_device_db_index; + if (le_device_index < 0) return; + log_info("Restore CCC values of remote %s, le device id %d", bd_addr_to_str(att_server->peer_address), le_device_index); // get all ccc tag int index; persistent_ccc_entry_t entry;