Skip to content

Commit f231c5f

Browse files
HaavardReijhedberg
authored andcommitted
Bluetooth: Host: Add req/rsp l2cap validation
L2CAP channels will now, along with the ident, store the opcode of the pending request. This commit expands the ident lookup function to also compare received response types to this opcode, and will ignore unsolicited responses. Setting of idents for channels are moved after verification of buffer allocation for the request to be sent. A TODO is added for improving this functionality at a later time. Signed-off-by: Håvard Reierstad <[email protected]>
1 parent c292b21 commit f231c5f

File tree

2 files changed

+53
-19
lines changed

2 files changed

+53
-19
lines changed

include/zephyr/bluetooth/l2cap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ struct bt_l2cap_le_chan {
264264
uint16_t psm;
265265
/** Helps match request context during CoC */
266266
uint8_t ident;
267+
/** Opcode of the pending request. Used to match responses with requests. */
268+
uint8_t pending_req;
267269
bt_security_t required_sec_level;
268270

269271
/* Response Timeout eXpired (RTX) timer */

subsys/bluetooth/host/l2cap.c

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,11 @@ NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1,
8080
sizeof(struct bt_l2cap_disconn_req)),
8181
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
8282

83-
#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
84-
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
83+
#define ANY_OPCODE 0x100
84+
#define l2cap_lookup_ident(conn, ident, req_opcode) \
85+
__l2cap_lookup_ident(conn, ident, req_opcode, false)
86+
#define l2cap_remove_ident(conn, ident, req_opcode) \
87+
__l2cap_lookup_ident(conn, ident, req_opcode, true)
8588

8689
static sys_slist_t servers = SYS_SLIST_STATIC_INIT(&servers);
8790
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
@@ -139,13 +142,15 @@ static struct bt_l2cap_le_chan *l2cap_chan_alloc_cid(struct bt_conn *conn,
139142
}
140143

141144
static struct bt_l2cap_le_chan *
142-
__l2cap_lookup_ident(struct bt_conn *conn, uint16_t ident, bool remove)
145+
__l2cap_lookup_ident(struct bt_conn *conn, uint16_t ident, uint16_t req_opcode, bool remove)
143146
{
144147
struct bt_l2cap_chan *chan;
145148
sys_snode_t *prev = NULL;
146149

147150
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
148-
if (BT_L2CAP_LE_CHAN(chan)->ident == ident) {
151+
if ((BT_L2CAP_LE_CHAN(chan)->ident == ident) &&
152+
((BT_L2CAP_LE_CHAN(chan)->pending_req == req_opcode) ||
153+
(req_opcode == ANY_OPCODE))) {
149154
if (remove) {
150155
sys_slist_remove(&conn->channels, prev,
151156
&chan->node);
@@ -303,7 +308,7 @@ static void l2cap_rtx_timeout(struct k_work *work)
303308
bt_l2cap_chan_del(&chan->chan);
304309

305310
/* Remove other channels if pending on the same ident */
306-
while ((chan = l2cap_remove_ident(conn, chan->ident))) {
311+
while ((chan = l2cap_remove_ident(conn, chan->ident, chan->pending_req))) {
307312
bt_l2cap_chan_del(&chan->chan);
308313
}
309314
}
@@ -523,15 +528,23 @@ static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch)
523528
{
524529
struct net_buf *buf;
525530
struct bt_l2cap_le_conn_req *req;
531+
uint8_t ident;
526532

527-
ch->ident = get_ident();
533+
ident = get_ident();
528534

529-
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_REQ,
530-
ch->ident, sizeof(*req));
535+
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_REQ, ident, sizeof(*req));
531536
if (!buf) {
532537
return -ENOMEM;
533538
}
534539

540+
/* TODO Ident handling/setting should ideally be done in l2cap_chan_send_req after the
541+
* request is successfully sent on the channel but will require special considerations for
542+
* functions such as l2cap_ecred_conn_req and bt_l2cap_ecred_chan_reconfigure where the
543+
* ident is set for multiple channels, but the request is only sent on one.
544+
*/
545+
ch->ident = ident;
546+
ch->pending_req = BT_L2CAP_LE_CONN_REQ;
547+
535548
req = net_buf_add(buf, sizeof(*req));
536549
req->psm = sys_cpu_to_le16(ch->psm);
537550
req->scid = sys_cpu_to_le16(ch->rx.cid);
@@ -590,6 +603,7 @@ static int l2cap_ecred_conn_req(struct bt_l2cap_chan **chan, int channels)
590603
"The MTU shall be the same for channels in the same request.");
591604

592605
ch->ident = ident;
606+
ch->pending_req = BT_L2CAP_ECRED_CONN_REQ;
593607

594608
net_buf_add_le16(buf, ch->rx.cid);
595609
}
@@ -1781,7 +1795,7 @@ static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
17811795
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
17821796
result = sys_le16_to_cpu(rsp->result);
17831797

1784-
while ((ch = l2cap_lookup_ident(conn, ident))) {
1798+
while ((ch = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_RECONF_REQ))) {
17851799
/* Stop timer started on REQ send. The timer is only set on one
17861800
* of the channels, but we don't want to make assumptions on
17871801
* which one it is.
@@ -1794,6 +1808,7 @@ static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
17941808

17951809
ch->pending_rx_mtu = 0;
17961810
ch->ident = 0U;
1811+
ch->pending_req = 0U;
17971812

17981813
if (ch->chan.ops->reconfigured) {
17991814
ch->chan.ops->reconfigured(&ch->chan);
@@ -1939,7 +1954,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
19391954

19401955
LOG_DBG("mtu 0x%04x mps 0x%04x credits 0x%04x result %u", mtu, mps, credits, result);
19411956

1942-
chan = l2cap_lookup_ident(conn, ident);
1957+
chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ);
19431958
if (chan) {
19441959
psm = chan->psm;
19451960
} else {
@@ -1949,7 +1964,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
19491964
switch (result) {
19501965
case BT_L2CAP_LE_ERR_AUTHENTICATION:
19511966
case BT_L2CAP_LE_ERR_ENCRYPTION:
1952-
while ((chan = l2cap_lookup_ident(conn, ident))) {
1967+
while ((chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ))) {
19531968

19541969
/* Cancel RTX work */
19551970
k_work_cancel_delayable(&chan->rtx_work);
@@ -1969,7 +1984,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
19691984
case BT_L2CAP_LE_ERR_SCID_IN_USE:
19701985
/* Some connections refused – not enough resources available */
19711986
case BT_L2CAP_LE_ERR_NO_RESOURCES:
1972-
while ((chan = l2cap_lookup_ident(conn, ident))) {
1987+
while ((chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ))) {
19731988
struct bt_l2cap_chan *c;
19741989

19751990
/* Cancel RTX work */
@@ -2025,6 +2040,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20252040
chan->tx.cid = dcid;
20262041

20272042
chan->ident = 0U;
2043+
chan->pending_req = 0U;
20282044

20292045
chan->tx.mtu = mtu;
20302046
chan->tx.mps = mps;
@@ -2045,7 +2061,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20452061
break;
20462062
case BT_L2CAP_LE_ERR_PSM_NOT_SUPP:
20472063
default:
2048-
while ((chan = l2cap_remove_ident(conn, ident))) {
2064+
while ((chan = l2cap_remove_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ))) {
20492065
bt_l2cap_chan_del(&chan->chan);
20502066
}
20512067
break;
@@ -2084,9 +2100,9 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20842100
if (result == BT_L2CAP_LE_SUCCESS ||
20852101
result == BT_L2CAP_LE_ERR_AUTHENTICATION ||
20862102
result == BT_L2CAP_LE_ERR_ENCRYPTION) {
2087-
chan = l2cap_lookup_ident(conn, ident);
2103+
chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_LE_CONN_REQ);
20882104
} else {
2089-
chan = l2cap_remove_ident(conn, ident);
2105+
chan = l2cap_remove_ident(conn, ident, BT_L2CAP_LE_CONN_REQ);
20902106
}
20912107

20922108
if (!chan) {
@@ -2099,6 +2115,7 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20992115

21002116
/* Reset ident since it got a response */
21012117
chan->ident = 0U;
2118+
chan->pending_req = 0U;
21022119

21032120
switch (result) {
21042121
case BT_L2CAP_LE_SUCCESS:
@@ -2158,7 +2175,17 @@ static void le_disconn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
21582175
return;
21592176
}
21602177

2178+
chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_DISCONN_REQ);
2179+
if (!chan) {
2180+
LOG_ERR("Cannot find channel for ident %u", ident);
2181+
return;
2182+
}
2183+
21612184
scid = sys_le16_to_cpu(rsp->scid);
2185+
if (scid != chan->rx.cid) {
2186+
LOG_ERR("Invalid scid 0x%04x", scid);
2187+
return;
2188+
}
21622189

21632190
LOG_DBG("dcid 0x%04x scid 0x%04x", sys_le16_to_cpu(rsp->dcid), scid);
21642191

@@ -2226,7 +2253,7 @@ static void reject_cmd(struct bt_l2cap *l2cap, uint8_t ident,
22262253
struct bt_conn *conn = l2cap->chan.chan.conn;
22272254
struct bt_l2cap_le_chan *chan;
22282255

2229-
while ((chan = l2cap_remove_ident(conn, ident))) {
2256+
while ((chan = l2cap_remove_ident(conn, ident, ANY_OPCODE))) {
22302257
bt_l2cap_chan_del(&chan->chan);
22312258
}
22322259
}
@@ -3093,6 +3120,7 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
30933120
ch = BT_L2CAP_LE_CHAN(chans[j]);
30943121

30953122
ch->ident = ident;
3123+
ch->pending_req = BT_L2CAP_ECRED_RECONF_REQ;
30963124
ch->pending_rx_mtu = mtu;
30973125

30983126
net_buf_add_le16(buf, ch->rx.cid);
@@ -3180,6 +3208,7 @@ int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, size_
31803208
ch = BT_L2CAP_LE_CHAN(chans[i]);
31813209

31823210
ch->ident = ident;
3211+
ch->pending_req = BT_L2CAP_ECRED_RECONF_REQ;
31833212
ch->pending_rx_mtu = mtu;
31843213

31853214
net_buf_add_le16(buf, ch->rx.cid);
@@ -3232,6 +3261,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
32323261
struct net_buf *buf;
32333262
struct bt_l2cap_disconn_req *req;
32343263
struct bt_l2cap_le_chan *le_chan;
3264+
uint8_t ident;
32353265

32363266
if (!conn) {
32373267
return -ENOTCONN;
@@ -3246,14 +3276,16 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
32463276

32473277
LOG_DBG("chan %p scid 0x%04x dcid 0x%04x", chan, le_chan->rx.cid, le_chan->tx.cid);
32483278

3249-
le_chan->ident = get_ident();
3279+
ident = get_ident();
32503280

3251-
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ,
3252-
le_chan->ident, sizeof(*req));
3281+
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ, ident, sizeof(*req));
32533282
if (!buf) {
32543283
return -ENOMEM;
32553284
}
32563285

3286+
le_chan->ident = ident;
3287+
le_chan->pending_req = BT_L2CAP_DISCONN_REQ;
3288+
32573289
req = net_buf_add(buf, sizeof(*req));
32583290
req->dcid = sys_cpu_to_le16(le_chan->tx.cid);
32593291
req->scid = sys_cpu_to_le16(le_chan->rx.cid);

0 commit comments

Comments
 (0)