2011-08-20 00:06:49

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 0/7] Bluetooth: LE Pairing support

Hi,

This patch series add support for LE devices using the mgmt interface.

The greatest change is that we now have to allow LE connections to be reused,
the restriction that we should only have one user for each LE Channel ID is
now in the L2CAP level.

If this patch series is to be applied, it should be applied first. I messed up
here when sending the patches. My bad.


--

Vinicius Costa Gomes (7):
Bluetooth: Reset the security timer when a command is queued
Bluetooth: Add a flag to indicate that SMP is going on
Bluetooth: Use the same timeouts for both ACL and LE links
Bluetooth: Add support for reusing the same hci_conn for LE links
Bluetooth: Add support for pairing via mgmt over LE
Bluetooth: Add support for running SMP without a socket
Bluetooth: Add link_type information to the mgmt Connected event

include/net/bluetooth/hci_core.h | 5 ++-
include/net/bluetooth/mgmt.h | 1 +
net/bluetooth/hci_conn.c | 31 +++++++++--------
net/bluetooth/hci_event.c | 4 +-
net/bluetooth/l2cap_core.c | 70 +++++++++++++++++++++++++++-----------
net/bluetooth/mgmt.c | 18 ++++++++--
net/bluetooth/smp.c | 59 ++++++++++++++++++++------------
7 files changed, 124 insertions(+), 64 deletions(-)

--
1.7.6



2011-08-25 22:13:16

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH] Bluetooth: Add support for reusing the same hci_conn for LE links

From: Vinicius Costa Gomes <[email protected]>

As most LE devices leave advertising mode when they enter the connected
state, we may want to "pass" that connection to other users.

The first user will be the pairing procedure, the connection is
established without an associated socket, after the pairing is
complete, userspace may want to discover via GATT what services the
newly bonded device has.

If userspace establishes the connection while the timeout still
hasn't expired, the connection will be re-used.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/hci_conn.c | 31 +++++++++++++------------
net/bluetooth/l2cap_core.c | 53 +++++++++++++++++++++++++++++--------------
2 files changed, 52 insertions(+), 32 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fa6820e..f5e2bd8 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -501,23 +501,24 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
BT_DBG("%s dst %s", hdev->name, batostr(dst));

if (type == LE_LINK) {
- struct adv_entry *entry;
+ struct adv_entry *entry = NULL;

le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
- if (le)
- return ERR_PTR(-EBUSY);
-
- entry = hci_find_adv_entry(hdev, dst);
- if (!entry)
- return ERR_PTR(-EHOSTUNREACH);
-
- le = hci_conn_add(hdev, LE_LINK, dst);
- if (!le)
- return ERR_PTR(-ENOMEM);
-
- le->dst_type = entry->bdaddr_type;
-
- hci_le_connect(le);
+ if (!le) {
+ entry = hci_find_adv_entry(hdev, dst);
+ if (!entry)
+ return ERR_PTR(-EHOSTUNREACH);
+
+ le = hci_conn_add(hdev, LE_LINK, dst);
+ if (!le)
+ return ERR_PTR(-ENOMEM);
+
+ le->dst_type = entry->bdaddr_type;
+ le->pending_sec_level = sec_level;
+ le->sec_level = BT_SECURITY_LOW;
+ le->auth_type = auth_type;
+ hci_le_connect(le);
+ }

hci_conn_hold(le);

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ebe2703..09ca51e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -627,10 +627,32 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}

+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);
+
+ l2cap_state_change(chan, BT_CONNECTED);
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;

+ if (conn->hcon->type == LE_LINK) {
+ l2cap_chan_ready(chan->sk);
+ return;
+ }
+
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
@@ -881,23 +903,6 @@ 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);
-
- l2cap_state_change(chan, 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;
@@ -1131,6 +1136,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
goto done;
}

+ if (hcon->type == LE_LINK) {
+ err = 0;
+
+ read_lock(&conn->chan_lock);
+ if (!list_empty(&conn->chan_l)) {
+ err = -EBUSY;
+ hci_conn_put(hcon);
+ }
+ read_unlock(&conn->chan_lock);
+
+ if (err)
+ goto done;
+ }
+
/* Update source addr of the socket */
bacpy(src, conn->src);

