From cdcf81ac5b0188115f4267e662d131ee2dffb5af Mon Sep 17 00:00:00 2001 From: Brian Gix Date: Tue, 5 Jun 2012 13:35:39 -0700 Subject: [PATCH] Bluetooth: Fix ATT Indicate/Confirm handling If due to timing issues out of our control, an outbound ATT Indicate is delayed to the point that user space code does not receive confirmation within it time-out period, both Client and Server sockets must be torn down. We also must always respond to incoming ATT Indicate pkt with a Confirmation, as the Error Response is an illegal response for Indicate. CRs-Fixed: 363355 Change-Id: I4003a59e1a731a08818f18d5b79db537e2aa2619 Signed-off-by: Brian Gix --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 59 +++++++++++++++++++++++------------ net/bluetooth/l2cap_sock.c | 19 +++++------ 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 893aeea0792..e312ab3be48 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -654,6 +654,7 @@ struct l2cap_pinfo { #define L2CAP_ATT_MTU_RSP 0x03 #define L2CAP_ATT_RESPONSE_BIT 0x01 #define L2CAP_ATT_INDICATE 0x1D +#define L2CAP_ATT_CONFIRM 0x1E #define L2CAP_ATT_NOT_SUPPORTED 0x06 #define __delta_seq(x, y, pi) ((x) >= (y) ? (x) - (y) : \ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e9e85214075..20a96e3b9a2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7259,14 +7259,31 @@ done: static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb) { - struct sock *sk; + struct sock *sk = NULL; struct sk_buff *skb_rsp; struct l2cap_hdr *lh; int dir; - u8 mtu_rsp[] = {L2CAP_ATT_MTU_RSP, 23, 0}; u8 err_rsp[] = {L2CAP_ATT_ERROR, 0x00, 0x00, 0x00, L2CAP_ATT_NOT_SUPPORTED}; + if (skb->data[0] == L2CAP_ATT_MTU_REQ) { + u8 mtu_rsp[] = {L2CAP_ATT_MTU_RSP, 23, 0}; + + skb_rsp = bt_skb_alloc(sizeof(mtu_rsp) + L2CAP_HDR_SIZE, + GFP_ATOMIC); + if (!skb_rsp) + goto drop; + + lh = (struct l2cap_hdr *) skb_put(skb_rsp, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(sizeof(mtu_rsp)); + lh->cid = cpu_to_le16(L2CAP_CID_LE_DATA); + memcpy(skb_put(skb_rsp, sizeof(mtu_rsp)), mtu_rsp, + sizeof(mtu_rsp)); + hci_send_acl(conn->hcon, NULL, skb_rsp, 0); + + goto free_skb; + } + dir = (skb->data[0] & L2CAP_ATT_RESPONSE_BIT) ? 0 : 1; sk = l2cap_find_sock_by_fixed_cid_and_dir(cid, conn->src, @@ -7287,28 +7304,30 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, if (l2cap_pi(sk)->imtu < skb->len) goto drop; - if (skb->data[0] == L2CAP_ATT_MTU_REQ) { - skb_rsp = bt_skb_alloc(sizeof(mtu_rsp) + L2CAP_HDR_SIZE, - GFP_ATOMIC); - if (!skb_rsp) - goto drop; - - lh = (struct l2cap_hdr *) skb_put(skb_rsp, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(sizeof(mtu_rsp)); - lh->cid = cpu_to_le16(L2CAP_CID_LE_DATA); - memcpy(skb_put(skb_rsp, sizeof(mtu_rsp)), mtu_rsp, - sizeof(mtu_rsp)); - hci_send_acl(conn->hcon, NULL, skb_rsp, 0); - - goto free_skb; - } - if (!sock_queue_rcv_skb(sk, skb)) goto done; drop: - if (skb->data[0] & L2CAP_ATT_RESPONSE_BIT && - skb->data[0] != L2CAP_ATT_INDICATE) + if (skb->data[0] != L2CAP_ATT_INDICATE) + goto not_indicate; + + /* If this is an incoming Indication, we are required to confirm */ + + skb_rsp = bt_skb_alloc(sizeof(u8) + L2CAP_HDR_SIZE, GFP_ATOMIC); + if (!skb_rsp) + goto free_skb; + + lh = (struct l2cap_hdr *) skb_put(skb_rsp, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(sizeof(u8)); + lh->cid = cpu_to_le16(L2CAP_CID_LE_DATA); + err_rsp[0] = L2CAP_ATT_CONFIRM; + memcpy(skb_put(skb_rsp, sizeof(u8)), err_rsp, sizeof(u8)); + hci_send_acl(conn->hcon, NULL, skb_rsp, 0); + goto free_skb; + +not_indicate: + if (skb->data[0] & L2CAP_ATT_RESPONSE_BIT || + skb->data[0] == L2CAP_ATT_CONFIRM) goto free_skb; /* If this is an incoming PDU that requires a response, respond with diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 45d2888b77c..48e7d8b8d24 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1174,7 +1174,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; - struct sock *srv_sk = NULL; + struct sock *sk2 = NULL; int err; BT_DBG("sock %p, sk %p", sock, sk); @@ -1182,15 +1182,16 @@ static int l2cap_sock_release(struct socket *sock) if (!sk) return 0; - /* If this is an ATT Client socket, find the matching Server */ - if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA && !l2cap_pi(sk)->incoming) - srv_sk = l2cap_find_sock_by_fixed_cid_and_dir(L2CAP_CID_LE_DATA, - &bt_sk(sk)->src, &bt_sk(sk)->dst, 1); + /* If this is an ATT socket, find it's matching server/client */ + if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA) + sk2 = l2cap_find_sock_by_fixed_cid_and_dir(L2CAP_CID_LE_DATA, + &bt_sk(sk)->src, &bt_sk(sk)->dst, + l2cap_pi(sk)->incoming ? 0 : 1); - /* If server socket found, request tear down */ - BT_DBG("client:%p server:%p", sk, srv_sk); - if (srv_sk) - l2cap_sock_set_timer(srv_sk, 1); + /* If matching socket found, request tear down */ + BT_DBG("sock:%p companion:%p", sk, sk2); + if (sk2) + l2cap_sock_set_timer(sk2, 1); err = l2cap_sock_shutdown(sock, 2);