aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2007-05-24 08:27:19 -0400
committerMarcel Holtmann <marcel@holtmann.org>2007-05-24 08:27:19 -0400
commit5dee9e7c4c869fcffccc3d432b755793dfa71376 (patch)
tree4142c425d5941c405dc0a8bf23c59e0749192bfa /net/bluetooth
parentfd184ab4283a6a6105e8312a497d2a2a193eaf67 (diff)
[Bluetooth] Fix L2CAP configuration parameter handling
The L2CAP configuration parameter handling was missing the support for rejecting unknown options. The capability to reject unknown options is mandatory since the Bluetooth 1.2 specification. This patch implements its and also simplifies the parameter parsing. Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/l2cap.c144
1 files changed, 80 insertions, 64 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index a59b1fb63b76..670ff95ca64b 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -507,6 +507,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
507 } 507 }
508 508
509 /* Default config options */ 509 /* Default config options */
510 pi->conf_len = 0;
510 pi->conf_mtu = L2CAP_DEFAULT_MTU; 511 pi->conf_mtu = L2CAP_DEFAULT_MTU;
511 pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; 512 pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
512} 513}
@@ -1271,42 +1272,6 @@ static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned
1271 return len; 1272 return len;
1272} 1273}
1273 1274
1274static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len)
1275{
1276 int type, hint, olen;
1277 unsigned long val;
1278 void *ptr = data;
1279
1280 BT_DBG("sk %p len %d", sk, len);
1281
1282 while (len >= L2CAP_CONF_OPT_SIZE) {
1283 len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
1284
1285 hint = type & 0x80;
1286 type &= 0x7f;
1287
1288 switch (type) {
1289 case L2CAP_CONF_MTU:
1290 l2cap_pi(sk)->conf_mtu = val;
1291 break;
1292
1293 case L2CAP_CONF_FLUSH_TO:
1294 l2cap_pi(sk)->flush_to = val;
1295 break;
1296
1297 case L2CAP_CONF_QOS:
1298 break;
1299
1300 default:
1301 if (hint)
1302 break;
1303
1304 /* FIXME: Reject unknown option */
1305 break;
1306 }
1307 }
1308}
1309
1310static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) 1275static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
1311{ 1276{
1312 struct l2cap_conf_opt *opt = *ptr; 1277 struct l2cap_conf_opt *opt = *ptr;
@@ -1358,39 +1323,75 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)
1358 return ptr - data; 1323 return ptr - data;
1359} 1324}
1360 1325
1361static inline int l2cap_conf_output(struct sock *sk, void **ptr) 1326static int l2cap_parse_conf_req(struct sock *sk, void *data)
1362{ 1327{
1363 struct l2cap_pinfo *pi = l2cap_pi(sk); 1328 struct l2cap_pinfo *pi = l2cap_pi(sk);
1364 int result = 0; 1329 struct l2cap_conf_rsp *rsp = data;
1330 void *ptr = rsp->data;
1331 void *req = pi->conf_req;
1332 int len = pi->conf_len;
1333 int type, hint, olen;
1334 unsigned long val;
1335 u16 result = L2CAP_CONF_SUCCESS;
1365 1336
1366 /* Configure output options and let the other side know 1337 BT_DBG("sk %p", sk);
1367 * which ones we don't like. */ 1338
1368 if (pi->conf_mtu < pi->omtu) 1339 while (len >= L2CAP_CONF_OPT_SIZE) {
1369 result = L2CAP_CONF_UNACCEPT; 1340 len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
1370 else
1371 pi->omtu = pi->conf_mtu;
1372 1341
1373 l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu); 1342 hint = type & 0x80;
1343 type &= 0x7f;
1344
1345 switch (type) {
1346 case L2CAP_CONF_MTU:
1347 pi->conf_mtu = val;
1348 break;
1349
1350 case L2CAP_CONF_FLUSH_TO:
1351 pi->flush_to = val;
1352 break;
1353
1354 case L2CAP_CONF_QOS:
1355 break;
1356
1357 default:
1358 if (hint)
1359 break;
1360
1361 result = L2CAP_CONF_UNKNOWN;
1362 *((u8 *) ptr++) = type;
1363 break;
1364 }
1365 }
1366
1367 if (result == L2CAP_CONF_SUCCESS) {
1368 /* Configure output options and let the other side know
1369 * which ones we don't like. */
1370
1371 if (pi->conf_mtu < pi->omtu)
1372 result = L2CAP_CONF_UNACCEPT;
1373 else
1374 pi->omtu = pi->conf_mtu;
1375
1376 l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
1377 }
1374 1378
1375 BT_DBG("sk %p result %d", sk, result); 1379 rsp->scid = cpu_to_le16(pi->dcid);
1376 return result; 1380 rsp->result = cpu_to_le16(result);
1381 rsp->flags = cpu_to_le16(0x0000);
1382
1383 return ptr - data;
1377} 1384}
1378 1385
1379static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result) 1386static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags)
1380{ 1387{
1381 struct l2cap_conf_rsp *rsp = data; 1388 struct l2cap_conf_rsp *rsp = data;
1382 void *ptr = rsp->data; 1389 void *ptr = rsp->data;
1383 u16 flags = 0;
1384
1385 BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
1386 1390
1387 if (result) 1391 BT_DBG("sk %p", sk);
1388 *result = l2cap_conf_output(sk, &ptr);
1389 else
1390 flags = 0x0001;
1391 1392
1392 rsp->scid = cpu_to_le16(l2cap_pi(sk)->dcid); 1393 rsp->scid = cpu_to_le16(l2cap_pi(sk)->dcid);
1393 rsp->result = cpu_to_le16(result ? *result : 0); 1394 rsp->result = cpu_to_le16(result);
1394 rsp->flags = cpu_to_le16(flags); 1395 rsp->flags = cpu_to_le16(flags);
1395 1396
1396 return ptr - data; 1397 return ptr - data;
@@ -1535,7 +1536,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
1535 u16 dcid, flags; 1536 u16 dcid, flags;
1536 u8 rsp[64]; 1537 u8 rsp[64];
1537 struct sock *sk; 1538 struct sock *sk;
1538 int result; 1539 int len;
1539 1540
1540 dcid = __le16_to_cpu(req->dcid); 1541 dcid = __le16_to_cpu(req->dcid);
1541 flags = __le16_to_cpu(req->flags); 1542 flags = __le16_to_cpu(req->flags);
@@ -1548,25 +1549,40 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
1548 if (sk->sk_state == BT_DISCONN) 1549 if (sk->sk_state == BT_DISCONN)
1549 goto unlock; 1550 goto unlock;
1550 1551
1551 l2cap_parse_conf_req(sk, req->data, cmd->len - sizeof(*req)); 1552 /* Reject if config buffer is too small. */
1553 len = cmd->len - sizeof(*req);
1554 if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) {
1555 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
1556 l2cap_build_conf_rsp(sk, rsp,
1557 L2CAP_CONF_REJECT, flags), rsp);
1558 goto unlock;
1559 }
1560
1561 /* Store config. */
1562 memcpy(l2cap_pi(sk)->conf_req + l2cap_pi(sk)->conf_len, req->data, len);
1563 l2cap_pi(sk)->conf_len += len;
1552 1564
1553 if (flags & 0x0001) { 1565 if (flags & 0x0001) {
1554 /* Incomplete config. Send empty response. */ 1566 /* Incomplete config. Send empty response. */
1555 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, 1567 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
1556 l2cap_build_conf_rsp(sk, rsp, NULL), rsp); 1568 l2cap_build_conf_rsp(sk, rsp,
1569 L2CAP_CONF_SUCCESS, 0x0001), rsp);
1557 goto unlock; 1570 goto unlock;
1558 } 1571 }
1559 1572
1560 /* Complete config. */ 1573 /* Complete config. */
1561 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, 1574 len = l2cap_parse_conf_req(sk, rsp);
1562 l2cap_build_conf_rsp(sk, rsp, &result), rsp); 1575 if (len < 0)
1563
1564 if (result)
1565 goto unlock; 1576 goto unlock;
1566 1577
1567 /* Output config done */ 1578 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
1579
1580 /* Output config done. */
1568 l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE; 1581 l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
1569 1582
1583 /* Reset config buffer. */
1584 l2cap_pi(sk)->conf_len = 0;
1585
1570 if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { 1586 if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
1571 sk->sk_state = BT_CONNECTED; 1587 sk->sk_state = BT_CONNECTED;
1572 l2cap_chan_ready(sk); 1588 l2cap_chan_ready(sk);