From 402bd20ec91786fe1e235bc36d8a0a2408d14ad4 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Wed, 21 Apr 2021 20:33:41 +0200 Subject: [PATCH 1/3] pkg/nimble/netif: use random connection intervals --- pkg/nimble/Makefile.dep | 1 + pkg/nimble/netif/include/nimble_netif.h | 37 +++++++++- pkg/nimble/netif/include/nimble_netif_conn.h | 52 ++++++++++++++ pkg/nimble/netif/nimble_netif.c | 30 +++++++- pkg/nimble/netif/nimble_netif_conn.c | 75 ++++++++++++++++++++ 5 files changed, 192 insertions(+), 3 deletions(-) diff --git a/pkg/nimble/Makefile.dep b/pkg/nimble/Makefile.dep index abe65814a1..7a34d5942a 100644 --- a/pkg/nimble/Makefile.dep +++ b/pkg/nimble/Makefile.dep @@ -68,6 +68,7 @@ endif ifneq (,$(filter nimble_netif,$(USEMODULE))) FEATURES_REQUIRED += ble_nimble_netif + USEMODULE += random USEMODULE += l2util USEMODULE += bluetil_addr ifneq (,$(filter gnrc_ipv6_%,$(USEMODULE))) diff --git a/pkg/nimble/netif/include/nimble_netif.h b/pkg/nimble/netif/include/nimble_netif.h index bb033e500c..fd4b7927fd 100644 --- a/pkg/nimble/netif/include/nimble_netif.h +++ b/pkg/nimble/netif/include/nimble_netif.h @@ -102,6 +102,37 @@ extern "C" { #define NIMBLE_NETIF_MTU (1280U) #endif +/** + * @brief Set to > 0 to enforce different connection intervals for each of the + * nodes BLE connections + * + * Enabling this option will enforce that every BLE connection a node maintains, + * independent of the nodes role, uses a different connection interval. The + * value of NIMBLE_NETIF_CONN_ITVL_SPACING specifies the minimum spacing between + * connection intervals as multiple of 1,25ms. E.g. a value of 2 will force each + * connection to use a connection interval that is at least 2.5ms different from + * all other used connection intervals. + * + * If a node is the coordinator of a connection, it will generate a connection + * interval for each new connection based on a random value by adhering to the + * spacing constraint. + * + * If a node is the subordinate of a new connection, it will check if the given + * connection interval is fulfilling the spacing constraint with respect to + * already existing connections of that node. If the connection interval of the + * new connection is not properly spaced, the node will drop the connection + * right away, giving the coordinator node the possibly to reconnect with a + * different connection interval. + */ +#ifndef NIMBLE_NETIF_CONN_ITVL_SPACING +#define NIMBLE_NETIF_CONN_ITVL_SPACING 0 +#endif + +/** + * @brief Minimum spacing of connection interval when using randomized + * intervals, in multiples of 1.25ms + */ + /** * @brief Return codes used by the NimBLE netif module */ @@ -183,16 +214,18 @@ void nimble_netif_eventcb(nimble_netif_eventcb_t cb); * * @param[in] addr address of the advertising BLE slave, in the NimBLE * addr format (little endian) - * @param[in] conn_params connection (timing) parameters + * @param[in] conn_params connection (timing) parameters, set to NULL to use + * NimBLEs default parameters * @param[in] timeout connect timeout [in ms] * * @return the used connection handle on success * @return NIMBLE_NETIF_BUSY if already connected to the given address or if * a connection setup procedure is in progress * @return NIMBLE_NETIF_NOMEM if no connection context memory is available + * @return NIMBLE_NETIF_NOTFOUND if unable to find valid connection interval */ int nimble_netif_connect(const ble_addr_t *addr, - const struct ble_gap_conn_params *conn_params, + struct ble_gap_conn_params *conn_params, uint32_t timeout); /** diff --git a/pkg/nimble/netif/include/nimble_netif_conn.h b/pkg/nimble/netif/include/nimble_netif_conn.h index d1cd7f4432..423bbd3651 100644 --- a/pkg/nimble/netif/include/nimble_netif_conn.h +++ b/pkg/nimble/netif/include/nimble_netif_conn.h @@ -23,6 +23,7 @@ #define NIMBLE_NETIF_CONN_H #include +#include #include "nimble_netif.h" @@ -175,6 +176,57 @@ int nimble_netif_conn_start_adv(void); */ void nimble_netif_conn_free(int handle, uint8_t *addr); +/** + * @brief Get the used connection interval for the given connection handle + * + * @param[in] handle connection handle + * + * @return used connection interval on success, multiples of 1.25ms + * @return 0 if unable to get connection interval + */ +uint16_t nimble_netif_conn_get_itvl(int handle); + +/** + * @brief Check if the given connection interval is used, taking the minimal + * spacing as defined by NIMBLE_NETIF_CONN_ITVL_SPACING into account + * + * @param[in] itvl connection interval to check, multiples of 1.25ms + * @param[in] skip_handle do not compare against connection interval for this + * handle, set to NIMBLE_NETIF_CONN_INVALID to check + * all + * + * @return true if given interval is used + * @return false if given interval is not used + */ +bool nimble_netif_conn_itvl_used(uint16_t itvl, int skip_handle); + +/** + * @brief Check if connection interval used by the given connection is valid + * + * @param[in] handle connection to verify + * + * @return true if the connection interval of the given connection collides + * with the connection interval of another BLE connection + * @return false if the connection interval of the given connection is valid + */ +bool nimble_netif_conn_itvl_invalid(int handle); + +/** + * @brief Generate a pseudorandom connection interval from the given range + * + * If the NIMBLE_NETIF_CONN_ITVL_SPACING option is enabled, this function + * ensures that the generated connection interval is spaced at least + * NIMBLE_NETIF_CONN_ITVL_SPACING from the connection interval of each open + * BLE connection. + * + * @param[in] min minimum connection interval + * @param[in] max maximum connection interval + * + * @return generated connection interval on success, multiples of 1.25ms + * @return 0 if no valid connection interval could be generated + */ +uint16_t nimble_netif_conn_gen_itvl(uint16_t min, uint16_t max); + /** * @brief Find the connection context with a given GAP handle and return a * pointer to it diff --git a/pkg/nimble/netif/nimble_netif.c b/pkg/nimble/netif/nimble_netif.c index e2ad84f251..d991ced593 100644 --- a/pkg/nimble/netif/nimble_netif.c +++ b/pkg/nimble/netif/nimble_netif.c @@ -485,6 +485,11 @@ static int _on_gap_slave_evt(struct ble_gap_event *event, void *arg) _notify(handle, NIMBLE_NETIF_ABORT_SLAVE, addr); break; } + if ((NIMBLE_NETIF_CONN_ITVL_SPACING > 0) && + nimble_netif_conn_itvl_invalid(handle)) { + nimble_netif_close(handle); + break; + } _on_gap_connected(conn, event->connect.conn_handle); assert(conn->state == NIMBLE_NETIF_ADV); conn->state = NIMBLE_NETIF_GAP_SLAVE; @@ -537,12 +542,15 @@ void nimble_netif_eventcb(nimble_netif_eventcb_t cb) } int nimble_netif_connect(const ble_addr_t *addr, - const struct ble_gap_conn_params *conn_params, + struct ble_gap_conn_params *conn_params, uint32_t timeout) { assert(addr); assert(_eventcb); + uint16_t itvl_min = 0; + uint16_t itvl_max = 0; + /* the netif_conn module expects addresses in network byte order */ uint8_t addrn[BLE_ADDR_LEN]; bluetil_addr_swapped_cp(addr->val, addrn); @@ -559,11 +567,31 @@ int nimble_netif_connect(const ble_addr_t *addr, return NIMBLE_NETIF_NOMEM; } + if ((conn_params != NULL) + && (conn_params->itvl_min != conn_params->itvl_max)) { + /* we need to save the min/max intervals in order to restore them + * later on */ + itvl_min = conn_params->itvl_min; + itvl_max = conn_params->itvl_max; + + uint16_t itvl = nimble_netif_conn_gen_itvl(itvl_min, itvl_max); + if (itvl == 0) { + return NIMBLE_NETIF_NOTFOUND; + } + conn_params->itvl_min = itvl; + conn_params->itvl_max = itvl; + } + int res = ble_gap_connect(nimble_riot_own_addr_type, addr, timeout, conn_params, _on_gap_master_evt, (void *)handle); assert(res == 0); (void)res; + if (itvl_min != itvl_max) { + conn_params->itvl_min = itvl_min; + conn_params->itvl_max = itvl_max; + } + _notify(handle, NIMBLE_NETIF_INIT_MASTER, addrn); return handle; diff --git a/pkg/nimble/netif/nimble_netif_conn.c b/pkg/nimble/netif/nimble_netif_conn.c index 8f907dab6a..87f48f281b 100644 --- a/pkg/nimble/netif/nimble_netif_conn.c +++ b/pkg/nimble/netif/nimble_netif_conn.c @@ -21,6 +21,7 @@ #include #include "nimble_netif_conn.h" +#include "random.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -220,3 +221,77 @@ unsigned nimble_netif_conn_count(uint16_t filter) return cnt; } + +uint16_t nimble_netif_conn_get_itvl(int handle) +{ + assert((handle >= 0) && (handle < CONN_CNT)); + struct ble_gap_conn_desc desc; + + if (!(_conn[handle].state & NIMBLE_NETIF_GAP_CONNECTED)) { + return 0; + } + int res = ble_gap_conn_find(_conn[handle].gaphandle, &desc); + if (res != 0) { + return 0; + } + + return desc.conn_itvl; +} + +bool nimble_netif_conn_itvl_used(uint16_t itvl, int skip_handle) +{ + for (unsigned i = 0; i < CONN_CNT; i++) { + if (i != skip_handle) { + uint16_t conn_itvl = nimble_netif_conn_get_itvl(i); + if (conn_itvl != 0) { + uint16_t diff = (conn_itvl < itvl) ? itvl - conn_itvl + : conn_itvl - itvl; + if (diff < NIMBLE_NETIF_CONN_ITVL_SPACING) { + return true; + } + } + } + } + + return false; +} + +bool nimble_netif_conn_itvl_invalid(int handle) +{ + if (NIMBLE_NETIF_CONN_ITVL_SPACING == 0) { + return false; + } + + uint16_t to_check = nimble_netif_conn_get_itvl(handle); + if (to_check == 0) { + return false; + } + return nimble_netif_conn_itvl_used(to_check, handle); +} + +uint16_t nimble_netif_conn_gen_itvl(uint16_t min, uint16_t max) +{ + assert(min <= max); + + uint16_t start = random_uint32_range(min, max); + + if (NIMBLE_NETIF_CONN_ITVL_SPACING == 0) { + return start; + } + + for (uint16_t itvl = start; + itvl <= max; + itvl += NIMBLE_NETIF_CONN_ITVL_SPACING) { + if (!nimble_netif_conn_itvl_used(itvl, NIMBLE_NETIF_CONN_INVALID)) { + return itvl; + } + } + for (uint16_t itvl = start - NIMBLE_NETIF_CONN_ITVL_SPACING; + itvl >= min; + itvl -= NIMBLE_NETIF_CONN_ITVL_SPACING) { + if (!nimble_netif_conn_itvl_used(itvl, NIMBLE_NETIF_CONN_INVALID)) { + return itvl; + } + } + return 0; +} From 2a0f1ac6348770c3cf8f4c3c61db4f83ff5a45e0 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Wed, 21 Apr 2021 20:34:31 +0200 Subject: [PATCH 2/3] pkt/nimble/statconn: use netifs rand conn intervals --- pkg/nimble/statconn/nimble_statconn.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/pkg/nimble/statconn/nimble_statconn.c b/pkg/nimble/statconn/nimble_statconn.c index ebbcdbbfa3..e57fe0f6b9 100644 --- a/pkg/nimble/statconn/nimble_statconn.c +++ b/pkg/nimble/statconn/nimble_statconn.c @@ -27,14 +27,10 @@ #include "host/ble_hs.h" -#if NIMBLE_STATCONN_CONN_ITVL_MIN_MS != NIMBLE_STATCONN_CONN_ITVL_MAX_MS -#include "random.h" - /* sanity check on the conn interval range to catch configuration errors */ #if NIMBLE_STATCONN_CONN_ITVL_MIN_MS > NIMBLE_STATCONN_CONN_ITVL_MAX_MS #error "nimble_statconn: CONN_ITVL_MIN_MS must be <= CONN_ITVL_MAX_MS" #endif -#endif #define ENABLE_DEBUG 0 #include "debug.h" @@ -91,16 +87,6 @@ static void _activate(uint8_t role) ble_addr_t peer; peer.type = BLE_ADDR_RANDOM; bluetil_addr_swapped_cp(slot->addr, peer.val); - /* compute a random new random connection interval if configured */ -#if NIMBLE_STATCONN_CONN_ITVL_MIN_MS != NIMBLE_STATCONN_CONN_ITVL_MAX_MS - uint32_t itvl = random_uint32_range(NIMBLE_STATCONN_CONN_ITVL_MIN_MS, - NIMBLE_STATCONN_CONN_ITVL_MAX_MS); - _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS(itvl); -#else - _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS( - NIMBLE_STATCONN_CONN_ITVL_MIN_MS); -#endif - _conn_params.itvl_max = _conn_params.itvl_min; /* try to (re)open the connection */ nimble_netif_connect(&peer, &_conn_params, _conn_timeout); } @@ -208,6 +194,10 @@ void nimble_statconn_init(void) _conn_params.latency = NIMBLE_STATCONN_CONN_LATENCY; _conn_params.supervision_timeout = BLE_GAP_SUPERVISION_TIMEOUT_MS( NIMBLE_STATCONN_CONN_SUPERTO_MS); + _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS( + NIMBLE_STATCONN_CONN_ITVL_MIN_MS); + _conn_params.itvl_max = BLE_GAP_CONN_ITVL_MS( + NIMBLE_STATCONN_CONN_ITVL_MAX_MS); _conn_params.min_ce_len = 0; _conn_params.max_ce_len = 0; _conn_timeout = NIMBLE_STATCONN_CONN_TIMEOUT_MS; From c3c4b64f83c57f700bb42941a6e8c741fbb8d7ce Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Thu, 22 Apr 2021 14:50:34 +0200 Subject: [PATCH 3/3] pkg/nimble/autoconn: allow conn interval range --- pkg/nimble/autoconn/include/nimble_autoconn.h | 7 +++++-- pkg/nimble/autoconn/include/nimble_autoconn_params.h | 10 +++++++--- pkg/nimble/autoconn/nimble_autoconn.c | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/nimble/autoconn/include/nimble_autoconn.h b/pkg/nimble/autoconn/include/nimble_autoconn.h index 0730657f2d..d9e23bfb4f 100644 --- a/pkg/nimble/autoconn/include/nimble_autoconn.h +++ b/pkg/nimble/autoconn/include/nimble_autoconn.h @@ -145,8 +145,11 @@ typedef struct { uint32_t scan_win; /** opening a new connection is aborted after this time [in ms] */ uint32_t conn_timeout; - /** connection interval used when opening a new connection [in ms] */ - uint32_t conn_itvl; + /** connection interval used when opening a new connection, lower bound. + * [in ms] */ + uint32_t conn_itvl_min; + /** connection interval, upper bound [in ms] */ + uint32_t conn_itvl_max; /** slave latency used for new connections [in ms] */ uint16_t conn_latency; /** supervision timeout used for new connections [in ms] */ diff --git a/pkg/nimble/autoconn/include/nimble_autoconn_params.h b/pkg/nimble/autoconn/include/nimble_autoconn_params.h index 9c259fdbf5..67532286f9 100644 --- a/pkg/nimble/autoconn/include/nimble_autoconn_params.h +++ b/pkg/nimble/autoconn/include/nimble_autoconn_params.h @@ -51,8 +51,11 @@ extern "C" { #ifndef NIMBLE_AUTOCONN_CONN_TIMEOUT_MS #define NIMBLE_AUTOCONN_CONN_TIMEOUT_MS (3 * NIMBLE_AUTOCONN_SCAN_WIN_MS) #endif -#ifndef NIMBLE_AUTOCONN_CONN_ITVL_MS -#define NIMBLE_AUTOCONN_CONN_ITVL_MS (75U) /* 75ms */ +#ifndef NIMBLE_AUTOCONN_CONN_ITVL_MIN_MS +#define NIMBLE_AUTOCONN_CONN_ITVL_MIN_MS 75U /* 75ms */ +#endif +#ifndef NIMBLE_AUTOCONN_CONN_ITVL_MAX_MS +#define NIMBLE_AUTOCONN_CONN_ITVL_MAX_MS 75U /* 75ms */ #endif #ifndef NIMBLE_AUTOCONN_CONN_LATENCY #define NIMBLE_AUTOCONN_CONN_LATENCY (0) @@ -74,7 +77,8 @@ extern "C" { .scan_itvl = NIMBLE_AUTOCONN_SCAN_ITVL_MS, \ .scan_win = NIMBLE_AUTOCONN_SCAN_WIN_MS, \ .conn_timeout = NIMBLE_AUTOCONN_CONN_TIMEOUT_MS, \ - .conn_itvl = NIMBLE_AUTOCONN_CONN_ITVL_MS, \ + .conn_itvl_min = NIMBLE_AUTOCONN_CONN_ITVL_MIN_MS, \ + .conn_itvl_max = NIMBLE_AUTOCONN_CONN_ITVL_MAX_MS, \ .conn_latency = NIMBLE_AUTOCONN_CONN_LATENCY, \ .conn_super_to = NIMBLE_AUTOCONN_CONN_SVTO_MS, \ .node_id = NIMBLE_AUTOCONN_NODE_ID, } diff --git a/pkg/nimble/autoconn/nimble_autoconn.c b/pkg/nimble/autoconn/nimble_autoconn.c index b75469b474..10f85d7b69 100644 --- a/pkg/nimble/autoconn/nimble_autoconn.c +++ b/pkg/nimble/autoconn/nimble_autoconn.c @@ -277,8 +277,8 @@ int nimble_autoconn_update(const nimble_autoconn_params_t *params, /* populate the connection parameters */ _conn_params.scan_itvl = BLE_GAP_SCAN_ITVL_MS(params->scan_win); _conn_params.scan_window = _conn_params.scan_itvl; - _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS(params->conn_itvl); - _conn_params.itvl_max = _conn_params.itvl_min; + _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS(params->conn_itvl_min); + _conn_params.itvl_max = BLE_GAP_CONN_ITVL_MS(params->conn_itvl_max); _conn_params.latency = 0; _conn_params.supervision_timeout = BLE_GAP_SUPERVISION_TIMEOUT_MS( params->conn_super_to);