aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/af_bluetooth.c72
-rw-r--r--net/bluetooth/bnep/core.c13
-rw-r--r--net/bluetooth/bnep/netdev.c2
-rw-r--r--net/bluetooth/cmtp/capi.c3
-rw-r--r--net/bluetooth/cmtp/cmtp.h1
-rw-r--r--net/bluetooth/cmtp/core.c30
-rw-r--r--net/bluetooth/hci_conn.c115
-rw-r--r--net/bluetooth/hci_core.c61
-rw-r--r--net/bluetooth/hci_event.c97
-rw-r--r--net/bluetooth/hci_sock.c22
-rw-r--r--net/bluetooth/hci_sysfs.c3
-rw-r--r--net/bluetooth/hidp/core.c13
-rw-r--r--net/bluetooth/l2cap_core.c548
-rw-r--r--net/bluetooth/l2cap_sock.c10
-rw-r--r--net/bluetooth/mgmt.c215
-rw-r--r--net/bluetooth/rfcomm/core.c25
-rw-r--r--net/bluetooth/rfcomm/sock.c13
-rw-r--r--net/bluetooth/sco.c71
-rw-r--r--net/bluetooth/smp.c444
19 files changed, 874 insertions, 884 deletions
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 9047512..0938f6b 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -40,15 +40,6 @@
#include <net/bluetooth/bluetooth.h>
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-#include <linux/android_aid.h>
-#endif
-
-#ifndef CONFIG_BT_SOCK_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "2.16"
/* Bluetooth sockets */
@@ -134,40 +125,11 @@ int bt_sock_unregister(int proto)
}
EXPORT_SYMBOL(bt_sock_unregister);
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-static inline int current_has_bt_admin(void)
-{
- return (!current_euid() || in_egroup_p(AID_NET_BT_ADMIN));
-}
-
-static inline int current_has_bt(void)
-{
- return (current_has_bt_admin() || in_egroup_p(AID_NET_BT));
-}
-# else
-static inline int current_has_bt_admin(void)
-{
- return 1;
-}
-
-static inline int current_has_bt(void)
-{
- return 1;
-}
-#endif
-
static int bt_sock_create(struct net *net, struct socket *sock, int proto,
int kern)
{
int err;
- if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO ||
- proto == BTPROTO_L2CAP) {
- if (!current_has_bt())
- return -EPERM;
- } else if (!current_has_bt_admin())
- return -EPERM;
-
if (net != &init_net)
return -EAFNOSUPPORT;
@@ -283,8 +245,6 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
- msg->msg_namelen = 0;
-
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb) {
if (sk->sk_shutdown & RCV_SHUTDOWN)
@@ -349,8 +309,6 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags & MSG_OOB)
return -EOPNOTSUPP;
- msg->msg_namelen = 0;
-
BT_DBG("sk %p size %zu", sk, size);
lock_sock(sk);
@@ -387,7 +345,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
}
chunk = min_t(unsigned int, skb->len, size);
- if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) {
skb_queue_head(&sk->sk_receive_queue, skb);
if (!copied)
copied = -EFAULT;
@@ -399,7 +357,33 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
sock_recv_ts_and_drops(msg, sk, skb);
if (!(flags & MSG_PEEK)) {
- skb_pull(skb, chunk);
+ int skb_len = skb_headlen(skb);
+
+ if (chunk <= skb_len) {
+ __skb_pull(skb, chunk);
+ } else {
+ struct sk_buff *frag;
+
+ __skb_pull(skb, skb_len);
+ chunk -= skb_len;
+
+ skb_walk_frags(skb, frag) {
+ if (chunk <= frag->len) {
+ /* Pulling partial data */
+ skb->len -= chunk;
+ skb->data_len -= chunk;
+ __skb_pull(frag, chunk);
+ break;
+ } else if (frag->len) {
+ /* Pulling all frag data */
+ chunk -= frag->len;
+ skb->len -= frag->len;
+ skb->data_len -= frag->len;
+ __skb_pull(frag, frag->len);
+ }
+ }
+ }
+
if (skb->len) {
skb_queue_head(&sk->sk_receive_queue, skb);
break;
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index d9edfe8..1eea820 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -79,17 +79,12 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
static void __bnep_link_session(struct bnep_session *s)
{
- /* It's safe to call __module_get() here because sessions are added
- by the socket layer which has to hold the reference to this module.
- */
- __module_get(THIS_MODULE);
list_add(&s->list, &bnep_session_list);
}
static void __bnep_unlink_session(struct bnep_session *s)
{
list_del(&s->list);
- module_put(THIS_MODULE);
}
static int bnep_send(struct bnep_session *s, void *data, size_t len)
@@ -492,7 +487,10 @@ static int bnep_session(void *arg)
/* RX */
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
- bnep_rx_frame(s, skb);
+ if (!skb_linearize(skb))
+ bnep_rx_frame(s, skb);
+ else
+ kfree_skb(skb);
}
if (sk->sk_state != BT_CONNECTED)
@@ -527,6 +525,7 @@ static int bnep_session(void *arg)
up_write(&bnep_session_sem);
free_netdev(dev);
+ module_put_and_exit(0);
return 0;
}
@@ -613,9 +612,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
__bnep_link_session(s);
+ __module_get(THIS_MODULE);
s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */
+ module_put(THIS_MODULE);
unregister_netdev(dev);
__bnep_unlink_session(s);
err = PTR_ERR(s->task);
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index d4f5dff..bc40864 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -217,7 +217,7 @@ static const struct net_device_ops bnep_netdev_ops = {
.ndo_stop = bnep_net_close,
.ndo_start_xmit = bnep_net_xmit,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_multicast_list = bnep_net_set_mc_list,
+ .ndo_set_rx_mode = bnep_net_set_mc_list,
.ndo_set_mac_address = bnep_net_set_mac_addr,
.ndo_tx_timeout = bnep_net_timeout,
.ndo_change_mtu = eth_change_mtu,
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 040f67b..50f0d13 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -386,7 +386,8 @@ static void cmtp_reset_ctr(struct capi_ctr *ctrl)
capi_ctr_down(ctrl);
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
}
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h
index db43b54..c32638d 100644
--- a/net/bluetooth/cmtp/cmtp.h
+++ b/net/bluetooth/cmtp/cmtp.h
@@ -81,6 +81,7 @@ struct cmtp_session {
char name[BTNAMSIZ];
+ atomic_t terminate;
struct task_struct *task;
wait_queue_head_t wait;
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index c5b11af..5a6e634 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -67,14 +67,12 @@ static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
static void __cmtp_link_session(struct cmtp_session *session)
{
- __module_get(THIS_MODULE);
list_add(&session->list, &cmtp_session_list);
}
static void __cmtp_unlink_session(struct cmtp_session *session)
{
list_del(&session->list);
- module_put(THIS_MODULE);
}
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
@@ -292,22 +290,27 @@ static int cmtp_session(void *arg)
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
- while (!kthread_should_stop()) {
+ while (1) {
set_current_state(TASK_INTERRUPTIBLE);
+ if (atomic_read(&session->terminate))
+ break;
if (sk->sk_state != BT_CONNECTED)
break;
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
- cmtp_recv_frame(session, skb);
+ if (!skb_linearize(skb))
+ cmtp_recv_frame(session, skb);
+ else
+ kfree_skb(skb);
}
cmtp_process_transmit(session);
schedule();
}
- set_current_state(TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
down_write(&cmtp_session_sem);
@@ -322,6 +325,7 @@ static int cmtp_session(void *arg)
up_write(&cmtp_session_sem);
kfree(session);
+ module_put_and_exit(0);
return 0;
}
@@ -371,25 +375,28 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
__cmtp_link_session(session);
+ __module_get(THIS_MODULE);
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
session->num);
if (IS_ERR(session->task)) {
+ module_put(THIS_MODULE);
err = PTR_ERR(session->task);
goto unlink;
}
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
- if (err < 0)
- goto detach;
+ if (err < 0) {
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
+ up_write(&cmtp_session_sem);
+ return err;
+ }
}
up_write(&cmtp_session_sem);
return 0;
-detach:
- cmtp_detach_device(session);
-
unlink:
__cmtp_unlink_session(session);
@@ -414,7 +421,8 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
skb_queue_purge(&session->transmit);
/* Stop session thread */
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
} else
err = -ENOENT;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 9d8b593..f456645 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -44,6 +44,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/smp.h>
static void hci_le_connect(struct hci_conn *conn)
{
@@ -56,15 +57,15 @@ static void hci_le_connect(struct hci_conn *conn)
conn->sec_level = BT_SECURITY_LOW;
memset(&cp, 0, sizeof(cp));
- cp.scan_interval = cpu_to_le16(0x0004);
- cp.scan_window = cpu_to_le16(0x0004);
+ cp.scan_interval = cpu_to_le16(0x0060);
+ cp.scan_window = cpu_to_le16(0x0030);
bacpy(&cp.peer_addr, &conn->dst);
cp.peer_addr_type = conn->dst_type;
- cp.conn_interval_min = cpu_to_le16(0x0008);
- cp.conn_interval_max = cpu_to_le16(0x0100);
- cp.supervision_timeout = cpu_to_le16(0x0064);
- cp.min_ce_len = cpu_to_le16(0x0001);
- cp.max_ce_len = cpu_to_le16(0x0001);
+ cp.conn_interval_min = cpu_to_le16(0x0028);
+ cp.conn_interval_max = cpu_to_le16(0x0038);
+ cp.supervision_timeout = cpu_to_le16(0x002a);
+ cp.min_ce_len = cpu_to_le16(0x0000);
+ cp.max_ce_len = cpu_to_le16(0x0000);
hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
}
@@ -218,7 +219,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
cp.handle = cpu_to_le16(conn->handle);
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
cp.ediv = ediv;
- memcpy(cp.rand, rand, sizeof(rand));
+ memcpy(cp.rand, rand, sizeof(cp.rand));
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
}
@@ -333,8 +334,7 @@ static void hci_conn_auto_accept(unsigned long arg)
hci_dev_unlock(hdev);
}
-struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type,
- __u16 pkt_type, bdaddr_t *dst)
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -362,22 +362,14 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type,
conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
break;
case SCO_LINK:
- if (!pkt_type)
- pkt_type = SCO_ESCO_MASK;
+ if (lmp_esco_capable(hdev))
+ conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
+ (hdev->esco_type & EDR_ESCO_MASK);
+ else
+ conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
+ break;
case ESCO_LINK:
- if (!pkt_type)
- pkt_type = ALL_ESCO_MASK;
- if (lmp_esco_capable(hdev)) {
- /* HCI Setup Synchronous Connection Command uses
- reverse logic on the EDR_ESCO_MASK bits */
- conn->pkt_type = (pkt_type ^ EDR_ESCO_MASK) &
- hdev->esco_type;
- } else {
- /* Legacy HCI Add Sco Connection Command uses a
- shifted bitmask */
- conn->pkt_type = (pkt_type << 5) & hdev->pkt_type &
- SCO_PTYPE_MASK;
- }
+ conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
break;
}
@@ -501,14 +493,11 @@ EXPORT_SYMBOL(hci_get_route);
/* Create SCO, ACL or LE connection.
* Device _must_ be locked */
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type,
- __u16 pkt_type, bdaddr_t *dst,
- __u8 sec_level, __u8 auth_type)
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
{
struct hci_conn *acl;
struct hci_conn *sco;
struct hci_conn *le;
- struct inquiry_entry *ie;
BT_DBG("%s dst %s", hdev->name, batostr(dst));
@@ -523,7 +512,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type,
if (!entry)
return ERR_PTR(-EHOSTUNREACH);
- le = hci_conn_add(hdev, LE_LINK, 0, dst);
+ le = hci_conn_add(hdev, LE_LINK, dst);
if (!le)
return ERR_PTR(-ENOMEM);
@@ -538,7 +527,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type,
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
- acl = hci_conn_add(hdev, ACL_LINK, 0, dst);
+ acl = hci_conn_add(hdev, ACL_LINK, dst);
if (!acl)
return NULL;
}
@@ -557,7 +546,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type,
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
if (!sco) {
- sco = hci_conn_add(hdev, type, pkt_type, dst);
+ sco = hci_conn_add(hdev, type, dst);
if (!sco) {
hci_conn_put(acl);
return NULL;
@@ -621,14 +610,17 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
- /* encrypt must be pending if auth is also pending */
- set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
-
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
sizeof(cp), &cp);
- if (conn->key_type != 0xff)
+
+ /* If we're already encrypted set the REAUTH_PEND flag,
+ * otherwise set the ENCRYPT_PEND.
+ */
+ if (conn->link_mode & HCI_LM_ENCRYPT)
set_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
+ else
+ set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
}
return 0;
@@ -653,6 +645,11 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("conn %p", conn);
+#ifdef CONFIG_BT_L2CAP
+ if (conn->type == LE_LINK)
+ return smp_conn_security(conn, sec_level);
+#endif
+
/* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
return 1;
@@ -754,34 +751,6 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
}
EXPORT_SYMBOL(hci_conn_switch_role);
-/* BEGIN SS_BLUEZ_BT +kjh 2011.06.23 : */
-/* workaround for a2dp chopping in multi connection. */
-/* Change Policy */
-int hci_conn_change_policy(struct hci_conn *conn, __u8 policy)
-{
- struct hci_cp_write_link_policy cp;
- cp.handle = cpu_to_le16(conn->handle);
- cp.policy = policy;
- hci_send_cmd(conn->hdev, HCI_OP_WRITE_LINK_POLICY, sizeof(cp), &cp);
-
- return 0;
-}
-EXPORT_SYMBOL(hci_conn_change_policy);
-
-/* Set Encrypt*/
-int hci_conn_set_encrypt(struct hci_conn *conn, __u8 enable)
-{
- struct hci_cp_set_conn_encrypt cp;
- cp.handle = cpu_to_le16(conn->handle);
- cp.encrypt = enable;
- hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp);
-
- return 0;
-}
-EXPORT_SYMBOL(hci_conn_set_encrypt);
-/* END SS_BLUEZ_BT */
-
-
/* Enter active mode */
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
{
@@ -936,15 +905,6 @@ int hci_get_conn_list(void __user *arg)
(ci + n)->out = c->out;
(ci + n)->state = c->state;
(ci + n)->link_mode = c->link_mode;
- if (c->type == SCO_LINK) {
- (ci + n)->mtu = hdev->sco_mtu;
- (ci + n)->cnt = hdev->sco_cnt;
- (ci + n)->pkts = hdev->sco_pkts;
- } else {
- (ci + n)->mtu = hdev->acl_mtu;
- (ci + n)->cnt = hdev->acl_cnt;
- (ci + n)->pkts = hdev->acl_pkts;
- }
if (++n >= req.conn_num)
break;
}
@@ -981,15 +941,6 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
ci.out = conn->out;
ci.state = conn->state;
ci.link_mode = conn->link_mode;
- if (req.type == SCO_LINK) {
- ci.mtu = hdev->sco_mtu;
- ci.cnt = hdev->sco_cnt;
- ci.pkts = hdev->sco_pkts;
- } else {
- ci.mtu = hdev->acl_mtu;
- ci.cnt = hdev->acl_cnt;
- ci.pkts = hdev->acl_pkts;
- }
}
hci_dev_unlock_bh(hdev);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b1b7b3d..857dc88 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1317,59 +1317,41 @@ int hci_blacklist_clear(struct hci_dev *hdev)
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *entry;
- int err;
if (bacmp(bdaddr, BDADDR_ANY) == 0)
return -EBADF;
- hci_dev_lock_bh(hdev);
-
- if (hci_blacklist_lookup(hdev, bdaddr)) {
- err = -EEXIST;
- goto err;
- }
+ if (hci_blacklist_lookup(hdev, bdaddr))
+ return -EEXIST;
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
- if (!entry) {
+ if (!entry)
return -ENOMEM;
- goto err;
- }
bacpy(&entry->bdaddr, bdaddr);
list_add(&entry->list, &hdev->blacklist);
- err = 0;
-
-err:
- hci_dev_unlock_bh(hdev);
- return err;
+ return mgmt_device_blocked(hdev->id, bdaddr);
}
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *entry;
- int err = 0;
-
- hci_dev_lock_bh(hdev);
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
- hci_blacklist_clear(hdev);
- goto done;
+ return hci_blacklist_clear(hdev);
}
entry = hci_blacklist_lookup(hdev, bdaddr);
if (!entry) {
- err = -ENOENT;
- goto done;
+ return -ENOENT;
}
list_del(&entry->list);
kfree(entry);
-done:
- hci_dev_unlock_bh(hdev);
- return err;
+ return mgmt_device_unblocked(hdev->id, bdaddr);
}
static void hci_clear_adv_cache(unsigned long arg)
@@ -1528,11 +1510,6 @@ int hci_register_dev(struct hci_dev *hdev)
if (!hdev->workqueue)
goto nomem;
- hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(hdev->tfm))
- BT_INFO("Failed to load transform for ecb(aes): %ld",
- PTR_ERR(hdev->tfm));
-
hci_register_sysfs(hdev);
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
@@ -1579,15 +1556,10 @@ int hci_unregister_dev(struct hci_dev *hdev)
for (i = 0; i < NUM_REASSEMBLY; i++)
kfree_skb(hdev->reassembly[i]);
- cancel_work_sync(&hdev->power_on);
-
if (!test_bit(HCI_INIT, &hdev->flags) &&
!test_bit(HCI_SETUP, &hdev->flags))
mgmt_index_removed(hdev->id);
- if (!IS_ERR(hdev->tfm))
- crypto_free_blkcipher(hdev->tfm);
-
hci_notify(hdev, HCI_DEV_UNREG);
if (hdev->rfkill) {
@@ -1779,7 +1751,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
data += (count - rem);
count = rem;
- };
+ }
return rem;
}
@@ -1814,7 +1786,7 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
data += (count - rem);
count = rem;
- };
+ }
return rem;
}
@@ -2083,6 +2055,9 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
min = c->sent;
conn = c;
}
+
+ if (hci_conn_num(hdev, type) == num)
+ break;
}
if (conn) {
@@ -2140,6 +2115,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ if (!hci_conn_num(hdev, ACL_LINK))
+ return;
+
if (!test_bit(HCI_RAW, &hdev->flags)) {
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
@@ -2171,6 +2149,9 @@ static inline void hci_sched_sco(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ if (!hci_conn_num(hdev, SCO_LINK))
+ return;
+
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
@@ -2191,6 +2172,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ if (!hci_conn_num(hdev, ESCO_LINK))
+ return;
+
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
@@ -2211,6 +2195,9 @@ static inline void hci_sched_le(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ if (!hci_conn_num(hdev, LE_LINK))
+ return;
+
if (!test_bit(HCI_RAW, &hdev->flags)) {
/* LE tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d8a2d32..d99075a 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -545,7 +545,7 @@ static void hci_setup(struct hci_dev *hdev)
{
hci_setup_event_mask(hdev);
- if (hdev->lmp_ver > 1)
+ if (hdev->hci_ver > 1)
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
@@ -898,16 +898,15 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
if (!cp)
return;
- hci_dev_lock(hdev);
-
if (cp->enable == 0x01) {
del_timer(&hdev->adv_timer);
+
+ hci_dev_lock(hdev);
hci_adv_entries_clear(hdev);
+ hci_dev_unlock(hdev);
} else if (cp->enable == 0x00) {
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
}
-
- hci_dev_unlock(hdev);
}
static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -992,7 +991,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
}
} else {
if (!conn) {
- conn = hci_conn_add(hdev, ACL_LINK, 0, &cp->bdaddr);
+ conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr);
if (conn) {
conn->out = 1;
conn->link_mode |= HCI_LM_MASTER;
@@ -1103,9 +1102,10 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
return 0;
/* Only request authentication for SSP connections or non-SSP
- * devices with sec_level HIGH */
+ * devices with sec_level HIGH or if MITM protection is requested */
if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) &&
- conn->pending_sec_level != BT_SECURITY_HIGH)
+ conn->pending_sec_level != BT_SECURITY_HIGH &&
+ !(conn->auth_type & 0x01))
return 0;
return 1;
@@ -1315,7 +1315,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
}
} else {
if (!conn) {
- conn = hci_conn_add(hdev, LE_LINK, 0, &cp->peer_addr);
+ conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
if (conn) {
conn->dst_type = cp->peer_addr_type;
conn->out = 1;
@@ -1411,8 +1411,14 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (conn->type == ACL_LINK) {
conn->state = BT_CONFIG;
hci_conn_hold(conn);
- conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- mgmt_connected(hdev->id, &ev->bdaddr);
+
+ if (!conn->out &&
+ !(conn->ssp_mode && conn->hdev->ssp_mode) &&
+ !hci_find_link_key(hdev, &ev->bdaddr))
+ conn->disc_timeout = HCI_PAIRING_TIMEOUT;
+ else
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
} else
conn->state = BT_CONNECTED;
@@ -1462,15 +1468,6 @@ unlock:
hci_conn_check_pending(hdev);
}
-static inline bool is_sco_active(struct hci_dev *hdev)
-{
- if (hci_conn_hash_lookup_state(hdev, SCO_LINK, BT_CONNECTED) ||
- (hci_conn_hash_lookup_state(hdev, ESCO_LINK,
- BT_CONNECTED)))
- return true;
- return false;
-}
-
static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_conn_request *ev = (void *) skb->data;
@@ -1495,8 +1492,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
if (!conn) {
- /* pkt_type not yet used for incoming connections */
- conn = hci_conn_add(hdev, ev->link_type, 0, &ev->bdaddr);
+ conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr);
if (!conn) {
BT_ERR("No memory for new connection");
hci_dev_unlock(hdev);
@@ -1514,8 +1510,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
bacpy(&cp.bdaddr, &ev->bdaddr);
- if (lmp_rswitch_capable(hdev) && ((mask & HCI_LM_MASTER)
- || is_sco_active(hdev)))
+ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
cp.role = 0x00; /* Become master */
else
cp.role = 0x01; /* Remain slave */
@@ -1590,21 +1585,6 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (!conn)
goto unlock;
- /* SS_BLUETOOTH(gudam.ryu) 2012. 03. 02 - Fixed for opp sending fail,
- if the devices were unpaired on the remote end */
- if (ev->status == 0x06 && hdev->ssp_mode > 0 &&
- conn->ssp_mode > 0) {
- struct hci_cp_auth_requested cp;
- hci_remove_link_key(hdev, &conn->dst);
- cp.handle = cpu_to_le16(conn->handle);
- hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
- sizeof(cp), &cp);
- hci_dev_unlock(hdev);
- BT_DBG("Pin or key missing !!!");
- return;
- }
- /* SS_Bluetooth(gudam.ryu) End */
-
if (!ev->status) {
if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) &&
test_bit(HCI_CONN_REAUTH_PEND, &conn->pend)) {
@@ -1615,7 +1595,6 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
}
} else {
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
- conn->disc_timeout = HCI_DISCONN_TIMEOUT/200; /* 0.01 sec */
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
@@ -2201,7 +2180,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
- if (conn && conn->state == BT_CONNECTED) {
+ if (!conn)
+ goto unlock;
+
+ if (conn->state == BT_CONNECTED) {
hci_conn_hold(conn);
conn->disc_timeout = HCI_PAIRING_TIMEOUT;
hci_conn_put(conn);
@@ -2221,6 +2203,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure);
}
+unlock:
hci_dev_unlock(hdev);
}
@@ -2506,7 +2489,6 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
hci_conn_add_sysfs(conn);
break;
- case 0x10: /* Connection Accept Timeout */
case 0x11: /* Unsupported Feature or Parameter Value */
case 0x1c: /* SCO interval rejected */
case 0x1a: /* Unsupported Remote Feature */
@@ -2708,8 +2690,11 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
/* If we're not the initiators request authorization to
* proceed from user space (mgmt_user_confirm with
- * confirm_hint set to 1). */
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ * confirm_hint set to 1). The exception is if neither
+ * side had MITM in which case we do auto-accept.
+ */
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) &&
+ (loc_mitm || rem_mitm)) {
BT_DBG("Confirming auto-accept as acceptor");
confirm_hint = 1;
goto confirm;
@@ -2755,11 +2740,9 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
* initiated the authentication. A traditional auth_complete
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0) {
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
- conn->out = 1;
- conn->disc_timeout = HCI_DISCONN_TIMEOUT/200; /* 0.01 sec */
- }
+
hci_conn_put(conn);
unlock:
@@ -2828,7 +2811,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
if (!conn) {
- conn = hci_conn_add(hdev, LE_LINK, 0, &ev->bdaddr);
+ conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
if (!conn) {
BT_ERR("No memory for new connection");
hci_dev_unlock(hdev);
@@ -2846,7 +2829,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);
@@ -2864,19 +2847,17 @@ unlock:
static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
- struct hci_ev_le_advertising_info *ev;
- u8 num_reports;
-
- num_reports = skb->data[0];
- ev = (void *) &skb->data[1];
+ u8 num_reports = skb->data[0];
+ void *ptr = &skb->data[1];
hci_dev_lock(hdev);
- hci_add_adv_entry(hdev, ev);
+ while (num_reports--) {
+ struct hci_ev_le_advertising_info *ev = ptr;
- while (--num_reports) {
- ev = (void *) (ev->data + ev->length + 1);
hci_add_adv_entry(hdev, ev);
+
+ ptr += sizeof(*ev) + ev->length + 1;
}
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index ce1424a..bb78c75 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -183,21 +183,35 @@ static int hci_sock_release(struct socket *sock)
static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
{
bdaddr_t bdaddr;
+ int err;
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT;
- return hci_blacklist_add(hdev, &bdaddr);
+ hci_dev_lock_bh(hdev);
+
+ err = hci_blacklist_add(hdev, &bdaddr);
+
+ hci_dev_unlock_bh(hdev);
+
+ return err;
}
static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
{
bdaddr_t bdaddr;
+ int err;
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT;
- return hci_blacklist_del(hdev, &bdaddr);
+ hci_dev_lock_bh(hdev);
+
+ err = hci_blacklist_del(hdev, &bdaddr);
+
+ hci_dev_unlock_bh(hdev);
+
+ return err;
}
/* Ioctls that require bound socket */
@@ -434,8 +448,6 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!skb)
return err;
- msg->msg_namelen = 0;
-
copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
@@ -587,7 +599,6 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char
{
struct hci_filter *f = &hci_pi(sk)->filter;
- memset(&uf, 0, sizeof(uf));
uf.type_mask = f->type_mask;
uf.opcode = f->opcode;
uf.event_mask[0] = *((u32 *) f->event_mask + 0);
@@ -659,6 +670,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
{
struct hci_filter *f = &hci_pi(sk)->filter;
+ memset(&uf, 0, sizeof(uf));
uf.type_mask = f->type_mask;
uf.opcode = f->opcode;
uf.event_mask[0] = *((u32 *) f->event_mask + 0);
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index a6c3aa8..661b461 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -5,6 +5,7 @@
#include <linux/init.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/module.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -23,6 +24,8 @@ static inline char *link_typetostr(int type)
return "SCO";
case ESCO_LINK:
return "eSCO";
+ case LE_LINK:
+ return "LE";
default:
return "UNKNOWN";
}
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 06f1197..0274157 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -716,12 +716,18 @@ static int hidp_session(void *arg)
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
skb_orphan(skb);
- hidp_recv_ctrl_frame(session, skb);
+ if (!skb_linearize(skb))
+ hidp_recv_ctrl_frame(session, skb);
+ else
+ kfree_skb(skb);
}
while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
skb_orphan(skb);
- hidp_recv_intr_frame(session, skb);
+ if (!skb_linearize(skb))
+ hidp_recv_intr_frame(session, skb);
+ else
+ kfree_skb(skb);
}
hidp_process_transmit(session);
@@ -872,6 +878,9 @@ static int hidp_start(struct hid_device *hid)
struct hidp_session *session = hid->driver_data;
struct hid_report *report;
+ if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS)
+ return 0;
+
list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
report_list, list)
hidp_send_report(session, report);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8d0e801..dd7c019 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -56,24 +56,6 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
-/* BEGIN SLP_Bluetooth :: fix av chopping issue. */
-#ifdef CONFIG_SLP
-#define HCI_BROADCOMM_QOS_PATCH
-#endif
-
-#ifdef HCI_BROADCOMM_QOS_PATCH
-#define L2CAP_PSM_AVDTP 25
-#define HCI_BROADCOM_QOS_CMD 0xFC57 /* For bcm4329/bcm4330 chipset */
-#define PRIORITY_NORMAL 0x00 /* Broadcom ACL priority for bcm4330 chipset */
-#define PRIORITY_HIGH 0x01
-
-struct hci_cp_broadcom_cmd {
- __le16 handle;
- __u8 priority; /* Only for bcm4330 chipset */
-} __attribute__ ((__packed__));
-#endif
-/* END SLP_Bluetooth */
-
int disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
@@ -92,14 +74,6 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn,
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
-/* BEGIN SS_BLUEZ_BT +kjh 2011.06.23 : */
-/* workaround for a2dp chopping in multi connection. */
-static struct l2cap_conn *av_conn;
-static struct l2cap_conn *hid_conn;
-static struct l2cap_conn *rfc_conn;
-/* END SS_BLUEZ_BT */
-
-
/* ---- L2CAP channels ---- */
static inline void chan_hold(struct l2cap_chan *c)
@@ -277,7 +251,7 @@ static void l2cap_chan_timeout(unsigned long arg)
if (sock_owned_by_user(sk)) {
/* sk is owned by user. Try again later */
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
chan_put(chan);
return;
@@ -336,37 +310,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
chan->psm, chan->dcid);
-/* BEGIN SS_BLUEZ_BT +kjh 2011.06.23 : */
-/* workaround for a2dp chopping in multi connection.*/
-/* todo : now, we can't check obex properly. */
- switch (chan->psm) {
- case 0x03:
- rfc_conn = conn;
-
- if (av_conn != NULL && rfc_conn == av_conn)
- rfc_conn = NULL;
- break;
- case 0x11:
- hid_conn = conn;
- break;
- case 0x17:
- av_conn = conn;
- if (rfc_conn != NULL && rfc_conn == av_conn)
- rfc_conn = NULL;
- break;
- default:
- break;
- }
-
- if (av_conn != NULL && (hid_conn != NULL || rfc_conn != NULL)) {
- hci_conn_set_encrypt(av_conn->hcon, 0x00);
- hci_conn_switch_role(av_conn->hcon, 0x00);
- hci_conn_set_encrypt(av_conn->hcon, 0x01);
- hci_conn_change_policy(av_conn->hcon, 0x04);
- av_conn = NULL;
- }
-/* END SS_BLUEZ_BT */
-
conn->disc_reason = 0x13;
chan->conn = conn;
@@ -419,26 +362,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
chan_put(chan);
chan->conn = NULL;
-
-/* BEGIN SS_BLUEZ_BT +kjh 2011.06.23 : */
-/* workaround for a2dp chopping in multi connection.*/
- switch (chan->psm) {
- case 0x03:
- rfc_conn = NULL;
- break;
- case 0x11:
- hid_conn = NULL;
- break;
- case 0x17:
- av_conn = NULL;
- break;
- default:
- break;
- }
-/* END SS_BLUEZ_BT */
-
- if (conn->hcon)
- conn->hcon->out = 1;
hci_conn_put(conn->hcon);
}
@@ -939,6 +862,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
write_lock_bh(&conn->chan_lock);
hci_conn_hold(conn->hcon);
+ conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
@@ -978,39 +902,25 @@ static void l2cap_chan_ready(struct sock *sk)
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
+ struct hci_conn *hcon = conn->hcon;
BT_DBG("conn %p", conn);
- if (!conn->hcon->out && conn->hcon->type == LE_LINK)
+ if (!hcon->out && hcon->type == LE_LINK)
l2cap_le_conn_ready(conn);
- read_lock(&conn->chan_lock);
-
-/* This is SBH650 issue. and this is only workaround */
-/* We don not send info request at this time */
-/* somtimes SBH650 will send disconnect */
- if (!conn->hcon->out
- && !(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) {
- 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));
+ if (hcon->out && hcon->type == LE_LINK)
+ smp_conn_security(hcon, hcon->pending_sec_level);
- l2cap_send_cmd(conn, conn->info_ident,
- L2CAP_INFO_REQ, sizeof(req), &req);
- }
+ read_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;
bh_lock_sock(sk);
- if (conn->hcon->type == LE_LINK) {
- if (smp_conn_security(conn, chan->sec_level))
+ if (hcon->type == LE_LINK) {
+ if (smp_conn_security(hcon, chan->sec_level))
l2cap_chan_ready(sk);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1081,8 +991,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);
+ smp_chan_destroy(conn);
+ }
hcon->l2cap_data = NULL;
kfree(conn);
@@ -1203,10 +1115,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
auth_type = l2cap_get_auth_type(chan);
if (chan->dcid == L2CAP_CID_LE_DATA)
- hcon = hci_connect(hdev, LE_LINK, 0, dst,
+ hcon = hci_connect(hdev, LE_LINK, dst,
chan->sec_level, auth_type);
else
- hcon = hci_connect(hdev, ACL_LINK, 0, dst,
+ hcon = hci_connect(hdev, ACL_LINK, dst,
chan->sec_level, auth_type);
if (IS_ERR(hcon)) {
@@ -1335,7 +1247,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
__clear_retrans_timer(chan);
}
-void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
+static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
{
struct hci_conn *hcon = chan->conn->hcon;
u16 flags;
@@ -1351,7 +1263,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
hci_send_acl(hcon, skb, flags);
}
-void l2cap_streaming_send(struct l2cap_chan *chan)
+static void l2cap_streaming_send(struct l2cap_chan *chan)
{
struct sk_buff *skb;
u16 control, fcs;
@@ -1417,7 +1329,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
l2cap_do_send(chan, tx_skb);
}
-int l2cap_ertm_send(struct l2cap_chan *chan)
+static int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
u16 control, fcs;
@@ -1555,7 +1467,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
return sent;
}
-struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
@@ -1585,7 +1497,7 @@ struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr
return skb;
}
-struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
@@ -1614,7 +1526,9 @@ struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *m
return skb;
}
-struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen)
+static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
+ struct msghdr *msg, size_t len,
+ u16 control, u16 sdulen)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
@@ -1660,7 +1574,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *
return skb;
}
-int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
struct sk_buff *skb;
struct sk_buff_head sar_queue;
@@ -2243,7 +2157,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
void *ptr = req->data;
int type, olen;
unsigned long val;
- struct l2cap_conf_rfc rfc;
+ struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
@@ -2354,14 +2268,26 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
- switch (type) {
- case L2CAP_CONF_RFC:
- if (olen == sizeof(rfc))
- memcpy(&rfc, (void *)val, olen);
- goto done;
- }
+ if (type != L2CAP_CONF_RFC)
+ continue;
+
+ if (olen != sizeof(rfc))
+ break;
+
+ memcpy(&rfc, (void *)val, olen);
+ goto done;
}
+ /* Use sane default values in case a misbehaving remote device
+ * did not send an RFC option.
+ */
+ rfc.mode = chan->mode;
+ rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
+ rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ rfc.max_pdu_size = cpu_to_le16(chan->imtu);
+
+ BT_ERR("Expected RFC option was not found, using defaults");
+
done:
switch (rfc.mode) {
case L2CAP_MODE_ERTM:
@@ -2374,11 +2300,16 @@ done:
}
}
-static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_command_rej(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
- struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data;
+ struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
+
+ if (cmd_len < sizeof(*rej))
+ return -EPROTO;
- if (rej->reason != 0x0000)
+ if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD)
return 0;
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
@@ -2394,7 +2325,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0;
}
-static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static int l2cap_connect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
@@ -2402,8 +2334,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
struct sock *parent, *sk = NULL;
int result, status = L2CAP_CS_NO_INFO;
- u16 dcid = 0, scid = __le16_to_cpu(req->scid);
- __le16 psm = req->psm;
+ u16 dcid = 0, scid;
+ __le16 psm;
+
+ if (cmd_len < sizeof(struct l2cap_conn_req))
+ return -EPROTO;
+
+ scid = __le16_to_cpu(req->scid);
+ psm = req->psm;
BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
@@ -2516,13 +2454,6 @@ sendresp:
L2CAP_INFO_REQ, sizeof(info), &info);
}
-/* this is workaround for windows mobile phone. */
-/* maybe, conf negotiation has some problem in wm phone. */
-/* wm phone send first pdu over max size. (we expect 1013, but recved 1014) */
-/* this code is mandatory for SIG CERTI 3.0 */
-/* this code is only for Honeycomb and ICS. */
-/* Gingerbread doesn't have this part. */
-/*
if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
result == L2CAP_CR_SUCCESS) {
u8 buf[128];
@@ -2531,12 +2462,13 @@ sendresp:
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
-*/
return 0;
}
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static int l2cap_connect_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
@@ -2544,6 +2476,9 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
struct sock *sk;
u8 req[128];
+ if (cmd_len < sizeof(*rsp))
+ return -EPROTO;
+
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
result = __le16_to_cpu(rsp->result);
@@ -2573,20 +2508,6 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
break;
-/* BEGIN SLP_Bluetooth :: fix av chopping issue. */
-#ifdef HCI_BROADCOMM_QOS_PATCH
- /* To gurantee the A2DP packet*/
- if (chan->psm == L2CAP_PSM_AVDTP) {
- struct hci_cp_broadcom_cmd cp;
- cp.handle = cpu_to_le16(conn->hcon->handle);
- cp.priority = PRIORITY_HIGH;
-
- hci_send_cmd(conn->hcon->hdev, HCI_BROADCOM_QOS_CMD,
- sizeof(cp), &cp);
- }
-#endif
-/* END SLP_Bluetooth */
-
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, req), req);
chan->num_conf_req++;
@@ -2601,7 +2522,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
break;
}
@@ -2633,6 +2554,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
struct sock *sk;
int len;
+ if (cmd_len < sizeof(*req))
+ return -EPROTO;
+
dcid = __le16_to_cpu(req->dcid);
flags = __le16_to_cpu(req->flags);
@@ -2644,10 +2568,13 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
sk = chan->sk;
- if (sk->sk_state != BT_CONFIG && sk->sk_state != BT_CONNECT2) {
- struct l2cap_cmd_rej rej;
+ if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
+ struct l2cap_cmd_rej_cid rej;
+
+ rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID);
+ rej.scid = cpu_to_le16(chan->scid);
+ rej.dcid = cpu_to_le16(chan->dcid);
- rej.reason = cpu_to_le16(0x0002);
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
sizeof(rej), &rej);
goto unlock;
@@ -2655,7 +2582,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Reject if config buffer is too small. */
len = cmd_len - sizeof(*req);
- if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) {
+ if (chan->conf_len + len > sizeof(chan->conf_req)) {
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_REJECT, flags), rsp);
@@ -2717,13 +2644,18 @@ unlock:
return 0;
}
-static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_config_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result;
struct l2cap_chan *chan;
struct sock *sk;
- int len = cmd->len - sizeof(*rsp);
+ int len = cmd_len - sizeof(*rsp);
+
+ if (cmd_len < sizeof(*rsp))
+ return -EPROTO;
scid = __le16_to_cpu(rsp->scid);
flags = __le16_to_cpu(rsp->flags);
@@ -2771,7 +2703,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
default:
sk->sk_err = ECONNRESET;
- __set_chan_timer(chan, HZ * 5);
+ __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
@@ -2799,7 +2731,9 @@ done:
return 0;
}
-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;
struct l2cap_disconn_rsp rsp;
@@ -2807,6 +2741,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
struct l2cap_chan *chan;
struct sock *sk;
+ if (cmd_len != sizeof(*req))
+ return -EPROTO;
+
scid = __le16_to_cpu(req->scid);
dcid = __le16_to_cpu(req->dcid);
@@ -2828,7 +2765,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
return 0;
}
@@ -2840,13 +2777,18 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
return 0;
}
-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
struct l2cap_chan *chan;
struct sock *sk;
+ if (cmd_len != sizeof(*rsp))
+ return -EPROTO;
+
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
@@ -2862,7 +2804,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan,BT_DISCONN);
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
return 0;
}
@@ -2874,11 +2816,16 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
return 0;
}
-static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_information_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_info_req *req = (struct l2cap_info_req *) data;
u16 type;
+ if (cmd_len != sizeof(*req))
+ return -EPROTO;
+
type = __le16_to_cpu(req->type);
BT_DBG("type 0x%4.4x", type);
@@ -2914,11 +2861,16 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
return 0;
}
-static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_information_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;
u16 type, result;
+ if (cmd_len < sizeof(*rsp))
+ return -EPROTO;
+
type = __le16_to_cpu(rsp->type);
result = __le16_to_cpu(rsp->result);
@@ -3037,15 +2989,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
- l2cap_command_rej(conn, cmd, data);
+ l2cap_command_rej(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_REQ:
- err = l2cap_connect_req(conn, cmd, data);
+ err = l2cap_connect_req(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_RSP:
- err = l2cap_connect_rsp(conn, cmd, data);
+ err = l2cap_connect_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_CONF_REQ:
@@ -3053,15 +3005,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_CONF_RSP:
- err = l2cap_config_rsp(conn, cmd, data);
+ err = l2cap_config_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_REQ:
- err = l2cap_disconnect_req(conn, cmd, data);
+ err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_RSP:
- err = l2cap_disconnect_rsp(conn, cmd, data);
+ err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_ECHO_REQ:
@@ -3072,11 +3024,11 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_INFO_REQ:
- err = l2cap_information_req(conn, cmd, data);
+ err = l2cap_information_req(conn, cmd, cmd_len, data);
break;
case L2CAP_INFO_RSP:
- err = l2cap_information_rsp(conn, cmd, data);
+ err = l2cap_information_rsp(conn, cmd, cmd_len, data);
break;
default:
@@ -3138,12 +3090,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
if (err) {
- struct l2cap_cmd_rej rej;
+ struct l2cap_cmd_rej_unk rej;
BT_ERR("Wrong link type (%d)", err);
/* FIXME: Map err to a valid reason */
- rej.reason = cpu_to_le16(0);
+ rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
}
@@ -3238,102 +3190,104 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
return 0;
}
-static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+static void append_skb_frag(struct sk_buff *skb,
+ struct sk_buff *new_frag, struct sk_buff **last_frag)
{
- struct sk_buff *_skb;
- int err;
+ /* skb->len reflects data in skb as well as all fragments
+ * skb->data_len reflects only data in fragments
+ */
+ if (!skb_has_frag_list(skb))
+ skb_shinfo(skb)->frag_list = new_frag;
+
+ new_frag->next = NULL;
+
+ (*last_frag)->next = new_frag;
+ *last_frag = new_frag;
+
+ skb->len += new_frag->len;
+ skb->data_len += new_frag->len;
+ skb->truesize += new_frag->truesize;
+}
+
+static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+{
+ int err = -EINVAL;
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (test_bit(CONN_SAR_SDU, &chan->conn_state))
- goto drop;
+ if (chan->sdu)
+ break;
- return chan->ops->recv(chan->data, skb);
+ err = chan->ops->recv(chan->data, skb);
+ break;
case L2CAP_SDU_START:
- if (test_bit(CONN_SAR_SDU, &chan->conn_state))
- goto drop;
+ if (chan->sdu)
+ break;
chan->sdu_len = get_unaligned_le16(skb->data);
+ skb_pull(skb, 2);
- if (chan->sdu_len > chan->imtu)
- goto disconnect;
-
- chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
- if (!chan->sdu)
- return -ENOMEM;
+ if (chan->sdu_len > chan->imtu) {
+ err = -EMSGSIZE;
+ break;
+ }
- /* pull sdu_len bytes only after alloc, because of Local Busy
- * condition we have to be sure that this will be executed
- * only once, i.e., when alloc does not fail */
- skb_pull(skb, 2);
+ if (skb->len >= chan->sdu_len)
+ break;
- memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+ chan->sdu = skb;
+ chan->sdu_last_frag = skb;
- set_bit(CONN_SAR_SDU, &chan->conn_state);
- chan->partial_sdu_len = skb->len;
+ skb = NULL;
+ err = 0;
break;
case L2CAP_SDU_CONTINUE:
- if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
- goto disconnect;
-
if (!chan->sdu)
- goto disconnect;
+ break;
- chan->partial_sdu_len += skb->len;
- if (chan->partial_sdu_len > chan->sdu_len)
- goto drop;
+ append_skb_frag(chan->sdu, skb,
+ &chan->sdu_last_frag);
+ skb = NULL;
- memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+ if (chan->sdu->len >= chan->sdu_len)
+ break;
+ err = 0;
break;
case L2CAP_SDU_END:
- if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
- goto disconnect;
-
if (!chan->sdu)
- goto disconnect;
-
- chan->partial_sdu_len += skb->len;
-
- if (chan->partial_sdu_len > chan->imtu)
- goto drop;
+ break;
- if (chan->partial_sdu_len != chan->sdu_len)
- goto drop;
+ append_skb_frag(chan->sdu, skb,
+ &chan->sdu_last_frag);
+ skb = NULL;
- memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+ if (chan->sdu->len != chan->sdu_len)
+ break;
- _skb = skb_clone(chan->sdu, GFP_ATOMIC);
- if (!_skb) {
- return -ENOMEM;
- }
+ err = chan->ops->recv(chan->data, chan->sdu);
- err = chan->ops->recv(chan->data, _skb);
- if (err < 0) {
- kfree_skb(_skb);
- return err;
+ if (!err) {
+ /* Reassembly complete */
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
}
-
- clear_bit(CONN_SAR_SDU, &chan->conn_state);
-
- kfree_skb(chan->sdu);
break;
}
- kfree_skb(skb);
- return 0;
-
-drop:
- kfree_skb(chan->sdu);
- chan->sdu = NULL;
+ if (err) {
+ kfree_skb(skb);
+ kfree_skb(chan->sdu);
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+ }
-disconnect:
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
- kfree_skb(skb);
- return 0;
+ return err;
}
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
@@ -3387,99 +3341,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
}
}
-static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
-{
- struct sk_buff *_skb;
- int err = -EINVAL;
-
- /*
- * TODO: We have to notify the userland if some data is lost with the
- * Streaming Mode.
- */
-
- switch (control & L2CAP_CTRL_SAR) {
- case L2CAP_SDU_UNSEGMENTED:
- if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
- kfree_skb(chan->sdu);
- break;
- }
-
- err = chan->ops->recv(chan->data, skb);
- if (!err)
- return 0;
-
- break;
-
- case L2CAP_SDU_START:
- if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
- kfree_skb(chan->sdu);
- break;
- }
-
- chan->sdu_len = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
-
- if (chan->sdu_len > chan->imtu) {
- err = -EMSGSIZE;
- break;
- }
-
- chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
- if (!chan->sdu) {
- err = -ENOMEM;
- break;
- }
-
- memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
-
- set_bit(CONN_SAR_SDU, &chan->conn_state);
- chan->partial_sdu_len = skb->len;
- err = 0;
- break;
-
- case L2CAP_SDU_CONTINUE:
- if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
- break;
-
- memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
-
- chan->partial_sdu_len += skb->len;
- if (chan->partial_sdu_len > chan->sdu_len)
- kfree_skb(chan->sdu);
- else
- err = 0;
-
- break;
-
- case L2CAP_SDU_END:
- if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
- break;
-
- memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
-
- clear_bit(CONN_SAR_SDU, &chan->conn_state);
- chan->partial_sdu_len += skb->len;
-
- if (chan->partial_sdu_len > chan->imtu)
- goto drop;
-
- if (chan->partial_sdu_len == chan->sdu_len) {
- _skb = skb_clone(chan->sdu, GFP_ATOMIC);
- err = chan->ops->recv(chan->data, _skb);
- if (err < 0)
- kfree_skb(_skb);
- }
- err = 0;
-
-drop:
- kfree_skb(chan->sdu);
- break;
- }
-
- kfree_skb(skb);
- return err;
-}
-
static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
{
struct sk_buff *skb;
@@ -3494,7 +3355,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
skb = skb_dequeue(&chan->srej_q);
control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
- err = l2cap_ertm_reassembly_sdu(chan, skb, control);
+ err = l2cap_reassemble_sdu(chan, skb, control);
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3654,7 +3515,7 @@ expected:
return 0;
}
- err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control);
+ err = l2cap_reassemble_sdu(chan, skb, rx_control);
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3970,12 +3831,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
tx_seq = __get_txseq(control);
- if (chan->expected_tx_seq == tx_seq)
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
- else
- chan->expected_tx_seq = (tx_seq + 1) % 64;
+ if (chan->expected_tx_seq != tx_seq) {
+ /* Frame(s) missing - must discard partial SDU */
+ kfree_skb(chan->sdu);
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
- l2cap_streaming_reassembly_sdu(chan, skb, control);
+ /* TODO: Notify userland of missing data */
+ }
+
+ chan->expected_tx_seq = (tx_seq + 1) % 64;
+
+ if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto done;
@@ -4191,7 +4060,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ * 5);
+ __set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
} else if (chan->sec_level == BT_SECURITY_HIGH)
l2cap_chan_close(chan, ECONNREFUSED);
} else {
@@ -4210,6 +4079,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) {
@@ -4222,9 +4096,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);
@@ -4256,7 +4128,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
L2CAP_CONN_REQ, sizeof(req), &req);
} else {
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 10);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
}
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
@@ -4276,7 +4148,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
}
} else {
l2cap_state_change(chan, BT_DISCONN);
- __set_chan_timer(chan, HZ / 10);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
}
@@ -4423,7 +4295,7 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
- }
+}
read_unlock_bh(&chan_list_lock);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 785e84f..74e59cd 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -26,6 +26,9 @@
/* Bluetooth L2CAP sockets. */
+#include <linux/security.h>
+#include <linux/export.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
@@ -613,7 +616,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
break;
}
- if (smp_conn_security(conn, sec.level))
+ if (smp_conn_security(conn->hcon, sec.level))
break;
err = 0;
@@ -801,7 +804,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
sk->sk_shutdown = SHUTDOWN_MASK;
l2cap_chan_close(chan, 0);
- if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+ if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+ !(current->flags & PF_EXITING))
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
@@ -934,6 +938,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->force_reliable = pchan->force_reliable;
chan->flushable = pchan->flushable;
chan->force_active = pchan->force_active;
+
+ security_sk_clone(parent, sk);
} else {
switch (sk->sk_type) {
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 9832721..2c76342 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -23,6 +23,7 @@
/* Bluetooth HCI Management interface */
#include <linux/uaccess.h>
+#include <linux/module.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -147,8 +148,6 @@ static int read_index_list(struct sock *sk)
hci_del_off_timer(d);
- set_bit(HCI_MGMT, &d->flags);
-
if (test_bit(HCI_SETUP, &d->flags))
continue;
@@ -908,7 +907,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
struct hci_dev *hdev;
struct mgmt_cp_load_keys *cp;
u16 key_count, expected_len;
- int i, err;
+ int i;
cp = (void *) data;
@@ -918,9 +917,9 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
key_count = get_unaligned_le16(&cp->key_count);
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
- if (expected_len > len) {
- BT_ERR("load_keys: expected at least %u bytes, got %u bytes",
- expected_len, len);
+ if (expected_len != len) {
+ BT_ERR("load_keys: expected %u bytes, got %u bytes",
+ len, expected_len);
return -EINVAL;
}
@@ -942,36 +941,17 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
else
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
- len -= sizeof(*cp);
- i = 0;
-
- while (i < len) {
- struct mgmt_key_info *key = (void *) cp->keys + i;
-
- i += sizeof(*key) + key->dlen;
-
- if (key->type == HCI_LK_SMP_LTK) {
- struct key_master_id *id = (void *) key->data;
-
- if (key->dlen != sizeof(struct key_master_id))
- continue;
-
- hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len,
- id->ediv, id->rand, key->val);
-
- continue;
- }
+ for (i = 0; i < key_count; i++) {
+ struct mgmt_key_info *key = &cp->keys[i];
hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
key->pin_len);
}
- err = cmd_complete(sk, index, MGMT_OP_LOAD_KEYS, NULL, 0);
-
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
- return err;
+ return 0;
}
static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
@@ -1347,6 +1327,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;
@@ -1364,15 +1345,20 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
hci_dev_lock_bh(hdev);
- if (cp->io_cap == 0x03) {
- sec_level = BT_SECURITY_MEDIUM;
+ sec_level = BT_SECURITY_MEDIUM;
+ if (cp->io_cap == 0x03)
auth_type = HCI_AT_DEDICATED_BONDING;
- } else {
- sec_level = BT_SECURITY_HIGH;
+ else
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
- }
- conn = hci_connect(hdev, ACL_LINK, 0, &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 +1377,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;
@@ -1689,13 +1678,12 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct hci_dev *hdev;
- struct mgmt_cp_block_device *cp;
+ struct pending_cmd *cmd;
+ struct mgmt_cp_block_device *cp = (void *) data;
int err;
BT_DBG("hci%u", index);
- cp = (void *) data;
-
if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
EINVAL);
@@ -1705,6 +1693,14 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
ENODEV);
+ hci_dev_lock_bh(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
err = hci_blacklist_add(hdev, &cp->bdaddr);
if (err < 0)
@@ -1712,6 +1708,11 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
else
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
NULL, 0);
+
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
@@ -1721,13 +1722,12 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct hci_dev *hdev;
- struct mgmt_cp_unblock_device *cp;
+ struct pending_cmd *cmd;
+ struct mgmt_cp_unblock_device *cp = (void *) data;
int err;
BT_DBG("hci%u", index);
- cp = (void *) data;
-
if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
EINVAL);
@@ -1737,6 +1737,14 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
ENODEV);
+ hci_dev_lock_bh(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
err = hci_blacklist_del(hdev, &cp->bdaddr);
if (err < 0)
@@ -1744,6 +1752,67 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
else
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
NULL, 0);
+
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int set_fast_connectable(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_set_fast_connectable *cp = (void *) data;
+ struct hci_cp_write_page_scan_activity acp;
+ u8 type;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ if (cp->enable) {
+ type = PAGE_SCAN_TYPE_INTERLACED;
+ acp.interval = 0x0024; /* 22.5 msec page scan interval */
+ } else {
+ type = PAGE_SCAN_TYPE_STANDARD; /* default */
+ acp.interval = 0x0800; /* default 1.28 sec page scan */
+ }
+
+ acp.window = 0x0012; /* default 11.25 msec page scan window */
+
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
+ sizeof(acp), &acp);
+ if (err < 0) {
+ err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
+ -err);
+ goto done;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
+ if (err < 0) {
+ err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
+ -err);
+ goto done;
+ }
+
+ err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
+ NULL, 0);
+done:
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1869,6 +1938,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_UNBLOCK_DEVICE:
err = unblock_device(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_SET_FAST_CONNECTABLE:
+ err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01);
@@ -1977,35 +2050,25 @@ int mgmt_connectable(u16 index, u8 connectable)
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
{
- struct mgmt_ev_new_key *ev;
- int err, total;
-
- total = sizeof(struct mgmt_ev_new_key) + key->dlen;
- ev = kzalloc(total, GFP_ATOMIC);
- if (!ev)
- return -ENOMEM;
-
- bacpy(&ev->key.bdaddr, &key->bdaddr);
- ev->key.type = key->type;
- memcpy(ev->key.val, key->val, 16);
- ev->key.pin_len = key->pin_len;
- ev->key.dlen = key->dlen;
- ev->store_hint = persistent;
+ struct mgmt_ev_new_key ev;
- memcpy(ev->key.data, key->data, key->dlen);
-
- err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL);
+ memset(&ev, 0, sizeof(ev));
- kfree(ev);
+ ev.store_hint = persistent;
+ bacpy(&ev.key.bdaddr, &key->bdaddr);
+ ev.key.type = key->type;
+ memcpy(ev.key.val, key->val, 16);
+ ev.key.pin_len = key->pin_len;
- return err;
+ return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
}
-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);
}
@@ -2260,12 +2323,14 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
memset(&ev, 0, sizeof(ev));
bacpy(&ev.bdaddr, bdaddr);
- memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
ev.rssi = rssi;
if (eir)
memcpy(ev.eir, eir, sizeof(ev.eir));
+ if (dev_class)
+ memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
+
return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
}
@@ -2286,3 +2351,29 @@ int mgmt_discovering(u16 index, u8 discovering)
return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering,
sizeof(discovering), NULL);
}
+
+int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr)
+{
+ struct pending_cmd *cmd;
+ struct mgmt_ev_device_blocked ev;
+
+ cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index);
+
+ bacpy(&ev.bdaddr, bdaddr);
+
+ return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
+}
+
+int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr)
+{
+ struct pending_cmd *cmd;
+ struct mgmt_ev_device_unblocked ev;
+
+ cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index);
+
+ bacpy(&ev.bdaddr, bdaddr);
+
+ return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
+}
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 754b153..2d28dfe 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -409,19 +409,11 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
return err;
}
- /* After L2sock created, increase refcnt immediately
- * It can make connection drop in rfcomm_security_cfm because of refcnt.
- */
- rfcomm_session_hold(s);
-
dlci = __dlci(!s->initiator, channel);
/* Check if DLCI already exists */
- if (rfcomm_dlc_get(s, dlci)) {
- /* decrement refcnt */
- rfcomm_session_put(s);
+ if (rfcomm_dlc_get(s, dlci))
return -EBUSY;
- }
rfcomm_dlc_clear_state(d);
@@ -437,9 +429,6 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
d->mtu = s->mtu;
d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
- /* decrement refcnt */
- rfcomm_session_put(s);
-
if (s->state == BT_CONNECTED) {
if (rfcomm_check_security(d))
rfcomm_send_pn(s, 1, d);
@@ -475,6 +464,7 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
switch (d->state) {
case BT_CONNECT:
+ case BT_CONFIG:
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
rfcomm_schedule();
@@ -1173,7 +1163,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
/* When socket is closed and we are not RFCOMM
* initiator rfcomm_process_rx already calls
* rfcomm_session_put() */
- if (s->sock->sk->sk_state != BT_CLOSED && !s->initiator)
+ if (s->sock->sk->sk_state != BT_CLOSED)
if (list_empty(&s->dlcs))
rfcomm_session_put(s);
break;
@@ -1813,6 +1803,11 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
continue;
}
+ if (test_bit(RFCOMM_ENC_DROP, &d->flags)) {
+ __rfcomm_dlc_close(d, ECONNREFUSED);
+ continue;
+ }
+
if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) {
rfcomm_dlc_clear_timer(d);
if (d->out) {
@@ -2088,7 +2083,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
rfcomm_dlc_clear_timer(d);
if (status || encrypt == 0x00) {
- __rfcomm_dlc_close(d, ECONNREFUSED);
+ set_bit(RFCOMM_ENC_DROP, &d->flags);
continue;
}
}
@@ -2099,7 +2094,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
continue;
} else if (d->sec_level == BT_SECURITY_HIGH) {
- __rfcomm_dlc_close(d, ECONNREFUSED);
+ set_bit(RFCOMM_ENC_DROP, &d->flags);
continue;
}
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 56fcfd1..93a8241 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -42,6 +42,7 @@
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/security.h>
#include <net/sock.h>
#include <asm/system.h>
@@ -264,6 +265,8 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
pi->sec_level = rfcomm_pi(parent)->sec_level;
pi->role_switch = rfcomm_pi(parent)->role_switch;
+
+ security_sk_clone(parent, sk);
} else {
pi->dlc->defer_setup = 0;
@@ -483,7 +486,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f
long timeo;
int err = 0;
- lock_sock(sk);
+ lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
if (sk->sk_type != SOCK_STREAM) {
err = -EINVAL;
@@ -520,7 +523,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f
release_sock(sk);
timeo = schedule_timeout(timeo);
- lock_sock(sk);
+ lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
@@ -624,7 +627,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
rfcomm_dlc_accept(d);
- msg->msg_namelen = 0;
return 0;
}
@@ -788,7 +790,6 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
break;
}
-
memset(&cinfo, 0, sizeof(cinfo));
cinfo.hci_handle = conn->hcon->handle;
memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
@@ -835,6 +836,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
}
sec.level = rfcomm_pi(sk)->sec_level;
+ sec.key_size = 0;
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
@@ -899,7 +901,8 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
sk->sk_shutdown = SHUTDOWN_MASK;
__rfcomm_sock_close(sk);
- if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+ if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+ !(current->flags & PF_EXITING))
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
}
release_sock(sk);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index ac4631d..2e80aee 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -41,6 +41,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
+#include <linux/security.h>
#include <net/sock.h>
#include <asm/system.h>
@@ -177,7 +178,6 @@ static int sco_connect(struct sock *sk)
{
bdaddr_t *src = &bt_sk(sk)->src;
bdaddr_t *dst = &bt_sk(sk)->dst;
- __u16 pkt_type = sco_pi(sk)->pkt_type;
struct sco_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
@@ -193,12 +193,10 @@ static int sco_connect(struct sock *sk)
if (lmp_esco_capable(hdev) && !disable_esco)
type = ESCO_LINK;
- else {
+ else
type = SCO_LINK;
- pkt_type &= SCO_ESCO_MASK;
- }
- hcon = hci_connect(hdev, type, pkt_type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
+ hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
if (IS_ERR(hcon)) {
err = PTR_ERR(hcon);
goto done;
@@ -381,7 +379,6 @@ static void __sco_sock_close(struct sock *sk)
sco_chan_del(sk, ECONNRESET);
break;
- case BT_CONNECT2:
case BT_CONNECT:
case BT_DISCONN:
sco_chan_del(sk, ECONNRESET);
@@ -407,8 +404,10 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
{
BT_DBG("sk %p", sk);
- if (parent)
+ if (parent) {
sk->sk_type = parent->sk_type;
+ security_sk_clone(parent, sk);
+ }
}
static struct proto sco_proto = {
@@ -464,22 +463,18 @@ static int sco_sock_create(struct net *net, struct socket *sock, int protocol,
return 0;
}
-static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
+static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
{
- struct sockaddr_sco sa;
+ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
struct sock *sk = sock->sk;
- bdaddr_t *src = &sa.sco_bdaddr;
- int len, err = 0;
+ bdaddr_t *src = &sa->sco_bdaddr;
+ int err = 0;
- BT_DBG("sk %p %s", sk, batostr(&sa.sco_bdaddr));
+ BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
- memset(&sa, 0, sizeof(sa));
- len = min_t(unsigned int, sizeof(sa), alen);
- memcpy(&sa, addr, len);
-
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
@@ -493,8 +488,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
err = -EADDRINUSE;
} else {
/* Save source address */
- bacpy(&bt_sk(sk)->src, &sa.sco_bdaddr);
- sco_pi(sk)->pkt_type = sa.sco_pkt_type;
+ bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr);
sk->sk_state = BT_BOUND;
}
@@ -507,34 +501,27 @@ done:
static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
{
+ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
struct sock *sk = sock->sk;
- struct sockaddr_sco sa;
- int len, err = 0;
+ int err = 0;
+
BT_DBG("sk %p", sk);
- if (!addr || addr->sa_family != AF_BLUETOOTH)
+ if (alen < sizeof(struct sockaddr_sco) ||
+ addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
- memset(&sa, 0, sizeof(sa));
- len = min_t(unsigned int, sizeof(sa), alen);
- memcpy(&sa, addr, len);
-
- lock_sock(sk);
+ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
+ return -EBADFD;
- if (sk->sk_type != SOCK_SEQPACKET) {
- err = -EINVAL;
- goto done;
- }
+ if (sk->sk_type != SOCK_SEQPACKET)
+ return -EINVAL;
- if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
- err = -EBADFD;
- goto done;
- }
+ lock_sock(sk);
/* Set destination address and psm */
- bacpy(&bt_sk(sk)->dst, &sa.sco_bdaddr);
- sco_pi(sk)->pkt_type = sa.sco_pkt_type;
+ bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr);
err = sco_connect(sk);
if (err)
@@ -641,7 +628,6 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len
bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst);
else
bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src);
- sa->sco_pkt_type = sco_pi(sk)->pkt_type;
return 0;
}
@@ -788,7 +774,8 @@ static int sco_sock_shutdown(struct socket *sock, int how)
sco_sock_clear_timer(sk);
__sco_sock_close(sk);
- if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+ if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+ !(current->flags & PF_EXITING))
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
@@ -808,7 +795,8 @@ static int sco_sock_release(struct socket *sock)
sco_sock_close(sk);
- if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) {
+ if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+ !(current->flags & PF_EXITING)) {
lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
release_sock(sk);
@@ -891,11 +879,6 @@ static void sco_conn_ready(struct sco_conn *conn)
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
- if (!conn->hcon) {
- BT_ERR("conn->hcon = NULL");
- /* to do */
- }
-
hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 391888b..9ab60e6 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -30,6 +30,8 @@
#define SMP_TIMEOUT 30000 /* 30 seconds */
+#define AUTH_REQ_MASK 0x07
+
static inline void swap128(u8 src[16], u8 dst[16])
{
int i;
@@ -182,18 +184,9 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
return;
hci_send_acl(conn->hcon, skb, 0);
-}
-
-static __u8 seclevel_to_authreq(__u8 level)
-{
- switch (level) {
- case BT_SECURITY_HIGH:
- /* Right now we don't support bonding */
- return SMP_AUTH_MITM;
- default:
- return SMP_AUTH_NONE;
- }
+ mod_timer(&conn->security_timer, jiffies +
+ msecs_to_jiffies(SMP_TIMEOUT));
}
static void build_pairing_cmd(struct l2cap_conn *conn,
@@ -205,7 +198,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
dist_keys = 0;
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
- dist_keys = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN;
+ dist_keys = SMP_DIST_ENC_KEY;
authreq |= SMP_AUTH_BONDING;
}
@@ -215,7 +208,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
req->init_key_dist = dist_keys;
req->resp_key_dist = dist_keys;
- req->auth_req = authreq;
+ req->auth_req = (authreq & AUTH_REQ_MASK);
return;
}
@@ -224,29 +217,189 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
rsp->init_key_dist = req->init_key_dist & dist_keys;
rsp->resp_key_dist = req->resp_key_dist & dist_keys;
- rsp->auth_req = authreq;
+ rsp->auth_req = (authreq & AUTH_REQ_MASK);
}
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
{
+ struct smp_chan *smp = conn->smp_chan;
+
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
return SMP_ENC_KEY_SIZE;
- conn->smp_key_size = max_key_size;
+ smp->smp_key_size = max_key_size;
return 0;
}
+static void confirm_work(struct work_struct *work)
+{
+ struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
+ struct l2cap_conn *conn = smp->conn;
+ struct crypto_blkcipher *tfm;
+ struct smp_cmd_pairing_confirm cp;
+ int ret;
+ u8 res[16], reason;
+
+ BT_DBG("conn %p", conn);
+
+ tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ reason = SMP_UNSPECIFIED;
+ goto error;
+ }
+
+ smp->tfm = tfm;
+
+ if (conn->hcon->out)
+ ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0,
+ conn->src, conn->hcon->dst_type, conn->dst,
+ res);
+ else
+ ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ conn->hcon->dst_type, conn->dst, 0, conn->src,
+ res);
+ if (ret) {
+ reason = SMP_UNSPECIFIED;
+ goto error;
+ }
+
+ swap128(res, cp.confirm_val);
+ smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+
+ return;
+
+error:
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
+ smp_chan_destroy(conn);
+}
+
+static void random_work(struct work_struct *work)
+{
+ struct smp_chan *smp = container_of(work, struct smp_chan, random);
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct crypto_blkcipher *tfm = smp->tfm;
+ u8 reason, confirm[16], res[16], key[16];
+ int ret;
+
+ if (IS_ERR_OR_NULL(tfm)) {
+ reason = SMP_UNSPECIFIED;
+ goto error;
+ }
+
+ BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
+
+ if (hcon->out)
+ ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
+ conn->src, hcon->dst_type, conn->dst,
+ res);
+ else
+ ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+ hcon->dst_type, conn->dst, 0, conn->src,
+ res);
+ if (ret) {
+ reason = SMP_UNSPECIFIED;
+ goto error;
+ }
+
+ swap128(res, confirm);
+
+ if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
+ BT_ERR("Pairing failed (confirmation values mismatch)");
+ reason = SMP_CONFIRM_FAILED;
+ goto error;
+ }
+
+ if (hcon->out) {
+ u8 stk[16], rand[8];
+ __le16 ediv;
+
+ memset(rand, 0, sizeof(rand));
+ ediv = 0;
+
+ smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key);
+ swap128(key, stk);
+
+ memset(stk + smp->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) {
+ reason = SMP_UNSPECIFIED;
+ goto error;
+ }
+
+ hci_le_start_enc(hcon, ediv, rand, stk);
+ hcon->enc_key_size = smp->smp_key_size;
+ } else {
+ u8 stk[16], r[16], rand[8];
+ __le16 ediv;
+
+ memset(rand, 0, sizeof(rand));
+ ediv = 0;
+
+ swap128(smp->prnd, r);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+
+ smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key);
+ swap128(key, stk);
+
+ memset(stk + smp->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+
+ hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size,
+ ediv, rand, stk);
+ }
+
+ return;
+
+error:
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
+ smp_chan_destroy(conn);
+}
+
+static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
+{
+ struct smp_chan *smp;
+
+ smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC);
+ if (!smp)
+ return NULL;
+
+ INIT_WORK(&smp->confirm, confirm_work);
+ INIT_WORK(&smp->random, random_work);
+
+ smp->conn = conn;
+ conn->smp_chan = smp;
+
+ hci_conn_hold(conn->hcon);
+
+ return smp;
+}
+
+void smp_chan_destroy(struct l2cap_conn *conn)
+{
+ kfree(conn->smp_chan);
+ hci_conn_put(conn->hcon);
+}
+
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
+ struct smp_chan *smp;
u8 key_size;
+ int ret;
BT_DBG("conn %p", conn);
- conn->preq[0] = SMP_CMD_PAIRING_REQ;
- memcpy(&conn->preq[1], req, sizeof(*req));
+ if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ smp = smp_chan_create(conn);
+
+ smp = conn->smp_chan;
+
+ smp->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
if (req->oob_flag)
@@ -260,32 +413,33 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
return SMP_ENC_KEY_SIZE;
/* Just works */
- memset(conn->tk, 0, sizeof(conn->tk));
+ memset(smp->tk, 0, sizeof(smp->tk));
+
+ ret = smp_rand(smp->prnd);
+ if (ret)
+ return SMP_UNSPECIFIED;
- conn->prsp[0] = SMP_CMD_PAIRING_RSP;
- memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
-
return 0;
}
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
- struct smp_cmd_pairing_confirm cp;
- struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_dev *hdev = conn->hcon->hdev;
+ u8 key_size;
int ret;
- u8 res[16], key_size;
BT_DBG("conn %p", conn);
skb_pull(skb, sizeof(*rsp));
- req = (void *) &conn->preq[1];
+ req = (void *) &smp->preq[1];
key_size = min(req->max_key_size, rsp->max_key_size);
if (check_enc_key_size(conn, key_size))
@@ -295,222 +449,154 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
return SMP_OOB_NOT_AVAIL;
/* Just works */
- memset(conn->tk, 0, sizeof(conn->tk));
+ memset(smp->tk, 0, sizeof(smp->tk));
- conn->prsp[0] = SMP_CMD_PAIRING_RSP;
- memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
-
- ret = smp_rand(conn->prnd);
- if (ret)
- return SMP_UNSPECIFIED;
-
- ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
- conn->src, conn->hcon->dst_type, conn->dst, res);
+ ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
- swap128(res, cp.confirm_val);
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
- smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+ queue_work(hdev->workqueue, &smp->confirm);
return 0;
}
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_dev *hdev = conn->hcon->hdev;
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
- memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
- skb_pull(skb, sizeof(conn->pcnf));
+ memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
+ skb_pull(skb, sizeof(smp->pcnf));
if (conn->hcon->out) {
u8 random[16];
- swap128(conn->prnd, random);
+ swap128(smp->prnd, random);
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
random);
} else {
- struct smp_cmd_pairing_confirm cp;
- int ret;
- u8 res[16];
-
- ret = smp_rand(conn->prnd);
- if (ret)
- return SMP_UNSPECIFIED;
-
- ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
- conn->hcon->dst_type, conn->dst,
- 0, conn->src, res);
- if (ret)
- return SMP_CONFIRM_FAILED;
-
- swap128(res, cp.confirm_val);
-
- smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+ queue_work(hdev->workqueue, &smp->confirm);
}
- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
-
return 0;
}
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct hci_conn *hcon = conn->hcon;
- struct crypto_blkcipher *tfm = hcon->hdev->tfm;
- int ret;
- u8 key[16], res[16], random[16], confirm[16];
-
- swap128(skb->data, random);
- skb_pull(skb, sizeof(random));
-
- if (conn->hcon->out)
- ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
- conn->src, conn->hcon->dst_type, conn->dst,
- res);
- else
- ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp,
- conn->hcon->dst_type, conn->dst, 0, conn->src,
- res);
- if (ret)
- return SMP_UNSPECIFIED;
-
- BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
-
- swap128(res, confirm);
-
- if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
- BT_ERR("Pairing failed (confirmation values mismatch)");
- return SMP_CONFIRM_FAILED;
- }
-
- if (conn->hcon->out) {
- u8 stk[16], rand[8];
- __le16 ediv;
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_dev *hdev = conn->hcon->hdev;
- memset(rand, 0, sizeof(rand));
- ediv = 0;
+ BT_DBG("conn %p", conn);
- smp_s1(tfm, conn->tk, random, conn->prnd, key);
- swap128(key, stk);
+ swap128(skb->data, smp->rrnd);
+ skb_pull(skb, sizeof(smp->rrnd));
- memset(stk + conn->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
+ queue_work(hdev->workqueue, &smp->random);
- hci_le_start_enc(hcon, ediv, rand, stk);
- hcon->enc_key_size = conn->smp_key_size;
- } else {
- u8 stk[16], r[16], rand[8];
- __le16 ediv;
+ return 0;
+}
- memset(rand, 0, sizeof(rand));
- ediv = 0;
+static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
+{
+ struct link_key *key;
+ struct key_master_id *master;
+ struct hci_conn *hcon = conn->hcon;
- swap128(conn->prnd, r);
- smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+ key = hci_find_link_key_type(hcon->hdev, conn->dst,
+ HCI_LK_SMP_LTK);
+ if (!key)
+ return 0;
- smp_s1(tfm, conn->tk, conn->prnd, random, key);
- swap128(key, stk);
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND,
+ &hcon->pend))
+ return 1;
- memset(stk + conn->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
+ master = (void *) key->data;
+ hci_le_start_enc(hcon, master->ediv, master->rand,
+ key->val);
+ hcon->enc_key_size = key->pin_len;
- hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size,
- ediv, rand, stk);
- }
+ return 1;
- return 0;
}
-
static u8 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;
+ struct smp_chan *smp;
BT_DBG("conn %p", conn);
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ hcon->pending_sec_level = BT_SECURITY_MEDIUM;
+
+ if (smp_ltk_encrypt(conn))
+ return 0;
+
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
return 0;
+ smp = smp_chan_create(conn);
+
skb_pull(skb, sizeof(*rp));
memset(&cp, 0, sizeof(cp));
build_pairing_cmd(conn, &cp, NULL, rp->auth_req);
- conn->preq[0] = SMP_CMD_PAIRING_REQ;
- memcpy(&conn->preq[1], &cp, sizeof(cp));
+ smp->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&smp->preq[1], &cp, sizeof(cp));
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;
}
-int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
- struct hci_conn *hcon = conn->hcon;
- __u8 authreq;
+ struct l2cap_conn *conn = hcon->l2cap_data;
+ struct smp_chan *smp = conn->smp_chan;
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
if (!lmp_host_le_capable(hcon->hdev))
return 1;
- 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,
- HCI_LK_SMP_LTK);
- if (key) {
- struct key_master_id *master = (void *) key->data;
+ if (hcon->link_mode & HCI_LM_MASTER)
+ if (smp_ltk_encrypt(conn))
+ goto done;
- hci_le_start_enc(hcon, master->ediv, master->rand,
- key->val);
- hcon->enc_key_size = key->pin_len;
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ return 0;
- goto done;
- }
+ smp = smp_chan_create(conn);
- build_pairing_cmd(conn, &cp, NULL, authreq);
- conn->preq[0] = SMP_CMD_PAIRING_REQ;
- memcpy(&conn->preq[1], &cp, sizeof(cp));
+ if (hcon->link_mode & HCI_LM_MASTER) {
+ struct smp_cmd_pairing cp;
- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
+ build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE);
+ smp->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&smp->preq[1], &cp, sizeof(cp));
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
} else {
struct smp_cmd_security_req cp;
- cp.auth_req = authreq;
+ cp.auth_req = SMP_AUTH_NONE;
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
}
done:
hcon->pending_sec_level = sec_level;
- set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
return 0;
}
@@ -518,10 +604,11 @@ done:
static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_encrypt_info *rp = (void *) skb->data;
+ struct smp_chan *smp = conn->smp_chan;
skb_pull(skb, sizeof(*rp));
- memcpy(conn->tk, rp->ltk, sizeof(conn->tk));
+ memcpy(smp->tk, rp->ltk, sizeof(smp->tk));
return 0;
}
@@ -529,11 +616,12 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_master_ident *rp = (void *) skb->data;
+ struct smp_chan *smp = conn->smp_chan;
skb_pull(skb, sizeof(*rp));
- hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size,
- rp->ediv, rp->rand, conn->tk);
+ hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size,
+ rp->ediv, rp->rand, smp->tk);
smp_distribute_keys(conn, 1);
@@ -552,14 +640,21 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
goto done;
}
- if (IS_ERR(conn->hcon->hdev->tfm)) {
- err = PTR_ERR(conn->hcon->hdev->tfm);
- reason = SMP_PAIRING_NOTSUPP;
- goto done;
- }
-
skb_pull(skb, sizeof(code));
+ /*
+ * The SMP context must be initialized for all other PDUs except
+ * pairing and security requests. If we get any other PDU when
+ * not initialized simply disconnect (done if this function
+ * returns an error).
+ */
+ if (code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ &&
+ !conn->smp_chan) {
+ BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code);
+ kfree_skb(skb);
+ return -ENOTSUPP;
+ }
+
switch (code) {
case SMP_CMD_PAIRING_REQ:
reason = smp_cmd_pairing_req(conn, skb);
@@ -621,20 +716,21 @@ done:
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
{
struct smp_cmd_pairing *req, *rsp;
+ struct smp_chan *smp = conn->smp_chan;
__u8 *keydist;
BT_DBG("conn %p force %d", conn, 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];
+ rsp = (void *) &smp->prsp[1];
/* The responder sends its keys first */
if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07))
return 0;
- req = (void *) &conn->preq[1];
+ req = (void *) &smp->preq[1];
if (conn->hcon->out) {
keydist = &rsp->init_key_dist;
@@ -658,7 +754,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
- hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->smp_key_size,
+ hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
ediv, ident.rand, enc.ltk);
ident.ediv = cpu_to_le16(ediv);
@@ -698,5 +794,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);
+ smp_chan_destroy(conn);
+ }
+
return 0;
}