Bluetooth: Fix double L2CAP connection request
authorMarcel Holtmann <marcel@holtmann.org>
Fri, 6 Feb 2009 22:56:36 +0000 (23:56 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 27 Feb 2009 05:14:41 +0000 (06:14 +0100)
If the remote L2CAP server uses authentication pending stage and
encryption is enabled it can happen that a L2CAP connection request is
sent twice due to a race condition in the connection state machine.

When the remote side indicates any kind of connection pending, then
track this state and skip sending of L2CAP commands for this period.

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

index 1c8cf3e9b1ca9c16f4a930e7bccb2a2af77a14cc..4781d285b2e9e2ce94d89e2b087be429947d76b1 100644 (file)
@@ -259,6 +259,7 @@ struct l2cap_pinfo {
 #define L2CAP_CONF_REQ_SENT    0x01
 #define L2CAP_CONF_INPUT_DONE  0x02
 #define L2CAP_CONF_OUTPUT_DONE 0x04
+#define L2CAP_CONF_CONNECT_PEND        0x80
 
 #define L2CAP_CONF_MAX_RETRIES 2
 
index 07fdbc7dd54d138dd4fc7e0fec9343a5245021dd..01f750142d55638dbcaee635e138835ee3b45703 100644 (file)
@@ -1946,11 +1946,14 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
                l2cap_pi(sk)->dcid = dcid;
                l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
 
+               l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
+
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                                        l2cap_build_conf_req(sk, req), req);
                break;
 
        case L2CAP_CR_PEND:
+               l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
                break;
 
        default:
@@ -2478,6 +2481,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
        for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
                bh_lock_sock(sk);
 
+               if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
+                       bh_unlock_sock(sk);
+                       continue;
+               }
+
                if (!status && (sk->sk_state == BT_CONNECTED ||
                                                sk->sk_state == BT_CONFIG)) {
                        l2cap_check_encryption(sk, encrypt);