--
1.7.6


2011-08-25 14:27:05

by Vinicius Costa Gomes

[permalink] [raw]
Subject: Re: [PATCH 4/7] Bluetooth: Add support for reusing the same hci_conn for LE links

Hi Gustavo,

On 22:27 Wed 24 Aug, Gustavo Padovan wrote:
> Hi Vinicius,
>
> * Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:53 -0300]:
>
> > As most LE devices leave advertising mode when they enter the connected
> > state, we may want to "pass" that connection to other users.
> >
> > The first user will be the pairing procedure, the connection is
> > established without an associated socket, after the pairing is
> > complete, userspace may want to discover via GATT what services the
> > newly bonded device has.
> >
> > If userspace establishes the connection while the timeout still
> > hasn't expired, the connection will be re-used.
> >
> > Signed-off-by: Vinicius Costa Gomes <[email protected]>
> > ---
> > net/bluetooth/hci_conn.c | 31 ++++++++++++-----------
> > net/bluetooth/l2cap_core.c | 56 ++++++++++++++++++++++++++++++-------------
> > 2 files changed, 55 insertions(+), 32 deletions(-)
> >
[ ... ]

> > @@ -1128,6 +1144,12 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
> > goto done;
> > }
> >
> > + if (hcon->type == LE_LINK && !l2cap_chan_empty(conn)) {
> > + hci_conn_put(hcon);
>
> The lock (in l2cap_chan_empty) have to be release after conn put. So just get
> rid of l2cap_chan_empty and do an inline locking here.

Sure. Will fix.

>
> Gustavo

Cheers,
--
Vinicius


2011-08-25 01:30:55

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 6/7] Bluetooth: Add support for running SMP without a socket

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:55 -0300]:

> When doing the pairing procedure we won't have an associated
> socket, but we still have to do the SMP negotiation. This
> adds support for encrypting the link and exchanging keys.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 10 ++++++++--
> 1 files changed, 8 insertions(+), 2 deletions(-)

Applied, thanks.

Gustavo

2011-08-25 01:30:27

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 5/7] Bluetooth: Add support for pairing via mgmt over LE

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:54 -0300]:

> Using the advertising cache we are able to infer the type
> of the remote device, and so trigger pairing over the correct
> link type.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> net/bluetooth/mgmt.c | 15 +++++++++++++--
> 1 files changed, 13 insertions(+), 2 deletions(-)

Applied, thanks.

Gustavo

2011-08-25 01:27:12

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 4/7] Bluetooth: Add support for reusing the same hci_conn for LE links

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:53 -0300]:

