diff options
-rw-r--r-- | include/net/bluetooth/l2cap.h | 8 | ||||
-rw-r--r-- | net/bluetooth/l2cap.c | 144 |
2 files changed, 86 insertions, 66 deletions
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 8242a0ee1f58..87df4e87622d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h | |||
@@ -129,8 +129,10 @@ struct l2cap_conf_rsp { | |||
129 | __u8 data[0]; | 129 | __u8 data[0]; |
130 | } __attribute__ ((packed)); | 130 | } __attribute__ ((packed)); |
131 | 131 | ||
132 | #define L2CAP_CONF_SUCCESS 0x00 | 132 | #define L2CAP_CONF_SUCCESS 0x0000 |
133 | #define L2CAP_CONF_UNACCEPT 0x01 | 133 | #define L2CAP_CONF_UNACCEPT 0x0001 |
134 | #define L2CAP_CONF_REJECT 0x0002 | ||
135 | #define L2CAP_CONF_UNKNOWN 0x0003 | ||
134 | 136 | ||
135 | struct l2cap_conf_opt { | 137 | struct l2cap_conf_opt { |
136 | __u8 type; | 138 | __u8 type; |
@@ -215,6 +217,8 @@ struct l2cap_pinfo { | |||
215 | 217 | ||
216 | __u32 link_mode; | 218 | __u32 link_mode; |
217 | 219 | ||
220 | __u8 conf_req[64]; | ||
221 | __u8 conf_len; | ||
218 | __u8 conf_state; | 222 | __u8 conf_state; |
219 | __u8 conf_retry; | 223 | __u8 conf_retry; |
220 | __u16 conf_mtu; | 224 | __u16 conf_mtu; |
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 | ||
1274 | static 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 | |||
1310 | static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) | 1275 | static 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 | ||
1361 | static inline int l2cap_conf_output(struct sock *sk, void **ptr) | 1326 | static 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 | ||
1379 | static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result) | 1386 | static 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); |