summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-07-14 20:13:44 +0200
committerMarcel Holtmann <marcel@holtmann.org>2008-07-14 20:13:44 +0200
commit79d554a6976a295aa9212172b218f29ca71c3b3d (patch)
tree430d106c1ebf194fcf208a145d32a2829ffca5a6
parentb7279469d66b55119784b8b9529c99c1955fe747 (diff)
[Bluetooth] Change retrieval of L2CAP features mask
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>
-rw-r--r--net/bluetooth/l2cap.c166
1 files changed, 118 insertions, 48 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 6e180d255505..2e3abdfbd69d 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -55,7 +55,7 @@
#define BT_DBG(D...)
#endif
-#define VERSION "2.9"
+#define VERSION "2.10"
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);
}
+/* 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;
@@ -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);
}
+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)
{
@@ -301,16 +344,35 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
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;
- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
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 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);
@@ -321,22 +383,27 @@ static void l2cap_conn_start(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 */
@@ -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) {
- 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;
- }
+ } else
+ l2cap_do_start(sk);
}
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;
- int result = 0, status = 0;
+ int result, status = 0;
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);
- /* Service level security */
- result = L2CAP_CR_PEND;
- status = L2CAP_CS_AUTHEN_PEND;
- sk->sk_state = BT_CONNECT2;
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:
@@ -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);
+
+ 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;
}
@@ -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)) {
- u8 req[64];
+ u8 buf[64];
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: