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);