summaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@openbossa.org>2011-01-26 21:42:57 -0300
committerGustavo F. Padovan <padovan@profusion.mobi>2011-06-13 16:05:34 -0300
commitf1cb9af557dd8fb5d98fbcc4b5d3eb9d6d235af7 (patch)
treee2701e2cecfbf5cea1a079a7a87f8bfe020b7598 /net/bluetooth
parent9b3d67405b17d61ba8be9d824222fb410f487b8a (diff)
downloadlinux-f1cb9af557dd8fb5d98fbcc4b5d3eb9d6d235af7.tar.bz2
Bluetooth: Add support for resuming socket when SMP is finished
This adds support for resuming the user space traffic when SMP negotiation is complete. Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@openbossa.org> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/l2cap_core.c61
-rw-r--r--net/bluetooth/l2cap_sock.c16
-rw-r--r--net/bluetooth/smp.c40
3 files changed, 75 insertions, 42 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 865716504396..584a4237eb3f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -890,6 +890,23 @@ clean:
bh_unlock_sock(parent);
}
+static void l2cap_chan_ready(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct sock *parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ chan->conf_state = 0;
+ __clear_chan_timer(chan);
+
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
@@ -906,13 +923,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
bh_lock_sock(sk);
- if (conn->hcon->type == LE_LINK) {
- __clear_chan_timer(chan);
- l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
+ if (conn->hcon->type == LE_LINK)
if (smp_conn_security(conn, chan->sec_level))
- BT_DBG("Insufficient security");
- }
+ l2cap_chan_ready(sk);
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
@@ -1675,30 +1688,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
return err;
}
-static void l2cap_chan_ready(struct sock *sk)
-{
- struct sock *parent = bt_sk(sk)->parent;
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-
- BT_DBG("sk %p, parent %p", sk, parent);
-
- chan->conf_state = 0;
- __clear_chan_timer(chan);
-
- if (!parent) {
- /* Outgoing channel.
- * Wake up socket sleeping on connect.
- */
- l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
- } else {
- /* Incoming channel.
- * Wake up socket sleeping on accept.
- */
- parent->sk_data_ready(parent, 0);
- }
-}
-
/* Copy frame to all raw sockets on that connection */
static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
{
@@ -4188,6 +4177,18 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
bh_lock_sock(sk);
+ BT_DBG("chan->scid %d", chan->scid);
+
+ if (chan->scid == L2CAP_CID_LE_DATA) {
+ if (!status && encrypt) {
+ chan->sec_level = hcon->sec_level;
+ l2cap_chan_ready(sk);
+ }
+
+ bh_unlock_sock(sk);
+ continue;
+ }
+
if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) {
bh_unlock_sock(sk);
continue;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 1d9c36509d7b..5c819e002fb1 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -29,6 +29,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
static const struct proto_ops l2cap_sock_ops;
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
@@ -562,6 +563,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct bt_security sec;
struct bt_power pwr;
+ struct l2cap_conn *conn;
int len, err = 0;
u32 opt;
@@ -598,6 +600,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
}
chan->sec_level = sec.level;
+
+ conn = chan->conn;
+ if (conn && chan->scid == L2CAP_CID_LE_DATA) {
+ if (!conn->hcon->out) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (smp_conn_security(conn, sec.level))
+ break;
+
+ err = 0;
+ sk->sk_state = BT_CONFIG;
+ }
break;
case BT_DEFER_SETUP:
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 69839797b7dc..da46d76fc13d 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -336,9 +336,13 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_security_req *rp = (void *) skb->data;
struct smp_cmd_pairing cp;
+ struct hci_conn *hcon = conn->hcon;
BT_DBG("conn %p", conn);
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ return;
+
skb_pull(skb, sizeof(*rp));
memset(&cp, 0, sizeof(cp));
@@ -353,6 +357,20 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&conn->preq[1], &cp, sizeof(cp));
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+}
+
+static __u8 seclevel_to_authreq(__u8 level)
+{
+ switch (level) {
+ case BT_SECURITY_HIGH:
+ /* For now we don't support bonding */
+ return SMP_AUTH_MITM;
+
+ default:
+ return SMP_AUTH_NONE;
+ }
}
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
@@ -365,21 +383,16 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
if (IS_ERR(hcon->hdev->tfm))
return 1;
- switch (sec_level) {
- case BT_SECURITY_MEDIUM:
- /* Encrypted, no MITM protection */
- authreq = HCI_AT_NO_BONDING_MITM;
- break;
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ return 0;
- case BT_SECURITY_HIGH:
- /* Bonding, MITM protection */
- authreq = HCI_AT_GENERAL_BONDING_MITM;
- break;
+ if (sec_level == BT_SECURITY_LOW)
+ return 1;
- case BT_SECURITY_LOW:
- default:
+ if (hcon->sec_level >= sec_level)
return 1;
- }
+
+ authreq = seclevel_to_authreq(sec_level);
if (hcon->link_mode & HCI_LM_MASTER) {
struct smp_cmd_pairing cp;
@@ -400,6 +413,9 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
}
+ hcon->pending_sec_level = sec_level;
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
return 0;
}