> As most LE devices leave advertising mode when they enter the connected
> state, we may want to "pass" that connection to other users.
>
> The first user will be the pairing procedure, the connection is
> established without an associated socket, after the pairing is
> complete, userspace may want to discover via GATT what services the
> newly bonded device has.
>
> If userspace establishes the connection while the timeout still
> hasn't expired, the connection will be re-used.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> net/bluetooth/hci_conn.c | 31 ++++++++++++-----------
> net/bluetooth/l2cap_core.c | 56 ++++++++++++++++++++++++++++++-------------
> 2 files changed, 55 insertions(+), 32 deletions(-)
>
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index fa6820e..f5e2bd8 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -501,23 +501,24 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
> BT_DBG("%s dst %s", hdev->name, batostr(dst));
>
> if (type == LE_LINK) {
> - struct adv_entry *entry;
> + struct adv_entry *entry = NULL;
>
> le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
> - if (le)
> - return ERR_PTR(-EBUSY);
> -
> - entry = hci_find_adv_entry(hdev, dst);
> - if (!entry)
> - return ERR_PTR(-EHOSTUNREACH);
> -
> - le = hci_conn_add(hdev, LE_LINK, dst);
> - if (!le)
> - return ERR_PTR(-ENOMEM);
> -
> - le->dst_type = entry->bdaddr_type;
> -
> - hci_le_connect(le);
> + if (!le) {
> + entry = hci_find_adv_entry(hdev, dst);
> + if (!entry)
> + return ERR_PTR(-EHOSTUNREACH);
> +
> + le = hci_conn_add(hdev, LE_LINK, dst);
> + if (!le)
> + return ERR_PTR(-ENOMEM);
> +
> + le->dst_type = entry->bdaddr_type;
> + le->pending_sec_level = sec_level;
> + le->sec_level = BT_SECURITY_LOW;
> + le->auth_type = auth_type;
> + hci_le_connect(le);
> + }
>
> hci_conn_hold(le);
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index d5ef9a2..ed1d2bd 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -627,10 +627,32 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
> return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
> }
>
> +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);
> +
> + l2cap_state_change(chan, BT_CONNECTED);
> + sk->sk_state_change(sk);
> +
> + if (parent)
> + parent->sk_data_ready(parent, 0);
> +}
> +
> static void l2cap_do_start(struct l2cap_chan *chan)
> {
> struct l2cap_conn *conn = chan->conn;
>
> + if (conn->hcon->type == LE_LINK) {
> + l2cap_chan_ready(chan->sk);
> + return;
> + }
> +
> if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
> if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
> return;
> @@ -881,23 +903,6 @@ 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);
> -
> - l2cap_state_change(chan, 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;
> @@ -1087,6 +1092,17 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
> return c1;
> }
>
> +static int l2cap_chan_empty(struct l2cap_conn *conn)
> +{
> + int ret;
> +
> + read_lock(&conn->chan_lock);
> + ret = list_empty(&conn->chan_l);
> + read_unlock(&conn->chan_lock);
> +
> + return ret;
> +}
> +
> int l2cap_chan_connect(struct l2cap_chan *chan)
> {
> struct sock *sk = chan->sk;
> @@ -1128,6 +1144,12 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
> goto done;
> }
>
> + if (hcon->type == LE_LINK && !l2cap_chan_empty(conn)) {
> + hci_conn_put(hcon);

The lock (in l2cap_chan_empty) have to be release after conn put. So just get
rid of l2cap_chan_empty and do an inline locking here.

Gustavo

2011-08-25 01:20:06

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 3/7] Bluetooth: Use the same timeouts for both ACL and LE links

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:52 -0300]:

> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)

Applied, thanks.

Gustavo

2011-08-25 01:19:29

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 2/7] Bluetooth: Add a flag to indicate that SMP is going on

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:51 -0300]:

> Add HCI_CONN_LE_SMP_PEND flag to indicate that SMP is pending
> for that connection. This allows to have information that an SMP
> procedure is going on for that connection.
>
> We use the HCI_CONN_ENCRYPT_PEND to indicate that encryption
> (HCI_LE_Start_Encryption) is pending for that connection.
>
> While a SMP procedure is going on we hold an reference to the
> connection, to avoid disconnections.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> net/bluetooth/l2cap_core.c | 4 ++-
> net/bluetooth/smp.c | 44 +++++++++++++++++++++++++++++--------
> 3 files changed, 38 insertions(+), 11 deletions(-)

Applied, thanks.

Gustavo

2011-08-25 01:17:52

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 1/7] Bluetooth: Reset the security timer when a command is queued

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-08-19 21:06:50 -0300]:

> Each time a SMP command is enqueued, we reset the SMP timer,
> this way we follow exactly what the spec mandates:
>
> "The Security Manager Timer shall be reset when an L2CAP SMP command is
> queued for transmission." Vol. 3, Part H, Section 3.4
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> net/bluetooth/smp.c | 15 +++------------
> 1 files changed, 3 insertions(+), 12 deletions(-)

Applied, thanks.

Gustavo

2011-08-20 00:06:56

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 7/7] Bluetooth: Add link_type information to the mgmt Connected event

One piece of information that was lost when using the mgmt interface,
was the type of the connection. Using HCI events we used to know
the type of the connection based on the type of the event, e.g.
HCI_LE_Connection_Complete for LE links.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 2 +-
include/net/bluetooth/mgmt.h | 1 +
net/bluetooth/hci_event.c | 4 ++--
net/bluetooth/mgmt.c | 3 ++-
4 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d8fc1f2..6e68e43 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -839,7 +839,7 @@ int mgmt_powered(u16 index, u8 powered);
int mgmt_discoverable(u16 index, u8 discoverable);
int mgmt_connectable(u16 index, u8 connectable);
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent);
-int mgmt_connected(u16 index, bdaddr_t *bdaddr);
+int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type);
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr);
int mgmt_disconnect_failed(u16 index);
int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status);
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 5428fd3..1c914dd 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -249,6 +249,7 @@ struct mgmt_ev_new_key {
#define MGMT_EV_CONNECTED 0x000B
struct mgmt_ev_connected {
bdaddr_t bdaddr;
+ __u8 link_type;
} __packed;

#define MGMT_EV_DISCONNECTED 0x000C
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index a40170e..8483cab 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1413,7 +1413,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- mgmt_connected(hdev->id, &ev->bdaddr);
+ mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
} else
conn->state = BT_CONNECTED;

@@ -2817,7 +2817,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
goto unlock;
}

- mgmt_connected(hdev->id, &ev->bdaddr);
+ mgmt_connected(hdev->id, &ev->bdaddr, conn->type);

conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 1ce8d80..dac7d39 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2012,11 +2012,12 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
return err;
}

-int mgmt_connected(u16 index, bdaddr_t *bdaddr)
+int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type)
{
struct mgmt_ev_connected ev;

bacpy(&ev.bdaddr, bdaddr);
+ ev.link_type = link_type;

return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL);
}
--
1.7.6


2011-08-20 00:06:55

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 6/7] Bluetooth: Add support for running SMP without a socket

When doing the pairing procedure we won't have an associated
socket, but we still have to do the SMP negotiation. This
adds support for encrypting the link and exchanging keys.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/l2cap_core.c | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ed1d2bd..00e93d1 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -912,6 +912,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (!conn->hcon->out && conn->hcon->type == LE_LINK)
l2cap_le_conn_ready(conn);

+ if (conn->hcon->out && conn->hcon->type == LE_LINK)
+ smp_conn_security(conn, conn->hcon->pending_sec_level);
+
read_lock(&conn->chan_lock);

list_for_each_entry(chan, &conn->chan_l, list) {
@@ -4034,6 +4037,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)

BT_DBG("conn %p", conn);

+ if (hcon->type == LE_LINK) {
+ smp_distribute_keys(conn, 0);
+ del_timer(&conn->security_timer);
+ }
+
read_lock(&conn->chan_lock);

list_for_each_entry(chan, &conn->chan_l, list) {
@@ -4046,9 +4054,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (chan->scid == L2CAP_CID_LE_DATA) {
if (!status && encrypt) {
chan->sec_level = hcon->sec_level;
- del_timer(&conn->security_timer);
l2cap_chan_ready(sk);
- smp_distribute_keys(conn, 0);
}

bh_unlock_sock(sk);
--
1.7.6


2011-08-20 00:06:54

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 5/7] Bluetooth: Add support for pairing via mgmt over LE

Using the advertising cache we are able to infer the type
of the remote device, and so trigger pairing over the correct
link type.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/mgmt.c | 15 +++++++++++++--
1 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 53e109e..1ce8d80 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1347,6 +1347,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
struct hci_dev *hdev;
struct mgmt_cp_pair_device *cp;
struct pending_cmd *cmd;
+ struct adv_entry *entry;
u8 sec_level, auth_type;
struct hci_conn *conn;
int err;
@@ -1372,7 +1373,14 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
}

- conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, auth_type);
+ entry = hci_find_adv_entry(hdev, &cp->bdaddr);
+ if (entry)
+ conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level,
+ auth_type);
+ else
+ conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level,
+ auth_type);
+
if (IS_ERR(conn)) {
err = PTR_ERR(conn);
goto unlock;
@@ -1391,7 +1399,10 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
goto unlock;
}

- conn->connect_cfm_cb = pairing_complete_cb;
+ /* For LE, just connecting isn't a proof that the pairing finished */
+ if (!entry)
+ conn->connect_cfm_cb = pairing_complete_cb;
+
conn->security_cfm_cb = pairing_complete_cb;
conn->disconn_cfm_cb = pairing_complete_cb;
conn->io_capability = cp->io_cap;
--
1.7.6


2011-08-20 00:06:53

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 4/7] Bluetooth: Add support for reusing the same hci_conn for LE links

