feat(nimble): Added two GATT features:

1. Automatically initiate security if a GATT service request fails
2. Encryption, Authentication, and Authorization requirement on CCCD
This commit is contained in:
Sumeet Singh
2025-06-16 16:58:01 +05:30
committed by Rahul Tank
parent cc3ac5416b
commit 84e02a6b82
5 changed files with 190 additions and 26 deletions
+26 -16
View File
@@ -127,50 +127,58 @@ struct ble_hs_cfg;
*/
/** GATT Characteristic Flag: Broadcast. */
#define BLE_GATT_CHR_F_BROADCAST 0x0001
#define BLE_GATT_CHR_F_BROADCAST 0x00000001
/** GATT Characteristic Flag: Read. */
#define BLE_GATT_CHR_F_READ 0x0002
#define BLE_GATT_CHR_F_READ 0x00000002
/** GATT Characteristic Flag: Write without Response. */
#define BLE_GATT_CHR_F_WRITE_NO_RSP 0x0004
#define BLE_GATT_CHR_F_WRITE_NO_RSP 0x00000004
/** GATT Characteristic Flag: Write. */
#define BLE_GATT_CHR_F_WRITE 0x0008
#define BLE_GATT_CHR_F_WRITE 0x00000008
/** GATT Characteristic Flag: Notify. */
#define BLE_GATT_CHR_F_NOTIFY 0x0010
#define BLE_GATT_CHR_F_NOTIFY 0x00000010
/** GATT Characteristic Flag: Indicate. */
#define BLE_GATT_CHR_F_INDICATE 0x0020
#define BLE_GATT_CHR_F_INDICATE 0x00000020
/** GATT Characteristic Flag: Authenticated Signed Writes. */
#define BLE_GATT_CHR_F_AUTH_SIGN_WRITE 0x0040
#define BLE_GATT_CHR_F_AUTH_SIGN_WRITE 0x00000040
/** GATT Characteristic Flag: Reliable Writes. */
#define BLE_GATT_CHR_F_RELIABLE_WRITE 0x0080
#define BLE_GATT_CHR_F_RELIABLE_WRITE 0x00000080
/** GATT Characteristic Flag: Auxiliary Writes. */
#define BLE_GATT_CHR_F_AUX_WRITE 0x0100
#define BLE_GATT_CHR_F_AUX_WRITE 0x00000100
/** GATT Characteristic Flag: Read Encrypted. */
#define BLE_GATT_CHR_F_READ_ENC 0x0200
#define BLE_GATT_CHR_F_READ_ENC 0x00000200
/** GATT Characteristic Flag: Read Authenticated. */
#define BLE_GATT_CHR_F_READ_AUTHEN 0x0400
#define BLE_GATT_CHR_F_READ_AUTHEN 0x00000400
/** GATT Characteristic Flag: Read Authorized. */
#define BLE_GATT_CHR_F_READ_AUTHOR 0x0800
#define BLE_GATT_CHR_F_READ_AUTHOR 0x00000800
/** GATT Characteristic Flag: Write Encrypted. */
#define BLE_GATT_CHR_F_WRITE_ENC 0x1000
#define BLE_GATT_CHR_F_WRITE_ENC 0x00001000
/** GATT Characteristic Flag: Write Authenticated. */
#define BLE_GATT_CHR_F_WRITE_AUTHEN 0x2000
#define BLE_GATT_CHR_F_WRITE_AUTHEN 0x00002000
/** GATT Characteristic Flag: Write Authorized. */
#define BLE_GATT_CHR_F_WRITE_AUTHOR 0x4000
#define BLE_GATT_CHR_F_WRITE_AUTHOR 0x00004000
/** GATT Characteristic Flag: CCCD Write Encrypted */
#define BLE_GATT_CHR_F_NOTIFY_INDICATE_ENC 0x00008000
/** GATT Characteristic Flag: CCCD Write Authenticated */
#define BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHEN 0x00010000
/** GATT Characteristic Flag: CCCD Write Authorized */
#define BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHOR 0x00020000
/** @} */
@@ -996,7 +1004,7 @@ typedef int ble_gatt_access_fn(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
/** Type definition for GATT characteristic flags. */
typedef uint16_t ble_gatt_chr_flags;
typedef uint32_t ble_gatt_chr_flags;
/** Represents the definition of a GATT characteristic. */
struct ble_gatt_chr_def {
@@ -1031,6 +1039,8 @@ struct ble_gatt_chr_def {
/**
* At registration time, this is filled in with the characteristic's value
* attribute handle.
*
* The attribute handle for CCCD is `val_handle + 1`
*/
uint16_t *val_handle;
+4
View File
@@ -8513,6 +8513,10 @@ ble_gap_enc_event(uint16_t conn_handle, int status,
ble_gap_event_listener_call(&event);
ble_gap_call_conn_event_cb(&event, conn_handle);
#if MYNEWT_VAL(BLE_GATTC) && MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
ble_gattc_recover_gatt_proc(conn_handle, status);
#endif
if (status != 0) {
return;
}
+3 -1
View File
@@ -161,6 +161,9 @@ int32_t ble_gattc_timer(void);
int ble_gattc_any_jobs(void);
int ble_gattc_init(void);
#if MYNEWT_VAL(BLE_GATTC) && MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
void ble_gattc_recover_gatt_proc(uint16_t conn_handle, int enc_status);
#endif
/*** @server. */
#define BLE_GATTS_CLT_CFG_F_NOTIFY 0x0001
@@ -224,7 +227,6 @@ int ble_gatts_register_svcs(const struct ble_gatt_svc_def *svcs,
int ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle,
uint8_t op, uint16_t offset, struct os_mbuf **om,
void *arg);
void ble_gatts_set_clt_cfg_perm_flags(uint8_t flags);
#if MYNEWT_VAL(BLE_GATT_CACHING)
struct ble_gatts_aware_state {
+133
View File
@@ -113,6 +113,9 @@
struct ble_gattc_proc {
STAILQ_ENTRY(ble_gattc_proc) next;
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
struct ble_gatt_error error;
#endif
uint32_t exp_os_ticks;
uint16_t conn_handle;
uint16_t cid;
@@ -210,6 +213,9 @@ struct ble_gattc_proc {
uint16_t att_handle;
ble_gatt_attr_fn *cb;
void *cb_arg;
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
struct os_mbuf * om;
#endif
} write;
struct {
@@ -461,6 +467,11 @@ static struct os_mempool ble_gattc_proc_pool;
/* The list of active GATT client procedures. */
static struct ble_gattc_proc_list ble_gattc_procs;
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
/** Retry procedure after encryption response. */
static struct ble_gattc_proc_list ble_gattc_cached_procs;
#endif
#if MYNEWT_VAL(BLE_GATTC)
/* The time when we should attempt to resume stalled procedures, in OS ticks.
* A value of 0 indicates no stalled procedures.
@@ -780,6 +791,13 @@ ble_gattc_proc_free(struct ble_gattc_proc *proc)
ble_gattc_dbg_assert_proc_not_inserted(proc);
switch (proc->op) {
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
case BLE_GATT_OP_WRITE:
if (MYNEWT_VAL(BLE_GATT_WRITE)) {
os_mbuf_free_chain(proc->write.om);
}
break;
#endif
case BLE_GATT_OP_WRITE_LONG:
if (MYNEWT_VAL(BLE_GATT_WRITE_LONG)) {
os_mbuf_free_chain(proc->write_long.attr.om);
@@ -1389,6 +1407,94 @@ ble_gattc_error(int status, uint16_t att_handle)
return &error;
}
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
void
ble_gattc_recover_gatt_proc(uint16_t conn_handle, int enc_status)
{
struct ble_gattc_proc * prev;
struct ble_gattc_proc * proc;
struct ble_gattc_proc * next;
struct os_mbuf * om;
struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)];
ble_gattc_err_fn *err_cb;
prev = NULL;
proc = STAILQ_FIRST(&ble_gattc_cached_procs);
while (proc != NULL) {
next = STAILQ_NEXT(proc, next);
if (proc->conn_handle == conn_handle) {
if (enc_status == 0) {
switch (proc->op) {
case BLE_GATT_OP_READ:
ble_gattc_read(conn_handle, proc->read.handle,
proc->read.cb, proc->read.cb_arg);
break;
case BLE_GATT_OP_READ_UUID:
ble_gattc_read_by_uuid(conn_handle, proc->read_uuid.start_handle,
proc->read_uuid.end_handle, &proc->read_uuid.chr_uuid.u,
proc->read_uuid.cb, proc->read_uuid.cb_arg);
break;
case BLE_GATT_OP_READ_LONG:
ble_gattc_read_long(conn_handle, proc->read_long.handle,
proc->read_long.offset, proc->read_long.cb,
proc->read_long.cb_arg);
break;
case BLE_GATT_OP_READ_MULT:
ble_gattc_read_mult(conn_handle, proc->read_mult.handles,
proc->read_mult.num_handles, proc->read_mult.cb,
proc->read_mult.cb_arg);
break;
case BLE_GATT_OP_READ_MULT_VAR:
ble_gattc_read_mult_var(conn_handle, proc->read_mult.handles,
proc->read_mult.num_handles, proc->read_mult.cb_mult,
proc->read_mult.cb_arg);
break;
case BLE_GATT_OP_WRITE:
/* Mbuf will be consumed by the API. */
om = os_mbuf_dup(proc->write.om);
ble_gattc_write(conn_handle, proc->write.att_handle,
om, proc->write.cb, proc->write.cb_arg);
break;
case BLE_GATT_OP_WRITE_LONG:
/* Mbuf will be consumed by the API. */
om = os_mbuf_dup(proc->write_long.attr.om);
ble_gattc_write_long(conn_handle, proc->write_long.attr.handle,
proc->write_long.attr.offset, om,
proc->write_long.cb, proc->write_long.cb_arg);
case BLE_GATT_OP_WRITE_RELIABLE:
for (int i = 0; i < proc->write_reliable.num_attrs; i++) {
attrs[i].handle = proc->write_reliable.attrs[i].handle;
attrs[i].offset = 0;
attrs[i].om = os_mbuf_dup(proc->write_reliable.attrs[i].om);
}
ble_gattc_write_reliable(conn_handle, attrs,
proc->write_reliable.num_attrs,
proc->write_reliable.cb, proc->write_reliable.cb_arg);
}
} else {
err_cb = ble_gattc_err_dispatch_get(proc->op);
if (err_cb != NULL) {
err_cb(proc, BLE_HS_ERR_ATT_BASE + proc->error.status, proc->error.att_handle);
}
}
if (!prev) {
STAILQ_REMOVE_HEAD(&ble_gattc_cached_procs, next);
} else {
STAILQ_REMOVE_AFTER(&ble_gattc_cached_procs, prev, next);
}
ble_gattc_proc_free(proc);
proc = prev;
}
prev = proc;
proc = next;
}
}
#endif /* MYNEWT_VAL(BLE_GATTC_AUTO_PAIR) */
/*****************************************************************************
* $mtu *
*****************************************************************************/
@@ -4391,6 +4497,9 @@ ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle,
proc->write.att_handle = attr_handle;
proc->write.cb = cb;
proc->write.cb_arg = cb_arg;
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
proc->write.om = os_mbuf_dup(txom);
#endif
ble_gattc_log_write(attr_handle, OS_MBUF_PKTLEN(txom), 1);
@@ -5486,9 +5595,30 @@ ble_gattc_rx_err(uint16_t conn_handle, uint16_t cid, uint16_t handle, uint16_t s
{
struct ble_gattc_proc *proc;
ble_gattc_err_fn *err_cb;
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
struct ble_gap_conn_desc desc;
int rc;
#endif
proc = ble_gattc_extract_first_by_conn_cid_op(conn_handle, cid, BLE_GATT_OP_NONE);
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
ble_gap_conn_find(conn_handle, &desc);
proc->error.att_handle = handle;
proc->error.status = status;
#endif
if (proc != NULL) {
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
if (desc.sec_state.encrypted == 0 &&
(status == BLE_ATT_ERR_INSUFFICIENT_ENC ||
status == BLE_ATT_ERR_INSUFFICIENT_AUTHEN)) {
rc = ble_gap_security_initiate(conn_handle);
if (rc == 0) {
STAILQ_INSERT_TAIL(&ble_gattc_cached_procs, proc, next);
return;
}
}
#endif
err_cb = ble_gattc_err_dispatch_get(proc->op);
if (err_cb != NULL) {
err_cb(proc, BLE_HS_ERR_ATT_BASE + status, handle);
@@ -5913,6 +6043,9 @@ ble_gattc_init(void)
STAILQ_INIT(&temp_proc_list);
#endif
STAILQ_INIT(&ble_gattc_procs);
#if MYNEWT_VAL(BLE_GATTC_AUTO_PAIR)
STAILQ_INIT(&ble_gattc_cached_procs);
#endif
if (MYNEWT_VAL(BLE_GATT_MAX_PROCS) > 0) {
rc = os_mempool_init(&ble_gattc_proc_pool,
+24 -9
View File
@@ -28,7 +28,6 @@
#include "esp_nimble_mem.h"
#if MYNEWT_VAL(BLE_GATTS)
static uint8_t perm_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE ;
#if MYNEWT_VAL(BLE_DYNAMIC_SERVICE)
#include "services/gatt/ble_svc_gatt.h"
@@ -179,11 +178,6 @@ ble_gatts_clt_cfg_free(struct ble_gatts_clt_cfg *cfg)
}
#endif
void ble_gatts_set_clt_cfg_perm_flags(uint8_t flags)
{
perm_flags = flags ;
}
static int
ble_gatts_svc_access(uint16_t conn_handle, uint16_t attr_handle,
uint8_t op, uint16_t offset, struct os_mbuf **om,
@@ -259,6 +253,26 @@ ble_gatts_chr_clt_cfg_allowed(const struct ble_gatt_chr_def *chr)
return flags;
}
static uint8_t
ble_gatts_chr_clt_cfg_flags_from_chr_flags(ble_gatt_chr_flags chr_flags)
{
uint8_t cccd_flags;
cccd_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE;
if (chr_flags & BLE_GATT_CHR_F_NOTIFY_INDICATE_ENC) {
cccd_flags |= BLE_ATT_F_WRITE_ENC;
}
if (chr_flags & BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHEN) {
cccd_flags |= BLE_ATT_F_WRITE_AUTHEN;
}
if (chr_flags & BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHOR) {
cccd_flags |= BLE_ATT_F_WRITE_AUTHOR;
}
return cccd_flags;
}
static uint8_t
ble_gatts_att_flags_from_chr_flags(ble_gatt_chr_flags chr_flags)
{
@@ -1011,10 +1025,10 @@ ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle,
}
static int
ble_gatts_register_clt_cfg_dsc(uint16_t *att_handle)
ble_gatts_register_clt_cfg_dsc(uint16_t *att_handle, uint8_t cccd_flags)
{
int rc;
rc = ble_att_svr_register(uuid_ccc, perm_flags, 0,
rc = ble_att_svr_register(uuid_ccc, cccd_flags, 0,
att_handle, ble_gatts_clt_cfg_access, NULL);
if (rc != 0) {
return rc;
@@ -1190,7 +1204,8 @@ ble_gatts_register_chr(const struct ble_gatt_svc_def *svc,
}
if (ble_gatts_chr_clt_cfg_allowed(chr) != 0) {
rc = ble_gatts_register_clt_cfg_dsc(&dsc_handle);
att_flags = ble_gatts_chr_clt_cfg_flags_from_chr_flags(chr->flags);
rc = ble_gatts_register_clt_cfg_dsc(&dsc_handle, att_flags);
if (rc != 0) {
return rc;
}