Repository: incubator-mynewt-core
Updated Branches:
  refs/heads/develop 5c5a987ef -> 0d402e54a


MYNEWT-341 BLE Host - Handle missing ACL fragments

If a packet is only partially received, and 30 seconds has passed since
the last fragment was received, a timeout occurs.  When this happens,
the connection is terminated.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/0d402e54
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/0d402e54
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/0d402e54

Branch: refs/heads/develop
Commit: 0d402e54aa6d0fa24f31eebac15038b1d00b969b
Parents: 5c5a987
Author: Christopher Collins <ccoll...@apache.org>
Authored: Thu Dec 1 16:35:59 2016 -0800
Committer: Christopher Collins <ccoll...@apache.org>
Committed: Thu Dec 1 16:35:59 2016 -0800

----------------------------------------------------------------------
 net/nimble/host/src/ble_hs.c                |  9 ++--
 net/nimble/host/src/ble_hs_conn.c           | 61 ++++++++++++++++++++++++
 net/nimble/host/src/ble_hs_conn_priv.h      |  3 ++
 net/nimble/host/src/ble_l2cap.c             |  2 +
 net/nimble/host/src/ble_l2cap_priv.h        |  2 +
 net/nimble/host/test/src/ble_hs_conn_test.c |  6 ++-
 net/nimble/host/test/src/ble_l2cap_test.c   | 46 ++++++++++++++++++
 7 files changed, 124 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/src/ble_hs.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs.c b/net/nimble/host/src/ble_hs.c
index b55dc2c..c5799ee 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -314,10 +314,13 @@ ble_hs_timer_exp(struct os_event *ev)
 
     ticks_until_next = ble_sm_timer();
     ble_hs_timer_sched(ticks_until_next);
+
+    ticks_until_next = ble_hs_conn_timer();
+    ble_hs_timer_sched(ticks_until_next);
 }
 
 static void
-ble_hs_timer_timer_reset(uint32_t ticks)
+ble_hs_timer_reset(uint32_t ticks)
 {
     int rc;
 
@@ -341,7 +344,7 @@ ble_hs_timer_sched(int32_t ticks_from_now)
     if (!os_callout_queued(&ble_hs_timer_timer) ||
         OS_TIME_TICK_LT(abs_time, ble_hs_timer_timer.c_ticks)) {
 
-        ble_hs_timer_timer_reset(ticks_from_now);
+        ble_hs_timer_reset(ticks_from_now);
     }
 }
 
@@ -351,7 +354,7 @@ ble_hs_timer_resched(void)
     /* Reschedule the timer to run immediately.  The timer callback will query
      * each module for an up-to-date expiration time.
      */
-    ble_hs_timer_timer_reset(0);
+    ble_hs_timer_reset(0);
 }
 
 static void

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/src/ble_hs_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.c 
b/net/nimble/host/src/ble_hs_conn.c
index 59c3ae5..9a6517e 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -378,6 +378,67 @@ ble_hs_conn_addrs(const struct ble_hs_conn *conn,
     }
 }
 