As most LE devices leave advertising mode when they enter the connected
state, we may want to "pass" that connection to other users.

The first user will be the pairing procedure, the connection is
established without an associated socket, after the pairing is
complete, userspace may want to discover via GATT what services the
newly bonded device has.

If userspace establishes the connection while the timeout still
hasn't expired, the connection will be re-used.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/hci_conn.c | 31 ++++++++++++-----------
net/bluetooth/l2cap_core.c | 56 ++++++++++++++++++++++++++++++-------------
2 files changed, 55 insertions(+), 32 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fa6820e..f5e2bd8 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -501,23 +501,24 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
BT_DBG("%s dst %s", hdev->name, batostr(dst));

if (type == LE_LINK) {
- struct adv_entry *entry;
+ struct adv_entry *entry = NULL;

le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
- if (le)
- return ERR_PTR(-EBUSY);
-
- entry = hci_find_adv_entry(hdev, dst);
- if (!entry)
- return ERR_PTR(-EHOSTUNREACH);
-
- le = hci_conn_add(hdev, LE_LINK, dst);
- if (!le)
- return ERR_PTR(-ENOMEM);
-
- le->dst_type = entry->bdaddr_type;
-
- hci_le_connect(le);
+ if (!le) {
+ entry = hci_find_adv_entry(hdev, dst);
+ if (!entry)
+ return ERR_PTR(-EHOSTUNREACH);
+
+ le = hci_conn_add(hdev, LE_LINK, dst);
+ if (!le)
+ return ERR_PTR(-ENOMEM);
+
+ le->dst_type = entry->bdaddr_type;
+ le->pending_sec_level = sec_level;
+ le->sec_level = BT_SECURITY_LOW;
+ le->auth_type = auth_type;
+ hci_le_connect(le);
+ }

hci_conn_hold(le);

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d5ef9a2..ed1d2bd 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -627,10 +627,32 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}

+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);
+
+ l2cap_state_change(chan, BT_CONNECTED);
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;

+ if (conn->hcon->type == LE_LINK) {
+ l2cap_chan_ready(chan->sk);
+ return;
+ }
+
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
@@ -881,23 +903,6 @@ 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);
-
- l2cap_state_change(chan, 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;
@@ -1087,6 +1092,17 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
return c1;
}

+static int l2cap_chan_empty(struct l2cap_conn *conn)
+{
+ int ret;
+
+ read_lock(&conn->chan_lock);
+ ret = list_empty(&conn->chan_l);
+ read_unlock(&conn->chan_lock);
+
+ return ret;
+}
+
int l2cap_chan_connect(struct l2cap_chan *chan)
{
struct sock *sk = chan->sk;
@@ -1128,6 +1144,12 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
goto done;
}

+ if (hcon->type == LE_LINK && !l2cap_chan_empty(conn)) {
+ hci_conn_put(hcon);
+ err = -EBUSY;
+ goto done;
+ }
+
/* Update source addr of the socket */
bacpy(src, conn->src);

--
1.7.6


2011-08-20 00:06:52

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 3/7] Bluetooth: Use the same timeouts for both ACL and LE links

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4376780..d8fc1f2 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -476,7 +476,7 @@ static inline void hci_conn_put(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->refcnt)) {
unsigned long timeo;
- if (conn->type == ACL_LINK) {
+ if (conn->type == ACL_LINK || conn->type == LE_LINK) {
del_timer(&conn->idle_timer);
if (conn->state == BT_CONNECTED) {
timeo = msecs_to_jiffies(conn->disc_timeout);
--
1.7.6


2011-08-20 00:06:51

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 2/7] Bluetooth: Add a flag to indicate that SMP is going on

Add HCI_CONN_LE_SMP_PEND flag to indicate that SMP is pending
for that connection. This allows to have information that an SMP
procedure is going on for that connection.

We use the HCI_CONN_ENCRYPT_PEND to indicate that encryption
(HCI_LE_Start_Encryption) is pending for that connection.

While a SMP procedure is going on we hold an reference to the
connection, to avoid disconnections.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/l2cap_core.c | 4 ++-
net/bluetooth/smp.c | 44 +++++++++++++++++++++++++++++--------
3 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 8f441b8..4376780 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -348,6 +348,7 @@ enum {
HCI_CONN_RSWITCH_PEND,
HCI_CONN_MODE_CHANGE_PEND,
HCI_CONN_SCO_SETUP_PEND,
+ HCI_CONN_LE_SMP_PEND,
};

static inline void hci_conn_hash_init(struct hci_dev *hdev)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index be30c32..d5ef9a2 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -986,8 +986,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
del_timer_sync(&conn->info_timer);

- if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) {
del_timer(&conn->security_timer);
+ hci_conn_put(hcon);
+ }

hcon->l2cap_data = NULL;
kfree(conn);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 20c82c7..f0c67f6 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -248,6 +248,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)

BT_DBG("conn %p", conn);

+ if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ hci_conn_hold(conn->hcon);
+
conn->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&conn->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
@@ -397,6 +400,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
memset(stk + conn->smp_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);

+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ return SMP_UNSPECIFIED;
+
hci_le_start_enc(hcon, ediv, rand, stk);
hcon->enc_key_size = conn->smp_key_size;
} else {
@@ -430,9 +436,11 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)

