Bluetooth: Use seperate socket for LE ATT Server
Because traffic between the local GATT client(s) and
remote servers are subject to different controls than
remote clients and the local server, all on the shared
fixed CID, it is important to manage the traffic on
seperate sockets.
Change-Id: I62385143c86522f4b123b32592b69f2a0ae6dc76
CRs-fixed: 336029
Signed-off-by: Brian Gix <bgix@codeaurora.org>
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index da688bb..9662795 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -930,18 +930,24 @@
}
/* Find socket with fixed cid with given source and destination bdaddrs.
- * Returns closest match, locked.
+ * Direction of the req/rsp must match.
*/
-static struct sock *l2cap_get_sock_by_fixed_scid(int state,
- __le16 cid, bdaddr_t *src, bdaddr_t *dst)
+struct sock *l2cap_find_sock_by_fixed_cid_and_dir(__le16 cid, bdaddr_t *src,
+ bdaddr_t *dst, int incoming)
{
struct sock *sk = NULL, *sk1 = NULL;
struct hlist_node *node;
+ BT_DBG(" %d", incoming);
+
read_lock(&l2cap_sk_list.lock);
sk_for_each(sk, node, &l2cap_sk_list.head) {
- if (state && sk->sk_state != state)
+
+ if (incoming && !l2cap_pi(sk)->incoming)
+ continue;
+
+ if (!incoming && l2cap_pi(sk)->incoming)
continue;
if (l2cap_pi(sk)->scid == cid && !bacmp(&bt_sk(sk)->dst, dst)) {
@@ -1022,13 +1028,12 @@
l2cap_sock_init(sk, parent);
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
+ l2cap_pi(sk)->incoming = 1;
bt_accept_enqueue(parent, sk);
__l2cap_chan_add(conn, sk);
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
-
sk->sk_state = BT_CONNECTED;
parent->sk_data_ready(parent, 0);
@@ -1081,6 +1086,9 @@
}
read_unlock(&l->lock);
+
+ if (conn->hcon->out && conn->hcon->type == LE_LINK)
+ l2cap_le_conn_ready(conn);
}
/* Notify sockets that we cannot guaranty reliability anymore */
@@ -7210,16 +7218,24 @@
return 0;
}
-static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
+static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid,
+ struct sk_buff *skb)
{
struct sock *sk;
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};
- sk = l2cap_get_sock_by_fixed_scid(0, cid, conn->src, conn->dst);
+ dir = (skb->data[0] & L2CAP_ATT_RESPONSE_BIT) ? 0 : 1;
+
+ sk = l2cap_find_sock_by_fixed_cid_and_dir(cid, conn->src,
+ conn->dst, dir);
+
+ BT_DBG("sk %p, dir:%d", sk, dir);
+
if (!sk)
goto drop;
@@ -7442,6 +7458,7 @@
struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data;
struct sock *sk;
+ int smp = 0;
if (!conn)
return 0;
@@ -7463,9 +7480,7 @@
l2cap_chan_ready(sk);
}
- del_timer(&hcon->smp_timer);
- smp_link_encrypt_cmplt(conn, status, encrypt);
-
+ smp = 1;
bh_unlock_sock(sk);
continue;
}
@@ -7529,6 +7544,11 @@
read_unlock(&l->lock);
+ if (smp) {
+ del_timer(&hcon->smp_timer);
+ smp_link_encrypt_cmplt(conn, status, encrypt);
+ }
+
return 0;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 6429f23..6a4a2f0 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1105,6 +1105,7 @@
static int l2cap_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
+ struct sock *srv_sk = NULL;
int err;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -1112,6 +1113,16 @@
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 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);
+
err = l2cap_sock_shutdown(sock, 2);
sock_orphan(sk);