+int32_t
+ble_hs_conn_timer(void)
+{
+    struct ble_hs_conn *conn;
+    os_time_t now;
+    int32_t next_exp_in;
+    int32_t time_diff;
+    uint16_t conn_handle;
+
+    conn_handle = BLE_HS_CONN_HANDLE_NONE;
+    next_exp_in = BLE_HS_FOREVER;
+    now = os_time_get();
+
+    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) {
+        /* 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_flags & BLE_HS_CONN_F_TERMINATING) &&
+            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;
+                conn->bhc_flags |= BLE_HS_CONN_F_TERMINATING;
+                break;
+            }
+
+            /* Determine if this connection is the soonest to time out. */
+            if (time_diff < next_exp_in) {
+                next_exp_in = time_diff;
+            }
+        }
+    }
+
+    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 
 ble_hs_conn_init(void)
 {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/src/ble_hs_conn_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn_priv.h 
b/net/nimble/host/src/ble_hs_conn_priv.h
index 92e6f37..3dfa5cf 100644
--- a/net/nimble/host/src/ble_hs_conn_priv.h
+++ b/net/nimble/host/src/ble_hs_conn_priv.h
@@ -35,6 +35,7 @@ struct ble_l2cap_chan;
 typedef uint8_t ble_hs_conn_flags_t;
 
 #define BLE_HS_CONN_F_MASTER        0x01
+#define BLE_HS_CONN_F_TERMINATING   0x02
 
 struct ble_hs_conn {
     SLIST_ENTRY(ble_hs_conn) bhc_next;
@@ -54,6 +55,7 @@ struct ble_hs_conn {
 
     struct ble_l2cap_chan_list bhc_channels;
     struct ble_l2cap_chan *bhc_rx_chan; /* Channel rxing current packet. */
+    uint32_t bhc_rx_timeout;
     uint16_t bhc_outstanding_pkts;
 
     struct ble_att_svr_conn bhc_att_svr;
@@ -93,6 +95,7 @@ int ble_hs_conn_chan_insert(struct ble_hs_conn *conn,
                             struct ble_l2cap_chan *chan);
 void ble_hs_conn_addrs(const struct ble_hs_conn *conn,
                        struct ble_hs_conn_addrs *addrs);
+int32_t ble_hs_conn_timer(void);
 
 int ble_hs_conn_init(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/src/ble_l2cap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap.c b/net/nimble/host/src/ble_l2cap.c
index 66701b1..aa37784 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -180,6 +180,8 @@ ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct 
ble_l2cap_chan *chan,
         rc = 0;
     } else {
         /* More fragments remain. */
+        conn->bhc_rx_timeout = os_time_get() + BLE_L2CAP_REASSEM_TIMEOUT;
+        ble_hs_timer_resched();
         rc = BLE_HS_EAGAIN;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/src/ble_l2cap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_priv.h 
b/net/nimble/host/src/ble_l2cap_priv.h
index 79d58a7..2087e81 100644
--- a/net/nimble/host/src/ble_l2cap_priv.h
+++ b/net/nimble/host/src/ble_l2cap_priv.h
@@ -29,6 +29,8 @@
 extern "C" {
 #endif
 
+#define BLE_L2CAP_REASSEM_TIMEOUT       (30 * OS_TICKS_PER_SEC)
+
 struct ble_hs_conn;
 struct hci_data_hdr;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/test/src/ble_hs_conn_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_hs_conn_test.c 
b/net/nimble/host/test/src/ble_hs_conn_test.c
index 4ba92d2..a5fdba7 100644
--- a/net/nimble/host/test/src/ble_hs_conn_test.c
+++ b/net/nimble/host/test/src/ble_hs_conn_test.c
@@ -167,8 +167,10 @@ TEST_CASE(ble_hs_conn_test_undirect_connectable_success)
 
     adv_params = ble_hs_test_util_adv_params;
     adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
-    rc = ble_hs_test_util_adv_start(BLE_ADDR_TYPE_PUBLIC, BLE_ADDR_TYPE_PUBLIC,
-                                    addr, &adv_params, BLE_HS_FOREVER,
+    rc = ble_hs_test_util_adv_start(BLE_ADDR_TYPE_PUBLIC,
+                                    BLE_ADDR_TYPE_PUBLIC,
+                                    addr, &adv_params,
+                                    BLE_HS_FOREVER,
                                     NULL, NULL, 0, 0);
     TEST_ASSERT(rc == 0);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/0d402e54/net/nimble/host/test/src/ble_l2cap_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_l2cap_test.c 
b/net/nimble/host/test/src/ble_l2cap_test.c
index 7be148c..55d6afe 100644
--- a/net/nimble/host/test/src/ble_l2cap_test.c
+++ b/net/nimble/host/test/src/ble_l2cap_test.c
@@ -352,6 +352,51 @@ TEST_CASE(ble_l2cap_test_case_frag_channels)
     ble_hs_unlock();
 }
 
+TEST_CASE(ble_l2cap_test_case_frag_timeout)
+{
+    int32_t ticks_from_now;
+    int rc;
+
+    ble_l2cap_test_util_init();
+
+    ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+                                    NULL, NULL);
+
+    /* Ensure timer is not set. */
+    ticks_from_now = ble_hs_conn_timer();
+    TEST_ASSERT_FATAL(ticks_from_now == BLE_HS_FOREVER);
+
+    /* Receive the first fragment of a multipart ACL data packet. */
+    rc = ble_l2cap_test_util_rx_first_frag(2, 14, BLE_L2CAP_TEST_CID, 30);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    /* Ensure timer will expire in 30 seconds. */
+    ticks_from_now = ble_hs_conn_timer();
+    TEST_ASSERT(ticks_from_now == BLE_L2CAP_REASSEM_TIMEOUT);
+
+    /* Almost let the timer expire. */
+    os_time_advance(BLE_L2CAP_REASSEM_TIMEOUT - 1);
+    ticks_from_now = ble_hs_conn_timer();
+    TEST_ASSERT(ticks_from_now == 1);
+
+    /* Receive a second fragment. */
+    rc = ble_l2cap_test_util_rx_next_frag(2, 14);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    /* Ensure timer got reset. */
+    ticks_from_now = ble_hs_conn_timer();
+    TEST_ASSERT(ticks_from_now == BLE_L2CAP_REASSEM_TIMEOUT);
+
+    /* Allow the timer to expire. */
+    ble_hs_test_util_set_ack_disconnect(0);
+    os_time_advance(BLE_L2CAP_REASSEM_TIMEOUT);
+    ticks_from_now = ble_hs_conn_timer();
+    TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
+
+    /* Ensure connection was terminated. */
+    ble_hs_test_util_verify_tx_disconnect(2, BLE_ERR_REM_USER_CONN_TERM);
+}
+
 /*****************************************************************************
  * $unsolicited response                                                     *
  *****************************************************************************/
@@ -569,6 +614,7 @@ TEST_SUITE(ble_l2cap_test_suite)
     ble_l2cap_test_case_frag_single();
     ble_l2cap_test_case_frag_multiple();
     ble_l2cap_test_case_frag_channels();
+    ble_l2cap_test_case_frag_timeout();
     ble_l2cap_test_case_sig_unsol_rsp();
     ble_l2cap_test_case_sig_update_accept();
     ble_l2cap_test_case_sig_update_reject();

Reply via email to