Commit 79d554a6 authored by Marcel Holtmann's avatar Marcel Holtmann

[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: default avatarMarcel Holtmann <marcel@holtmann.org>
parent b7279469
......@@ -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 */
l2cap_pi(sk)->ident = cmd->ident;
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;
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;
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 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment