[Bluetooth] Change retrieval of L2CAP features mask
authorMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Jul 2008 18:13:44 +0000 (20:13 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Jul 2008 18:13:44 +0000 (20:13 +0200)
Getting the remote L2CAP features mask is really important, but doing
this as less intrusive as possible is tricky. To play nice with older
systems and Bluetooth qualification testing, the features mask is now
only retrieved in two specific cases and only once per lifetime of an
ACL link.

When trying to establish a L2CAP connection and the remote features mask
is unknown, the L2CAP information request is sent when the ACL link goes
into connected state. This applies only to outgoing connections and also
only for the connection oriented channels.

The second case is when a connection request has been received. In this
case a connection response with the result pending and the information
request will be send. After receiving an information response or if the
timeout gets triggered, the normal connection setup process with security
setup will be initiated.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/l2cap.c

index 6e180d2..2e3abdf 100644 (file)
@@ -55,7 +55,7 @@
 #define BT_DBG(D...)
 #endif
 
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "2.9"
+#define VERSION "2.10"
 
 static u32 l2cap_feat_mask = 0x0000;
 
 
 static u32 l2cap_feat_mask = 0x0000;
 
@@ -253,6 +253,21 @@ static void l2cap_chan_del(struct sock *sk, int err)
                sk->sk_state_change(sk);
 }
 
                sk->sk_state_change(sk);
 }
 
+/* Service level security */
+static inline int l2cap_check_link_mode(struct sock *sk)
+{
+       struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+       if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
+                               (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE))
+               return hci_conn_encrypt(conn->hcon);
+
+       if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
+               return hci_conn_auth(conn->hcon);
+
+       return 1;
+}
+
 static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
 {
        u8 id;
 static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
 {
        u8 id;
@@ -287,6 +302,34 @@ static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16
        return hci_send_acl(conn->hcon, skb, 0);
 }
 
        return hci_send_acl(conn->hcon, skb, 0);
 }
 
+static void l2cap_do_start(struct sock *sk)
+{
+       struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+               struct l2cap_conn_req req;
+               req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+               req.psm  = l2cap_pi(sk)->psm;
+
+               l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
+               l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+                                       L2CAP_CONN_REQ, sizeof(req), &req);
+       } else {
+               struct l2cap_info_req req;
+               req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+               conn->info_ident = l2cap_get_ident(conn);
+
+               mod_timer(&conn->info_timer, jiffies +
+                                       msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+
+               l2cap_send_cmd(conn, conn->info_ident,
+                                       L2CAP_INFO_REQ, sizeof(req), &req);
+       }
+}
+
 /* ---- L2CAP connections ---- */
 static void l2cap_conn_start(struct l2cap_conn *conn)
 {
 /* ---- L2CAP connections ---- */
 static void l2cap_conn_start(struct l2cap_conn *conn)
 {
@@ -301,16 +344,35 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                bh_lock_sock(sk);
 
                if (sk->sk_type != SOCK_SEQPACKET) {
                bh_lock_sock(sk);
 
                if (sk->sk_type != SOCK_SEQPACKET) {
-                       l2cap_sock_clear_timer(sk);
-                       sk->sk_state = BT_CONNECTED;
-                       sk->sk_state_change(sk);
-               } else if (sk->sk_state == BT_CONNECT) {
+                       bh_unlock_sock(sk);
+                       continue;
+               }
+
+               if (sk->sk_state == BT_CONNECT) {
                        struct l2cap_conn_req req;
                        struct l2cap_conn_req req;
-                       l2cap_pi(sk)->ident = l2cap_get_ident(conn);
                        req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
                        req.psm  = l2cap_pi(sk)->psm;
                        req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
                        req.psm  = l2cap_pi(sk)->psm;
+
+                       l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
                        l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
                                        L2CAP_CONN_REQ, sizeof(req), &req);
                        l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
                                        L2CAP_CONN_REQ, sizeof(req), &req);
+               } else if (sk->sk_state == BT_CONNECT2) {
+                       struct l2cap_conn_rsp rsp;
+                       rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+                       rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+
+                       if (l2cap_check_link_mode(sk)) {
+                               sk->sk_state = BT_CONFIG;
+                               rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+                               rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+                       } else {
+                               rsp.result = cpu_to_le16(L2CAP_CR_PEND);
+                               rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
+                       }
+
+                       l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+                                       L2CAP_CONN_RSP, sizeof(rsp), &rsp);
                }
 
                bh_unlock_sock(sk);
                }
 
                bh_unlock_sock(sk);
@@ -321,22 +383,27 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
 
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
 
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
-       BT_DBG("conn %p", conn);
+       struct l2cap_chan_list *l = &conn->chan_list;
+       struct sock *sk;
 
 
-       if (conn->chan_list.head || !hlist_empty(&l2cap_sk_list.head)) {
-               struct l2cap_info_req req;
+       BT_DBG("conn %p", conn);
 
 
-               req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+       read_lock(&l->lock);
 
 
-               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
-               conn->info_ident = l2cap_get_ident(conn);
+       for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+               bh_lock_sock(sk);
 
 
-               mod_timer(&conn->info_timer,
-                       jiffies + msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+               if (sk->sk_type != SOCK_SEQPACKET) {
+                       l2cap_sock_clear_timer(sk);
+                       sk->sk_state = BT_CONNECTED;
+                       sk->sk_state_change(sk);
+               } else if (sk->sk_state == BT_CONNECT)
+                       l2cap_do_start(sk);
 
 
-               l2cap_send_cmd(conn, conn->info_ident,
-                                       L2CAP_INFO_REQ, sizeof(req), &req);
+               bh_unlock_sock(sk);
        }
        }
+
+       read_unlock(&l->lock);
 }
 
 /* Notify sockets that we cannot guaranty reliability anymore */
 }
 
 /* Notify sockets that we cannot guaranty reliability anymore */
@@ -729,22 +796,11 @@ static int l2cap_do_connect(struct sock *sk)
        l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
        if (hcon->state == BT_CONNECTED) {
        l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
        if (hcon->state == BT_CONNECTED) {
-               if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
-                       l2cap_conn_ready(conn);
-                       goto done;
-               }
-
-               if (sk->sk_type == SOCK_SEQPACKET) {
-                       struct l2cap_conn_req req;
-                       l2cap_pi(sk)->ident = l2cap_get_ident(conn);
-                       req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
-                       req.psm  = l2cap_pi(sk)->psm;
-                       l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
-                                       L2CAP_CONN_REQ, sizeof(req), &req);
-               } else {
+               if (sk->sk_type != SOCK_SEQPACKET) {
                        l2cap_sock_clear_timer(sk);
                        sk->sk_state = BT_CONNECTED;
                        l2cap_sock_clear_timer(sk);
                        sk->sk_state = BT_CONNECTED;
-               }
+               } else
+                       l2cap_do_start(sk);
        }
 
 done:
        }
 
 done:
@@ -1477,7 +1533,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
        struct sock *sk, *parent;
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
        struct sock *sk, *parent;
-       int result = 0, status = 0;
+       int result, status = 0;
 
        u16 dcid = 0, scid = __le16_to_cpu(req->scid);
        __le16 psm  = req->psm;
 
        u16 dcid = 0, scid = __le16_to_cpu(req->scid);
        __le16 psm  = req->psm;
@@ -1526,25 +1582,24 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
 
        l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
 
        l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
 
-       /* Service level security */
-       result = L2CAP_CR_PEND;
-       status = L2CAP_CS_AUTHEN_PEND;
-       sk->sk_state = BT_CONNECT2;
        l2cap_pi(sk)->ident = cmd->ident;
 
        l2cap_pi(sk)->ident = cmd->ident;
 
-       if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
-                       (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {
-               if (!hci_conn_encrypt(conn->hcon))
-                       goto done;
-       } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
-               if (!hci_conn_auth(conn->hcon))
-                       goto done;
+       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+               if (l2cap_check_link_mode(sk)) {
+                       sk->sk_state = BT_CONFIG;
+                       result = L2CAP_CR_SUCCESS;
+                       status = L2CAP_CS_NO_INFO;
+               } else {
+                       sk->sk_state = BT_CONNECT2;
+                       result = L2CAP_CR_PEND;
+                       status = L2CAP_CS_AUTHEN_PEND;
+               }
+       } else {
+               sk->sk_state = BT_CONNECT2;
+               result = L2CAP_CR_PEND;
+               status = L2CAP_CS_NO_INFO;
        }
 
        }
 
-       sk->sk_state = BT_CONFIG;
-       result = status = 0;
-
-done:
        write_unlock_bh(&list->lock);
 
 response:
        write_unlock_bh(&list->lock);
 
 response:
@@ -1556,6 +1611,21 @@ sendresp:
        rsp.result = cpu_to_le16(result);
        rsp.status = cpu_to_le16(status);
        l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
        rsp.result = cpu_to_le16(result);
        rsp.status = cpu_to_le16(status);
        l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+
+       if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
+               struct l2cap_info_req info;
+               info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+               conn->info_ident = l2cap_get_ident(conn);
+
+               mod_timer(&conn->info_timer, jiffies +
+                                       msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+
+               l2cap_send_cmd(conn, conn->info_ident,
+                                       L2CAP_INFO_REQ, sizeof(info), &info);
+       }
+
        return 0;
 }
 
        return 0;
 }
 
@@ -1664,9 +1734,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        }
 
        if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
        }
 
        if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
-               u8 req[64];
+               u8 buf[64];
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-                                       l2cap_build_conf_req(sk, req), req);
+                                       l2cap_build_conf_req(sk, buf), buf);
        }
 
 unlock:
        }
 
 unlock: