summaryrefslogtreecommitdiffstats
path: root/net/bluetooth/l2cap.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth/l2cap.c')
-rw-r--r--net/bluetooth/l2cap.c337
1 files changed, 234 insertions, 103 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 6e180d255505..c1239852834a 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;
@@ -76,11 +76,21 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static void l2cap_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
+ int reason;
BT_DBG("sock %p state %d", sk, sk->sk_state);
bh_lock_sock(sk);
- __l2cap_sock_close(sk, ETIMEDOUT);
+
+ if (sk->sk_state == BT_CONNECT &&
+ (l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH |
+ L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
+ reason = ECONNREFUSED;
+ else
+ reason = ETIMEDOUT;
+
+ __l2cap_sock_close(sk, reason);
+
bh_unlock_sock(sk);
l2cap_sock_kill(sk);
@@ -240,7 +250,7 @@ static void l2cap_chan_del(struct sock *sk, int err)
hci_conn_put(conn->hcon);
}
- sk->sk_state = BT_CLOSED;
+ sk->sk_state = BT_CLOSED;
sock_set_flag(sk, SOCK_ZAPPED);
if (err)
@@ -253,6 +263,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 +312,36 @@ 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) {
+ if (l2cap_check_link_mode(sk)) {
+ 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 +356,37 @@ 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) {
- 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,
+ bh_unlock_sock(sk);
+ continue;
+ }
+
+ if (sk->sk_state == BT_CONNECT) {
+ if (l2cap_check_link_mode(sk)) {
+ 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);
@@ -321,22 +397,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 */
@@ -388,7 +469,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0;
- setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long)conn);
+ setup_timer(&conn->info_timer, l2cap_info_timeout,
+ (unsigned long) conn);
spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
@@ -500,7 +582,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
while ((sk = bt_accept_dequeue(parent, NULL)))
l2cap_sock_close(sk);
- parent->sk_state = BT_CLOSED;
+ parent->sk_state = BT_CLOSED;
sock_set_flag(parent, SOCK_ZAPPED);
}
@@ -543,9 +625,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
- } else {
+ } else
l2cap_chan_del(sk, reason);
- }
break;
case BT_CONNECT:
@@ -614,9 +695,9 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
- sk->sk_state = BT_OPEN;
+ sk->sk_state = BT_OPEN;
- setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long)sk);
+ setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
bt_sock_link(&l2cap_sk_list, sk);
return sk;
@@ -729,22 +810,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:
@@ -1145,7 +1215,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
__l2cap_sock_close(sk, 0);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
- err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
+ err = bt_sock_wait_state(sk, BT_CLOSED,
+ sk->sk_lingertime);
}
release_sock(sk);
return err;
@@ -1189,6 +1260,11 @@ static void l2cap_chan_ready(struct sock *sk)
*/
parent->sk_data_ready(parent, 0);
}
+
+ if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ hci_conn_change_link_key(conn->hcon);
+ }
}
/* Copy frame to all raw sockets on that connection */
@@ -1477,7 +1553,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 +1602,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 +1631,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 +1754,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:
@@ -1708,7 +1798,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
default:
sk->sk_state = BT_DISCONN;
- sk->sk_err = ECONNRESET;
+ sk->sk_err = ECONNRESET;
l2cap_sock_set_timer(sk, HZ * 5);
{
struct l2cap_disconn_req req;
@@ -2080,10 +2170,8 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
{
struct l2cap_chan_list *l;
- struct l2cap_conn *conn = conn = hcon->l2cap_data;
- struct l2cap_conn_rsp rsp;
+ struct l2cap_conn *conn = hcon->l2cap_data;
struct sock *sk;
- int result;
if (!conn)
return 0;
@@ -2095,45 +2183,65 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+
bh_lock_sock(sk);
- if (sk->sk_state != BT_CONNECT2 ||
- (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
- (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {
+ if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
+ !(hcon->link_mode & HCI_LM_ENCRYPT) &&
+ !status) {
bh_unlock_sock(sk);
continue;
}
- if (!status) {
- sk->sk_state = BT_CONFIG;
- result = 0;
- } else {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_set_timer(sk, HZ/10);
- result = L2CAP_CR_SEC_BLOCK;
- }
+ if (sk->sk_state == BT_CONNECT) {
+ if (!status) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = cpu_to_le16(0);
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_REQ, sizeof(req), &req);
+ } else {
+ l2cap_sock_clear_timer(sk);
+ l2cap_sock_set_timer(sk, HZ / 10);
+ }
+ } else if (sk->sk_state == BT_CONNECT2) {
+ struct l2cap_conn_rsp rsp;
+ __u16 result;
+
+ if (!status) {
+ sk->sk_state = BT_CONFIG;
+ result = L2CAP_CR_SUCCESS;
+ } else {
+ sk->sk_state = BT_DISCONN;
+ l2cap_sock_set_timer(sk, HZ / 10);
+ result = L2CAP_CR_SEC_BLOCK;
+ }
+
+ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.result = cpu_to_le16(result);
+ rsp.status = cpu_to_le16(0);
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ }
bh_unlock_sock(sk);
}
read_unlock(&l->lock);
+
return 0;
}
-static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status)
+static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
{
struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data;
- struct l2cap_conn_rsp rsp;
struct sock *sk;
- int result;
if (!conn)
return 0;
@@ -2145,36 +2253,59 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status)
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+
bh_lock_sock(sk);
- if (sk->sk_state != BT_CONNECT2) {
+ if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
+ (sk->sk_state == BT_CONNECTED ||
+ sk->sk_state == BT_CONFIG) &&
+ !status && encrypt == 0x00) {
+ __l2cap_sock_close(sk, ECONNREFUSED);
bh_unlock_sock(sk);
continue;
}
- if (!status) {
- sk->sk_state = BT_CONFIG;
- result = 0;
- } else {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_set_timer(sk, HZ/10);
- result = L2CAP_CR_SEC_BLOCK;
- }
+ if (sk->sk_state == BT_CONNECT) {
+ if (!status) {
+ 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);
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = cpu_to_le16(0);
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_REQ, sizeof(req), &req);
+ } else {
+ l2cap_sock_clear_timer(sk);
+ l2cap_sock_set_timer(sk, HZ / 10);
+ }
+ } else if (sk->sk_state == BT_CONNECT2) {
+ struct l2cap_conn_rsp rsp;
+ __u16 result;
+
+ if (!status) {
+ sk->sk_state = BT_CONFIG;
+ result = L2CAP_CR_SUCCESS;
+ } else {
+ sk->sk_state = BT_DISCONN;
+ l2cap_sock_set_timer(sk, HZ / 10);
+ result = L2CAP_CR_SEC_BLOCK;
+ }
- if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)
- hci_conn_change_link_key(hcon);
+ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.result = cpu_to_le16(result);
+ rsp.status = cpu_to_le16(0);
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ }
bh_unlock_sock(sk);
}
read_unlock(&l->lock);
+
return 0;
}
@@ -2301,9 +2432,9 @@ static const struct proto_ops l2cap_sock_ops = {
.sendmsg = l2cap_sock_sendmsg,
.recvmsg = bt_sock_recvmsg,
.poll = bt_sock_poll,
+ .ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
- .ioctl = sock_no_ioctl,
.shutdown = l2cap_sock_shutdown,
.setsockopt = l2cap_sock_setsockopt,
.getsockopt = l2cap_sock_getsockopt
OpenPOWER on IntegriCloud