mirror of https://gitee.com/openkylin/linux.git
[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:
parent
b7279469d6
commit
79d554a697
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue