From 9a7b1501182477299e678b787e32fca5844c7ea1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 22 Jun 2011 10:08:11 -0700 Subject: Bluetooth: uses crypto interfaces, select CRYPTO Recent changes to hci_core.c use crypto interfaces, so select CRYPTO to make sure that those interfaces are present. Fixes these build errors when CRYPTO is not enabled: net/built-in.o: In function `hci_register_dev': (.text+0x4cf86): undefined reference to `crypto_alloc_base' net/built-in.o: In function `hci_unregister_dev': (.text+0x4f912): undefined reference to `crypto_destroy_tfm' Signed-off-by: Randy Dunlap Signed-off-by: Gustavo F. Padovan --- net/bluetooth/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index f495dea..bfb3dc0 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -6,6 +6,7 @@ menuconfig BT tristate "Bluetooth subsystem support" depends on NET && !S390 depends on RFKILL || !RFKILL + select CRYPTO help Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range -- cgit v1.1 From 774e5651460ee7c688914fd76b6caa1675de355c Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 29 Jun 2011 14:35:20 -0700 Subject: Bluetooth: Fix indentation whitespace Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9ec9c8c..ab2bfcf 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -223,18 +223,18 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); + BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); - if (!mod_timer(timer, jiffies + timeout)) - chan_hold(chan); + if (!mod_timer(timer, jiffies + timeout)) + chan_hold(chan); } static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer) { - BT_DBG("chan %p state %d", chan, chan->state); + BT_DBG("chan %p state %d", chan, chan->state); - if (timer_pending(timer) && del_timer(timer)) - chan_put(chan); + if (timer_pending(timer) && del_timer(timer)) + chan_put(chan); } static void l2cap_state_change(struct l2cap_chan *chan, int state) -- cgit v1.1 From 942ecc9c4643db5ce071562e0a23f99464d6b461 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 29 Jun 2011 14:35:21 -0700 Subject: Bluetooth: ERTM timeouts need to be converted to jiffies ERTM timeouts are defined in milliseconds, but need to be converted to jiffies when passed to mod_timer(). Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ab2bfcf..fb52381 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -225,7 +225,7 @@ static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, l { BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); - if (!mod_timer(timer, jiffies + timeout)) + if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout))) chan_hold(chan); } -- cgit v1.1 From e175072f377047e28e399c5c661e39e69722f35b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 29 Jun 2011 18:18:29 -0700 Subject: Bluetooth: Rename function bt_err to bt_to_errno Make it easier to use more normal logging styles later. Signed-off-by: Joe Perches Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 2 +- net/bluetooth/l2cap_core.c | 4 ++-- net/bluetooth/lib.c | 4 ++-- net/bluetooth/sco.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0029e17..cd59b84 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -148,7 +148,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, switch (hdev->req_status) { case HCI_REQ_DONE: - err = -bt_err(hdev->req_result); + err = -bt_to_errno(hdev->req_result); break; case HCI_REQ_CANCELED: diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fb52381..becf2ad 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4107,7 +4107,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) if (conn) l2cap_conn_ready(conn); } else - l2cap_conn_del(hcon, bt_err(status)); + l2cap_conn_del(hcon, bt_to_errno(status)); return 0; } @@ -4131,7 +4131,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK)) return -EINVAL; - l2cap_conn_del(hcon, bt_err(reason)); + l2cap_conn_del(hcon, bt_to_errno(reason)); return 0; } diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index b826d1b..4e7cf8b 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -59,7 +59,7 @@ char *batostr(bdaddr_t *ba) EXPORT_SYMBOL(batostr); /* Bluetooth error codes to Unix errno mapping */ -int bt_err(__u16 code) +int bt_to_errno(__u16 code) { switch (code) { case 0: @@ -149,4 +149,4 @@ int bt_err(__u16 code) return ENOSYS; } } -EXPORT_SYMBOL(bt_err); +EXPORT_SYMBOL(bt_to_errno); diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index cb4fb78..4c3621b 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -932,7 +932,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status) if (conn) sco_conn_ready(conn); } else - sco_conn_del(hcon, bt_err(status)); + sco_conn_del(hcon, bt_to_errno(status)); return 0; } @@ -944,7 +944,7 @@ static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK) return -EINVAL; - sco_conn_del(hcon, bt_err(reason)); + sco_conn_del(hcon, bt_to_errno(reason)); return 0; } -- cgit v1.1 From e1447d8d8da5ceea60dca027e78274e6ea9b660e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 30 Jun 2011 19:15:37 -0300 Subject: Bluetooth: Add bt_printk Add a local logging function to emit bluetooth specific messages. Using vsprintf extension %pV saves code/text space. Convert the current BT_INFO and BT_ERR macros to use bt_printk. Remove __func__ from BT_ERR macro (and the uses). Prefix "Bluetooth: " to BT_ERR Remove __func__ from BT_DBG as function can be prefixed when using dynamic_debug. With allyesconfig: text data bss dec hex filename 129956 8632 36096 174684 2aa5c drivers/bluetooth/built-in.o.new2 134402 8632 36064 179098 2bb9a drivers/bluetooth/built-in.o.old 14778 1012 3408 19198 4afe net/bluetooth/bnep/built-in.o.new2 15067 1012 3408 19487 4c1f net/bluetooth/bnep/built-in.o.old 346595 19163 86080 451838 6e4fe net/bluetooth/built-in.o.new2 353751 19163 86064 458978 700e2 net/bluetooth/built-in.o.old 18483 1172 4264 23919 5d6f net/bluetooth/cmtp/built-in.o.new2 18927 1172 4264 24363 5f2b net/bluetooth/cmtp/built-in.o.old 19237 1172 5152 25561 63d9 net/bluetooth/hidp/built-in.o.new2 19581 1172 5152 25905 6531 net/bluetooth/hidp/built-in.o.old 59461 3884 14464 77809 12ff1 net/bluetooth/rfcomm/built-in.o.new2 61206 3884 14464 79554 136c2 net/bluetooth/rfcomm/built-in.o.old with x86 defconfig (and just bluetooth): $ size net/bluetooth/built-in.o.defconfig.* text data bss dec hex filename 66358 933 100 67391 1073f net/bluetooth/built-in.o.defconfig.new 66643 933 100 67676 1085c net/bluetooth/built-in.o.defconfig.old Signed-off-by: Joe Perches Signed-off-by: Gustavo F. Padovan --- net/bluetooth/lib.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'net') diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 4e7cf8b..86a6bed 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -150,3 +150,22 @@ int bt_to_errno(__u16 code) } } EXPORT_SYMBOL(bt_to_errno); + +int bt_printk(const char *level, const char *format, ...) +{ + struct va_format vaf; + va_list args; + int r; + + va_start(args, format); + + vaf.fmt = format; + vaf.va = &args; + + r = printk("%sBluetooth: %pV\n", level, &vaf); + + va_end(args); + + return r; +} +EXPORT_SYMBOL(bt_printk); -- cgit v1.1 From 2461daacb3e2ecca5edea2fa637a1b0922e86515 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 17 Jun 2011 12:57:25 -0300 Subject: Bluetooth: Fix bad locking balance Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index becf2ad..27b2cd1 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -741,9 +741,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) &chan->conf_state)) { /* l2cap_chan_close() calls list_del(chan) * so release the lock */ - read_unlock_bh(&conn->chan_lock); + read_unlock(&conn->chan_lock); l2cap_chan_close(chan, ECONNRESET); - read_lock_bh(&conn->chan_lock); + read_lock(&conn->chan_lock); bh_unlock_sock(sk); continue; } -- cgit v1.1 From 02f1b641060486df8eecd66b060ae6551f398593 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 29 Jun 2011 14:35:19 -0700 Subject: Bluetooth: Check earlier for L2CAP ERTM frames to drop Even when the received tx_seq is expected, the frame still needs to be dropped if the TX window is exceeded or the receiver is in the local busy state. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 27b2cd1..bd5d992 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3522,9 +3522,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont chan->expected_ack_seq = req_seq; l2cap_drop_acked_frames(chan); - if (tx_seq == chan->expected_tx_seq) - goto expected; - tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; if (tx_seq_offset < 0) tx_seq_offset += 64; @@ -3538,6 +3535,9 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) goto drop; + if (tx_seq == chan->expected_tx_seq) + goto expected; + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { struct srej_list *first; -- cgit v1.1 From 971e3a4bbcbf7378315b85150853d86be59cffe0 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jun 2011 19:20:52 -0300 Subject: Bluetooth: Add extfeatures to struct hci_dev This new field holds the extended LMP features value. Some LE mechanism such as discovery procedure needs to read the extended LMP features to work properly. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ac2c5e8..93d528c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -542,6 +542,14 @@ static void hci_setup(struct hci_dev *hdev) if (hdev->features[7] & LMP_INQ_TX_PWR) hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); + + if (hdev->features[7] & LMP_EXTFEATURES) { + struct hci_cp_read_local_ext_features cp; + + cp.page = 0x01; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, + sizeof(cp), &cp); + } } static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) @@ -658,6 +666,21 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb hdev->features[6], hdev->features[7]); } +static void hci_cc_read_local_ext_features(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_ext_features *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + memcpy(hdev->extfeatures, rp->features, 8); + + hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); +} + static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_buffer_size *rp = (void *) skb->data; @@ -1826,6 +1849,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_read_local_features(hdev, skb); break; + case HCI_OP_READ_LOCAL_EXT_FEATURES: + hci_cc_read_local_ext_features(hdev, skb); + break; + case HCI_OP_READ_BUFFER_SIZE: hci_cc_read_buffer_size(hdev, skb); break; -- cgit v1.1 From f9b49306dc0b9f514ffb275ae853c50d7ccd6856 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jun 2011 19:20:53 -0300 Subject: Bluetooth: Write LE Host Supported command This patch adds a handler to Write LE Host Supported command complete events. Once this commands has completed successfully, we should read the extended LMP features and update the extfeatures field in hci_dev. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 93d528c..4ed59a8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -915,6 +915,21 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } +static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cp_read_local_ext_features cp; + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + if (status) + return; + + cp.page = 0x01; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1921,6 +1936,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_le_ltk_neg_reply(hdev, skb); break; + case HCI_OP_WRITE_LE_HOST_SUPPORTED: + hci_cc_write_le_host_supported(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; -- cgit v1.1 From e6100a2541987b84af37e4c4247d989644a3aa69 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jun 2011 19:20:54 -0300 Subject: Bluetooth: Add enable_le module parameter This patch adds a new module parameter to enable/disable host LE support. By default host LE support is disabled. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 4ed59a8..298cd9b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -45,6 +45,8 @@ #include #include +static int enable_le; + /* Handle HCI Event packets */ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) @@ -525,6 +527,20 @@ static void hci_setup_event_mask(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); } +static void hci_set_le_support(struct hci_dev *hdev) +{ + struct hci_cp_write_le_host_supported cp; + + memset(&cp, 0, sizeof(cp)); + + if (enable_le) { + cp.le = 1; + cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + } + + hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); +} + static void hci_setup(struct hci_dev *hdev) { hci_setup_event_mask(hdev); @@ -550,6 +566,9 @@ static void hci_setup(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp); } + + if (hdev->features[4] & LMP_LE) + hci_set_le_support(hdev); } static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) @@ -3068,3 +3087,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) hci_send_to_sock(hdev, skb, NULL); kfree_skb(skb); } + +module_param(enable_le, bool, 0444); +MODULE_PARM_DESC(enable_le, "Enable LE support"); -- cgit v1.1 From eead27da60df80a112d1ac3ea482226e9794c26b Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jun 2011 19:20:55 -0300 Subject: Bluetooth: Add lmp_host_le_capable() macro Since we have the extended LMP features properly implemented, we should check the LMP_HOST_LE bit to know if the host supports LE. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cd59b84..886cc44 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -542,7 +542,7 @@ int hci_dev_open(__u16 dev) ret = __hci_request(hdev, hci_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); - if (lmp_le_capable(hdev)) + if (lmp_host_le_capable(hdev)) ret = __hci_request(hdev, hci_le_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); -- cgit v1.1 From 2e65c9d2c5206eb24439f2dd2daa2f6702df358e Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jun 2011 19:20:56 -0300 Subject: Bluetooth: Remove enable_smp parameter The enable_smp parameter is no longer needed. It can be replaced by checking lmp_host_le_capable. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 15 +-------------- net/bluetooth/smp.c | 9 +++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 886cc44..2709335 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -60,8 +60,6 @@ static void hci_tx_task(unsigned long arg); static DEFINE_RWLOCK(hci_task_lock); -static int enable_smp; - /* HCI device list */ LIST_HEAD(hci_dev_list); DEFINE_RWLOCK(hci_dev_list_lock); @@ -1368,14 +1366,6 @@ int hci_add_adv_entry(struct hci_dev *hdev, return 0; } -static struct crypto_blkcipher *alloc_cypher(void) -{ - if (enable_smp) - return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - - return ERR_PTR(-ENOTSUPP); -} - /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1460,7 +1450,7 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; - hdev->tfm = alloc_cypher(); + 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)); @@ -2352,6 +2342,3 @@ static void hci_cmd_task(unsigned long arg) } } } - -module_param(enable_smp, bool, 0644); -MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)"); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a36f870..ba55bd4 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -434,6 +434,9 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) 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; @@ -477,6 +480,12 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) __u8 reason; int err = 0; + if (!lmp_host_le_capable(conn->hcon->hdev)) { + err = -ENOTSUPP; + reason = SMP_PAIRING_NOTSUPP; + goto done; + } + if (IS_ERR(conn->hcon->hdev->tfm)) { err = PTR_ERR(conn->hcon->hdev->tfm); reason = SMP_PAIRING_NOTSUPP; -- cgit v1.1 From 8aded7110a5625bc00aef05e94dd4b1a9cf3605f Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 7 Jul 2011 10:30:35 -0300 Subject: Bluetooth: Fix potential deadlock in hci_core Since hdev->lock may be acquired by threads runnning in interrupt context, all threads running in process context should disable local bottom halve before locking hdev->lock. This can be done by using hci_dev_lock_bh macro. This way, we avoid potencial deadlocks like this one reported by CONFIG_PROVE_LOCKING=y. [ 304.788780] ================================= [ 304.789686] [ INFO: inconsistent lock state ] [ 304.789686] 2.6.39+ #1 [ 304.789686] --------------------------------- [ 304.789686] inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage. [ 304.789686] ksoftirqd/0/3 [HC0[0]:SC1[1]:HE1:SE0] takes: [ 304.789686] (&(&hdev->lock)->rlock){+.?...}, at: [] hci_conn_check_pending+0x38/0x76 [bluetooth] [ 304.789686] {SOFTIRQ-ON-W} state was registered at: [ 304.789686] [] __lock_acquire+0x347/0xd52 [ 304.789686] [] lock_acquire+0x8a/0xa7 [ 304.789686] [] _raw_spin_lock+0x2c/0x3b [ 304.789686] [] hci_blacklist_del+0x1f/0x8a [bluetooth] [ 304.789686] [] hci_sock_ioctl+0x2d9/0x314 [bluetooth] [ 304.789686] [] sock_ioctl+0x1f2/0x214 [ 304.789686] [] do_vfs_ioctl+0x46c/0x4ad [ 304.789686] [] sys_ioctl+0x42/0x65 [ 304.789686] [] system_call_fastpath+0x16/0x1b [ 304.789686] irq event stamp: 9768 [ 304.789686] hardirqs last enabled at (9768): [] restore_args+0x0/0x30 [ 304.789686] hardirqs last disabled at (9767): [] save_args+0x6a/0x70 [ 304.789686] softirqs last enabled at (9726): [] __do_softirq+0x129/0x13f [ 304.789686] softirqs last disabled at (9739): [] run_ksoftirqd+0x82/0x133 [ 304.789686] [ 304.789686] other info that might help us debug this: [ 304.789686] Possible unsafe locking scenario: [ 304.789686] [ 304.789686] CPU0 [ 304.789686] ---- [ 304.789686] lock(&(&hdev->lock)->rlock); [ 304.789686] [ 304.789686] lock(&(&hdev->lock)->rlock); [ 304.789686] [ 304.789686] *** DEADLOCK *** [ 304.789686] [ 304.789686] 1 lock held by ksoftirqd/0/3: [ 304.789686] #0: (hci_task_lock){++.-..}, at: [] hci_rx_task+0x49/0x2f3 [bluetooth] [ 304.789686] [ 304.789686] stack backtrace: [ 304.789686] Pid: 3, comm: ksoftirqd/0 Not tainted 2.6.39+ #1 [ 304.789686] Call Trace: [ 304.789686] [] print_usage_bug+0x1e7/0x1f8 [ 304.789686] [] ? save_stack_trace+0x27/0x44 [ 304.789686] [] ? print_irq_inversion_bug.part.26+0x19a/0x19a [ 304.789686] [] mark_lock+0x106/0x258 [ 304.789686] [] ? retint_restore_args+0x13/0x13 [ 304.789686] [] __lock_acquire+0x2d3/0xd52 [ 304.789686] [] ? vprintk+0x3ab/0x3d7 [ 304.789686] [] ? printk+0x3c/0x3e [ 304.789686] [] lock_acquire+0x8a/0xa7 [ 304.789686] [] ? hci_conn_check_pending+0x38/0x76 [bluetooth] [ 304.789686] [] ? __dynamic_pr_debug+0x10c/0x11a [ 304.789686] [] _raw_spin_lock+0x2c/0x3b [ 304.789686] [] ? hci_conn_check_pending+0x38/0x76 [bluetooth] [ 304.789686] [] hci_conn_check_pending+0x38/0x76 [bluetooth] [ 304.789686] [] hci_event_packet+0x38e/0x3e12 [bluetooth] [ 304.789686] [] ? lock_release+0x16c/0x179 [ 304.789686] [] ? _raw_read_unlock+0x23/0x27 [ 304.789686] [] ? hci_send_to_sock+0x179/0x188 [bluetooth] [ 304.789686] [] hci_rx_task+0xc8/0x2f3 [bluetooth] [ 304.789686] [] tasklet_action+0x87/0xe6 [ 304.789686] [] __do_softirq+0x9f/0x13f [ 304.789686] [] run_ksoftirqd+0x82/0x133 [ 304.789686] [] ? __do_softirq+0x13f/0x13f [ 304.789686] [] kthread+0x7f/0x87 [ 304.789686] [] kernel_thread_helper+0x4/0x10 [ 304.789686] [] ? retint_restore_args+0x13/0x13 [ 304.789686] [] ? __init_kthread_worker+0x53/0x53 [ 304.789686] [] ? gs_change+0x13/0x13 Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2709335..7ba1ca1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1244,7 +1244,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (hci_blacklist_lookup(hdev, bdaddr)) { err = -EEXIST; @@ -1264,7 +1264,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) err = 0; err: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); return err; } @@ -1273,7 +1273,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) struct bdaddr_list *entry; int err = 0; - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (bacmp(bdaddr, BDADDR_ANY) == 0) { hci_blacklist_clear(hdev); @@ -1290,7 +1290,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) kfree(entry); done: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); return err; } -- cgit v1.1 From 8c156c322f8a300afe59259bd554db166cf88203 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 7 Jul 2011 10:30:36 -0300 Subject: Bluetooth: Fix potential deadlock in mgmt All threads running in process context should disable local bottom halve before locking hdev->lock. This patch fix the following message generated when Bluetooh module is loaded with enable_mgmt=y (CONFIG_PROVE_LOCKING enabled). [ 107.880781] ================================= [ 107.881631] [ INFO: inconsistent lock state ] [ 107.881631] 2.6.39+ #1 [ 107.881631] --------------------------------- [ 107.881631] inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage. [ 107.881631] rcuc0/7 [HC0[0]:SC1[3]:HE1:SE0] takes: [ 107.881631] (&(&hdev->lock)->rlock){+.?...}, at: [] mgmt_set_local_name_complete+0x84/0x10b [bluetooth] [ 107.881631] {SOFTIRQ-ON-W} state was registered at: [ 107.881631] [] __lock_acquire+0x347/0xd52 [ 107.881631] [] lock_acquire+0x8a/0xa7 [ 107.881631] [] _raw_spin_lock+0x2c/0x3b [ 107.881631] [] mgmt_control+0xd4d/0x175b [bluetooth] [ 107.881631] [] hci_sock_sendmsg+0x97/0x293 [bluetooth] [ 107.881631] [] sock_aio_write+0x126/0x13a [ 107.881631] [] do_sync_write+0xba/0xfa [ 107.881631] [] vfs_write+0xaa/0xca [ 107.881631] [] sys_write+0x45/0x69 [ 107.881631] [] system_call_fastpath+0x16/0x1b [ 107.881631] irq event stamp: 2100876 [ 107.881631] hardirqs last enabled at (2100876): [] restore_args+0x0/0x30 [ 107.881631] hardirqs last disabled at (2100875): [] save_args+0x6a/0x70 [ 107.881631] softirqs last enabled at (2100862): [] rcu_cpu_kthread+0x2b5/0x2e2 [ 107.881631] softirqs last disabled at (2100863): [] call_softirq+0x1c/0x26 [ 107.881631] [ 107.881631] other info that might help us debug this: [ 107.881631] Possible unsafe locking scenario: [ 107.881631] [ 107.881631] CPU0 [ 107.881631] ---- [ 107.881631] lock(&(&hdev->lock)->rlock); [ 107.881631] [ 107.881631] lock(&(&hdev->lock)->rlock); [ 107.881631] [ 107.881631] *** DEADLOCK *** [ 107.881631] [ 107.881631] 1 lock held by rcuc0/7: [ 107.881631] #0: (hci_task_lock){++.-..}, at: [] hci_rx_task+0x49/0x2f3 [bluetooth] [ 107.881631] [ 107.881631] stack backtrace: [ 107.881631] Pid: 7, comm: rcuc0 Not tainted 2.6.39+ #1 [ 107.881631] Call Trace: [ 107.881631] [] print_usage_bug+0x1e7/0x1f8 [ 107.881631] [] ? save_stack_trace+0x27/0x44 [ 107.881631] [] ? print_irq_inversion_bug.part.26+0x19a/0x19a [ 107.881631] [] mark_lock+0x106/0x258 [ 107.881631] [] __lock_acquire+0x2d3/0xd52 [ 107.881631] [] ? vprintk+0x3ab/0x3d7 [ 107.881631] [] lock_acquire+0x8a/0xa7 [ 107.881631] [] ? mgmt_set_local_name_complete+0x84/0x10b [bluetooth] [ 107.881631] [] ? lock_release+0x16c/0x179 [ 107.881631] [] _raw_spin_lock_bh+0x31/0x40 [ 107.881631] [] ? mgmt_set_local_name_complete+0x84/0x10b [bluetooth] [ 107.881631] [] mgmt_set_local_name_complete+0x84/0x10b [bluetooth] [ 107.881631] [] hci_event_packet+0x122b/0x3e12 [bluetooth] [ 107.881631] [] ? mark_held_locks+0x4b/0x6d [ 107.881631] [] ? _raw_spin_unlock_irqrestore+0x40/0x4d [ 107.881631] [] ? trace_hardirqs_on_caller+0x13f/0x172 [ 107.881631] [] ? _raw_spin_unlock_irqrestore+0x48/0x4d [ 107.881631] [] hci_rx_task+0xc8/0x2f3 [bluetooth] [ 107.881631] [] ? __local_bh_enable+0x90/0xa4 [ 107.881631] [] tasklet_action+0x87/0xe6 [ 107.881631] [] __do_softirq+0x9f/0x13f [ 107.881631] [] call_softirq+0x1c/0x26 [ 107.881631] [] ? do_softirq+0x46/0x9a [ 107.881631] [] ? rcu_cpu_kthread+0x2b5/0x2e2 [ 107.881631] [] _local_bh_enable_ip+0xac/0xc9 [ 107.881631] [] local_bh_enable+0xd/0xf [ 107.881631] [] rcu_cpu_kthread+0x2b5/0x2e2 [ 107.881631] [] ? __init_waitqueue_head+0x46/0x46 [ 107.881631] [] ? rcu_yield.constprop.42+0x98/0x98 [ 107.881631] [] kthread+0x7f/0x87 [ 107.881631] [] kernel_thread_helper+0x4/0x10 [ 107.881631] [] ? retint_restore_args+0x13/0x13 [ 107.881631] [] ? __init_kthread_worker+0x53/0x53 [ 107.881631] [] ? gs_change+0x13/0x13 Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 88 ++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 64c0418..4fd11e5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -179,7 +179,7 @@ static int read_controller_info(struct sock *sk, u16 index) hci_del_off_timer(hdev); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); set_bit(HCI_MGMT, &hdev->flags); @@ -208,7 +208,7 @@ static int read_controller_info(struct sock *sk, u16 index) memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); @@ -316,7 +316,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); up = test_bit(HCI_UP, &hdev->flags); if ((cp->val && up) || (!cp->val && !up)) { @@ -343,7 +343,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) err = 0; failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; } @@ -368,7 +368,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); @@ -403,7 +403,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -429,7 +429,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN); @@ -463,7 +463,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -522,7 +522,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (cp->val) set_bit(HCI_PAIRABLE, &hdev->flags); @@ -538,7 +538,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -739,7 +739,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); if (!uuid) { @@ -763,7 +763,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -788,7 +788,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { err = hci_uuids_clear(hdev); @@ -823,7 +823,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); unlock: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -847,7 +847,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); hdev->major_class = cp->major; hdev->minor_class = cp->minor; @@ -857,7 +857,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, if (err == 0) err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -879,7 +879,7 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); BT_DBG("hci%u enable %d", index, cp->enable); @@ -897,7 +897,7 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL, 0); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -931,7 +931,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys, key_count); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); hci_link_keys_clear(hdev); @@ -949,7 +949,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) key->pin_len); } - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return 0; @@ -971,7 +971,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); err = hci_remove_link_key(hdev, &cp->bdaddr); if (err < 0) { @@ -994,7 +994,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) } unlock: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1020,7 +1020,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN); @@ -1055,7 +1055,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1076,7 +1076,7 @@ static int get_connections(struct sock *sk, u16 index) if (!hdev) return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); count = 0; list_for_each(p, &hdev->conn_hash.list) { @@ -1103,7 +1103,7 @@ static int get_connections(struct sock *sk, u16 index) unlock: kfree(rp); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; } @@ -1149,7 +1149,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); @@ -1190,7 +1190,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1216,7 +1216,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, @@ -1227,7 +1227,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, err = send_pin_code_neg_reply(sk, index, hdev, cp); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1250,14 +1250,14 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); hdev->io_capability = cp->io_capability; BT_DBG("%s IO capability set to 0x%02x", hdev->name, hdev->io_capability); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0); @@ -1343,7 +1343,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (cp->io_cap == 0x03) { sec_level = BT_SECURITY_MEDIUM; @@ -1385,7 +1385,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) err = 0; unlock: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1417,7 +1417,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, mgmt_op, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, mgmt_op, ENETDOWN); @@ -1435,7 +1435,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1459,7 +1459,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len); if (!cmd) { @@ -1474,7 +1474,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1493,7 +1493,7 @@ static int read_local_oob_data(struct sock *sk, u16 index) return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, @@ -1523,7 +1523,7 @@ static int read_local_oob_data(struct sock *sk, u16 index) mgmt_pending_remove(cmd); unlock: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1547,7 +1547,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash, cp->randomizer); @@ -1557,7 +1557,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data, err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL, 0); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1581,7 +1581,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); err = hci_remove_remote_oob_data(hdev, &cp->bdaddr); if (err < 0) @@ -1591,7 +1591,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, NULL, 0); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; -- cgit v1.1 From 26f880d221302b5d061185d8a6795bb532693bf3 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 7 Jul 2011 09:39:01 -0700 Subject: Bluetooth: Move code for ERTM local busy state to separate functions The local busy state is entered and exited based on buffer status in the socket layer (or other upper layer). This change is in preparation for general buffer status reports from the socket layer, which will then be used to change the local busy status. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 62 ++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bd5d992..f7ada4a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3227,22 +3227,26 @@ disconnect: return 0; } -static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) +static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) { - struct sk_buff *skb; u16 control; - int err; - while ((skb = skb_dequeue(&chan->busy_q))) { - control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(chan, skb, control); - if (err < 0) { - skb_queue_head(&chan->busy_q, skb); - return -EBUSY; - } + BT_DBG("chan %p, Enter local busy", chan); - chan->buffer_seq = (chan->buffer_seq + 1) % 64; - } + set_bit(CONN_LOCAL_BUSY, &chan->conn_state); + + control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= L2CAP_SUPER_RCV_NOT_READY; + l2cap_send_sframe(chan, control); + + set_bit(CONN_RNR_SENT, &chan->conn_state); + + __clear_ack_timer(chan); +} + +static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) +{ + u16 control; if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; @@ -3262,6 +3266,26 @@ done: clear_bit(CONN_RNR_SENT, &chan->conn_state); BT_DBG("chan %p, Exit local busy", chan); +} + +static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) +{ + struct sk_buff *skb; + u16 control; + int err; + + while ((skb = skb_dequeue(&chan->busy_q))) { + control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + err = l2cap_ertm_reassembly_sdu(chan, skb, control); + if (err < 0) { + skb_queue_head(&chan->busy_q, skb); + return -EBUSY; + } + + chan->buffer_seq = (chan->buffer_seq + 1) % 64; + } + + l2cap_ertm_exit_local_busy(chan); return 0; } @@ -3315,7 +3339,7 @@ static void l2cap_busy_work(struct work_struct *work) static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) { - int sctrl, err; + int err; if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; @@ -3331,21 +3355,11 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c return err; } - /* Busy Condition */ - BT_DBG("chan %p, Enter local busy", chan); + l2cap_ertm_enter_local_busy(chan); - set_bit(CONN_LOCAL_BUSY, &chan->conn_state); bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(&chan->busy_q, skb); - sctrl = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - sctrl |= L2CAP_SUPER_RCV_NOT_READY; - l2cap_send_sframe(chan, sctrl); - - set_bit(CONN_RNR_SENT, &chan->conn_state); - - __clear_ack_timer(chan); - queue_work(_busy_wq, &chan->busy_work); return err; -- cgit v1.1 From e328140fdacbba43292a59a22fb55d9185288318 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 7 Jul 2011 09:39:02 -0700 Subject: Bluetooth: Use event-driven approach for handling ERTM receive buffer This change moves most L2CAP ERTM receive buffer handling out of the L2CAP core and in to the socket code. It's up to the higher layer (the socket code, in this case) to tell the core when its buffer is full or has space available. The recv op should always accept incoming ERTM data or else the connection will go down. Within the socket layer, an skb that does not fit in the socket receive buffer will be temporarily stored. When the socket is read from, that skb will be placed in the receive buffer if possible. Once adequate buffer space becomes available, the L2CAP core is informed and the ERTM local busy state is cleared. Receive buffer management for non-ERTM modes is unchanged. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 41 ++++++++++++++++++----------- net/bluetooth/l2cap_sock.c | 65 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f7ada4a..ea9c7d0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3350,21 +3350,21 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c } err = l2cap_ertm_reassembly_sdu(chan, skb, control); - if (err >= 0) { - chan->buffer_seq = (chan->buffer_seq + 1) % 64; - return err; - } - - l2cap_ertm_enter_local_busy(chan); - - bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; - __skb_queue_tail(&chan->busy_q, skb); - - queue_work(_busy_wq, &chan->busy_work); + chan->buffer_seq = (chan->buffer_seq + 1) % 64; return err; } +void l2cap_chan_busy(struct l2cap_chan *chan, int busy) +{ + if (chan->mode == L2CAP_MODE_ERTM) { + if (busy) + l2cap_ertm_enter_local_busy(chan); + else + l2cap_ertm_exit_local_busy(chan); + } +} + static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) { struct sk_buff *_skb; @@ -3463,13 +3463,22 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) struct sk_buff *skb; u16 control; - while ((skb = skb_peek(&chan->srej_q))) { + while ((skb = skb_peek(&chan->srej_q)) && + !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { + int err; + if (bt_cb(skb)->tx_seq != tx_seq) break; skb = skb_dequeue(&chan->srej_q); control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - l2cap_ertm_reassembly_sdu(chan, skb, control); + err = l2cap_ertm_reassembly_sdu(chan, skb, control); + + if (err < 0) { + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + break; + } + chan->buffer_seq_srej = (chan->buffer_seq_srej + 1) % 64; tx_seq = (tx_seq + 1) % 64; @@ -3625,8 +3634,10 @@ expected: } err = l2cap_push_rx_skb(chan, skb, rx_control); - if (err < 0) - return 0; + if (err < 0) { + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + return err; + } if (rx_control & L2CAP_CTRL_FINAL) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 39082d4..146b614 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -711,13 +711,15 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; + struct l2cap_pinfo *pi = l2cap_pi(sk); + int err; lock_sock(sk); if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONFIG; - __l2cap_connect_rsp_defer(l2cap_pi(sk)->chan); + __l2cap_connect_rsp_defer(pi->chan); release_sock(sk); return 0; } @@ -725,9 +727,37 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms release_sock(sk); if (sock->type == SOCK_STREAM) - return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + else + err = bt_sock_recvmsg(iocb, sock, msg, len, flags); + + if (pi->chan->mode != L2CAP_MODE_ERTM) + return err; + + /* Attempt to put pending rx data in the socket buffer */ + + lock_sock(sk); + + if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) + goto done; + + if (pi->rx_busy_skb) { + if (!sock_queue_rcv_skb(sk, pi->rx_busy_skb)) + pi->rx_busy_skb = NULL; + else + goto done; + } - return bt_sock_recvmsg(iocb, sock, msg, len, flags); + /* Restore data flow when half of the receive buffer is + * available. This avoids resending large numbers of + * frames. + */ + if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) + l2cap_chan_busy(pi->chan, 0); + +done: + release_sock(sk); + return err; } /* Kill socket (only if zapped and orphan) @@ -811,9 +841,31 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) { + int err; struct sock *sk = data; + struct l2cap_pinfo *pi = l2cap_pi(sk); - return sock_queue_rcv_skb(sk, skb); + if (pi->rx_busy_skb) + return -ENOMEM; + + err = sock_queue_rcv_skb(sk, skb); + + /* For ERTM, handle one skb that doesn't fit into the recv + * buffer. This is important to do because the data frames + * have already been acked, so the skb cannot be discarded. + * + * Notify the l2cap core that the buffer is full, so the + * LOCAL_BUSY state is entered and no more frames are + * acked and reassembled until there is buffer space + * available. + */ + if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) { + pi->rx_busy_skb = skb; + l2cap_chan_busy(pi->chan, 1); + err = 0; + } + + return err; } static void l2cap_sock_close_cb(void *data) @@ -842,6 +894,11 @@ static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); + if (l2cap_pi(sk)->rx_busy_skb) { + kfree_skb(l2cap_pi(sk)->rx_busy_skb); + l2cap_pi(sk)->rx_busy_skb = NULL; + } + skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); } -- cgit v1.1 From fadd192e81b0a8d8086531b8c11bd88b311b68c2 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 7 Jul 2011 09:39:03 -0700 Subject: Bluetooth: Remove L2CAP busy queue The ERTM receive buffer is now handled in a way that does not require the busy queue and the associated polling code. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 125 +++------------------------------------------ 1 file changed, 8 insertions(+), 117 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ea9c7d0..2c5d335 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -61,13 +61,9 @@ int disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; -static struct workqueue_struct *_busy_wq; - static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); -static void l2cap_busy_work(struct work_struct *work); - static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, @@ -395,7 +391,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) __clear_ack_timer(chan); skb_queue_purge(&chan->srej_q); - skb_queue_purge(&chan->busy_q); list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { list_del(&l->list); @@ -1873,11 +1868,9 @@ static inline void l2cap_ertm_init(struct l2cap_chan *chan) setup_timer(&chan->ack_timer, l2cap_ack_timeout, (unsigned long) chan); skb_queue_head_init(&chan->srej_q); - skb_queue_head_init(&chan->busy_q); INIT_LIST_HEAD(&chan->srej_l); - INIT_WORK(&chan->busy_work, l2cap_busy_work); sk->sk_backlog_rcv = l2cap_ertm_data_rcv; } @@ -3182,32 +3175,27 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk if (!chan->sdu) goto disconnect; - if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) { - chan->partial_sdu_len += skb->len; + chan->partial_sdu_len += skb->len; - if (chan->partial_sdu_len > chan->imtu) - goto drop; + if (chan->partial_sdu_len > chan->imtu) + goto drop; - if (chan->partial_sdu_len != chan->sdu_len) - goto drop; + if (chan->partial_sdu_len != chan->sdu_len) + goto drop; - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - } + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); _skb = skb_clone(chan->sdu, GFP_ATOMIC); if (!_skb) { - set_bit(CONN_SAR_RETRY, &chan->conn_state); return -ENOMEM; } err = chan->ops->recv(chan->data, _skb); if (err < 0) { kfree_skb(_skb); - set_bit(CONN_SAR_RETRY, &chan->conn_state); return err; } - clear_bit(CONN_SAR_RETRY, &chan->conn_state); clear_bit(CONN_SAR_SDU, &chan->conn_state); kfree_skb(chan->sdu); @@ -3268,93 +3256,6 @@ done: BT_DBG("chan %p, Exit local busy", chan); } -static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) -{ - struct sk_buff *skb; - u16 control; - int err; - - while ((skb = skb_dequeue(&chan->busy_q))) { - control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(chan, skb, control); - if (err < 0) { - skb_queue_head(&chan->busy_q, skb); - return -EBUSY; - } - - chan->buffer_seq = (chan->buffer_seq + 1) % 64; - } - - l2cap_ertm_exit_local_busy(chan); - - return 0; -} - -static void l2cap_busy_work(struct work_struct *work) -{ - DECLARE_WAITQUEUE(wait, current); - struct l2cap_chan *chan = - container_of(work, struct l2cap_chan, busy_work); - struct sock *sk = chan->sk; - int n_tries = 0, timeo = HZ/5, err; - struct sk_buff *skb; - - lock_sock(sk); - - add_wait_queue(sk_sleep(sk), &wait); - while ((skb = skb_peek(&chan->busy_q))) { - set_current_state(TASK_INTERRUPTIBLE); - - if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { - err = -EBUSY; - l2cap_send_disconn_req(chan->conn, chan, EBUSY); - break; - } - - if (!timeo) - timeo = HZ/5; - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - - err = sock_error(sk); - if (err) - break; - - if (l2cap_try_push_rx_skb(chan) == 0) - break; - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - release_sock(sk); -} - -static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) -{ - int err; - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; - __skb_queue_tail(&chan->busy_q, skb); - return l2cap_try_push_rx_skb(chan); - - - } - - err = l2cap_ertm_reassembly_sdu(chan, skb, control); - chan->buffer_seq = (chan->buffer_seq + 1) % 64; - - return err; -} - void l2cap_chan_busy(struct l2cap_chan *chan, int busy) { if (chan->mode == L2CAP_MODE_ERTM) { @@ -3612,7 +3513,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont chan->buffer_seq_srej = chan->buffer_seq; __skb_queue_head_init(&chan->srej_q); - __skb_queue_head_init(&chan->busy_q); l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); set_bit(CONN_SEND_PBIT, &chan->conn_state); @@ -3633,7 +3533,8 @@ expected: return 0; } - err = l2cap_push_rx_skb(chan, skb, rx_control); + err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control); + chan->buffer_seq = (chan->buffer_seq + 1) % 64; if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return err; @@ -4439,12 +4340,6 @@ int __init l2cap_init(void) if (err < 0) return err; - _busy_wq = create_singlethread_workqueue("l2cap"); - if (!_busy_wq) { - err = -ENOMEM; - goto error; - } - err = hci_register_proto(&l2cap_hci_proto); if (err < 0) { BT_ERR("L2CAP protocol registration failed"); @@ -4462,7 +4357,6 @@ int __init l2cap_init(void) return 0; error: - destroy_workqueue(_busy_wq); l2cap_cleanup_sockets(); return err; } @@ -4471,9 +4365,6 @@ void l2cap_exit(void) { debugfs_remove(l2cap_debugfs); - flush_workqueue(_busy_wq); - destroy_workqueue(_busy_wq); - if (hci_unregister_proto(&l2cap_hci_proto) < 0) BT_ERR("L2CAP protocol unregistration failed"); -- cgit v1.1 From 7034b911af1aa571995b56db3ed71a25daf00373 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:34 -0300 Subject: Bluetooth: Add support for SMP phase 3 (key distribution) This adds support for generating and distributing all the keys specified in the third phase of SMP. This will make possible to re-establish secure connections, resolve private addresses and sign commands. For now, the values generated are random. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 1 + net/bluetooth/smp.c | 114 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2c5d335..ab2e244 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4103,6 +4103,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 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); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index ba55bd4..82443b9 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -202,8 +202,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn, cmd->io_capability = conn->hcon->io_capability; cmd->oob_flag = SMP_OOB_NOT_PRESENT; cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE; - cmd->init_key_dist = 0x00; - cmd->resp_key_dist = 0x00; + cmd->init_key_dist = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN; + cmd->resp_key_dist = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN; cmd->auth_req = authreq; } @@ -474,6 +474,26 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) return 0; } +static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) +{ + BT_DBG("conn %p", conn); + /* FIXME: store the ltk */ + return 0; +} + +static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing *paircmd = (void *) &conn->prsp[1]; + u8 keydist = paircmd->init_key_dist; + + BT_DBG("keydist 0x%x", keydist); + /* FIXME: store ediv and rand */ + + smp_distribute_keys(conn, 1); + + return 0; +} + int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { __u8 code = skb->data[0]; @@ -521,10 +541,20 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) break; case SMP_CMD_ENCRYPT_INFO: + reason = smp_cmd_encrypt_info(conn, skb); + break; + case SMP_CMD_MASTER_IDENT: + reason = smp_cmd_master_ident(conn, skb); + break; + case SMP_CMD_IDENT_INFO: case SMP_CMD_IDENT_ADDR_INFO: case SMP_CMD_SIGN_INFO: + /* Just ignored */ + reason = 0; + break; + default: BT_DBG("Unknown command code 0x%2.2x", code); @@ -541,3 +571,83 @@ done: kfree_skb(skb); return err; } + +int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) +{ + struct smp_cmd_pairing *req, *rsp; + __u8 *keydist; + + BT_DBG("conn %p force %d", conn, force); + + if (IS_ERR(conn->hcon->hdev->tfm)) + return PTR_ERR(conn->hcon->hdev->tfm); + + rsp = (void *) &conn->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]; + + if (conn->hcon->out) { + keydist = &rsp->init_key_dist; + *keydist &= req->init_key_dist; + } else { + keydist = &rsp->resp_key_dist; + *keydist &= req->resp_key_dist; + } + + + BT_DBG("keydist 0x%x", *keydist); + + if (*keydist & SMP_DIST_ENC_KEY) { + struct smp_cmd_encrypt_info enc; + struct smp_cmd_master_ident ident; + __le16 ediv; + + get_random_bytes(enc.ltk, sizeof(enc.ltk)); + get_random_bytes(&ediv, sizeof(ediv)); + get_random_bytes(ident.rand, sizeof(ident.rand)); + + smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); + + ident.ediv = cpu_to_le16(ediv); + + smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); + + *keydist &= ~SMP_DIST_ENC_KEY; + } + + if (*keydist & SMP_DIST_ID_KEY) { + struct smp_cmd_ident_addr_info addrinfo; + struct smp_cmd_ident_info idinfo; + + /* Send a dummy key */ + get_random_bytes(idinfo.irk, sizeof(idinfo.irk)); + + smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); + + /* Just public address */ + memset(&addrinfo, 0, sizeof(addrinfo)); + bacpy(&addrinfo.bdaddr, conn->src); + + smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), + &addrinfo); + + *keydist &= ~SMP_DIST_ID_KEY; + } + + if (*keydist & SMP_DIST_SIGN) { + struct smp_cmd_sign_info sign; + + /* Send a dummy key */ + get_random_bytes(sign.csrk, sizeof(sign.csrk)); + + smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); + + *keydist &= ~SMP_DIST_SIGN; + } + + return 0; +} -- cgit v1.1 From 75d262c2ad927751bb5f096f3a6a37d81e7784f2 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:36 -0300 Subject: Bluetooth: Add functions to manipulate the link key list for SMP As the LTK (the new type of key being handled now) has more data associated with it, we need to store this extra data and retrieve the keys based on that data. Methods for searching for a key and for adding a new LTK are introduced here. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7ba1ca1..4885914 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1057,6 +1057,42 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, return 0; } +struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) +{ + struct link_key *k; + + list_for_each_entry(k, &hdev->link_keys, list) { + struct key_master_id *id; + + if (k->type != HCI_LK_SMP_LTK) + continue; + + if (k->dlen != sizeof(*id)) + continue; + + id = (void *) &k->data; + if (id->ediv == ediv && + (memcmp(rand, id->rand, sizeof(id->rand)) == 0)) + return k; + } + + return NULL; +} +EXPORT_SYMBOL(hci_find_ltk); + +struct link_key *hci_find_link_key_type(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type) +{ + struct link_key *k; + + list_for_each_entry(k, &hdev->link_keys, list) + if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0) + return k; + + return NULL; +} +EXPORT_SYMBOL(hci_find_link_key_type); + int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { @@ -1112,6 +1148,43 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, return 0; } +int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, + __le16 ediv, u8 rand[8], u8 ltk[16]) +{ + struct link_key *key, *old_key; + struct key_master_id *id; + u8 old_key_type; + + BT_DBG("%s addr %s", hdev->name, batostr(bdaddr)); + + old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK); + if (old_key) { + key = old_key; + old_key_type = old_key->type; + } else { + key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC); + if (!key) + return -ENOMEM; + list_add(&key->list, &hdev->link_keys); + old_key_type = 0xff; + } + + key->dlen = sizeof(*id); + + bacpy(&key->bdaddr, bdaddr); + memcpy(key->val, ltk, sizeof(key->val)); + key->type = HCI_LK_SMP_LTK; + + id = (void *) &key->data; + id->ediv = ediv; + memcpy(id->rand, rand, sizeof(id->rand)); + + if (new_key) + mgmt_new_key(hdev->id, key, old_key_type); + + return 0; +} + int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *key; -- cgit v1.1 From bea710feff617e3469789dd8f930b284c83a87f5 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:37 -0300 Subject: Bluetooth: Reject an encryption request when the key isn't found Now that we have methods to finding keys by its parameters we can reject an encryption request if the key isn't found. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 298cd9b..ca5ff6e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2858,21 +2858,35 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, { struct hci_ev_le_ltk_req *ev = (void *) skb->data; struct hci_cp_le_ltk_reply cp; + struct hci_cp_le_ltk_neg_reply neg; struct hci_conn *conn; + struct link_key *ltk; BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn == NULL) + goto not_found; - memset(&cp, 0, sizeof(cp)); + ltk = hci_find_ltk(hdev, ev->ediv, ev->random); + if (ltk == NULL) + goto not_found; + + memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk)); hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); hci_dev_unlock(hdev); + + return; + +not_found: + neg.handle = ev->handle; + hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg); + hci_dev_unlock(hdev); } static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.1 From 54790f73a3ec87c181e4d6e5eb6963de3d50ebd7 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:38 -0300 Subject: Bluetooth: Fix SM pairing parameters negotiation Before implementing SM key distribution, the pairing features exchange must be better negotiated, taking into account some features of the host and connection requirements. If we are in the "not pairable" state, it makes no sense to exchange any key. This allows for simplification of the key negociation method. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 82443b9..ffbfdd9 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -197,14 +197,34 @@ static __u8 seclevel_to_authreq(__u8 level) } static void build_pairing_cmd(struct l2cap_conn *conn, - struct smp_cmd_pairing *cmd, __u8 authreq) + struct smp_cmd_pairing *req, + struct smp_cmd_pairing *rsp, + __u8 authreq) { - cmd->io_capability = conn->hcon->io_capability; - cmd->oob_flag = SMP_OOB_NOT_PRESENT; - cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE; - cmd->init_key_dist = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN; - cmd->resp_key_dist = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN; - cmd->auth_req = authreq; + u8 dist_keys; + + 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; + authreq |= SMP_AUTH_BONDING; + } + + if (rsp == NULL) { + req->io_capability = conn->hcon->io_capability; + req->oob_flag = SMP_OOB_NOT_PRESENT; + 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; + return; + } + + rsp->io_capability = conn->hcon->io_capability; + rsp->oob_flag = SMP_OOB_NOT_PRESENT; + 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; } static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) @@ -233,7 +253,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_OOB_NOT_AVAIL; /* We didn't start the pairing, so no requirements */ - build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE); + build_pairing_cmd(conn, req, &rsp, SMP_AUTH_NONE); key_size = min(req->max_key_size, rsp.max_key_size); if (check_enc_key_size(conn, key_size)) @@ -412,7 +432,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rp)); memset(&cp, 0, sizeof(cp)); - build_pairing_cmd(conn, &cp, rp->auth_req); + build_pairing_cmd(conn, &cp, NULL, rp->auth_req); conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], &cp, sizeof(cp)); @@ -454,7 +474,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (hcon->link_mode & HCI_LM_MASTER) { struct smp_cmd_pairing cp; - build_pairing_cmd(conn, &cp, authreq); + build_pairing_cmd(conn, &cp, NULL, authreq); conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], &cp, sizeof(cp)); -- cgit v1.1 From 16b908396fbf1be49d417ffdb4a8b41c8c8cb670 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:39 -0300 Subject: Bluetooth: Add support for storing the LTK Now when the LTK is received from the remote or generated it is stored, so it can later be used. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index ffbfdd9..600a70b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -496,18 +496,23 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) { - BT_DBG("conn %p", conn); - /* FIXME: store the ltk */ + struct smp_cmd_encrypt_info *rp = (void *) skb->data; + + skb_pull(skb, sizeof(*rp)); + + memcpy(conn->tk, rp->ltk, sizeof(conn->tk)); + return 0; } static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing *paircmd = (void *) &conn->prsp[1]; - u8 keydist = paircmd->init_key_dist; + struct smp_cmd_master_ident *rp = (void *) skb->data; + + skb_pull(skb, sizeof(*rp)); - BT_DBG("keydist 0x%x", keydist); - /* FIXME: store ediv and rand */ + hci_add_ltk(conn->hcon->hdev, 1, conn->src, rp->ediv, + rp->rand, conn->tk); smp_distribute_keys(conn, 1); @@ -632,6 +637,9 @@ 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, ediv, + ident.rand, enc.ltk); + ident.ediv = cpu_to_le16(ediv); smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); -- cgit v1.1 From e7e62c8592484f79469312fc694d2995918aa152 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:40 -0300 Subject: Bluetooth: Use the link key list to temporarily store the STK With this we can use only one place to store all keys, without need to use a field in the connection structure for this purpose. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 600a70b..6df5101 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -390,29 +390,36 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) } if (conn->hcon->out) { + u8 stk[16], rand[8]; __le16 ediv; - u8 rand[8]; + + memset(rand, 0, sizeof(rand)); + ediv = 0; smp_s1(tfm, conn->tk, random, conn->prnd, key); - swap128(key, hcon->ltk); + swap128(key, stk); - memset(hcon->ltk + conn->smp_key_size, 0, + memset(stk + conn->smp_key_size, 0, SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + hci_le_start_enc(hcon, ediv, rand, stk); + } else { + u8 stk[16], r[16], rand[8]; + __le16 ediv; + memset(rand, 0, sizeof(rand)); ediv = 0; - hci_le_start_enc(hcon, ediv, rand, hcon->ltk); - } else { - u8 r[16]; swap128(conn->prnd, r); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); smp_s1(tfm, conn->tk, conn->prnd, random, key); - swap128(key, hcon->ltk); + swap128(key, stk); - memset(hcon->ltk + conn->smp_key_size, 0, + memset(stk + conn->smp_key_size, 0, SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + + hci_add_ltk(conn->hcon->hdev, 0, conn->dst, ediv, rand, stk); } return 0; -- cgit v1.1 From 02bc74556a3f1b26adf3feb372376c56ba990564 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:41 -0300 Subject: Bluetooth: Use the stored LTK for restabilishing security Now that it's possible that the exchanged key is present in the link key list, we may be able to estabilish security with an already existing key, without need to perform any SMP procedure. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6df5101..5b72179 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -480,6 +480,17 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 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; + + hci_le_start_enc(hcon, master->ediv, master->rand, + key->val); + goto done; + } build_pairing_cmd(conn, &cp, NULL, authreq); conn->preq[0] = SMP_CMD_PAIRING_REQ; @@ -495,6 +506,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) 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); -- cgit v1.1 From 0eb08e339836989557ea3aa4c427377bd32ce19c Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 7 Jul 2011 18:59:42 -0300 Subject: Bluetooth: Remove unused field in hci_conn Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 5b72179..a8b971b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -367,8 +367,6 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) swap128(skb->data, random); skb_pull(skb, sizeof(random)); - memset(hcon->ltk, 0, sizeof(hcon->ltk)); - if (conn->hcon->out) ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, -- cgit v1.1 From 5a0a8b49746771fba79866fb9185ffa051a6a183 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 8 Jul 2011 18:31:44 -0300 Subject: Bluetooth: Add support for communicating keys with userspace As the key format has changed to something that has a dynamic size, the way that keys are received and sent must be changed. The structure fields order is changed to make the parsing of the information received from the Management Interface easier. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 60 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4fd11e5..f424d6a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -908,7 +908,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; + int i, err; cp = (void *) data; @@ -918,9 +918,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 %u bytes, got %u bytes", - len, expected_len); + if (expected_len > len) { + BT_ERR("load_keys: expected at least %u bytes, got %u bytes", + expected_len, len); return -EINVAL; } @@ -942,17 +942,36 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) else clear_bit(HCI_DEBUG_KEYS, &hdev->flags); - for (i = 0; i < key_count; i++) { - struct mgmt_key_info *key = &cp->keys[i]; + 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, id->ediv, + id->rand, key->val); + + continue; + } 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 0; + return err; } static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) @@ -1958,17 +1977,28 @@ 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; + struct mgmt_ev_new_key *ev; + int err, total; - memset(&ev, 0, sizeof(ev)); + 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; + + memcpy(ev->key.data, key->data, key->dlen); - 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; + err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL); - return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); + kfree(ev); + + return err; } int mgmt_connected(u16 index, bdaddr_t *bdaddr) -- cgit v1.1 From 726b4ffcaa450d9593b9b6ac8605967ce9f3e506 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 8 Jul 2011 18:31:45 -0300 Subject: Bluetooth: Add support for storing the key size In some cases it will be useful having the key size used for encrypting the link. For example, some profiles may restrict some operations depending on the key length. The key size is stored in the key that is passed to userspace using the pin_length field in the key structure. For now this field is only valid for LE controllers. 3.0+HS controllers define the Read Encryption Key Size command, this field is intended for storing the value returned by that command. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 3 ++- net/bluetooth/hci_event.c | 1 + net/bluetooth/mgmt.c | 4 ++-- net/bluetooth/smp.c | 14 +++++++++----- 4 files changed, 14 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4885914..908fcd3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1149,7 +1149,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, } int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - __le16 ediv, u8 rand[8], u8 ltk[16]) + u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]) { struct link_key *key, *old_key; struct key_master_id *id; @@ -1174,6 +1174,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, bacpy(&key->bdaddr, bdaddr); memcpy(key->val, ltk, sizeof(key->val)); key->type = HCI_LK_SMP_LTK; + key->pin_len = key_size; id = (void *) &key->data; id->ediv = ediv; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ca5ff6e..a40170e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2876,6 +2876,7 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); cp.handle = cpu_to_le16(conn->handle); + conn->pin_length = ltk->pin_len; hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f424d6a..53e109e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -956,8 +956,8 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) if (key->dlen != sizeof(struct key_master_id)) continue; - hci_add_ltk(hdev, 0, &key->bdaddr, id->ediv, - id->rand, key->val); + hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len, + id->ediv, id->rand, key->val); continue; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a8b971b..391888b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -401,6 +401,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); 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; @@ -417,7 +418,8 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) memset(stk + conn->smp_key_size, 0, SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); - hci_add_ltk(conn->hcon->hdev, 0, conn->dst, ediv, rand, stk); + hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size, + ediv, rand, stk); } return 0; @@ -487,6 +489,8 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) hci_le_start_enc(hcon, master->ediv, master->rand, key->val); + hcon->enc_key_size = key->pin_len; + goto done; } @@ -528,8 +532,8 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rp)); - hci_add_ltk(conn->hcon->hdev, 1, conn->src, rp->ediv, - rp->rand, conn->tk); + hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size, + rp->ediv, rp->rand, conn->tk); smp_distribute_keys(conn, 1); @@ -654,8 +658,8 @@ 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, ediv, - ident.rand, enc.ltk); + hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->smp_key_size, + ediv, ident.rand, enc.ltk); ident.ediv = cpu_to_le16(ediv); -- cgit v1.1 From 8f360119243c214eb5c5549c61981d51a276154a Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 8 Jul 2011 18:31:46 -0300 Subject: Bluetooth: Add support for returning the encryption key size This will be useful when userspace wants to restrict some kinds of operations based on the length of the key size used to encrypt the link. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_sock.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 146b614..5c36b3e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -422,8 +422,12 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; } + memset(&sec, 0, sizeof(sec)); sec.level = chan->sec_level; + if (sk->sk_state == BT_CONNECTED) + sec.key_size = chan->conn->hcon->enc_key_size; + len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) err = -EFAULT; -- cgit v1.1 From e2fd318e3a9208245ee1041f6d413c8593fba29d Mon Sep 17 00:00:00 2001 From: Ilia Kolomisnky Date: Sun, 10 Jul 2011 08:47:44 +0300 Subject: Bluetooth: Fixes l2cap "command reject" reply according to spec There can 3 reasons for the "command reject" reply produced by the stack. Each such reply should be accompanied by the relevand data ( as defined in spec. ). Currently there is one instance of "command reject" reply with reason "invalid cid" wich is fixed. Also, added clean-up definitions related to the "command reject" replies. Signed-off-by: Ilia Kolomisnky Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ab2e244..52c791e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2277,9 +2277,9 @@ done: static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, 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 (rej->reason != 0x0000) + if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD) return 0; if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && @@ -2524,9 +2524,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr sk = chan->sk; if (chan->state != BT_CONFIG) { - struct l2cap_cmd_rej rej; + 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; @@ -3017,12 +3020,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); } -- cgit v1.1