BT_DBG("conn %p", conn);

- if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
return 0;

+ hci_conn_hold(hcon);
+
skb_pull(skb, sizeof(*rp));

memset(&cp, 0, sizeof(cp));
@@ -443,8 +451,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)

smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);

- set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
-
return 0;
}

@@ -461,19 +467,13 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
if (IS_ERR(hcon->hdev->tfm))
return 1;

- if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
- return 0;
-
if (sec_level == BT_SECURITY_LOW)
return 1;

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;
struct link_key *key;

key = hci_find_link_key_type(hcon->hdev, conn->dst,
@@ -481,12 +481,28 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
if (key) {
struct key_master_id *master = (void *) key->data;

+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND,
+ &hcon->pend))
+ goto done;
+
hci_le_start_enc(hcon, master->ediv, master->rand,
key->val);
hcon->enc_key_size = key->pin_len;

goto done;
}
+ }
+
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ return 0;
+
+ /* While SMP is going on */
+ hci_conn_hold(hcon);
+
+ authreq = seclevel_to_authreq(sec_level);
+
+ if (hcon->link_mode & HCI_LM_MASTER) {
+ struct smp_cmd_pairing cp;

build_pairing_cmd(conn, &cp, NULL, authreq);
conn->preq[0] = SMP_CMD_PAIRING_REQ;
@@ -501,7 +517,6 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)

done:
hcon->pending_sec_level = sec_level;
- set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);

return 0;
}
@@ -619,6 +634,9 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
if (IS_ERR(conn->hcon->hdev->tfm))
return PTR_ERR(conn->hcon->hdev->tfm);

+ if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ return 0;
+
rsp = (void *) &conn->prsp[1];

/* The responder sends its keys first */
@@ -689,5 +707,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
*keydist &= ~SMP_DIST_SIGN;
}

+ if (conn->hcon->out || force) {
+ clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ del_timer(&conn->security_timer);
+ hci_conn_put(conn->hcon);
+ }
+
return 0;
}
--
1.7.6


2011-08-20 00:06:50

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 1/7] Bluetooth: Reset the security timer when a command is queued

Each time a SMP command is enqueued, we reset the SMP timer,
this way we follow exactly what the spec mandates:

"The Security Manager Timer shall be reset when an L2CAP SMP command is
queued for transmission." Vol. 3, Part H, Section 3.4

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/smp.c | 15 +++------------
1 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 391888b..20c82c7 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -182,6 +182,9 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
return;

hci_send_acl(conn->hcon, skb, 0);
+
+ mod_timer(&conn->security_timer, jiffies +
+ msecs_to_jiffies(SMP_TIMEOUT));
}

static __u8 seclevel_to_authreq(__u8 level)
@@ -267,9 +270,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)

smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);

- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
-
return 0;
}

@@ -351,9 +351,6 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
}

- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
-
return 0;
}

@@ -446,9 +443,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)

smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);

- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
-
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);

return 0;
@@ -498,9 +492,6 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
conn->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&conn->preq[1], &cp, sizeof(cp));

- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
-
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
} else {
struct smp_cmd_security_req cp;
--
1.7.6