[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>
This commit is contained in:
Marcel Holtmann 2008-07-14 20:13:44 +02:00
parent b7279469d6
commit 79d554a697
1 changed files with 131 additions and 61 deletions

View File

@ -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)
{
@ -297,6 +340,56 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
if (sk->sk_type != SOCK_SEQPACKET) {
bh_unlock_sock(sk);
continue;
}
if (sk->sk_state == BT_CONNECT) {
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 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);
}
read_unlock(&l->lock);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan_list *l = &conn->chan_list;
struct sock *sk;
BT_DBG("conn %p", conn);
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
@ -304,14 +397,8 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
l2cap_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
sk->sk_state_change(sk);
} else 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_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
}
} else if (sk->sk_state == BT_CONNECT)
l2cap_do_start(sk);
bh_unlock_sock(sk);
}
@ -319,26 +406,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
read_unlock(&l->lock);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
BT_DBG("conn %p", conn);
if (conn->chan_list.head || !hlist_empty(&l2cap_sk_list.head)) {
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);
}
}
/* Notify sockets that we cannot guaranty reliability anymore */
static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
{
@ -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 @@ 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: