1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <string.h>
21 #include <errno.h>
22 #include "syscfg/syscfg.h"
23 #include "os/os.h"
24 #include "host/ble_hs_id.h"
25 #include "ble_hs_priv.h"
26
27 /** At least three channels required per connection (sig, att, sm). */
28 #define BLE_HS_CONN_MIN_CHANS 3
29
30 static SLIST_HEAD(, ble_hs_conn) ble_hs_conns;
31 static struct os_mempool ble_hs_conn_pool;
32
33 static os_membuf_t ble_hs_conn_elem_mem[
34 OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_CONNECTIONS),
35 sizeof(struct ble_hs_conn))
36 ];
37
38 static const uint8_t ble_hs_conn_null_addr[6];
39
ble_hs_conn_can_alloc(void)40 int ble_hs_conn_can_alloc(void)
41 {
42 #if !NIMBLE_BLE_CONNECT
43 return 0;
44 #endif
45 return ble_hs_conn_pool.mp_num_free >= 1 &&
46 ble_l2cap_chan_pool.mp_num_free >= BLE_HS_CONN_MIN_CHANS &&
47 ble_gatts_conn_can_alloc();
48 }
49
ble_hs_conn_chan_find_by_scid(struct ble_hs_conn * conn,uint16_t cid)50 struct ble_l2cap_chan *ble_hs_conn_chan_find_by_scid(struct ble_hs_conn *conn, uint16_t cid)
51 {
52 #if !NIMBLE_BLE_CONNECT
53 return NULL;
54 #endif
55 struct ble_l2cap_chan *chan;
56 SLIST_FOREACH(chan, &conn->bhc_channels, next) {
57 if (chan->scid == cid) {
58 return chan;
59 }
60
61 if (chan->scid > cid) {
62 return NULL;
63 }
64 }
65 return NULL;
66 }
67
ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn * conn,uint16_t cid)68 struct ble_l2cap_chan *ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, uint16_t cid)
69 {
70 #if !NIMBLE_BLE_CONNECT
71 return NULL;
72 #endif
73 struct ble_l2cap_chan *chan;
74 SLIST_FOREACH(chan, &conn->bhc_channels, next) {
75 if (chan->dcid == cid) {
76 return chan;
77 }
78 }
79 return NULL;
80 }
81
ble_hs_conn_chan_exist(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)82 bool ble_hs_conn_chan_exist(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
83 {
84 #if !NIMBLE_BLE_CONNECT
85 return NULL;
86 #endif
87 struct ble_l2cap_chan *tmp;
88 SLIST_FOREACH(tmp, &conn->bhc_channels, next) {
89 if (chan == tmp) {
90 return true;
91 }
92 }
93 return false;
94 }
95
ble_hs_conn_chan_insert(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)96 int ble_hs_conn_chan_insert(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
97 {
98 #if !NIMBLE_BLE_CONNECT
99 return BLE_HS_ENOTSUP;
100 #endif
101 struct ble_l2cap_chan *prev;
102 struct ble_l2cap_chan *cur;
103 prev = NULL;
104 SLIST_FOREACH(cur, &conn->bhc_channels, next) {
105 if (cur->scid == chan->scid) {
106 return BLE_HS_EALREADY;
107 }
108
109 if (cur->scid > chan->scid) {
110 break;
111 }
112
113 prev = cur;
114 }
115
116 if (prev == NULL) {
117 SLIST_INSERT_HEAD(&conn->bhc_channels, chan, next);
118 } else {
119 SLIST_INSERT_AFTER(prev, chan, next);
120 }
121
122 return 0;
123 }
124
ble_hs_conn_alloc(uint16_t conn_handle)125 struct ble_hs_conn *ble_hs_conn_alloc(uint16_t conn_handle)
126 {
127 #if !NIMBLE_BLE_CONNECT
128 return NULL;
129 #endif
130 struct ble_l2cap_chan *chan;
131 struct ble_hs_conn *conn;
132 int rc;
133 conn = os_memblock_get(&ble_hs_conn_pool);
134 if (conn == NULL) {
135 goto err;
136 }
137
138 memset_s(conn, sizeof * conn, 0, sizeof * conn);
139 conn->bhc_handle = conn_handle;
140 conn->tx_ll_mtu = MYNEWT_VAL(BLE_LL_CONN_INIT_MAX_TX_BYTES);
141 SLIST_INIT(&conn->bhc_channels);
142 chan = ble_att_create_chan(conn_handle);
143 if (chan == NULL) {
144 goto err;
145 }
146
147 rc = ble_hs_conn_chan_insert(conn, chan);
148 if (rc != 0) {
149 goto err;
150 }
151
152 chan = ble_l2cap_sig_create_chan(conn_handle);
153 if (chan == NULL) {
154 goto err;
155 }
156
157 rc = ble_hs_conn_chan_insert(conn, chan);
158 if (rc != 0) {
159 goto err;
160 }
161
162 /* Create the SM channel even if not configured. We need it to reject SM
163 * messages.
164 */
165 chan = ble_sm_create_chan(conn_handle);
166 if (chan == NULL) {
167 goto err;
168 }
169
170 rc = ble_hs_conn_chan_insert(conn, chan);
171 if (rc != 0) {
172 goto err;
173 }
174
175 rc = ble_gatts_conn_init(&conn->bhc_gatt_svr);
176 if (rc != 0) {
177 goto err;
178 }
179
180 STAILQ_INIT(&conn->bhc_tx_q);
181 STATS_INC(ble_hs_stats, conn_create);
182 return conn;
183 err:
184 ble_hs_conn_free(conn);
185 return NULL;
186 }
187
ble_hs_conn_delete_chan(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)188 void ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
189 {
190 if (conn->bhc_rx_chan == chan) {
191 conn->bhc_rx_chan = NULL;
192 }
193
194 SLIST_REMOVE(&conn->bhc_channels, chan, ble_l2cap_chan, next);
195 ble_l2cap_chan_free(conn, chan);
196 }
197
ble_hs_conn_foreach(ble_hs_conn_foreach_fn * cb,void * arg)198 void ble_hs_conn_foreach(ble_hs_conn_foreach_fn *cb, void *arg)
199 {
200 struct ble_hs_conn *conn;
201 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
202 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
203 if (cb(conn, arg) != 0) {
204 return;
205 }
206 }
207 }
208
ble_hs_conn_free(struct ble_hs_conn * conn)209 void ble_hs_conn_free(struct ble_hs_conn *conn)
210 {
211 #if !NIMBLE_BLE_CONNECT
212 return;
213 #endif
214 struct ble_l2cap_chan *chan;
215 struct os_mbuf_pkthdr *omp;
216 int rc;
217
218 if (conn == NULL) {
219 return;
220 }
221
222 ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
223
224 while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {
225 ble_hs_conn_delete_chan(conn, chan);
226 }
227
228 while ((omp = STAILQ_FIRST(&conn->bhc_tx_q)) != NULL) {
229 STAILQ_REMOVE_HEAD(&conn->bhc_tx_q, omp_next);
230 os_mbuf_free_chain(OS_MBUF_PKTHDR_TO_MBUF(omp));
231 }
232
233 #if MYNEWT_VAL(BLE_HS_DEBUG)
234 memset_s(conn, sizeof * conn, 0xff, sizeof * conn);
235 #endif
236 rc = os_memblock_put(&ble_hs_conn_pool, conn);
237 BLE_HS_DBG_ASSERT_EVAL(rc == 0);
238 STATS_INC(ble_hs_stats, conn_delete);
239 }
240
ble_hs_conn_insert(struct ble_hs_conn * conn)241 void ble_hs_conn_insert(struct ble_hs_conn *conn)
242 {
243 #if !NIMBLE_BLE_CONNECT
244 return;
245 #endif
246 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
247 BLE_HS_DBG_ASSERT_EVAL(ble_hs_conn_find(conn->bhc_handle) == NULL);
248 SLIST_INSERT_HEAD(&ble_hs_conns, conn, bhc_next);
249 }
250
ble_hs_conn_remove(struct ble_hs_conn * conn)251 void ble_hs_conn_remove(struct ble_hs_conn *conn)
252 {
253 #if !NIMBLE_BLE_CONNECT
254 return;
255 #endif
256 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
257 SLIST_REMOVE(&ble_hs_conns, conn, ble_hs_conn, bhc_next);
258 }
259
ble_hs_conn_find(uint16_t conn_handle)260 struct ble_hs_conn *ble_hs_conn_find(uint16_t conn_handle)
261 {
262 #if !NIMBLE_BLE_CONNECT
263 return NULL;
264 #endif
265 struct ble_hs_conn *conn;
266 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
267 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
268 if (conn->bhc_handle == conn_handle) {
269 return conn;
270 }
271 }
272 return NULL;
273 }
274
ble_hs_conn_find_assert(uint16_t conn_handle)275 struct ble_hs_conn *ble_hs_conn_find_assert(uint16_t conn_handle)
276 {
277 struct ble_hs_conn *conn;
278 conn = ble_hs_conn_find(conn_handle);
279 BLE_HS_DBG_ASSERT(conn != NULL);
280 return conn;
281 }
282
ble_hs_conn_find_by_addr(const ble_addr_t * addr)283 struct ble_hs_conn *ble_hs_conn_find_by_addr(const ble_addr_t *addr)
284 {
285 #if !NIMBLE_BLE_CONNECT
286 return NULL;
287 #endif
288 struct ble_hs_conn *conn;
289 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
290
291 if (!addr) {
292 return NULL;
293 }
294
295 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
296 if (BLE_ADDR_IS_RPA(addr)) {
297 if (ble_addr_cmp(&conn->bhc_peer_rpa_addr, addr) == 0) {
298 return conn;
299 }
300 } else {
301 if (ble_addr_cmp(&conn->bhc_peer_addr, addr) == 0) {
302 return conn;
303 }
304 }
305 }
306 return NULL;
307 }
308
ble_hs_conn_find_by_idx(int idx)309 struct ble_hs_conn *ble_hs_conn_find_by_idx(int idx)
310 {
311 #if !NIMBLE_BLE_CONNECT
312 return NULL;
313 #endif
314 struct ble_hs_conn *conn;
315 int i;
316 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
317 i = 0;
318 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
319 if (i == idx) {
320 return conn;
321 }
322
323 i++;
324 }
325 return NULL;
326 }
327
ble_hs_conn_exists(uint16_t conn_handle)328 int ble_hs_conn_exists(uint16_t conn_handle)
329 {
330 #if !NIMBLE_BLE_CONNECT
331 return 0;
332 #endif
333 return ble_hs_conn_find(conn_handle) != NULL;
334 }
335
336 /**
337 * Retrieves the first connection in the list.
338 */
ble_hs_conn_first(void)339 struct ble_hs_conn *ble_hs_conn_first(void)
340 {
341 #if !NIMBLE_BLE_CONNECT
342 return NULL;
343 #endif
344 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
345 return SLIST_FIRST(&ble_hs_conns);
346 }
347
ble_hs_conn_addrs(const struct ble_hs_conn * conn,struct ble_hs_conn_addrs * addrs)348 void ble_hs_conn_addrs(const struct ble_hs_conn *conn, struct ble_hs_conn_addrs *addrs)
349 {
350 const uint8_t *our_id_addr_val;
351 int rc;
352 /* Determine our address information. */
353 addrs->our_id_addr.type = ble_hs_misc_own_addr_type_to_id(conn->bhc_our_addr_type);
354 #if MYNEWT_VAL(BLE_EXT_ADV)
355 /* With EA enabled random address for slave connection is per advertising
356 * instance and requires special handling here.
357 */
358
359 if (!(conn->bhc_flags & BLE_HS_CONN_F_MASTER) &&
360 addrs->our_id_addr.type == BLE_ADDR_RANDOM) {
361 our_id_addr_val = conn->bhc_our_rnd_addr;
362 } else {
363 rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
364 assert(rc == 0);
365 }
366
367 #else
368 rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
369 assert(rc == 0);
370 #endif
371 memcpy_s(addrs->our_id_addr.val, sizeof(addrs->our_id_addr.val), our_id_addr_val, 6); // 6:size
372
373 if (memcmp(conn->bhc_our_rpa_addr.val, ble_hs_conn_null_addr, 6) == 0) { // 6:size
374 addrs->our_ota_addr = addrs->our_id_addr;
375 } else {
376 addrs->our_ota_addr = conn->bhc_our_rpa_addr;
377 }
378
379 /* Determine peer address information. */
380 addrs->peer_id_addr = conn->bhc_peer_addr;
381 addrs->peer_ota_addr = conn->bhc_peer_addr;
382
383 switch (conn->bhc_peer_addr.type) {
384 case BLE_ADDR_PUBLIC:
385 case BLE_ADDR_RANDOM:
386 break;
387
388 case BLE_ADDR_PUBLIC_ID:
389 addrs->peer_id_addr.type = BLE_ADDR_PUBLIC;
390 addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
391 break;
392
393 case BLE_ADDR_RANDOM_ID:
394 addrs->peer_id_addr.type = BLE_ADDR_RANDOM;
395 addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
396 break;
397
398 default:
399 BLE_HS_DBG_ASSERT(0);
400 break;
401 }
402 }
403
ble_hs_conn_timer(void)404 int32_t ble_hs_conn_timer(void)
405 {
406 /* If there are no timeouts configured, then there is nothing to check. */
407 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) == 0 && \
408 BLE_HS_ATT_SVR_QUEUED_WRITE_TMO == 0
409 return BLE_HS_FOREVER;
410 #endif
411 struct ble_hs_conn *conn;
412 ble_npl_time_t now;
413 int32_t next_exp_in;
414 int32_t time_diff;
415 uint16_t conn_handle;
416 conn_handle = BLE_HS_CONN_HANDLE_NONE;
417 next_exp_in = BLE_HS_FOREVER;
418 now = ble_npl_time_get();
419 ble_hs_lock();
420 /* This loop performs one of two tasks:
421 * 1. Determine if any connections need to be terminated due to timeout.
422 * If so, break out of the loop and terminate the connection. This
423 * function will need to be executed again.
424 * 2. Otherwise, determine when the next timeout will occur.
425 */
426 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
427 if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
428 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
429
430 /* Check each connection's rx fragment timer. If too much time
431 * passes after a partial packet is received, the connection is
432 * terminated.
433 */
434 if (conn->bhc_rx_chan != NULL) {
435 time_diff = conn->bhc_rx_timeout - now;
436 if (time_diff <= 0) {
437 /* ACL reassembly has timed out. Remember the connection
438 * handle so it can be terminated after the mutex is
439 * unlocked.
440 */
441 conn_handle = conn->bhc_handle;
442 break;
443 }
444
445 /* Determine if this connection is the soonest to time out. */
446 if (time_diff < next_exp_in) {
447 next_exp_in = time_diff;
448 }
449 }
450
451 #endif
452 #if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
453 /* Check each connection's rx queued write timer. If too much
454 * time passes after a prep write is received, the queue is
455 * cleared.
456 */
457 time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
458 if (time_diff <= 0) {
459 /* ACL reassembly has timed out. Remember the connection
460 * handle so it can be terminated after the mutex is
461 * unlocked.
462 */
463 conn_handle = conn->bhc_handle;
464 break;
465 }
466
467 /* Determine if this connection is the soonest to time out. */
468 if (time_diff < next_exp_in) {
469 next_exp_in = time_diff;
470 }
471
472 #endif
473 }
474 }
475 ble_hs_unlock();
476
477 /* If a connection has timed out, terminate it. We need to recursively
478 * call this function again to determine when the next timeout is. This
479 * is a tail-recursive call, so it should be optimized to execute in the
480 * same stack frame.
481 */
482 if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
483 ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
484 return ble_hs_conn_timer();
485 }
486
487 return next_exp_in;
488 }
489
ble_hs_conn_init(void)490 int ble_hs_conn_init(void)
491 {
492 int rc;
493 rc = os_mempool_init(&ble_hs_conn_pool, MYNEWT_VAL(BLE_MAX_CONNECTIONS),
494 sizeof(struct ble_hs_conn),
495 ble_hs_conn_elem_mem, "ble_hs_conn_pool");
496 if (rc != 0) {
497 return BLE_HS_EOS;
498 }
499
500 SLIST_INIT(&ble_hs_conns);
501 return 0;
502 }