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
40 int
ble_hs_conn_can_alloc(void)41 ble_hs_conn_can_alloc(void)
42 {
43 #if !NIMBLE_BLE_CONNECT
44 return 0;
45 #endif
46
47 return ble_hs_conn_pool.mp_num_free >= 1 &&
48 ble_l2cap_chan_pool.mp_num_free >= BLE_HS_CONN_MIN_CHANS &&
49 ble_gatts_conn_can_alloc();
50 }
51
52 struct ble_l2cap_chan *
ble_hs_conn_chan_find_by_scid(struct ble_hs_conn * conn,uint16_t cid)53 ble_hs_conn_chan_find_by_scid(struct ble_hs_conn *conn, uint16_t cid)
54 {
55 #if !NIMBLE_BLE_CONNECT
56 return NULL;
57 #endif
58
59 struct ble_l2cap_chan *chan;
60
61 SLIST_FOREACH(chan, &conn->bhc_channels, next) {
62 if (chan->scid == cid) {
63 return chan;
64 }
65 if (chan->scid > cid) {
66 return NULL;
67 }
68 }
69
70 return NULL;
71 }
72
73 struct ble_l2cap_chan *
ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn * conn,uint16_t cid)74 ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, uint16_t cid)
75 {
76 #if !NIMBLE_BLE_CONNECT
77 return NULL;
78 #endif
79
80 struct ble_l2cap_chan *chan;
81
82 SLIST_FOREACH(chan, &conn->bhc_channels, next) {
83 if (chan->dcid == cid) {
84 return chan;
85 }
86 if (chan->dcid > cid) {
87 return NULL;
88 }
89 }
90
91 return NULL;
92 }
93
94 int
ble_hs_conn_chan_insert(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)95 ble_hs_conn_chan_insert(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
96 {
97 #if !NIMBLE_BLE_CONNECT
98 return BLE_HS_ENOTSUP;
99 #endif
100
101 struct ble_l2cap_chan *prev;
102 struct ble_l2cap_chan *cur;
103
104 prev = NULL;
105 SLIST_FOREACH(cur, &conn->bhc_channels, next) {
106 if (cur->scid == chan->scid) {
107 return BLE_HS_EALREADY;
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
125 struct ble_hs_conn *
ble_hs_conn_alloc(uint16_t conn_handle)126 ble_hs_conn_alloc(uint16_t conn_handle)
127 {
128 #if !NIMBLE_BLE_CONNECT
129 return NULL;
130 #endif
131
132 struct ble_l2cap_chan *chan;
133 struct ble_hs_conn *conn;
134 int rc;
135
136 conn = os_memblock_get(&ble_hs_conn_pool);
137 if (conn == NULL) {
138 goto err;
139 }
140 memset(conn, 0, sizeof *conn);
141 conn->bhc_handle = conn_handle;
142
143 SLIST_INIT(&conn->bhc_channels);
144
145 chan = ble_att_create_chan(conn_handle);
146 if (chan == NULL) {
147 goto err;
148 }
149 rc = ble_hs_conn_chan_insert(conn, chan);
150 if (rc != 0) {
151 goto err;
152 }
153
154 chan = ble_l2cap_sig_create_chan(conn_handle);
155 if (chan == NULL) {
156 goto err;
157 }
158 rc = ble_hs_conn_chan_insert(conn, chan);
159 if (rc != 0) {
160 goto err;
161 }
162
163 /* Create the SM channel even if not configured. We need it to reject SM
164 * messages.
165 */
166 chan = ble_sm_create_chan(conn_handle);
167 if (chan == NULL) {
168 goto err;
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
182 STATS_INC(ble_hs_stats, conn_create);
183
184 return conn;
185
186 err:
187 ble_hs_conn_free(conn);
188 return NULL;
189 }
190
191 void
ble_hs_conn_delete_chan(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)192 ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
193 {
194 if (conn->bhc_rx_chan == chan) {
195 conn->bhc_rx_chan = NULL;
196 }
197
198 SLIST_REMOVE(&conn->bhc_channels, chan, ble_l2cap_chan, next);
199 ble_l2cap_chan_free(chan);
200 }
201
202 void
ble_hs_conn_free(struct ble_hs_conn * conn)203 ble_hs_conn_free(struct ble_hs_conn *conn)
204 {
205 #if !NIMBLE_BLE_CONNECT
206 return;
207 #endif
208
209 struct ble_l2cap_chan *chan;
210 int rc;
211
212 if (conn == NULL) {
213 return;
214 }
215
216 ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
217
218 while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {
219 ble_hs_conn_delete_chan(conn, chan);
220 }
221
222 #if MYNEWT_VAL(BLE_HS_DEBUG)
223 memset(conn, 0xff, sizeof *conn);
224 #endif
225 rc = os_memblock_put(&ble_hs_conn_pool, conn);
226 BLE_HS_DBG_ASSERT_EVAL(rc == 0);
227
228 STATS_INC(ble_hs_stats, conn_delete);
229 }
230
231 void
ble_hs_conn_insert(struct ble_hs_conn * conn)232 ble_hs_conn_insert(struct ble_hs_conn *conn)
233 {
234 #if !NIMBLE_BLE_CONNECT
235 return;
236 #endif
237
238 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
239
240 BLE_HS_DBG_ASSERT_EVAL(ble_hs_conn_find(conn->bhc_handle) == NULL);
241 SLIST_INSERT_HEAD(&ble_hs_conns, conn, bhc_next);
242 }
243
244 void
ble_hs_conn_remove(struct ble_hs_conn * conn)245 ble_hs_conn_remove(struct ble_hs_conn *conn)
246 {
247 #if !NIMBLE_BLE_CONNECT
248 return;
249 #endif
250
251 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
252
253 SLIST_REMOVE(&ble_hs_conns, conn, ble_hs_conn, bhc_next);
254 }
255
256 struct ble_hs_conn *
ble_hs_conn_find(uint16_t conn_handle)257 ble_hs_conn_find(uint16_t conn_handle)
258 {
259 #if !NIMBLE_BLE_CONNECT
260 return NULL;
261 #endif
262
263 struct ble_hs_conn *conn;
264
265 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
266
267 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
268 if (conn->bhc_handle == conn_handle) {
269 return conn;
270 }
271 }
272
273 return NULL;
274 }
275
276 struct ble_hs_conn *
ble_hs_conn_find_assert(uint16_t conn_handle)277 ble_hs_conn_find_assert(uint16_t conn_handle)
278 {
279 struct ble_hs_conn *conn;
280
281 conn = ble_hs_conn_find(conn_handle);
282 BLE_HS_DBG_ASSERT(conn != NULL);
283
284 return conn;
285 }
286
287 struct ble_hs_conn *
ble_hs_conn_find_by_addr(const ble_addr_t * addr)288 ble_hs_conn_find_by_addr(const ble_addr_t *addr)
289 {
290 #if !NIMBLE_BLE_CONNECT
291 return NULL;
292 #endif
293
294 struct ble_hs_conn *conn;
295
296 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
297
298 if (!addr) {
299 return NULL;
300 }
301
302 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
303 if (ble_addr_cmp(&conn->bhc_peer_addr, addr) == 0) {
304 return conn;
305 }
306 }
307
308 return NULL;
309 }
310
311 struct ble_hs_conn *
ble_hs_conn_find_by_idx(int idx)312 ble_hs_conn_find_by_idx(int idx)
313 {
314 #if !NIMBLE_BLE_CONNECT
315 return NULL;
316 #endif
317
318 struct ble_hs_conn *conn;
319 int i;
320
321 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
322
323 i = 0;
324 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
325 if (i == idx) {
326 return conn;
327 }
328
329 i++;
330 }
331
332 return NULL;
333 }
334
335 int
ble_hs_conn_exists(uint16_t conn_handle)336 ble_hs_conn_exists(uint16_t conn_handle)
337 {
338 #if !NIMBLE_BLE_CONNECT
339 return 0;
340 #endif
341 return ble_hs_conn_find(conn_handle) != NULL;
342 }
343
344 /**
345 * Retrieves the first connection in the list.
346 */
347 struct ble_hs_conn *
ble_hs_conn_first(void)348 ble_hs_conn_first(void)
349 {
350 #if !NIMBLE_BLE_CONNECT
351 return NULL;
352 #endif
353
354 BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
355 return SLIST_FIRST(&ble_hs_conns);
356 }
357
358 void
ble_hs_conn_addrs(const struct ble_hs_conn * conn,struct ble_hs_conn_addrs * addrs)359 ble_hs_conn_addrs(const struct ble_hs_conn *conn,
360 struct ble_hs_conn_addrs *addrs)
361 {
362 const uint8_t *our_id_addr_val;
363 int rc;
364
365 /* Determine our address information. */
366 addrs->our_id_addr.type =
367 ble_hs_misc_addr_type_to_id(conn->bhc_our_addr_type);
368
369 #if MYNEWT_VAL(BLE_EXT_ADV)
370 /* With EA enabled random address for slave connection is per advertising
371 * instance and requires special handling here.
372 */
373
374 if (!(conn->bhc_flags & BLE_HS_CONN_F_MASTER) &&
375 addrs->our_id_addr.type == BLE_ADDR_RANDOM) {
376 our_id_addr_val = conn->bhc_our_rnd_addr;
377 } else {
378 rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
379 assert(rc == 0);
380 }
381 #else
382 rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
383 assert(rc == 0);
384 #endif
385
386 memcpy(addrs->our_id_addr.val, our_id_addr_val, 6);
387
388 if (memcmp(conn->bhc_our_rpa_addr.val, ble_hs_conn_null_addr, 6) == 0) {
389 addrs->our_ota_addr = addrs->our_id_addr;
390 } else {
391 addrs->our_ota_addr = conn->bhc_our_rpa_addr;
392 }
393
394 /* Determine peer address information. */
395 addrs->peer_id_addr = conn->bhc_peer_addr;
396 addrs->peer_ota_addr = conn->bhc_peer_addr;
397 switch (conn->bhc_peer_addr.type) {
398 case BLE_ADDR_PUBLIC:
399 case BLE_ADDR_RANDOM:
400 break;
401
402 case BLE_ADDR_PUBLIC_ID:
403 addrs->peer_id_addr.type = BLE_ADDR_PUBLIC;
404 addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
405 break;
406
407 case BLE_ADDR_RANDOM_ID:
408 addrs->peer_id_addr.type = BLE_ADDR_RANDOM;
409 addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
410 break;
411
412 default:
413 BLE_HS_DBG_ASSERT(0);
414 break;
415 }
416 }
417
418 int32_t
ble_hs_conn_timer(void)419 ble_hs_conn_timer(void)
420 {
421 /* If there are no timeouts configured, then there is nothing to check. */
422 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) == 0 && \
423 BLE_HS_ATT_SVR_QUEUED_WRITE_TMO == 0
424
425 return BLE_HS_FOREVER;
426 #endif
427
428 struct ble_hs_conn *conn;
429 ble_npl_time_t now;
430 int32_t next_exp_in;
431 int32_t time_diff;
432 uint16_t conn_handle;
433
434 conn_handle = BLE_HS_CONN_HANDLE_NONE;
435 next_exp_in = BLE_HS_FOREVER;
436 now = ble_npl_time_get();
437
438 ble_hs_lock();
439
440 /* This loop performs one of two tasks:
441 * 1. Determine if any connections need to be terminated due to timeout.
442 * If so, break out of the loop and terminate the connection. This
443 * function will need to be executed again.
444 * 2. Otherwise, determine when the next timeout will occur.
445 */
446 SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
447 if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
448
449 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
450 /* Check each connection's rx fragment timer. If too much time
451 * passes after a partial packet is received, the connection is
452 * terminated.
453 */
454 if (conn->bhc_rx_chan != NULL) {
455 time_diff = conn->bhc_rx_timeout - now;
456
457 if (time_diff <= 0) {
458 /* ACL reassembly has timed out. Remember the connection
459 * handle so it can be terminated after the mutex is
460 * unlocked.
461 */
462 conn_handle = conn->bhc_handle;
463 break;
464 }
465
466 /* Determine if this connection is the soonest to time out. */
467 if (time_diff < next_exp_in) {
468 next_exp_in = time_diff;
469 }
470 }
471 #endif
472
473 #if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
474 /* Check each connection's rx queued write timer. If too much
475 * time passes after a prep write is received, the queue is
476 * cleared.
477 */
478 time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
479 if (time_diff <= 0) {
480 /* ACL reassembly has timed out. Remember the connection
481 * handle so it can be terminated after the mutex is
482 * unlocked.
483 */
484 conn_handle = conn->bhc_handle;
485 break;
486 }
487
488 /* Determine if this connection is the soonest to time out. */
489 if (time_diff < next_exp_in) {
490 next_exp_in = time_diff;
491 }
492 #endif
493 }
494 }
495
496 ble_hs_unlock();
497
498 /* If a connection has timed out, terminate it. We need to recursively
499 * call this function again to determine when the next timeout is. This
500 * is a tail-recursive call, so it should be optimized to execute in the
501 * same stack frame.
502 */
503 if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
504 ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
505 return ble_hs_conn_timer();
506 }
507
508 return next_exp_in;
509 }
510
511 int
ble_hs_conn_init(void)512 ble_hs_conn_init(void)
513 {
514 int rc;
515
516 rc = os_mempool_init(&ble_hs_conn_pool, MYNEWT_VAL(BLE_MAX_CONNECTIONS),
517 sizeof (struct ble_hs_conn),
518 ble_hs_conn_elem_mem, "ble_hs_conn_pool");
519 if (rc != 0) {
520 return BLE_HS_EOS;
521 }
522
523 SLIST_INIT(&ble_hs_conns);
524
525 return 0;
526 }
527