From 6befc6445ffc6868ee6e6d0e012fc149e88d96db Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:53 -0700 Subject: [PATCH 01/59] Bluetooth: Add flags field and setting function for HCI sockets To filter out certain actions for certain HCI sockets introcuce a flags field that allows to configure specific settings on individual sockets. Since the hci_pinfo structure is private in hci_sock.c, provide helper functions for setting and clearing a given flag. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 3 +++ net/bluetooth/hci_sock.c | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index e598ca096ec9..ae1f2ee1eaf3 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -335,6 +335,9 @@ out: int bt_to_errno(__u16 code); +void hci_sock_set_flag(struct sock *sk, int nr); +void hci_sock_clear_flag(struct sock *sk, int nr); + int hci_sock_init(void); void hci_sock_cleanup(void); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b614543b4fe3..bf5365c49c9c 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -47,8 +47,19 @@ struct hci_pinfo { struct hci_filter filter; __u32 cmsg_mask; unsigned short channel; + unsigned long flags; }; +void hci_sock_set_flag(struct sock *sk, int nr) +{ + set_bit(nr, &hci_pi(sk)->flags); +} + +void hci_sock_clear_flag(struct sock *sk, int nr) +{ + clear_bit(nr, &hci_pi(sk)->flags); +} + static inline int hci_test_bit(int nr, const void *addr) { return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); From 17711c62915dd62ab83a5a83a64c0d6105d13b6c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:54 -0700 Subject: [PATCH 02/59] Bluetooth: Provide hci_send_to_flagged_channel helper function The hci_send_to_flagged_channel helper function can be used to send packets to all channels that have a certain HCI socket flag set. This is especially useful for managment events that are limited to sockets that have first enabled certain functionality. This allows for filtering of events without confusing existing users. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_sock.c | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6afbf5b014a1..d38f6e426e84 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1285,6 +1285,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, struct sock *skip_sk); +void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, + int flag); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index bf5365c49c9c..174a353a7dcf 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -231,6 +231,39 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, read_unlock(&hci_sk_list.lock); } +/* Send frame to sockets with specific channel flag set */ +void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, + int flag) +{ + struct sock *sk; + + BT_DBG("channel %u len %d", channel, skb->len); + + read_lock(&hci_sk_list.lock); + + sk_for_each(sk, &hci_sk_list.head) { + struct sk_buff *nskb; + + if (!test_bit(flag, &hci_pi(sk)->flags)) + continue; + + if (sk->sk_state != BT_BOUND) + continue; + + if (hci_pi(sk)->channel != channel) + continue; + + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&hci_sk_list.lock); +} + /* Send frame to monitor socket */ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) { From f920733885546af2fd8d4b3dd5f8a1ac029f6248 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:55 -0700 Subject: [PATCH 03/59] Bluetooth: Use special function to send filter management index events For sending Index Added, Index Removed, Unconfigured Index Added and Unconfigured Index Removed managment events the new helper functions allows taking into account if these events are enabled for a certain management socket or not. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 6 ++++ net/bluetooth/hci_sock.c | 10 ++++++ net/bluetooth/mgmt.c | 64 +++++++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d942fedbaedd..0995ec755959 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -179,6 +179,12 @@ enum { HCI_RESET, }; +/* HCI socket flags */ +enum { + HCI_MGMT_INDEX_EVENTS, + HCI_MGMT_UNCONF_INDEX_EVENTS, +}; + /* * BR/EDR and/or LE controller flags: the flags defined here should represent * states from the controller. diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 174a353a7dcf..00775c4fef83 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -817,6 +817,16 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } + /* At the moment the index and unconfigured index events + * are enabled unconditionally. Setting them on each + * socket when binding keeps this functionality. They + * however might be cleared later and then sending of these + * events will be disabled, but that is then intentional. + */ + if (haddr.hci_channel == HCI_CHANNEL_CONTROL) { + hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS); + hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); + } break; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c58908652519..6b58c13b2b51 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -250,6 +250,33 @@ static int mgmt_send_event(u16 event, struct hci_dev *hdev, return 0; } +static int mgmt_index_event(u16 event, struct hci_dev *hdev, + void *data, u16 data_len, int flag) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + + skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(event); + hdr->index = cpu_to_le16(hdev->id); + hdr->len = cpu_to_le16(data_len); + + if (data) + memcpy(skb_put(skb, data_len), data, data_len); + + /* Time stamp */ + __net_timestamp(skb); + + hci_send_to_flagged_channel(HCI_CHANNEL_CONTROL, skb, flag); + kfree_skb(skb); + + return 0; +} + static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { @@ -6343,34 +6370,43 @@ done: void mgmt_index_added(struct hci_dev *hdev) { - if (hdev->dev_type != HCI_BREDR) - return; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; - if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) - mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL); - else - mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); + switch (hdev->dev_type) { + case HCI_BREDR: + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { + mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, + NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + } else { + mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, + HCI_MGMT_INDEX_EVENTS); + } + break; + } } void mgmt_index_removed(struct hci_dev *hdev) { u8 status = MGMT_STATUS_INVALID_INDEX; - if (hdev->dev_type != HCI_BREDR) - return; - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; - mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); + switch (hdev->dev_type) { + case HCI_BREDR: + mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); - if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) - mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL); - else - mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { + mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, + NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + } else { + mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, + HCI_MGMT_INDEX_EVENTS); + } + break; + } } /* This function requires the caller holds hdev->lock */ From ced85549c3a769dfb9d084bb8d6d9ca8075f8728 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:56 -0700 Subject: [PATCH 04/59] Bluetooth: Add support for extended index management events This introduces support for using Extended Index Added and Extended Index Removed events. These events contain the controller type and also the hardware bus information from the driver. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 9 +++++++++ net/bluetooth/mgmt.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0995ec755959..b8318711135a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -183,6 +183,7 @@ enum { enum { HCI_MGMT_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS, + HCI_MGMT_EXT_INDEX_EVENTS, }; /* diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5bf6af9cee78..8562f9ecf230 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -692,3 +692,12 @@ struct mgmt_ev_new_conn_param { #define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e #define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f + +struct mgmt_ev_ext_index { + __u8 type; + __u8 bus; +} __packed; + +#define MGMT_EV_EXT_INDEX_ADDED 0x0020 + +#define MGMT_EV_EXT_INDEX_REMOVED 0x0021 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6b58c13b2b51..56f49e9c4189 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -128,6 +128,8 @@ static const u16 mgmt_events[] = { MGMT_EV_UNCONF_INDEX_ADDED, MGMT_EV_UNCONF_INDEX_REMOVED, MGMT_EV_NEW_CONFIG_OPTIONS, + MGMT_EV_EXT_INDEX_ADDED, + MGMT_EV_EXT_INDEX_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -6370,6 +6372,7 @@ done: void mgmt_index_added(struct hci_dev *hdev) { + struct mgmt_ev_ext_index ev; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return; @@ -6379,16 +6382,29 @@ void mgmt_index_added(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + ev.type = 0x01; } else { mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, HCI_MGMT_INDEX_EVENTS); + ev.type = 0x00; } break; + case HCI_AMP: + ev.type = 0x02; + break; + default: + return; } + + ev.bus = hdev->bus; + + mgmt_index_event(MGMT_EV_EXT_INDEX_ADDED, hdev, &ev, sizeof(ev), + HCI_MGMT_EXT_INDEX_EVENTS); } void mgmt_index_removed(struct hci_dev *hdev) { + struct mgmt_ev_ext_index ev; u8 status = MGMT_STATUS_INVALID_INDEX; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) @@ -6401,12 +6417,24 @@ void mgmt_index_removed(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); + ev.type = 0x01; } else { mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, HCI_MGMT_INDEX_EVENTS); + ev.type = 0x00; } break; + case HCI_AMP: + ev.type = 0x02; + break; + default: + return; } + + ev.bus = hdev->bus; + + mgmt_index_event(MGMT_EV_EXT_INDEX_REMOVED, hdev, &ev, sizeof(ev), + HCI_MGMT_EXT_INDEX_EVENTS); } /* This function requires the caller holds hdev->lock */ From 96f1474af040a4ec267efe141cbf264891e67e5a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:57 -0700 Subject: [PATCH 05/59] Bluetooth: Add support for extended index management command The Read Extended Contoller Index List command can be used for retrieving the complete list of local available controllers. This included configured, unconfigured and also AMP controllers. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++ net/bluetooth/mgmt.c | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 8562f9ecf230..2534bd4d22b2 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -505,6 +505,17 @@ struct mgmt_cp_start_service_discovery { } __packed; #define MGMT_START_SERVICE_DISCOVERY_SIZE 4 +#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C +#define MGMT_READ_EXT_INDEX_LIST_SIZE 0 +struct mgmt_rp_read_ext_index_list { + __le16 num_controllers; + struct { + __le16 index; + __u8 type; + __u8 bus; + } entry[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 56f49e9c4189..ff636bd9523b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -96,6 +96,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_EXTERNAL_CONFIG, MGMT_OP_SET_PUBLIC_ADDRESS, MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_OP_READ_EXT_INDEX_LIST, }; static const u16 mgmt_events[] = { @@ -518,6 +519,82 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_ext_index_list *rp; + struct hci_dev *d; + size_t rp_len; + u16 count; + int err; + + BT_DBG("sock %p", sk); + + read_lock(&hci_dev_list_lock); + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (d->dev_type == HCI_BREDR || d->dev_type == HCI_AMP) + count++; + } + + rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + read_unlock(&hci_dev_list_lock); + return -ENOMEM; + } + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (hci_dev_test_flag(d, HCI_SETUP) || + hci_dev_test_flag(d, HCI_CONFIG) || + hci_dev_test_flag(d, HCI_USER_CHANNEL)) + continue; + + /* Devices marked as raw-only are neither configured + * nor unconfigured controllers. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + continue; + + if (d->dev_type == HCI_BREDR) { + if (hci_dev_test_flag(d, HCI_UNCONFIGURED)) + rp->entry[count].type = 0x01; + else + rp->entry[count].type = 0x00; + } else if (d->dev_type == HCI_AMP) { + rp->entry[count].type = 0x02; + } else { + continue; + } + + rp->entry[count].bus = d->bus; + rp->entry[count++].index = cpu_to_le16(d->id); + BT_DBG("Added hci%u", d->id); + } + + rp->num_controllers = cpu_to_le16(count); + rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count); + + read_unlock(&hci_dev_list_lock); + + /* If this command is called at least once, then all the + * default index and unconfigured index events are disabled + * and from now on only extended index events are used. + */ + hci_sock_set_flag(sk, HCI_MGMT_EXT_INDEX_EVENTS); + hci_sock_clear_flag(sk, HCI_MGMT_INDEX_EVENTS); + hci_sock_clear_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); + + err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, + MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, rp_len); + + kfree(rp); + + return err; +} + static bool is_configured(struct hci_dev *hdev) { if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && @@ -6264,6 +6341,9 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_UNCONFIGURED }, { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE, HCI_MGMT_VAR_LEN }, + { NULL }, + { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, + HCI_MGMT_NO_HDEV }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, From 50ebc055fa758c731e6e1ce174608327aab07aec Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:58 -0700 Subject: [PATCH 06/59] Bluetooth: Introduce trusted flag for management control sockets Providing a global trusted flag for management control sockets provides an easy way for identifying sockets and imposing restriction on it. For now all management sockets are trusted since they require CAP_NET_ADMIN. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_sock.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b8318711135a..7a24acaafeea 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -181,6 +181,7 @@ enum { /* HCI socket flags */ enum { + HCI_SOCK_TRUSTED, HCI_MGMT_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_EXT_INDEX_EVENTS, diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 00775c4fef83..54118868b3f6 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -796,6 +796,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } + /* The monitor interface is restricted to CAP_NET_RAW + * capabilities and with that implicitly trusted. + */ + hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); + send_monitor_replay(sk); atomic_inc(&monitor_promisc); @@ -817,6 +822,12 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } + /* Since the access to control channels is currently + * restricted to CAP_NET_ADMIN capabilities, every + * socket is implicitly trusted. + */ + hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); + /* At the moment the index and unconfigured index events * are enabled unconditionally. Setting them on each * socket when binding keeps this functionality. They From c08b1a1dba524c1cdef331c1f169db3a1b37bb4c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:27:59 -0700 Subject: [PATCH 07/59] Bluetooth: Consolidate socket channel sending function back into one With the introduction of trusted socket flag for control and monitor channels, it is now possible to use a single function for sending packets to these sockets. And with that consolidate the handling. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 4 +-- net/bluetooth/hci_sock.c | 45 +++++++------------------------- net/bluetooth/mgmt.c | 34 +++++------------------- 3 files changed, 17 insertions(+), 66 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d38f6e426e84..859005c9a8fc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1284,9 +1284,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, - struct sock *skip_sk); -void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, - int flag); + int flag, struct sock *skip_sk); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 54118868b3f6..e7f463f6fd69 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -199,7 +199,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) /* Send frame to sockets with specific channel */ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, - struct sock *skip_sk) + int flag, struct sock *skip_sk) { struct sock *sk; @@ -210,6 +210,10 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, sk_for_each(sk, &hci_sk_list.head) { struct sk_buff *nskb; + /* Ignore socket without the flag set */ + if (!test_bit(flag, &hci_pi(sk)->flags)) + continue; + /* Skip the original socket */ if (sk == skip_sk) continue; @@ -231,39 +235,6 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, read_unlock(&hci_sk_list.lock); } -/* Send frame to sockets with specific channel flag set */ -void hci_send_to_flagged_channel(unsigned short channel, struct sk_buff *skb, - int flag) -{ - struct sock *sk; - - BT_DBG("channel %u len %d", channel, skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (!test_bit(flag, &hci_pi(sk)->flags)) - continue; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != channel) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - /* Send frame to monitor socket */ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) { @@ -310,7 +281,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) hdr->index = cpu_to_le16(hdev->id); hdr->len = cpu_to_le16(skb->len); - hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, NULL); + hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, + HCI_SOCK_TRUSTED, NULL); kfree_skb(skb_copy); } @@ -417,7 +389,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) skb = create_monitor_event(hdev, event); if (skb) { - hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL); + hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, + HCI_SOCK_TRUSTED, NULL); kfree_skb(skb); } } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ff636bd9523b..1e5afa76e371 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -224,7 +224,7 @@ static u8 mgmt_status(u8 hci_status) static int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel, void *data, u16 data_len, - struct sock *skip_sk) + int flag, struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -247,44 +247,24 @@ static int mgmt_send_event(u16 event, struct hci_dev *hdev, /* Time stamp */ __net_timestamp(skb); - hci_send_to_channel(channel, skb, skip_sk); + hci_send_to_channel(channel, skb, flag, skip_sk); kfree_skb(skb); return 0; } -static int mgmt_index_event(u16 event, struct hci_dev *hdev, - void *data, u16 data_len, int flag) +static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, + u16 len, int flag) { - struct sk_buff *skb; - struct mgmt_hdr *hdr; - - skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(event); - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(data_len); - - if (data) - memcpy(skb_put(skb, data_len), data, data_len); - - /* Time stamp */ - __net_timestamp(skb); - - hci_send_to_flagged_channel(HCI_CHANNEL_CONTROL, skb, flag); - kfree_skb(skb); - - return 0; + return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, + flag, NULL); } static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, - skip_sk); + HCI_SOCK_TRUSTED, skip_sk); } static int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) From c85be545ea23a4fe590c89683242a9be823394e0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:00 -0700 Subject: [PATCH 08/59] Bluetooth: Add hci_sock_test_flag helper function The management interface will need access to the socket flags and so provide a helper function for checking them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/hci_sock.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index ae1f2ee1eaf3..d871ba313f64 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -337,6 +337,7 @@ int bt_to_errno(__u16 code); void hci_sock_set_flag(struct sock *sk, int nr); void hci_sock_clear_flag(struct sock *sk, int nr); +int hci_sock_test_flag(struct sock *sk, int nr); int hci_sock_init(void); void hci_sock_cleanup(void); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index e7f463f6fd69..df23c184c897 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -60,6 +60,11 @@ void hci_sock_clear_flag(struct sock *sk, int nr) clear_bit(nr, &hci_pi(sk)->flags); } +int hci_sock_test_flag(struct sock *sk, int nr) +{ + return test_bit(nr, &hci_pi(sk)->flags); +} + static inline int hci_test_bit(int nr, const void *addr) { return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); @@ -211,7 +216,7 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, struct sk_buff *nskb; /* Ignore socket without the flag set */ - if (!test_bit(flag, &hci_pi(sk)->flags)) + if (!hci_sock_test_flag(sk, flag)) continue; /* Skip the original socket */ From c91041dc4efff71f29f1dd4c9a4a5e80b841395c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:01 -0700 Subject: [PATCH 09/59] Bluetooth: Add support for untrusted access to management commands Some management commands are safe to be accessed from any user without special permissions. First step for allowing access to any of these commands from untrusted application is to mark them accordingly. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/mgmt.c | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 859005c9a8fc..3546789c1616 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1291,7 +1291,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define HCI_MGMT_VAR_LEN (1 << 0) #define HCI_MGMT_NO_HDEV (1 << 1) -#define HCI_MGMT_UNCONFIGURED (1 << 2) +#define HCI_MGMT_UNTRUSTED (1 << 2) +#define HCI_MGMT_UNCONFIGURED (1 << 3) struct hci_mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e5afa76e371..09f31f9642b7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6251,12 +6251,16 @@ unlock: static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, MGMT_READ_VERSION_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, { read_commands, MGMT_READ_COMMANDS_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, { read_index_list, MGMT_READ_INDEX_LIST_SIZE, - HCI_MGMT_NO_HDEV }, - { read_controller_info, MGMT_READ_INFO_SIZE, 0 }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, + { read_controller_info, MGMT_READ_INFO_SIZE, + HCI_MGMT_UNTRUSTED }, { set_powered, MGMT_SETTING_SIZE, 0 }, { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE, 0 }, { set_connectable, MGMT_SETTING_SIZE, 0 }, @@ -6312,9 +6316,11 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE, HCI_MGMT_VAR_LEN }, { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, { read_config_info, MGMT_READ_CONFIG_INFO_SIZE, - HCI_MGMT_UNCONFIGURED }, + HCI_MGMT_UNCONFIGURED | + HCI_MGMT_UNTRUSTED }, { set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE, HCI_MGMT_UNCONFIGURED }, { set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE, @@ -6323,7 +6329,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_VAR_LEN }, { NULL }, { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, - HCI_MGMT_NO_HDEV }, + HCI_MGMT_NO_HDEV | + HCI_MGMT_UNTRUSTED }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, From 7aea8616cd26d138b0696ba5605751fb21ee970f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:02 -0700 Subject: [PATCH 10/59] Bluetooth: Remove unneeded initializer for management command table The flags field for the management command table will be always initialized to zero and thus no need to do that manually. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 86 ++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 09f31f9642b7..2162f7bc89be 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6261,58 +6261,58 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_UNTRUSTED }, { read_controller_info, MGMT_READ_INFO_SIZE, HCI_MGMT_UNTRUSTED }, - { set_powered, MGMT_SETTING_SIZE, 0 }, - { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE, 0 }, - { set_connectable, MGMT_SETTING_SIZE, 0 }, - { set_fast_connectable, MGMT_SETTING_SIZE, 0 }, - { set_bondable, MGMT_SETTING_SIZE, 0 }, - { set_link_security, MGMT_SETTING_SIZE, 0 }, - { set_ssp, MGMT_SETTING_SIZE, 0 }, - { set_hs, MGMT_SETTING_SIZE, 0 }, - { set_le, MGMT_SETTING_SIZE, 0 }, - { set_dev_class, MGMT_SET_DEV_CLASS_SIZE, 0 }, - { set_local_name, MGMT_SET_LOCAL_NAME_SIZE, 0 }, - { add_uuid, MGMT_ADD_UUID_SIZE, 0 }, - { remove_uuid, MGMT_REMOVE_UUID_SIZE, 0 }, + { set_powered, MGMT_SETTING_SIZE }, + { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE }, + { set_connectable, MGMT_SETTING_SIZE }, + { set_fast_connectable, MGMT_SETTING_SIZE }, + { set_bondable, MGMT_SETTING_SIZE }, + { set_link_security, MGMT_SETTING_SIZE }, + { set_ssp, MGMT_SETTING_SIZE }, + { set_hs, MGMT_SETTING_SIZE }, + { set_le, MGMT_SETTING_SIZE }, + { set_dev_class, MGMT_SET_DEV_CLASS_SIZE }, + { set_local_name, MGMT_SET_LOCAL_NAME_SIZE }, + { add_uuid, MGMT_ADD_UUID_SIZE }, + { remove_uuid, MGMT_REMOVE_UUID_SIZE }, { load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE, HCI_MGMT_VAR_LEN }, { load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE, HCI_MGMT_VAR_LEN }, - { disconnect, MGMT_DISCONNECT_SIZE, 0 }, - { get_connections, MGMT_GET_CONNECTIONS_SIZE, 0 }, - { pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE, 0 }, - { pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE, 0 }, - { set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE, 0 }, - { pair_device, MGMT_PAIR_DEVICE_SIZE, 0 }, - { cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE, 0 }, - { unpair_device, MGMT_UNPAIR_DEVICE_SIZE, 0 }, - { user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE, 0 }, - { user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE, 0 }, - { user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE, 0 }, - { user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE, 0 }, + { disconnect, MGMT_DISCONNECT_SIZE }, + { get_connections, MGMT_GET_CONNECTIONS_SIZE }, + { pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE }, + { pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE }, + { set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE }, + { pair_device, MGMT_PAIR_DEVICE_SIZE }, + { cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE }, + { unpair_device, MGMT_UNPAIR_DEVICE_SIZE }, + { user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE }, + { user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE }, + { user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE }, + { user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, { read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE }, { add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE, HCI_MGMT_VAR_LEN }, - { remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE, 0 }, - { start_discovery, MGMT_START_DISCOVERY_SIZE, 0 }, - { stop_discovery, MGMT_STOP_DISCOVERY_SIZE, 0 }, - { confirm_name, MGMT_CONFIRM_NAME_SIZE, 0 }, - { block_device, MGMT_BLOCK_DEVICE_SIZE, 0 }, - { unblock_device, MGMT_UNBLOCK_DEVICE_SIZE, 0 }, - { set_device_id, MGMT_SET_DEVICE_ID_SIZE, 0 }, - { set_advertising, MGMT_SETTING_SIZE, 0 }, - { set_bredr, MGMT_SETTING_SIZE, 0 }, - { set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE, 0 }, - { set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE, 0 }, - { set_secure_conn, MGMT_SETTING_SIZE, 0 }, - { set_debug_keys, MGMT_SETTING_SIZE, 0 }, - { set_privacy, MGMT_SET_PRIVACY_SIZE, 0 }, + { remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, + { start_discovery, MGMT_START_DISCOVERY_SIZE }, + { stop_discovery, MGMT_STOP_DISCOVERY_SIZE }, + { confirm_name, MGMT_CONFIRM_NAME_SIZE }, + { block_device, MGMT_BLOCK_DEVICE_SIZE }, + { unblock_device, MGMT_UNBLOCK_DEVICE_SIZE }, + { set_device_id, MGMT_SET_DEVICE_ID_SIZE }, + { set_advertising, MGMT_SETTING_SIZE }, + { set_bredr, MGMT_SETTING_SIZE }, + { set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE }, + { set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE }, + { set_secure_conn, MGMT_SETTING_SIZE }, + { set_debug_keys, MGMT_SETTING_SIZE }, + { set_privacy, MGMT_SET_PRIVACY_SIZE }, { load_irks, MGMT_LOAD_IRKS_SIZE, HCI_MGMT_VAR_LEN }, - { get_conn_info, MGMT_GET_CONN_INFO_SIZE, 0 }, - { get_clock_info, MGMT_GET_CLOCK_INFO_SIZE, 0 }, - { add_device, MGMT_ADD_DEVICE_SIZE, 0 }, - { remove_device, MGMT_REMOVE_DEVICE_SIZE, 0 }, + { get_conn_info, MGMT_GET_CONN_INFO_SIZE }, + { get_clock_info, MGMT_GET_CLOCK_INFO_SIZE }, + { add_device, MGMT_ADD_DEVICE_SIZE }, + { remove_device, MGMT_REMOVE_DEVICE_SIZE }, { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE, HCI_MGMT_VAR_LEN }, { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE, From c927a10487477eeed0441e5c88147700e69e5db9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:03 -0700 Subject: [PATCH 11/59] Bluetooth: Add support for trust verification of management commands Check the required trust level of each management command with the trust level of the management socket. If it does not match up, then return the newly introduced permission denied error. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 2534bd4d22b2..f3baad589db0 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -44,6 +44,7 @@ #define MGMT_STATUS_INVALID_INDEX 0x11 #define MGMT_STATUS_RFKILLED 0x12 #define MGMT_STATUS_ALREADY_PAIRED 0x13 +#define MGMT_STATUS_PERMISSION_DENIED 0x14 struct mgmt_hdr { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2162f7bc89be..920acf0625f6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6379,6 +6379,13 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, handler = &chan->handlers[opcode]; + if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && + !(handler->flags & HCI_MGMT_UNTRUSTED)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_PERMISSION_DENIED); + goto done; + } + if (index != MGMT_INDEX_NONE) { hdev = hci_dev_get(index); if (!hdev) { From 1195fbb8d03e5a6d7e7e49a73592caeb113a0f70 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:04 -0700 Subject: [PATCH 12/59] Bluetooth: Open management interface for untrusted users Until now the management interface was restricted to CAP_NET_ADMIN. With this change every user can open the management socket. However the list of commands is heavily restricted to getting basic information about the attached controllers. No access for configuration or other operation is provided. The events are also limited. This is done so that no keys can leak or untrusted users can mess with the Bluetooth configuration. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_sock.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index df23c184c897..f4b10344b1e5 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -795,16 +795,13 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - if (!capable(CAP_NET_ADMIN)) { - err = -EPERM; - goto done; - } - - /* Since the access to control channels is currently - * restricted to CAP_NET_ADMIN capabilities, every - * socket is implicitly trusted. + /* Users with CAP_NET_ADMIN capabilities are allowed + * access to all management commands and events. For + * untrusted users the interface is restricted and + * also only untrusted events are sent. */ - hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); + if (capable(CAP_NET_ADMIN)) + hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); /* At the moment the index and unconfigured index events * are enabled unconditionally. Setting them on each From f6b7712eb660c50877a56772908326cd31125b21 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:05 -0700 Subject: [PATCH 13/59] Bluetooth: Send global configuration updates to all management users Changes to the global configuration updates like settings, class of device, name etc. can be received by every user. They are allowed to read them in the first place so provide the updates via events as well. Otherwise untrusted users start polling for updates and that is not a desired behavior. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_sock.c | 5 +++++ net/bluetooth/mgmt.c | 34 ++++++++++++++++++++-------------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7a24acaafeea..7f41c7741e76 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -185,6 +185,7 @@ enum { HCI_MGMT_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_EXT_INDEX_EVENTS, + HCI_MGMT_GENERIC_EVENTS, }; /* diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index f4b10344b1e5..7c719602dbca 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -808,10 +808,15 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, * socket when binding keeps this functionality. They * however might be cleared later and then sending of these * events will be disabled, but that is then intentional. + * + * This also enables generic events that are safe to be + * received by untrusted users. Example for such events + * are changes to settings, class of device, name etc. */ if (haddr.hci_channel == HCI_CHANNEL_CONTROL) { hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS); hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); + hci_sock_set_flag(sk, HCI_MGMT_GENERIC_EVENTS); } break; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 920acf0625f6..fa5654d89702 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -260,6 +260,13 @@ static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, flag, NULL); } +static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data, + u16 len, struct sock *skip_sk) +{ + return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, + HCI_MGMT_GENERIC_EVENTS, skip_sk); +} + static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { @@ -607,8 +614,8 @@ static int new_options(struct hci_dev *hdev, struct sock *skip) { __le32 options = get_missing_options(hdev); - return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, - sizeof(options), skip); + return mgmt_generic_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, + sizeof(options), skip); } static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) @@ -1552,11 +1559,10 @@ failed: static int new_settings(struct hci_dev *hdev, struct sock *skip) { - __le32 ev; + __le32 ev = cpu_to_le32(get_current_settings(hdev)); - ev = cpu_to_le32(get_current_settings(hdev)); - - return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); + return mgmt_generic_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, + sizeof(ev), skip); } int mgmt_new_settings(struct hci_dev *hdev) @@ -3677,8 +3683,8 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) goto failed; - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len, - sk); + err = mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, + data, len, sk); goto failed; } @@ -6673,8 +6679,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) - mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, - zero_cod, sizeof(zero_cod), NULL); + mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + zero_cod, sizeof(zero_cod), NULL); new_settings: err = new_settings(hdev, match.sk); @@ -7325,8 +7331,8 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); if (!status) - mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3, - NULL); + mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + dev_class, 3, NULL); if (match.sk) sock_put(match.sk); @@ -7355,8 +7361,8 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) return; } - mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, From a958452aa40c57a0407ecf84ba1bfa31ad532313 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 19:28:06 -0700 Subject: [PATCH 14/59] Bluetooth: Use BIT(n) macro instead of manually encoding (1 << n) The flags for the management command table used manual encoding of bits in the form of (1 << n). It is however preferred to use BIT(n) macro instead. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3546789c1616..ce94bcb33600 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1289,10 +1289,10 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); -#define HCI_MGMT_VAR_LEN (1 << 0) -#define HCI_MGMT_NO_HDEV (1 << 1) -#define HCI_MGMT_UNTRUSTED (1 << 2) -#define HCI_MGMT_UNCONFIGURED (1 << 3) +#define HCI_MGMT_VAR_LEN BIT(0) +#define HCI_MGMT_NO_HDEV BIT(1) +#define HCI_MGMT_UNTRUSTED BIT(2) +#define HCI_MGMT_UNCONFIGURED BIT(3) struct hci_mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, From d3d5305bfd1cb48c8f44207abb567276a1e09cc7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 20:53:25 -0700 Subject: [PATCH 15/59] Bluetooth: Add simple version of Read Advertising Features command This adds support for the simplest possible version of Read Advertising Features management command. It allows basic testing of the interface. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++++++++ net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f3baad589db0..4d0ccd194c01 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -517,6 +517,17 @@ struct mgmt_rp_read_ext_index_list { } entry[0]; } __packed; +#define MGMT_OP_READ_ADV_FEATURES 0x0003D +#define MGMT_READ_ADV_FEATURES_SIZE 0 +struct mgmt_rp_read_adv_features { + __le32 supported_flags; + __u8 max_adv_data_len; + __u8 max_scan_rsp_len; + __u8 max_instances; + __u8 num_instances; + __u8 instance[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fa5654d89702..25a687c2a112 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -97,6 +97,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_PUBLIC_ADDRESS, MGMT_OP_START_SERVICE_DISCOVERY, MGMT_OP_READ_EXT_INDEX_LIST, + MGMT_OP_READ_ADV_FEATURES, }; static const u16 mgmt_events[] = { @@ -6254,6 +6255,40 @@ unlock: return err; } +static int read_adv_features(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_adv_features *rp; + size_t rp_len; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + rp_len = sizeof(*rp); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + hci_dev_unlock(hdev); + return -ENOMEM; + } + + rp->supported_flags = cpu_to_le32(0); + rp->max_adv_data_len = 31; + rp->max_scan_rsp_len = 31; + rp->max_instances = 0; + rp->num_instances = 0; + + hci_dev_unlock(hdev); + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES, + MGMT_STATUS_SUCCESS, rp, rp_len); + + kfree(rp); + + return err; +} + static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, MGMT_READ_VERSION_SIZE, @@ -6337,6 +6372,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, HCI_MGMT_NO_HDEV | HCI_MGMT_UNTRUSTED }, + { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, }; int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, From bea41609de527830cc4c843e0e833cf021adc6b1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 22:43:17 -0700 Subject: [PATCH 16/59] Bluetooth: Move eir_append_data function to a different location The eir_append_data helper function is needed for generating the extended local OOB data fields. So move it up into the right location. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 25a687c2a112..07c75a04829a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6255,6 +6255,17 @@ unlock: return err; } +static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, + u8 data_len) +{ + eir[eir_len++] = sizeof(type) + data_len; + eir[eir_len++] = type; + memcpy(&eir[eir_len], data, data_len); + eir_len += data_len; + + return eir_len; +} + static int read_adv_features(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6932,17 +6943,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL); } -static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, - u8 data_len) -{ - eir[eir_len++] = sizeof(type) + data_len; - eir[eir_len++] = type; - memcpy(&eir[eir_len], data, data_len); - eir_len += data_len; - - return eir_len; -} - void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, u32 flags, u8 *name, u8 name_len) { From 1471aae0d04d4e0df2bf1e5a5af861e28371b8b4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 22:43:18 -0700 Subject: [PATCH 17/59] Bluetooth: Add defines for LE Bluetooth Device Address and LE Role The OOB data requires to include LE Bluetooth Device Address and LE Role and so add the type constants for these fields. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f41c7741e76..af9893b704ff 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -456,6 +456,8 @@ enum { #define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ +#define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ +#define EIR_LE_ROLE 0x1C /* LE role */ /* Low Energy Advertising Flags */ #define LE_AD_LIMITED 0x01 /* Limited Discoverable */ From 4f0f155ceaf7e1b59d210a8afb24d4ea63ce13cc Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 14 Mar 2015 22:43:19 -0700 Subject: [PATCH 18/59] Bluetooth: Add simple version of Read Local OOB Extended Data command This adds support for the simplest possible version of Read Local OOB Extended Data management command. It includes all mandatory fields, but none of the actual pairing related ones. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 ++++ net/bluetooth/mgmt.c | 111 ++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4d0ccd194c01..543c1ba3d892 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -506,6 +506,17 @@ struct mgmt_cp_start_service_discovery { } __packed; #define MGMT_START_SERVICE_DISCOVERY_SIZE 4 +#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B +struct mgmt_cp_read_local_oob_ext_data { + __u8 type; +} __packed; +#define MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE 1 +struct mgmt_rp_read_local_oob_ext_data { + __u8 type; + __le16 eir_len; + __u8 eir[0]; +} __packed; + #define MGMT_OP_READ_EXT_INDEX_LIST 0x003C #define MGMT_READ_EXT_INDEX_LIST_SIZE 0 struct mgmt_rp_read_ext_index_list { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 07c75a04829a..6cb0a304182f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -96,6 +96,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_EXTERNAL_CONFIG, MGMT_OP_SET_PUBLIC_ADDRESS, MGMT_OP_START_SERVICE_DISCOVERY, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_OP_READ_ADV_FEATURES, }; @@ -6266,6 +6267,114 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, return eir_len; } +static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_cp_read_local_oob_ext_data *cp = data; + struct mgmt_rp_read_local_oob_ext_data *rp; + size_t rp_len; + u16 eir_len; + u8 status, flags, role, addr[7]; + int err; + + BT_DBG("%s", hdev->name); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_NOT_POWERED, + &cp->type, sizeof(cp->type)); + + switch (cp->type) { + case BIT(BDADDR_BREDR): + status = mgmt_bredr_support(hdev); + if (status) + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + status, &cp->type, + sizeof(cp->type)); + eir_len = 5; + break; + case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): + status = mgmt_le_support(hdev); + if (status) + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + status, &cp->type, + sizeof(cp->type)); + eir_len = 15; + break; + default: + return mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_INVALID_PARAMS, + &cp->type, sizeof(cp->type)); + } + + hci_dev_lock(hdev); + + rp_len = sizeof(*rp) + eir_len; + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + hci_dev_unlock(hdev); + return -ENOMEM; + } + + eir_len = 0; + switch (cp->type) { + case BIT(BDADDR_BREDR): + eir_len = eir_append_data(rp->eir, eir_len, EIR_CLASS_OF_DEV, + hdev->dev_class, 3); + break; + case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) { + memcpy(addr, &hdev->rpa, 6); + addr[6] = 0x01; + } else if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || + !bacmp(&hdev->bdaddr, BDADDR_ANY) || + (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && + bacmp(&hdev->static_addr, BDADDR_ANY))) { + memcpy(addr, &hdev->static_addr, 6); + addr[6] = 0x01; + } else { + memcpy(addr, &hdev->bdaddr, 6); + addr[6] = 0x00; + } + + eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_BDADDR, + addr, sizeof(addr)); + + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) + role = 0x02; + else + role = 0x01; + + eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE, + &role, sizeof(role)); + + flags = get_adv_discov_flags(hdev); + + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) + flags |= LE_AD_NO_BREDR; + + eir_len = eir_append_data(rp->eir, eir_len, EIR_FLAGS, + &flags, sizeof(flags)); + break; + } + + rp->type = cp->type; + rp->eir_len = cpu_to_le16(eir_len); + + hci_dev_unlock(hdev); + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_SUCCESS, rp, rp_len); + + kfree(rp); + + return err; +} + static int read_adv_features(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6379,7 +6488,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_UNCONFIGURED }, { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE, HCI_MGMT_VAR_LEN }, - { NULL }, + { read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE }, { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, HCI_MGMT_NO_HDEV | HCI_MGMT_UNTRUSTED }, From 455c2ff0a558c04d53a2f5bb55d16093a7dee41d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 15 Mar 2015 16:42:53 -0700 Subject: [PATCH 19/59] Bluetooth: Fix BR/EDR out-of-band pairing with only initiator data When only the pairing initiator is providing out-of-band data, then the receiver side was ignoring the data. For some reason the code was checking if the initiator has received out-of-band data and only then also provide the required inidication that the acceptor actually has the needed data available. For BR/EDR out-of-band pairing it is enough if one side has received out-of-band data. There are no extra checks needed here to make this work smoothly. The only thing that is needed is to tell the controller if data is present (and if it is P-192 or P-256 or both) and then let the controller actually figure out the rest. This means the check for outgoing connection or if the initiator has indicated data are completely pointless and are in fact actually causing harm. The check in question is this one: if (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) { After just taking the conditional check out and always executing the code for determining the type of out-of-band data, the pairing works flawlessly and prodcudes authenticated link keys. The patch itself looks more complicated due to the reformatting of the indentation, but it essentially just a two-line change. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_event.c | 56 ++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c7376cd42b1c..10d760c46df1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3889,41 +3889,37 @@ static u8 bredr_oob_data_present(struct hci_conn *conn) if (!data) return 0x00; - if (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) { - if (bredr_sc_enabled(hdev)) { - /* When Secure Connections is enabled, then just - * return the present value stored with the OOB - * data. The stored value contains the right present - * information. However it can only be trusted when - * not in Secure Connection Only mode. - */ - if (!hci_dev_test_flag(hdev, HCI_SC_ONLY)) - return data->present; - - /* When Secure Connections Only mode is enabled, then - * the P-256 values are required. If they are not - * available, then do not declare that OOB data is - * present. - */ - if (!memcmp(data->rand256, ZERO_KEY, 16) || - !memcmp(data->hash256, ZERO_KEY, 16)) - return 0x00; - - return 0x02; - } - - /* When Secure Connections is not enabled or actually - * not supported by the hardware, then check that if - * P-192 data values are present. + if (bredr_sc_enabled(hdev)) { + /* When Secure Connections is enabled, then just + * return the present value stored with the OOB + * data. The stored value contains the right present + * information. However it can only be trusted when + * not in Secure Connection Only mode. */ - if (!memcmp(data->rand192, ZERO_KEY, 16) || - !memcmp(data->hash192, ZERO_KEY, 16)) + if (!hci_dev_test_flag(hdev, HCI_SC_ONLY)) + return data->present; + + /* When Secure Connections Only mode is enabled, then + * the P-256 values are required. If they are not + * available, then do not declare that OOB data is + * present. + */ + if (!memcmp(data->rand256, ZERO_KEY, 16) || + !memcmp(data->hash256, ZERO_KEY, 16)) return 0x00; - return 0x01; + return 0x02; } - return 0x00; + /* When Secure Connections is not enabled or actually + * not supported by the hardware, then check that if + * P-192 data values are present. + */ + if (!memcmp(data->rand192, ZERO_KEY, 16) || + !memcmp(data->hash192, ZERO_KEY, 16)) + return 0x00; + + return 0x01; } static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) From aefedc1a4cfe4c874b8a7ac743f9deedc289d9e6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 15 Mar 2015 17:08:19 -0700 Subject: [PATCH 20/59] Bluetooth: Remove unneeded HCI_CONN_REMOTE_OOB connection flag The HCI_CONN_REMOTE_OOB connection flag is used to indicate if the pairing initiator has provided out-of-band data. However since that value is no longer used in any decision making, just remove it. It is actually unclear what purpose the OOB data present field from the HCI IO Capability Response event serves in the first place. If either side provided out-of-band data, then that data will be used for pairing. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ce94bcb33600..e4dc18eed446 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -596,7 +596,6 @@ enum { HCI_CONN_SC_ENABLED, HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, - HCI_CONN_REMOTE_OOB, HCI_CONN_FLUSH_KEY, HCI_CONN_ENCRYPT, HCI_CONN_AUTH, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 10d760c46df1..d800f0c5aa21 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4006,8 +4006,6 @@ static void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->remote_cap = ev->capability; conn->remote_auth = ev->authentication; - if (ev->oob_data) - set_bit(HCI_CONN_REMOTE_OOB, &conn->flags); unlock: hci_dev_unlock(hdev); From 276812ec3e945493443e399921a07e9d6aa4d5b2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:18 -0700 Subject: [PATCH 21/59] Bluetooth: Use kzfree instead of kfree in security manager Within the security manager, it makes sense to use kzfree instead of kfree for all data structures. This ensures that no key material leaks by accident. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9155840068cf..a2be6fcc3c51 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -681,9 +681,9 @@ static void smp_chan_destroy(struct l2cap_conn *conn) complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags); mgmt_smp_complete(hcon, complete); - kfree(smp->csrk); - kfree(smp->slave_csrk); - kfree(smp->link_key); + kzfree(smp->csrk); + kzfree(smp->slave_csrk); + kzfree(smp->link_key); crypto_free_blkcipher(smp->tfm_aes); crypto_free_hash(smp->tfm_cmac); @@ -717,7 +717,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) } chan->data = NULL; - kfree(smp); + kzfree(smp); hci_conn_drop(hcon); } @@ -1097,13 +1097,13 @@ static void sc_generate_link_key(struct smp_chan *smp) return; if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { - kfree(smp->link_key); + kzfree(smp->link_key); smp->link_key = NULL; return; } if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) { - kfree(smp->link_key); + kzfree(smp->link_key); smp->link_key = NULL; return; } @@ -1300,7 +1300,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(smp->tfm_aes)) { BT_ERR("Unable to create ECB crypto context"); - kfree(smp); + kzfree(smp); return NULL; } @@ -1308,7 +1308,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) if (IS_ERR(smp->tfm_cmac)) { BT_ERR("Unable to create CMAC crypto context"); crypto_free_blkcipher(smp->tfm_aes); - kfree(smp); + kzfree(smp); return NULL; } From 88a479d9507eb7a510a612705aa686c52d24b2ab Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:19 -0700 Subject: [PATCH 22/59] Bluetooth: Create SMP device structure for local crypto context Every Bluetooth Low Energy controller requires a local crypto context to handle the resolvable private addresses. At the moment this is just a single crypto context, but for out-of-band data generation it will require an additional. To facility this, create a struct smp_dev that will hold all the extra information. This patch is just the refactoring in preparation for future changes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 47 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a2be6fcc3c51..952ba6376e1c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -73,6 +73,10 @@ enum { SMP_FLAG_OOB, }; +struct smp_dev { + struct crypto_blkcipher *tfm_aes; +}; + struct smp_chan { struct l2cap_conn *conn; struct delayed_work security_timer; @@ -478,18 +482,18 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], const bdaddr_t *bdaddr) { struct l2cap_chan *chan = hdev->smp_data; - struct crypto_blkcipher *tfm; + struct smp_dev *smp; u8 hash[3]; int err; if (!chan || !chan->data) return false; - tfm = chan->data; + smp = chan->data; BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk); - err = smp_ah(tfm, irk, &bdaddr->b[3], hash); + err = smp_ah(smp->tfm_aes, irk, &bdaddr->b[3], hash); if (err) return false; @@ -499,20 +503,20 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa) { struct l2cap_chan *chan = hdev->smp_data; - struct crypto_blkcipher *tfm; + struct smp_dev *smp; int err; if (!chan || !chan->data) return -EOPNOTSUPP; - tfm = chan->data; + smp = chan->data; get_random_bytes(&rpa->b[3], 3); rpa->b[5] &= 0x3f; /* Clear two most significant bits */ rpa->b[5] |= 0x40; /* Set second most significant bit */ - err = smp_ah(tfm, irk, &rpa->b[3], rpa->b); + err = smp_ah(smp->tfm_aes, irk, &rpa->b[3], rpa->b); if (err < 0) return err; @@ -2930,27 +2934,36 @@ static const struct l2cap_ops smp_root_chan_ops = { static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) { struct l2cap_chan *chan; - struct crypto_blkcipher *tfm_aes; + struct smp_dev *smp; + struct crypto_blkcipher *tfm_aes; if (cid == L2CAP_CID_SMP_BREDR) { - tfm_aes = NULL; + smp = NULL; goto create_chan; } - tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0); + smp = kzalloc(sizeof(*smp), GFP_KERNEL); + if (!smp) + return ERR_PTR(-ENOMEM); + + tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm_aes)) { - BT_ERR("Unable to create crypto context"); + BT_ERR("Unable to create ECB crypto context"); + kzfree(smp); return ERR_CAST(tfm_aes); } + smp->tfm_aes = tfm_aes; + create_chan: chan = l2cap_chan_create(); if (!chan) { - crypto_free_blkcipher(tfm_aes); + crypto_free_blkcipher(smp->tfm_aes); + kzfree(smp); return ERR_PTR(-ENOMEM); } - chan->data = tfm_aes; + chan->data = smp; l2cap_add_scid(chan, cid); @@ -2983,14 +2996,16 @@ create_chan: static void smp_del_chan(struct l2cap_chan *chan) { - struct crypto_blkcipher *tfm_aes; + struct smp_dev *smp; BT_DBG("chan %p", chan); - tfm_aes = chan->data; - if (tfm_aes) { + smp = chan->data; + if (smp) { chan->data = NULL; - crypto_free_blkcipher(tfm_aes); + if (smp->tfm_aes) + crypto_free_blkcipher(smp->tfm_aes); + kzfree(smp); } l2cap_chan_put(chan); From f709bfcf6a292560ce187c33fd099ee495c0a404 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:20 -0700 Subject: [PATCH 23/59] Bluetooth: Add constants for LE SC Confirmation and Random values The LE Secure Connections Confirmation Value and LE Secure Connections Random Value contants are required for the out-of-band data and so just define them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index af9893b704ff..ce757303dc07 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -458,6 +458,8 @@ enum { #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ #define EIR_LE_ROLE 0x1C /* LE role */ +#define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */ +#define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */ /* Low Energy Advertising Flags */ #define LE_AD_LIMITED 0x01 /* Limited Discoverable */ From 6e2dc6d1133f5f8bfd028ba7d1c3fb0b3fa717e9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:21 -0700 Subject: [PATCH 24/59] Bluetooth: Add support for AES-CMAC hash for security manager device The security manager device will require the use of AES-CMAC hash for out-of-band data generation. This patch makes sure it is correctly set up and available. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 952ba6376e1c..12e9c833885b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -75,6 +75,7 @@ enum { struct smp_dev { struct crypto_blkcipher *tfm_aes; + struct crypto_hash *tfm_cmac; }; struct smp_chan { @@ -2936,6 +2937,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) struct l2cap_chan *chan; struct smp_dev *smp; struct crypto_blkcipher *tfm_aes; + struct crypto_hash *tfm_cmac; if (cid == L2CAP_CID_SMP_BREDR) { smp = NULL; @@ -2953,12 +2955,22 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) return ERR_CAST(tfm_aes); } + tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm_cmac)) { + BT_ERR("Unable to create CMAC crypto context"); + crypto_free_blkcipher(tfm_aes); + kzfree(smp); + return ERR_CAST(tfm_cmac); + } + smp->tfm_aes = tfm_aes; + smp->tfm_cmac = tfm_cmac; create_chan: chan = l2cap_chan_create(); if (!chan) { crypto_free_blkcipher(smp->tfm_aes); + crypto_free_hash(smp->tfm_cmac); kzfree(smp); return ERR_PTR(-ENOMEM); } @@ -3005,6 +3017,8 @@ static void smp_del_chan(struct l2cap_chan *chan) chan->data = NULL; if (smp->tfm_aes) crypto_free_blkcipher(smp->tfm_aes); + if (smp->tfm_cmac) + crypto_free_hash(smp->tfm_cmac); kzfree(smp); } From 60a27d653d972584e5e98ab3558c62c3d3bc547a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:22 -0700 Subject: [PATCH 25/59] Bluetooth: Add function for generating LE SC out-of-band data This patch adds a smp_generate_oob function that allows to create local out-of-band data that can be used for pairing and also provides the confirmation and random value. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/smp.h | 1 + 2 files changed, 54 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 12e9c833885b..1669e7127e2e 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -74,6 +74,12 @@ enum { }; struct smp_dev { + /* Secure Connections OOB data */ + u8 local_pk[64]; + u8 local_sk[32]; + u8 local_rr[16]; + bool debug_key; + struct crypto_blkcipher *tfm_aes; struct crypto_hash *tfm_cmac; }; @@ -526,6 +532,53 @@ int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa) return 0; } +int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) +{ + struct l2cap_chan *chan = hdev->smp_data; + struct smp_dev *smp; + int err; + + if (!chan || !chan->data) + return -EOPNOTSUPP; + + smp = chan->data; + + if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) { + BT_DBG("Using debug keys"); + memcpy(smp->local_pk, debug_pk, 64); + memcpy(smp->local_sk, debug_sk, 32); + smp->debug_key = true; + } else { + while (true) { + /* Generate local key pair for Secure Connections */ + if (!ecc_make_key(smp->local_pk, smp->local_sk)) + return -EIO; + + /* This is unlikely, but we need to check that + * we didn't accidentially generate a debug key. + */ + if (memcmp(smp->local_sk, debug_sk, 32)) + break; + } + smp->debug_key = false; + } + + SMP_DBG("OOB Public Key X: %32phN", smp->local_pk); + SMP_DBG("OOB Public Key Y: %32phN", smp->local_pk + 32); + SMP_DBG("OOB Private Key: %32phN", smp->local_sk); + + get_random_bytes(smp->local_rr, 16); + + err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->local_pk, + smp->local_rr, 0, hash); + if (err < 0) + return err; + + memcpy(rand, smp->local_rr, 16); + + return 0; +} + static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) { struct l2cap_chan *chan = conn->smp; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 60c5b73fcb4b..6cf872563ea7 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -188,6 +188,7 @@ int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], const bdaddr_t *bdaddr); int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa); +int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]); int smp_register(struct hci_dev *hdev); void smp_unregister(struct hci_dev *hdev); From 0821a2c5ab76d8ef81c1c2a8571a7ba4aa850976 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:23 -0700 Subject: [PATCH 26/59] Bluetooth: Return LE SC confirm and random values for out-of-band data Then the local out-of-band data for LE SC pairing is requested via Read Local OOB Extended Data command, then fill in the values generated by the smp_generate_oob function. Every call of this command will overwrite previously generated values. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6cb0a304182f..5322584460c1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6274,7 +6274,7 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, struct mgmt_rp_read_local_oob_ext_data *rp; size_t rp_len; u16 eir_len; - u8 status, flags, role, addr[7]; + u8 status, flags, role, addr[7], hash[16], rand[16]; int err; BT_DBG("%s", hdev->name); @@ -6302,7 +6302,7 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, status, &cp->type, sizeof(cp->type)); - eir_len = 15; + eir_len = 9 + 3 + 18 + 18 + 3; break; default: return mgmt_cmd_complete(sk, hdev->id, @@ -6327,6 +6327,15 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, hdev->dev_class, 3); break; case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): + if (smp_generate_oob(hdev, hash, rand) < 0) { + hci_dev_unlock(hdev); + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_FAILED, + &cp->type, sizeof(cp->type)); + goto done; + } + if (hci_dev_test_flag(hdev, HCI_PRIVACY)) { memcpy(addr, &hdev->rpa, 6); addr[6] = 0x01; @@ -6352,6 +6361,12 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE, &role, sizeof(role)); + eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_SC_CONFIRM, + hash, sizeof(hash)); + + eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_SC_RANDOM, + rand, sizeof(rand)); + flags = get_adv_discov_flags(hdev); if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) @@ -6370,6 +6385,7 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, MGMT_STATUS_SUCCESS, rp, rp_len); +done: kfree(rp); return err; From 33d0c030717bd939dab467f95966d8a64187e5c1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:24 -0700 Subject: [PATCH 27/59] Bluetooth: Use OOB key pair for LE SC pairing with OOB method The OOB public and secret key pair is different from the non-OOB pairing procedure. SO when OOB method is in use, then use this key pair instead of generating a new one. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1669e7127e2e..0fcd8c8f1a6b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1819,6 +1819,25 @@ static u8 sc_send_public_key(struct smp_chan *smp) BT_DBG(""); + if (test_bit(SMP_FLAG_OOB, &smp->flags)) { + struct l2cap_chan *chan = hdev->smp_data; + struct smp_dev *smp_dev; + + if (!chan || !chan->data) + return SMP_UNSPECIFIED; + + smp_dev = chan->data; + + memcpy(smp->local_pk, smp_dev->local_pk, 64); + memcpy(smp->local_sk, smp_dev->local_sk, 32); + memcpy(smp->rr, smp_dev->local_rr, 16); + + if (smp_dev->debug_key) + set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); + + goto done; + } + if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) { BT_DBG("Using debug keys"); memcpy(smp->local_pk, debug_pk, 64); @@ -1838,6 +1857,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) } } +done: SMP_DBG("Local Public Key X: %32phN", smp->local_pk); SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]); SMP_DBG("Local Private Key: %32phN", smp->local_sk); From 8e4e2ee5d80875177e03d57b807e0784f3d91e0e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 01:10:25 -0700 Subject: [PATCH 28/59] Bluetooth: Use smp->local_pk + 32 instead of &smp->local_pk[32] Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0fcd8c8f1a6b..f0c5c2827372 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1859,7 +1859,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) done: SMP_DBG("Local Public Key X: %32phN", smp->local_pk); - SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]); + SMP_DBG("Local Public Key Y: %32phN", smp->local_pk + 32); SMP_DBG("Local Private Key: %32phN", smp->local_sk); smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk); From 882fafad71a4bac8e8a2445dfb08c38a71b4eef1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Mar 2015 11:45:43 +0200 Subject: [PATCH 29/59] Bluetooth: Fix local OOB data handling for SMP We need to store the local ra/rb value in order to verify the Check value received from the remote. This patch adds a new 'lr' for the local ra/rb value and makes sure it gets used when verifying the DHKey Check PDU received from the remote. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f0c5c2827372..1cc15de6ff1e 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -95,7 +95,8 @@ struct smp_chan { u8 rrnd[16]; /* SMP Pairing Random (remote) */ u8 pcnf[16]; /* SMP Pairing Confirm */ u8 tk[16]; /* SMP Temporary Key */ - u8 rr[16]; + u8 rr[16]; /* Remote OOB ra/rb value */ + u8 lr[16]; /* Local OOB ra/rb value */ u8 enc_key_size; u8 remote_key_dist; bdaddr_t id_addr; @@ -1830,7 +1831,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) memcpy(smp->local_pk, smp_dev->local_pk, 64); memcpy(smp->local_sk, smp_dev->local_sk, 32); - memcpy(smp->rr, smp_dev->local_rr, 16); + memcpy(smp->lr, smp_dev->local_rr, 16); if (smp_dev->debug_key) set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); @@ -2634,6 +2635,8 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) put_unaligned_le32(hcon->passkey_notify, r); + else if (smp->method == REQ_OOB) + memcpy(r, smp->lr, 16); err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r, io_cap, remote_addr, local_addr, e); From 1a8bab4f390130268e5384ccf1bde47925102c72 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Mar 2015 11:45:44 +0200 Subject: [PATCH 30/59] Bluetooth: Track local vs remote OOB data availability There are several decisions in the SMP logic that depend not only on whether we're doing SMP or not, but also whether local and/or remote OOB data is present. This patch splits the existing SMP_FLAG_OOB into two new flags to track local and remote OOB data respectively. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1cc15de6ff1e..c8382f4fcd5c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -70,7 +70,8 @@ enum { SMP_FLAG_DEBUG_KEY, SMP_FLAG_WAIT_USER, SMP_FLAG_DHKEY_PENDING, - SMP_FLAG_OOB, + SMP_FLAG_REMOTE_OOB, + SMP_FLAG_LOCAL_OOB, }; struct smp_dev { @@ -680,7 +681,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, oob_data = hci_find_remote_oob_data(hdev, &hcon->dst, bdaddr_type); if (oob_data && oob_data->present) { - set_bit(SMP_FLAG_OOB, &smp->flags); + set_bit(SMP_FLAG_REMOTE_OOB, &smp->flags); oob_flag = SMP_OOB_PRESENT; memcpy(smp->rr, oob_data->rand256, 16); memcpy(smp->pcnf, oob_data->hash256, 16); @@ -1820,7 +1821,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) BT_DBG(""); - if (test_bit(SMP_FLAG_OOB, &smp->flags)) { + if (test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags)) { struct l2cap_chan *chan = hdev->smp_data; struct smp_dev *smp_dev; @@ -2453,7 +2454,8 @@ static u8 sc_select_method(struct smp_chan *smp) struct smp_cmd_pairing *local, *remote; u8 local_mitm, remote_mitm, local_io, remote_io, method; - if (test_bit(SMP_FLAG_OOB, &smp->flags)) + if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags) || + test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags)) return REQ_OOB; /* The preq/prsp contain the raw Pairing Request/Response PDUs From 58428563b5ea19c2ac8b6aca8073e48539023b26 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Mar 2015 11:45:45 +0200 Subject: [PATCH 31/59] Bluetooth: Set local OOB data flag if remote has our OOB data If the SMP Pairing Request or Response PDU received from the remote device indicates that it has received our OOB data we should set the SMP_FLAG_LOCAL_OOB flag. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index c8382f4fcd5c..b8a6ce840603 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1735,6 +1735,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); + if (req->oob_flag == SMP_OOB_PRESENT) + set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags); + /* SMP over BR/EDR requires special treatment */ if (conn->hcon->type == ACL_LINK) { /* We must have a BR/EDR SC link */ @@ -1899,6 +1902,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC)) return SMP_AUTH_REQUIREMENTS; + if (rsp->oob_flag == SMP_OOB_PRESENT) + set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); From 94ea7257ef24a007cb0e26476ed8871f179a749f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Mar 2015 11:45:46 +0200 Subject: [PATCH 32/59] Bluetooth: Fix verifying confirm value when lacking remote OOB data If we haven't received remote OOB data we cannot perform any special checks on the confirm value. This patch updates the check after having received the public key to only perform the verification if we have remote OOB data present. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b8a6ce840603..de53ba1905fe 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2562,7 +2562,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY); } - if (smp->method == REQ_OOB) { + if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) { err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk, smp->rr, 0, cfm.confirm_val); if (err) @@ -2570,7 +2570,9 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) if (memcmp(cfm.confirm_val, smp->pcnf, 16)) return SMP_CONFIRM_FAILED; + } + if (smp->method == REQ_OOB) { if (hcon->out) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); From a8ca617c1356cafa669dc467a386787cc1d83809 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Mar 2015 18:12:57 +0200 Subject: [PATCH 33/59] Bluetooth: Don't send public key if OOB data verification fails When we receive the remote public key, if we have remote OOB data there's no point in sending our public key to the remote if the OOB data doesn't match. This patch moves the test for this higher up in the smp_cmd_public_key() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index de53ba1905fe..82824213d0fb 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2515,6 +2515,16 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(smp->remote_pk, key, 64); + if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) { + err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk, + smp->rr, 0, cfm.confirm_val); + if (err) + return SMP_UNSPECIFIED; + + if (memcmp(cfm.confirm_val, smp->pcnf, 16)) + return SMP_CONFIRM_FAILED; + } + /* Non-initiating device sends its public key after receiving * the key from the initiating device. */ @@ -2562,16 +2572,6 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY); } - if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) { - err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk, - smp->rr, 0, cfm.confirm_val); - if (err) - return SMP_UNSPECIFIED; - - if (memcmp(cfm.confirm_val, smp->pcnf, 16)) - return SMP_CONFIRM_FAILED; - } - if (smp->method == REQ_OOB) { if (hcon->out) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, From cb06d366fbf88f3923951d862f8c5b03fb483b43 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Mar 2015 21:12:34 +0200 Subject: [PATCH 34/59] Bluetooth: Add clarifying comment when setting local OOB flag It might be a bit counterintuitive to set a 'local' flag based on remote data. This patch adds a clarifying comment to the pairing req/rsp handlers when setting the LOCAL_OOB flag based on the PDU received from the remote side. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 82824213d0fb..1bd281060de2 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1735,6 +1735,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); + /* If the remote side's OOB flag is set it means it has + * successfully received our local OOB data - therefore set the + * flag to indicate that local OOB is in use. + */ if (req->oob_flag == SMP_OOB_PRESENT) set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags); @@ -1902,6 +1906,10 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC)) return SMP_AUTH_REQUIREMENTS; + /* If the remote side's OOB flag is set it means it has + * successfully received our local OOB data - therefore set the + * flag to indicate that local OOB is in use. + */ if (rsp->oob_flag == SMP_OOB_PRESENT) set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags); From e091526dfdaa955a7481a696094ac9e5d1bdb0fe Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 12:34:55 -0700 Subject: [PATCH 35/59] Bluetooth: Use smp->remote_pk + 32 instead of &smp->remote_pk[32] Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1bd281060de2..2b5c13932b0a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2543,7 +2543,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) } SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk); - SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]); + SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32); if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey)) return SMP_UNSPECIFIED; From bc07cd696e1863d082fdc8650351b288bd41629b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 12:34:56 -0700 Subject: [PATCH 36/59] Bluetooth: Add extra SMP_DBG statement for remote OOB data Just for pure debugging purposes print the remote out-of-band data that has been received and is going to be used. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2b5c13932b0a..ebe69e91fb6c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -685,6 +685,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn, oob_flag = SMP_OOB_PRESENT; memcpy(smp->rr, oob_data->rand256, 16); memcpy(smp->pcnf, oob_data->hash256, 16); + SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf); + SMP_DBG("OOB Remote Random: %16phN", smp->rr); } } else { From fb334fee60ebd1aea23cc5daf3abac10139c9a24 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 12:34:57 -0700 Subject: [PATCH 37/59] Bluetooth: Rename smp->local_rr into smp->local_rand The variable for the out-of-band random number was badly named and with that confusing. Just rename it to local_rand so it is clear what value it represents. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index ebe69e91fb6c..60180b47ce71 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -78,7 +78,7 @@ struct smp_dev { /* Secure Connections OOB data */ u8 local_pk[64]; u8 local_sk[32]; - u8 local_rr[16]; + u8 local_rand[16]; bool debug_key; struct crypto_blkcipher *tfm_aes; @@ -569,14 +569,14 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) SMP_DBG("OOB Public Key Y: %32phN", smp->local_pk + 32); SMP_DBG("OOB Private Key: %32phN", smp->local_sk); - get_random_bytes(smp->local_rr, 16); + get_random_bytes(smp->local_rand, 16); err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->local_pk, - smp->local_rr, 0, hash); + smp->local_rand, 0, hash); if (err < 0) return err; - memcpy(rand, smp->local_rr, 16); + memcpy(rand, smp->local_rand, 16); return 0; } @@ -1841,7 +1841,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) memcpy(smp->local_pk, smp_dev->local_pk, 64); memcpy(smp->local_sk, smp_dev->local_sk, 32); - memcpy(smp->lr, smp_dev->local_rr, 16); + memcpy(smp->lr, smp_dev->local_rand, 16); if (smp_dev->debug_key) set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); From b880ab869c4e1e7cadb040d4ee9b189aebd6cb38 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 12:34:58 -0700 Subject: [PATCH 38/59] Bluetooth: The P-256 randomizer is 16 octets long and not 19 octets This seems to be a simple typo in the debugfs entry for the remote out-of-band data entries. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index bc801e9db834..0818fabf346a 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -166,7 +166,7 @@ static int remote_oob_show(struct seq_file *f, void *ptr) seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n", &data->bdaddr, data->bdaddr_type, data->present, 16, data->hash192, 16, data->rand192, - 16, data->hash256, 19, data->rand256); + 16, data->hash256, 16, data->rand256); } hci_dev_unlock(hdev); From 5082a59965d4f13245658b2927b6c021f17ed88e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 12:39:00 -0700 Subject: [PATCH 39/59] Bluetooth: Do not include LE SC out-of-band data if not enabled In case LE Secure Connections is not enabled, then the command for returning local out-of-band data should not include the confirmation and random value for LE SC pairing. All other fields are still valid, but these two need to be left out. In that case it is also no needed to generate the public/private key pair for out-of-band pairing. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5322584460c1..32c2c75c0888 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6327,12 +6327,13 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, hdev->dev_class, 3); break; case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): - if (smp_generate_oob(hdev, hash, rand) < 0) { + if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) && + smp_generate_oob(hdev, hash, rand) < 0) { hci_dev_unlock(hdev); err = mgmt_cmd_complete(sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_EXT_DATA, - MGMT_STATUS_FAILED, - &cp->type, sizeof(cp->type)); + MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + MGMT_STATUS_FAILED, + &cp->type, sizeof(cp->type)); goto done; } @@ -6361,11 +6362,15 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE, &role, sizeof(role)); - eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_SC_CONFIRM, - hash, sizeof(hash)); + if (hci_dev_test_flag(hdev, HCI_SC_ENABLED)) { + eir_len = eir_append_data(rp->eir, eir_len, + EIR_LE_SC_CONFIRM, + hash, sizeof(hash)); - eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_SC_RANDOM, - rand, sizeof(rand)); + eir_len = eir_append_data(rp->eir, eir_len, + EIR_LE_SC_RANDOM, + rand, sizeof(rand)); + } flags = get_adv_discov_flags(hdev); From 5425f98e863ac5e4798a186475d4a8d95a2c08e8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 16:05:44 -0700 Subject: [PATCH 40/59] Bluetooth: Fix length for Read Local OOB Extended Data respone packet The length of the respone packet for Read Local OOB Extended Data command has a calculation error. In case LE Secure Connections support is not enabled, the actual response is shorter. Keep this in mind and update the value accordingly. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 32c2c75c0888..7fa3c4b8384b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6388,7 +6388,7 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, hci_dev_unlock(hdev); err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, - MGMT_STATUS_SUCCESS, rp, rp_len); + MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len); done: kfree(rp); From 72000df2c01d6927319ad7e3f43460f6d0227de5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 16:11:21 -0700 Subject: [PATCH 41/59] Bluetooth: Add support for Local OOB Extended Data Update events When a different user requests a new set of local out-of-band data, then inform all previous users that the data has been updated. To limit the scope of users, the updates are limited to previous users. If a user has never requested out-of-band data, it will also not see the update. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 7 +++++++ net/bluetooth/mgmt.c | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ce757303dc07..a6ea156dc7e9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -186,6 +186,7 @@ enum { HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_EXT_INDEX_EVENTS, HCI_MGMT_GENERIC_EVENTS, + HCI_MGMT_OOB_DATA_EVENTS, }; /* diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 543c1ba3d892..a1a68671bf88 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -735,3 +735,10 @@ struct mgmt_ev_ext_index { #define MGMT_EV_EXT_INDEX_ADDED 0x0020 #define MGMT_EV_EXT_INDEX_REMOVED 0x0021 + +#define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022 +struct mgmt_ev_local_oob_data_updated { + __u8 type; + __le16 eir_len; + __u8 eir[0]; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7fa3c4b8384b..72e41d29e301 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -133,6 +133,7 @@ static const u16 mgmt_events[] = { MGMT_EV_NEW_CONFIG_OPTIONS, MGMT_EV_EXT_INDEX_ADDED, MGMT_EV_EXT_INDEX_REMOVED, + MGMT_EV_LOCAL_OOB_DATA_UPDATED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -262,6 +263,13 @@ static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, flag, NULL); } +static int mgmt_limited_event(u16 event, struct hci_dev *hdev, void *data, + u16 len, int flag, struct sock *skip_sk) +{ + return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, + flag, skip_sk); +} + static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data, u16 len, struct sock *skip_sk) { @@ -6387,8 +6395,16 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, hci_dev_unlock(hdev); + hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS); + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len); + if (err < 0) + goto done; + + err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev, + rp, sizeof(*rp) + eir_len, + HCI_MGMT_OOB_DATA_EVENTS, sk); done: kfree(rp); From c9e44474f27e251fcdc1b52d7bd0a7607af4473a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Mar 2015 23:56:04 -0700 Subject: [PATCH 42/59] Bluetooth: btusb: Fix minor whitespace issue in QCA ROME device entries Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 6fa9338745cf..f6ef75f4d7cc 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -215,8 +215,8 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 }, /* QCA ROME chipset */ - { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME}, - { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME}, + { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME }, /* Broadcom BCM2035 */ { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, From d907c4f05cc7cb18d0f41f23cadf7f8ec10bfd29 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 17 Mar 2015 10:32:39 +0100 Subject: [PATCH 43/59] at86rf230: fix at86rf230_read_subreg succeed handling This patch fix an issue when at86rf230_read_subreg was successful. The function at86rf230_read_subreg will directly call regmap_read which returns zero on successful. Nobody figured out issues yet because it was only necessary to evaluate dvdd state while probing. This could make trouble because the stack variable could have an random value. The function is also used by reset the irq line before requesting irq, but the value isn't evaluated afterwards. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/at86rf230.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index b64c5c7b2a50..cc5efa149da1 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -325,7 +325,7 @@ at86rf230_read_subreg(struct at86rf230_local *lp, int rc; rc = __at86rf230_read(lp, addr, data); - if (rc > 0) + if (!rc) *data = (*data & mask) >> shift; return rc; From 695c4cb61926ddef2481dbcefef44498a7c1e5b5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:12 -0700 Subject: [PATCH 44/59] Bluetooth: Introduce HCI_QUIRK_SIMULTANEOUS_DISCOVERY Some controllers allow both LE scan and BR/EDR inquiry to run at the same time, while others allow only one, LE SCAN or BR/EDR inquiry at given time. Since this is specific to each controller, add a new quirk setting that allows drivers to tell the core wether given controller can do both LE scan and BR/EDR inquiry at same time. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a6ea156dc7e9..06e7eee31ce4 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -160,6 +160,14 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_STRICT_DUPLICATE_FILTER, + + /* When this quirk is set, LE scan and BR/EDR inquiry is done + * simultaneously, otherwise it's interleaved. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_SIMULTANEOUS_DISCOVERY, }; /* HCI device flags */ From 812abb13a97b7049c883ffb8431b81eb404a0938 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:13 -0700 Subject: [PATCH 45/59] Bluetooth: Refactor BR/EDR inquiry and LE scan triggering. This patch refactor BR/EDR inquiry and LE scan triggering logic into separate methods. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 154 ++++++++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 67 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 72e41d29e301..5a4b9d5a224f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3921,93 +3921,113 @@ done: return err; } -static bool trigger_discovery(struct hci_request *req, u8 *status) +static bool trigger_bredr_inquiry(struct hci_request *req, u8 *status) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_inquiry cp; + /* General inquiry access code (GIAC) */ + u8 lap[3] = { 0x33, 0x8b, 0x9e }; + + *status = mgmt_bredr_support(hdev); + if (*status) + return false; + + if (hci_dev_test_flag(hdev, HCI_INQUIRY)) { + *status = MGMT_STATUS_BUSY; + return false; + } + + hci_inquiry_cache_flush(hdev); + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.lap, lap, sizeof(cp.lap)); + cp.length = DISCOV_BREDR_INQUIRY_LEN; + + hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp); + + return true; +} + +static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status) { struct hci_dev *hdev = req->hdev; struct hci_cp_le_set_scan_param param_cp; struct hci_cp_le_set_scan_enable enable_cp; - struct hci_cp_inquiry inq_cp; - /* General inquiry access code (GIAC) */ - u8 lap[3] = { 0x33, 0x8b, 0x9e }; u8 own_addr_type; int err; - switch (hdev->discovery.type) { - case DISCOV_TYPE_BREDR: - *status = mgmt_bredr_support(hdev); - if (*status) - return false; + *status = mgmt_le_support(hdev); + if (*status) + return false; - if (test_bit(HCI_INQUIRY, &hdev->flags)) { - *status = MGMT_STATUS_BUSY; + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) { + /* Don't let discovery abort an outgoing connection attempt + * that's using directed advertising. + */ + if (hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { + *status = MGMT_STATUS_REJECTED; return false; } - hci_inquiry_cache_flush(hdev); + disable_advertising(req); + } - memset(&inq_cp, 0, sizeof(inq_cp)); - memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap)); - inq_cp.length = DISCOV_BREDR_INQUIRY_LEN; - hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp); + /* If controller is scanning, it means the background scanning is + * running. Thus, we should temporarily stop it in order to set the + * discovery scanning parameters. + */ + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) + hci_req_add_le_scan_disable(req); + + /* All active scans will be done with either a resolvable private + * address (when privacy feature has been enabled) or non-resolvable + * private address. + */ + err = hci_update_random_address(req, true, &own_addr_type); + if (err < 0) { + *status = MGMT_STATUS_FAILED; + return false; + } + + memset(¶m_cp, 0, sizeof(param_cp)); + param_cp.type = LE_SCAN_ACTIVE; + param_cp.interval = cpu_to_le16(interval); + param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN); + param_cp.own_address_type = own_addr_type; + + hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; + + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); + + return true; +} + +static bool trigger_discovery(struct hci_request *req, u8 *status) +{ + struct hci_dev *hdev = req->hdev; + + switch (hdev->discovery.type) { + case DISCOV_TYPE_BREDR: + if (!trigger_bredr_inquiry(req, status)) + return false; break; - case DISCOV_TYPE_LE: case DISCOV_TYPE_INTERLEAVED: - *status = mgmt_le_support(hdev); - if (*status) - return false; - - if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && - !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { *status = MGMT_STATUS_NOT_SUPPORTED; return false; } + /* fall through */ - if (hci_dev_test_flag(hdev, HCI_LE_ADV)) { - /* Don't let discovery abort an outgoing - * connection attempt that's using directed - * advertising. - */ - if (hci_conn_hash_lookup_state(hdev, LE_LINK, - BT_CONNECT)) { - *status = MGMT_STATUS_REJECTED; - return false; - } - - disable_advertising(req); - } - - /* If controller is scanning, it means the background scanning - * is running. Thus, we should temporarily stop it in order to - * set the discovery scanning parameters. - */ - if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) - hci_req_add_le_scan_disable(req); - - memset(¶m_cp, 0, sizeof(param_cp)); - - /* All active scans will be done with either a resolvable - * private address (when privacy feature has been enabled) - * or non-resolvable private address. - */ - err = hci_update_random_address(req, true, &own_addr_type); - if (err < 0) { - *status = MGMT_STATUS_FAILED; + case DISCOV_TYPE_LE: + if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT, status)) return false; - } - - param_cp.type = LE_SCAN_ACTIVE; - param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT); - param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN); - param_cp.own_address_type = own_addr_type; - hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), - ¶m_cp); - - memset(&enable_cp, 0, sizeof(enable_cp)); - enable_cp.enable = LE_SCAN_ENABLE; - enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; - hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), - &enable_cp); break; default: From 07d2334ae747772fc4426077340aa3efe499f50b Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:14 -0700 Subject: [PATCH 46/59] Bluetooth: Add simultaneous dual mode scan When doing scan through mgmt api, some controllers can do both le and classic scan at same time. They can be distinguished by HCI_QUIRK_SIMULTANEOUS_DISCOVERY set. This patch enables them to use this feature when doing dual mode scan. Instead of doing le, then classic scan, both scans are run at once. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 24 +++++++++++++++++++----- net/bluetooth/hci_event.c | 22 ++++++++++++++++++++-- net/bluetooth/mgmt.c | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 750d3445f2d2..773f2164d9a1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2902,12 +2902,26 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); - hci_inquiry_cache_flush(hdev); + if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, + &hdev->quirks)) { + /* If we were running LE only scan, change discovery + * state. If we were running both LE and BR/EDR inquiry + * simultaneously, and BR/EDR inquiry is already + * finished, stop discovery, otherwise BR/EDR inquiry + * will stop discovery when finished. + */ + if (!test_bit(HCI_INQUIRY, &hdev->flags)) + hci_discovery_set_state(hdev, + DISCOVERY_STOPPED); + } else { + hci_inquiry_cache_flush(hdev); - err = hci_req_run(&req, inquiry_complete); - if (err) { - BT_ERR("Inquiry request failed: err %d", err); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + err = hci_req_run(&req, inquiry_complete); + if (err) { + BT_ERR("Inquiry request failed: err %d", err); + hci_discovery_set_state(hdev, + DISCOVERY_STOPPED); + } } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d800f0c5aa21..62f92a508961 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2126,7 +2126,16 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) goto unlock; if (list_empty(&discov->resolve)) { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + /* When BR/EDR inquiry is active and no LE scanning is in + * progress, then change discovery state to indicate completion. + * + * When running LE scanning and BR/EDR inquiry simultaneously + * and the LE scan already finished, then change the discovery + * state to indicate completion. + */ + if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) || + !test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks)) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } @@ -2135,7 +2144,16 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) e->name_state = NAME_PENDING; hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); } else { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + /* When BR/EDR inquiry is active and no LE scanning is in + * progress, then change discovery state to indicate completion. + * + * When running LE scanning and BR/EDR inquiry simultaneously + * and the LE scan already finished, then change the discovery + * state to indicate completion. + */ + if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) || + !test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks)) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); } unlock: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5a4b9d5a224f..7bcdf61afe11 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1408,9 +1408,10 @@ static bool hci_stop_discovery(struct hci_request *req) switch (hdev->discovery.state) { case DISCOVERY_FINDING: - if (test_bit(HCI_INQUIRY, &hdev->flags)) { + if (test_bit(HCI_INQUIRY, &hdev->flags)) hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL); - } else { + + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) { cancel_delayed_work(&hdev->le_scan_disable); hci_req_add_le_scan_disable(req); } @@ -4019,6 +4020,22 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) break; case DISCOV_TYPE_INTERLEAVED: + if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, + &hdev->quirks)) { + /* During simultaneous discovery, we double LE scan + * interval. We must leave some time for the controller + * to do BR/EDR inquiry. + */ + if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT * 2, + status)) + return false; + + if (!trigger_bredr_inquiry(req, status)) + return false; + + return true; + } + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { *status = MGMT_STATUS_NOT_SUPPORTED; return false; @@ -4072,7 +4089,18 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status, timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT); break; case DISCOV_TYPE_INTERLEAVED: - timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout); + /* When running simultaneous discovery, the LE scanning time + * should occupy the whole discovery time sine BR/EDR inquiry + * and LE scanning are scheduled by the controller. + * + * For interleaving discovery in comparison, BR/EDR inquiry + * and LE scanning are done sequentially with separate + * timeouts. + */ + if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks)) + timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT); + else + timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout); break; case DISCOV_TYPE_BREDR: timeout = 0; From 3d50d51a530d7c672cfbac03fef365b1fa7a244e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:15 -0700 Subject: [PATCH 47/59] Bluetooth: Set HCI_QUIRK_SIMULTANEOUS_DISCOVERY for BTUSB_ATH3012 Atheros controllers can do both LE scan and BR/EDR inquiry at once. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f6ef75f4d7cc..321bfeb2bc2f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3042,6 +3042,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_ATH3012) { hdev->set_bdaddr = btusb_set_bdaddr_ath3012; + set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); } From c11548427deded1c742888c64c8b07530f8134e6 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:16 -0700 Subject: [PATCH 48/59] Bluetooth: Set HCI_QUIRK_SIMULTANEOUS_DISCOVERY for BTUSB_INTEL Intel controllers can do both LE scan and BR/EDR inquiry at once. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 321bfeb2bc2f..21df7b231f0a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3019,6 +3019,7 @@ static int btusb_probe(struct usb_interface *intf, hdev->shutdown = btusb_shutdown_intel; hdev->set_bdaddr = btusb_set_bdaddr_intel; set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); } if (id->driver_info & BTUSB_INTEL_NEW) { From 49c989a0d6cd097f00f45e5c180bf3e315ca2bfa Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Mar 2015 09:04:17 -0700 Subject: [PATCH 49/59] Bluetooth: Set HCI_QUIRK_SIMULTANEOUS_DISCOVERY for BTUSB_CSR CSR controllers can do both LE scan and BR/EDR inquiry at once. Signed-off-by: Jakub Pawlowski Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 21df7b231f0a..708b6574d805 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3087,6 +3087,8 @@ static int btusb_probe(struct usb_interface *intf, /* Fake CSR devices with broken commands */ if (bcdDevice <= 0x100) hdev->setup = btusb_setup_csr; + + set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); } if (id->driver_info & BTUSB_SNIFFER) { From d0f172b14afa7e0d8a19db5baa07e20b3ec8dcc8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:46 +0200 Subject: [PATCH 50/59] Bluetooth: Add helper to get HCI channel of a socket We'll need to have access to which HCI channel a socket is bound to, in order to manage pending mgmt commands in clean way. This patch adds a helper for the purpose. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/hci_sock.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index d871ba313f64..33a5e00025aa 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -338,6 +338,7 @@ int bt_to_errno(__u16 code); void hci_sock_set_flag(struct sock *sk, int nr); void hci_sock_clear_flag(struct sock *sk, int nr); int hci_sock_test_flag(struct sock *sk, int nr); +unsigned short hci_sock_get_channel(struct sock *sk); int hci_sock_init(void); void hci_sock_cleanup(void); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 7c719602dbca..9ba1a2667eaa 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -65,6 +65,11 @@ int hci_sock_test_flag(struct sock *sk, int nr) return test_bit(nr, &hci_pi(sk)->flags); } +unsigned short hci_sock_get_channel(struct sock *sk) +{ + return hci_pi(sk)->channel; +} + static inline int hci_test_bit(int nr, const void *addr) { return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); From 333ae95d054519f64303f0e149b4b9653cc4635b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:47 +0200 Subject: [PATCH 51/59] Bluetooth: Add channel parameter to mgmt_pending_find() API To be able to have pending commands for different HCI channels we need to be able to distinguish for which channel a command was sent to. The channel information is already part of the socket data and can be fetched using the recently added hci_sock_get_channel() function. To not require all mgmt.c code to pass an extra channel parameter this patch also adds a helper pending_find() & pending_find_data() functions which act as a wrapper to the new mgmt_pending_find() & mgmt_pending_find_data() APIs. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 108 +++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7bcdf61afe11..3d7dbdde7008 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -882,12 +882,15 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) return ptr; } -static struct mgmt_pending_cmd *mgmt_pending_find(u16 opcode, +static struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, + u16 opcode, struct hci_dev *hdev) { struct mgmt_pending_cmd *cmd; list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (hci_sock_get_channel(cmd->sk) != channel) + continue; if (cmd->opcode == opcode) return cmd; } @@ -895,7 +898,13 @@ static struct mgmt_pending_cmd *mgmt_pending_find(u16 opcode, return NULL; } -static struct mgmt_pending_cmd *mgmt_pending_find_data(u16 opcode, +static struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev) +{ + return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev); +} + +static struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel, + u16 opcode, struct hci_dev *hdev, const void *data) { @@ -911,6 +920,13 @@ static struct mgmt_pending_cmd *mgmt_pending_find_data(u16 opcode, return NULL; } +static struct mgmt_pending_cmd *pending_find_data(u16 opcode, + struct hci_dev *hdev, + const void *data) +{ + return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data); +} + static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) { u8 ad_len = 0; @@ -969,7 +985,7 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev) /* If there's a pending mgmt command the flags will not yet have * their final values, so check for this first. */ - cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); + cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); if (cmd) { struct mgmt_mode *cp = cmd->param; if (cp->val == 0x01) @@ -1177,7 +1193,7 @@ static bool get_connectable(struct hci_dev *hdev) /* If there's a pending mgmt command the flag will not yet have * it's final value, so check for this first. */ - cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev); + cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev); if (cmd) { struct mgmt_mode *cp = cmd->param; return cp->val; @@ -1517,7 +1533,7 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { + if (pending_find(MGMT_OP_SET_POWERED, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, MGMT_STATUS_BUSY); goto failed; @@ -1669,7 +1685,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); + cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); if (!cmd) goto unlock; @@ -1754,8 +1770,8 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { + if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || + pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_BUSY); goto failed; @@ -1926,7 +1942,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev); + cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev); if (!cmd) goto unlock; @@ -2021,8 +2037,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { + if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || + pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, MGMT_STATUS_BUSY); goto failed; @@ -2161,7 +2177,7 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { + if (pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, MGMT_STATUS_BUSY); goto failed; @@ -2240,7 +2256,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { + if (pending_find(MGMT_OP_SET_SSP, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY); goto failed; @@ -2299,7 +2315,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_dev_lock(hdev); - if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { + if (pending_find(MGMT_OP_SET_SSP, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_BUSY); goto unlock; @@ -2421,8 +2437,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto unlock; } - if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) || - mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) { + if (pending_find(MGMT_OP_SET_LE, hdev) || + pending_find(MGMT_OP_SET_ADVERTISING, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_BUSY); goto unlock; @@ -2506,7 +2522,7 @@ static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status) hci_dev_lock(hdev); - cmd = mgmt_pending_find(mgmt_op, hdev); + cmd = pending_find(mgmt_op, hdev); if (!cmd) goto unlock; @@ -3000,7 +3016,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { + if (pending_find(MGMT_OP_DISCONNECT, hdev)) { err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, MGMT_STATUS_BUSY, &rp, sizeof(rp)); goto failed; @@ -3464,7 +3480,7 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev); + cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev); if (!cmd) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); @@ -3642,7 +3658,7 @@ static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode) hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); + cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); if (!cmd) goto unlock; @@ -3752,7 +3768,7 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { + if (pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, MGMT_STATUS_BUSY); goto unlock; @@ -4065,9 +4081,9 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); + cmd = pending_find(MGMT_OP_START_DISCOVERY, hdev); if (!cmd) - cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev); + cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev); if (cmd) { cmd->cmd_complete(cmd, mgmt_status(status)); @@ -4322,7 +4338,7 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode) hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); + cmd = pending_find(MGMT_OP_STOP_DISCOVERY, hdev); if (cmd) { cmd->cmd_complete(cmd, mgmt_status(status)); mgmt_pending_remove(cmd); @@ -4631,8 +4647,8 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) || - mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { + if (pending_find(MGMT_OP_SET_ADVERTISING, hdev) || + pending_find(MGMT_OP_SET_LE, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, MGMT_STATUS_BUSY); goto unlock; @@ -4775,7 +4791,7 @@ static void fast_connectable_complete(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev); + cmd = pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev); if (!cmd) goto unlock; @@ -4821,7 +4837,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { + if (pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_BUSY); goto unlock; @@ -4873,7 +4889,7 @@ static void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode) hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev); + cmd = pending_find(MGMT_OP_SET_BREDR, hdev); if (!cmd) goto unlock; @@ -4973,7 +4989,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } } - if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) { + if (pending_find(MGMT_OP_SET_BREDR, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, MGMT_STATUS_BUSY); goto unlock; @@ -5018,7 +5034,7 @@ static void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev); + cmd = pending_find(MGMT_OP_SET_SECURE_CONN, hdev); if (!cmd) goto unlock; @@ -5109,7 +5125,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) { + if (pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_BUSY); goto failed; @@ -5502,7 +5518,7 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status, goto unlock; } - cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn); + cmd = pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn); if (!cmd) goto unlock; @@ -5555,7 +5571,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) { + if (pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) { err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, MGMT_STATUS_BUSY, &rp, sizeof(rp)); goto unlock; @@ -5691,7 +5707,7 @@ static void get_clock_info_complete(struct hci_dev *hdev, u8 status, u16 opcode) conn = NULL; } - cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn); + cmd = pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn); if (!cmd) goto unlock; @@ -5857,7 +5873,7 @@ static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode) hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_ADD_DEVICE, hdev); + cmd = pending_find(MGMT_OP_ADD_DEVICE, hdev); if (!cmd) goto unlock; @@ -5980,7 +5996,7 @@ static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode) hci_dev_lock(hdev); - cmd = mgmt_pending_find(MGMT_OP_REMOVE_DEVICE, hdev); + cmd = pending_find(MGMT_OP_REMOVE_DEVICE, hdev); if (!cmd) goto unlock; @@ -6937,7 +6953,7 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err) struct mgmt_pending_cmd *cmd; u8 status; - cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev); + cmd = pending_find(MGMT_OP_SET_POWERED, hdev); if (!cmd) return; @@ -7202,7 +7218,7 @@ bool mgmt_powering_down(struct hci_dev *hdev) struct mgmt_pending_cmd *cmd; struct mgmt_mode *cp; - cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev); + cmd = pending_find(MGMT_OP_SET_POWERED, hdev); if (!cmd) return false; @@ -7259,7 +7275,7 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, hdev); - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); + cmd = pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return; @@ -7311,7 +7327,7 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct mgmt_pending_cmd *cmd; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); + cmd = pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); if (!cmd) return; @@ -7324,7 +7340,7 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct mgmt_pending_cmd *cmd; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); + cmd = pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); if (!cmd) return; @@ -7369,7 +7385,7 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct mgmt_pending_cmd *cmd; - cmd = mgmt_pending_find(opcode, hdev); + cmd = pending_find(opcode, hdev); if (!cmd) return -ENOENT; @@ -7580,14 +7596,14 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); + cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); if (!cmd) { memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); /* If this is a HCI command related to powering on the * HCI dev don't send any mgmt signals. */ - if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) + if (pending_find(MGMT_OP_SET_POWERED, hdev)) return; } @@ -7603,7 +7619,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, BT_DBG("%s status %u", hdev->name, status); - cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); + cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); if (!cmd) return; From a380b6cff1a2d2139772e88219d08330f84d0381 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:48 +0200 Subject: [PATCH 52/59] Bluetooth: Add generic mgmt helper API There are several mgmt protocol features that will be needed by more than just the current HCI_CHANNEL_CONTROL. These include sending generic events as well as handling pending commands. This patch moves these functions out from mgmt.c to a new mgmt_util.c file. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/Makefile | 2 +- net/bluetooth/mgmt.c | 197 +---------------------------------- net/bluetooth/mgmt_util.c | 210 ++++++++++++++++++++++++++++++++++++++ net/bluetooth/mgmt_util.h | 53 ++++++++++ 4 files changed, 265 insertions(+), 197 deletions(-) create mode 100644 net/bluetooth/mgmt_util.c create mode 100644 net/bluetooth/mgmt_util.h diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 5d608799717e..9a8ea232d28f 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -13,7 +13,7 @@ bluetooth_6lowpan-y := 6lowpan.o bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o amp.o ecc.o hci_request.o + a2mp.o amp.o ecc.o hci_request.o mgmt_util.o bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3d7dbdde7008..915a2a1f0a71 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -35,6 +35,7 @@ #include "hci_request.h" #include "smp.h" +#include "mgmt_util.h" #define MGMT_VERSION 1 #define MGMT_REVISION 9 @@ -141,17 +142,6 @@ static const u16 mgmt_events[] = { #define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00" -struct mgmt_pending_cmd { - struct list_head list; - u16 opcode; - int index; - void *param; - size_t param_len; - struct sock *sk; - void *user_data; - int (*cmd_complete)(struct mgmt_pending_cmd *cmd, u8 status); -}; - /* HCI to MGMT error code conversion table */ static u8 mgmt_status_table[] = { MGMT_STATUS_SUCCESS, @@ -225,37 +215,6 @@ static u8 mgmt_status(u8 hci_status) return MGMT_STATUS_FAILED; } -static int mgmt_send_event(u16 event, struct hci_dev *hdev, - unsigned short channel, void *data, u16 data_len, - int flag, struct sock *skip_sk) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - - skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(event); - if (hdev) - hdr->index = cpu_to_le16(hdev->id); - else - hdr->index = cpu_to_le16(MGMT_INDEX_NONE); - hdr->len = cpu_to_le16(data_len); - - if (data) - memcpy(skb_put(skb, data_len), data, data_len); - - /* Time stamp */ - __net_timestamp(skb); - - hci_send_to_channel(channel, skb, flag, skip_sk); - kfree_skb(skb); - - return 0; -} - static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, u16 len, int flag) { @@ -284,70 +243,6 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, HCI_SOCK_TRUSTED, skip_sk); } -static int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_status *ev; - int err; - - BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); - hdr->index = cpu_to_le16(index); - hdr->len = cpu_to_le16(sizeof(*ev)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - ev->status = status; - ev->opcode = cpu_to_le16(cmd); - - err = sock_queue_rcv_skb(sk, skb); - if (err < 0) - kfree_skb(skb); - - return err; -} - -static int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, - void *rp, size_t rp_len) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - int err; - - BT_DBG("sock %p", sk); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->index = cpu_to_le16(index); - hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); - - ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); - ev->opcode = cpu_to_le16(cmd); - ev->status = status; - - if (rp) - memcpy(ev->data, rp, rp_len); - - err = sock_queue_rcv_skb(sk, skb); - if (err < 0) - kfree_skb(skb); - - return err; -} - static int read_version(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -882,44 +777,11 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) return ptr; } -static struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, - u16 opcode, - struct hci_dev *hdev) -{ - struct mgmt_pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (hci_sock_get_channel(cmd->sk) != channel) - continue; - if (cmd->opcode == opcode) - return cmd; - } - - return NULL; -} - static struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev) { return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev); } -static struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel, - u16 opcode, - struct hci_dev *hdev, - const void *data) -{ - struct mgmt_pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->user_data != data) - continue; - if (cmd->opcode == opcode) - return cmd; - } - - return NULL; -} - static struct mgmt_pending_cmd *pending_find_data(u16 opcode, struct hci_dev *hdev, const void *data) @@ -1341,63 +1203,6 @@ static int read_controller_info(struct sock *sk, struct hci_dev *hdev, sizeof(rp)); } -static void mgmt_pending_free(struct mgmt_pending_cmd *cmd) -{ - sock_put(cmd->sk); - kfree(cmd->param); - kfree(cmd); -} - -static struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, - struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_pending_cmd *cmd; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) - return NULL; - - cmd->opcode = opcode; - cmd->index = hdev->id; - - cmd->param = kmemdup(data, len, GFP_KERNEL); - if (!cmd->param) { - kfree(cmd); - return NULL; - } - - cmd->param_len = len; - - cmd->sk = sk; - sock_hold(sk); - - list_add(&cmd->list, &hdev->mgmt_pending); - - return cmd; -} - -static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, - void (*cb)(struct mgmt_pending_cmd *cmd, - void *data), - void *data) -{ - struct mgmt_pending_cmd *cmd, *tmp; - - list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) { - if (opcode > 0 && cmd->opcode != opcode) - continue; - - cb(cmd, data); - } -} - -static void mgmt_pending_remove(struct mgmt_pending_cmd *cmd) -{ - list_del(&cmd->list); - mgmt_pending_free(cmd); -} - static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) { __le32 settings = cpu_to_le32(get_current_settings(hdev)); diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c new file mode 100644 index 000000000000..8c30c7eb8bef --- /dev/null +++ b/net/bluetooth/mgmt_util.c @@ -0,0 +1,210 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + + Copyright (C) 2015 Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#include +#include +#include + +#include "mgmt_util.h" + +int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel, + void *data, u16 data_len, int flag, struct sock *skip_sk) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + + skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(event); + if (hdev) + hdr->index = cpu_to_le16(hdev->id); + else + hdr->index = cpu_to_le16(MGMT_INDEX_NONE); + hdr->len = cpu_to_le16(data_len); + + if (data) + memcpy(skb_put(skb, data_len), data, data_len); + + /* Time stamp */ + __net_timestamp(skb); + + hci_send_to_channel(channel, skb, flag, skip_sk); + kfree_skb(skb); + + return 0; +} + +int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_status *ev; + int err; + + BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); + hdr->index = cpu_to_le16(index); + hdr->len = cpu_to_le16(sizeof(*ev)); + + ev = (void *) skb_put(skb, sizeof(*ev)); + ev->status = status; + ev->opcode = cpu_to_le16(cmd); + + err = sock_queue_rcv_skb(sk, skb); + if (err < 0) + kfree_skb(skb); + + return err; +} + +int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, + void *rp, size_t rp_len) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + int err; + + BT_DBG("sock %p", sk); + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->index = cpu_to_le16(index); + hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); + + ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); + ev->opcode = cpu_to_le16(cmd); + ev->status = status; + + if (rp) + memcpy(ev->data, rp, rp_len); + + err = sock_queue_rcv_skb(sk, skb); + if (err < 0) + kfree_skb(skb); + + return err; +} + +struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode, + struct hci_dev *hdev) +{ + struct mgmt_pending_cmd *cmd; + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (hci_sock_get_channel(cmd->sk) != channel) + continue; + if (cmd->opcode == opcode) + return cmd; + } + + return NULL; +} + +struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel, + u16 opcode, + struct hci_dev *hdev, + const void *data) +{ + struct mgmt_pending_cmd *cmd; + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->user_data != data) + continue; + if (cmd->opcode == opcode) + return cmd; + } + + return NULL; +} + +void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, + void (*cb)(struct mgmt_pending_cmd *cmd, void *data), + void *data) +{ + struct mgmt_pending_cmd *cmd, *tmp; + + list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) { + if (opcode > 0 && cmd->opcode != opcode) + continue; + + cb(cmd, data); + } +} + +struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, + struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return NULL; + + cmd->opcode = opcode; + cmd->index = hdev->id; + + cmd->param = kmemdup(data, len, GFP_KERNEL); + if (!cmd->param) { + kfree(cmd); + return NULL; + } + + cmd->param_len = len; + + cmd->sk = sk; + sock_hold(sk); + + list_add(&cmd->list, &hdev->mgmt_pending); + + return cmd; +} + +void mgmt_pending_free(struct mgmt_pending_cmd *cmd) +{ + sock_put(cmd->sk); + kfree(cmd->param); + kfree(cmd); +} + +void mgmt_pending_remove(struct mgmt_pending_cmd *cmd) +{ + list_del(&cmd->list); + mgmt_pending_free(cmd); +} diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h new file mode 100644 index 000000000000..6559f189213c --- /dev/null +++ b/net/bluetooth/mgmt_util.h @@ -0,0 +1,53 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2015 Intel Coropration + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +struct mgmt_pending_cmd { + struct list_head list; + u16 opcode; + int index; + void *param; + size_t param_len; + struct sock *sk; + void *user_data; + int (*cmd_complete)(struct mgmt_pending_cmd *cmd, u8 status); +}; + +int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel, + void *data, u16 data_len, int flag, struct sock *skip_sk); +int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status); +int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, + void *rp, size_t rp_len); + +struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode, + struct hci_dev *hdev); +struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel, + u16 opcode, + struct hci_dev *hdev, + const void *data); +void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, + void (*cb)(struct mgmt_pending_cmd *cmd, void *data), + void *data); +struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, + struct hci_dev *hdev, + void *data, u16 len); +void mgmt_pending_free(struct mgmt_pending_cmd *cmd); +void mgmt_pending_remove(struct mgmt_pending_cmd *cmd); From 88b94ce925cb103851f39bfa7e23e09823573d30 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:49 +0200 Subject: [PATCH 53/59] Bluetooth: Add hdev_init callback for HCI channels In order to make the mgmt command handling more generic we can't have a direct call to mgmt_init_hdev() from mgmt_control(). This patch adds a new callback to struct hci_mgmt_chan. And sets it to point to the mgmt_init_hdev() function for the HCI_CHANNEL_CONTROL instance. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/mgmt.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e4dc18eed446..93e7b2b05a17 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1305,6 +1305,7 @@ struct hci_mgmt_chan { unsigned short channel; size_t handler_count; const struct hci_mgmt_handler *handlers; + void (*hdev_init) (struct sock *sk, struct hci_dev *hdev); }; int hci_mgmt_chan_register(struct hci_mgmt_chan *c); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 915a2a1f0a71..ac897e676d5e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6493,8 +6493,8 @@ int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, goto done; } - if (hdev) - mgmt_init_hdev(sk, hdev); + if (hdev && chan->hdev_init) + chan->hdev_init(sk, hdev); cp = buf + sizeof(*hdr); @@ -7706,6 +7706,7 @@ static struct hci_mgmt_chan chan = { .channel = HCI_CHANNEL_CONTROL, .handler_count = ARRAY_SIZE(mgmt_handlers), .handlers = mgmt_handlers, + .hdev_init = mgmt_init_hdev, }; int mgmt_init(void) From fa4335d71a1408d0112c15874538f3f4e153ba01 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Mar 2015 13:48:50 +0200 Subject: [PATCH 54/59] Bluetooth: Move generic mgmt command dispatcher to hci_sock.c The mgmt.c file should be reserved purely for HCI_CHANNEL_CONTROL. The mgmt_control() function in it is already completely generic and has a single user in hci_sock.c. This patch moves the function there and renames it a bit more appropriately to hci_mgmt_cmd() (as it's a command dispatcher). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 - net/bluetooth/hci_sock.c | 116 ++++++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 111 ----------------------------- 3 files changed, 115 insertions(+), 115 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 93e7b2b05a17..b65c53de6a69 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1330,9 +1330,6 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c); #define DISCOV_BREDR_INQUIRY_LEN 0x08 #define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */ -int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, - struct msghdr *msg, size_t msglen); - int mgmt_new_settings(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 9ba1a2667eaa..85a44a7dc150 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -30,6 +30,9 @@ #include #include #include +#include + +#include "mgmt_util.h" static LIST_HEAD(mgmt_chan_list); static DEFINE_MUTEX(mgmt_chan_list_lock); @@ -951,6 +954,117 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, return err ? : copied; } +static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk, + struct msghdr *msg, size_t msglen) +{ + void *buf; + u8 *cp; + struct mgmt_hdr *hdr; + u16 opcode, index, len; + struct hci_dev *hdev = NULL; + const struct hci_mgmt_handler *handler; + bool var_len, no_hdev; + int err; + + BT_DBG("got %zu bytes", msglen); + + if (msglen < sizeof(*hdr)) + return -EINVAL; + + buf = kmalloc(msglen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (memcpy_from_msg(buf, msg, msglen)) { + err = -EFAULT; + goto done; + } + + hdr = buf; + opcode = __le16_to_cpu(hdr->opcode); + index = __le16_to_cpu(hdr->index); + len = __le16_to_cpu(hdr->len); + + if (len != msglen - sizeof(*hdr)) { + err = -EINVAL; + goto done; + } + + if (opcode >= chan->handler_count || + chan->handlers[opcode].func == NULL) { + BT_DBG("Unknown op %u", opcode); + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_UNKNOWN_COMMAND); + goto done; + } + + handler = &chan->handlers[opcode]; + + if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && + !(handler->flags & HCI_MGMT_UNTRUSTED)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_PERMISSION_DENIED); + goto done; + } + + if (index != MGMT_INDEX_NONE) { + hdev = hci_dev_get(index); + if (!hdev) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + if (hci_dev_test_flag(hdev, HCI_SETUP) || + hci_dev_test_flag(hdev, HCI_CONFIG) || + hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && + !(handler->flags & HCI_MGMT_UNCONFIGURED)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + } + + no_hdev = (handler->flags & HCI_MGMT_NO_HDEV); + if (no_hdev != !hdev) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + var_len = (handler->flags & HCI_MGMT_VAR_LEN); + if ((var_len && len < handler->data_len) || + (!var_len && len != handler->data_len)) { + err = mgmt_cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_PARAMS); + goto done; + } + + if (hdev && chan->hdev_init) + chan->hdev_init(sk, hdev); + + cp = buf + sizeof(*hdr); + + err = handler->func(sk, hdev, cp, len); + if (err < 0) + goto done; + + err = msglen; + +done: + if (hdev) + hci_dev_put(hdev); + + kfree(buf); + return err; +} + static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { @@ -984,7 +1098,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, mutex_lock(&mgmt_chan_list_lock); chan = __hci_mgmt_chan_find(hci_pi(sk)->channel); if (chan) - err = mgmt_control(chan, sk, msg, len); + err = hci_mgmt_cmd(chan, sk, msg, len); else err = -EINVAL; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ac897e676d5e..f3a957905193 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6401,117 +6401,6 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, }; -int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk, - struct msghdr *msg, size_t msglen) -{ - void *buf; - u8 *cp; - struct mgmt_hdr *hdr; - u16 opcode, index, len; - struct hci_dev *hdev = NULL; - const struct hci_mgmt_handler *handler; - bool var_len, no_hdev; - int err; - - BT_DBG("got %zu bytes", msglen); - - if (msglen < sizeof(*hdr)) - return -EINVAL; - - buf = kmalloc(msglen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (memcpy_from_msg(buf, msg, msglen)) { - err = -EFAULT; - goto done; - } - - hdr = buf; - opcode = __le16_to_cpu(hdr->opcode); - index = __le16_to_cpu(hdr->index); - len = __le16_to_cpu(hdr->len); - - if (len != msglen - sizeof(*hdr)) { - err = -EINVAL; - goto done; - } - - if (opcode >= chan->handler_count || - chan->handlers[opcode].func == NULL) { - BT_DBG("Unknown op %u", opcode); - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_UNKNOWN_COMMAND); - goto done; - } - - handler = &chan->handlers[opcode]; - - if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) && - !(handler->flags & HCI_MGMT_UNTRUSTED)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_PERMISSION_DENIED); - goto done; - } - - if (index != MGMT_INDEX_NONE) { - hdev = hci_dev_get(index); - if (!hdev) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - if (hci_dev_test_flag(hdev, HCI_SETUP) || - hci_dev_test_flag(hdev, HCI_CONFIG) || - hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && - !(handler->flags & HCI_MGMT_UNCONFIGURED)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - } - - no_hdev = (handler->flags & HCI_MGMT_NO_HDEV); - if (no_hdev != !hdev) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - var_len = (handler->flags & HCI_MGMT_VAR_LEN); - if ((var_len && len < handler->data_len) || - (!var_len && len != handler->data_len)) { - err = mgmt_cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); - goto done; - } - - if (hdev && chan->hdev_init) - chan->hdev_init(sk, hdev); - - cp = buf + sizeof(*hdr); - - err = handler->func(sk, hdev, cp, len); - if (err < 0) - goto done; - - err = msglen; - -done: - if (hdev) - hci_dev_put(hdev); - - kfree(buf); - return err; -} - void mgmt_index_added(struct hci_dev *hdev) { struct mgmt_ev_ext_index ev; From 19c5ce9c5ff80a26cba3afb3684d56539444ee40 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 15 Mar 2015 19:34:04 +0200 Subject: [PATCH 55/59] Bluetooth: Add workaround for broken OS X legacy SMP pairing OS X version 10.10.2 (and possibly older versions) doesn't support LE Secure Connections but incorrectly copies all authentication request bits from a Security Request to its Pairing Request. The result is that an SC capable initiator (such as BlueZ) will think OS X intends to do SC when in fact it's incapable of it: < ACL Data TX: Handle 3585 flags 0x00 dlen 6 SMP: Security Request (0x0b) len 1 Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09) > ACL Data RX: Handle 3585 flags 0x02 dlen 11 SMP: Pairing Request (0x01) len 6 IO capability: KeyboardDisplay (0x04) OOB data: Authentication data not present (0x00) Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09) Max encryption key size: 16 Initiator key distribution: EncKey (0x01) Responder key distribution: EncKey IdKey Sign (0x07) < ACL Data TX: Handle 3585 flags 0x00 dlen 11 SMP: Pairing Response (0x02) len 6 IO capability: NoInputNoOutput (0x03) OOB data: Authentication data not present (0x00) Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09) Max encryption key size: 16 Initiator key distribution: EncKey (0x01) Responder key distribution: EncKey Sign (0x05) The pairing eventually fails when we get an unexpected Pairing Confirm PDU instead of a Public Key PDU: > ACL Data RX: Handle 3585 flags 0x02 dlen 21 SMP: Pairing Confirm (0x03) len 16 Confim value: bcc3bed31b8f313a78ec3cce32685faf It is only at this point that we can speculate that the remote doesn't really support SC. This patch creates a workaround for the just-works model, however the MITM case is unsolvable because the OS X user has already been requested to enter a PIN which we're now expected to randomly generate and show the user (i.e. a chicken-and-egg problem). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 75 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 60180b47ce71..6a5afb972358 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -880,6 +880,12 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, return 0; } + /* If this function is used for SC -> legacy fallback we + * can only recover the just-works case. + */ + if (test_bit(SMP_FLAG_SC, &smp->flags)) + return -EINVAL; + /* Not Just Works/Confirm results in MITM Authentication */ if (smp->method != JUST_CFM) { set_bit(SMP_FLAG_MITM_AUTH, &smp->flags); @@ -1806,6 +1812,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) clear_bit(SMP_FLAG_INITIATOR, &smp->flags); + /* Strictly speaking we shouldn't allow Pairing Confirm for the + * SC case, however some implementations incorrectly copy RFU auth + * req bits from our security request, which may create a false + * positive SC enablement. + */ + SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); + if (test_bit(SMP_FLAG_SC, &smp->flags)) { SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY); /* Clear bits which are generated but not distributed */ @@ -1814,8 +1827,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } - SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM); - /* Request setup of TK */ ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability); if (ret) @@ -1981,10 +1992,6 @@ static u8 sc_check_confirm(struct smp_chan *smp) BT_DBG(""); - /* Public Key exchange must happen before any other steps */ - if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags)) - return SMP_UNSPECIFIED; - if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY) return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM); @@ -1997,6 +2004,47 @@ static u8 sc_check_confirm(struct smp_chan *smp) return 0; } +/* Work-around for some implementations that incorrectly copy RFU bits + * from our security request and thereby create the impression that + * we're doing SC when in fact the remote doesn't support it. + */ +static int fixup_sc_false_positive(struct smp_chan *smp) +{ + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; + struct smp_cmd_pairing *req, *rsp; + u8 auth; + + /* The issue is only observed when we're in slave role */ + if (hcon->out) + return SMP_UNSPECIFIED; + + if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) { + BT_ERR("Refusing SMP SC -> legacy fallback in SC-only mode"); + return SMP_UNSPECIFIED; + } + + BT_ERR("Trying to fall back to legacy SMP"); + + req = (void *) &smp->preq[1]; + rsp = (void *) &smp->prsp[1]; + + /* Rebuild key dist flags which may have been cleared for SC */ + smp->remote_key_dist = (req->init_key_dist & rsp->resp_key_dist); + + auth = req->auth_req & AUTH_REQ_MASK(hdev); + + if (tk_request(conn, 0, auth, rsp->io_capability, req->io_capability)) { + BT_ERR("Failed to fall back to legacy SMP"); + return SMP_UNSPECIFIED; + } + + clear_bit(SMP_FLAG_SC, &smp->flags); + + return 0; +} + static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { struct l2cap_chan *chan = conn->smp; @@ -2010,8 +2058,19 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); skb_pull(skb, sizeof(smp->pcnf)); - if (test_bit(SMP_FLAG_SC, &smp->flags)) - return sc_check_confirm(smp); + if (test_bit(SMP_FLAG_SC, &smp->flags)) { + int ret; + + /* Public Key exchange must happen before any other steps */ + if (test_bit(SMP_FLAG_REMOTE_PK, &smp->flags)) + return sc_check_confirm(smp); + + BT_ERR("Unexpected SMP Pairing Confirm"); + + ret = fixup_sc_false_positive(smp); + if (ret) + return ret; + } if (conn->hcon->out) { smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), From 63511f6d5ba0c20850448991be297751ddb6798c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 17 Mar 2015 11:38:24 -0700 Subject: [PATCH 56/59] Bluetooth: Fix potential NULL dereference in SMP channel setup When the allocation of the L2CAP channel for the BR/EDR security manager fails, then the smp variable might be NULL. In that case do not try to free the non-existing crypto contexts Reported-by: Dan Carpenter Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6a5afb972358..1ec3f66b5a74 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3124,9 +3124,11 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) create_chan: chan = l2cap_chan_create(); if (!chan) { - crypto_free_blkcipher(smp->tfm_aes); - crypto_free_hash(smp->tfm_cmac); - kzfree(smp); + if (smp) { + crypto_free_blkcipher(smp->tfm_aes); + crypto_free_hash(smp->tfm_cmac); + kzfree(smp); + } return ERR_PTR(-ENOMEM); } From 0db055c934672bcbd9423cd5e729f602bec70a6e Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Tue, 17 Mar 2015 16:25:45 -0400 Subject: [PATCH 57/59] cc2520: Do not store platform_data in spi_device Storing the `platform_data` struct inside of the SPI struct when using the device tree allows for a later function to edit the content of that struct. This patch refactors the `cc2520_get_platformat_data` function to accept a pointer to a `cc2520_platform_data` struct and populates the fields inside of it. This change mirrors commit aaa1c4d226e4cd730075d3dac99a6d599a0190c7 ("at86rf230: copy pdata to driver allocated space"). Signed-off-by: Brad Campbell Acked-by: Varka Bhadram Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/cc2520.c | 95 ++++++++++++++++----------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index 181b349b060e..233b6c6017d4 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -714,6 +714,33 @@ static irqreturn_t cc2520_sfd_isr(int irq, void *data) return IRQ_HANDLED; } +static int cc2520_get_platform_data(struct spi_device *spi, + struct cc2520_platform_data *pdata) +{ + struct device_node *np = spi->dev.of_node; + struct cc2520_private *priv = spi_get_drvdata(spi); + + if (!np) { + struct cc2520_platform_data *spi_pdata = spi->dev.platform_data; + if (!spi_pdata) + return -ENOENT; + *pdata = *spi_pdata; + return 0; + } + + pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0); + priv->fifo_pin = pdata->fifo; + + pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0); + + pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0); + pdata->cca = of_get_named_gpio(np, "cca-gpio", 0); + pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); + pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); + + return 0; +} + static int cc2520_hw_init(struct cc2520_private *priv) { u8 status = 0, state = 0xff; @@ -808,40 +835,10 @@ err_ret: return ret; } -static struct cc2520_platform_data * -cc2520_get_platform_data(struct spi_device *spi) -{ - struct cc2520_platform_data *pdata; - struct device_node *np = spi->dev.of_node; - struct cc2520_private *priv = spi_get_drvdata(spi); - - if (!np) - return spi->dev.platform_data; - - pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - goto done; - - pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0); - priv->fifo_pin = pdata->fifo; - - pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0); - - pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0); - pdata->cca = of_get_named_gpio(np, "cca-gpio", 0); - pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); - pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); - - spi->dev.platform_data = pdata; - -done: - return pdata; -} - static int cc2520_probe(struct spi_device *spi) { struct cc2520_private *priv; - struct cc2520_platform_data *pdata; + struct cc2520_platform_data pdata; int ret; priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); @@ -850,8 +847,8 @@ static int cc2520_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); - pdata = cc2520_get_platform_data(spi); - if (!pdata) { + ret = cc2520_get_platform_data(spi, &pdata); + if (ret < 0) { dev_err(&spi->dev, "no platform data\n"); return -EINVAL; } @@ -869,76 +866,76 @@ static int cc2520_probe(struct spi_device *spi) init_completion(&priv->tx_complete); /* Request all the gpio's */ - if (!gpio_is_valid(pdata->fifo)) { + if (!gpio_is_valid(pdata.fifo)) { dev_err(&spi->dev, "fifo gpio is not valid\n"); ret = -EINVAL; goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata->fifo, + ret = devm_gpio_request_one(&spi->dev, pdata.fifo, GPIOF_IN, "fifo"); if (ret) goto err_hw_init; - if (!gpio_is_valid(pdata->cca)) { + if (!gpio_is_valid(pdata.cca)) { dev_err(&spi->dev, "cca gpio is not valid\n"); ret = -EINVAL; goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata->cca, + ret = devm_gpio_request_one(&spi->dev, pdata.cca, GPIOF_IN, "cca"); if (ret) goto err_hw_init; - if (!gpio_is_valid(pdata->fifop)) { + if (!gpio_is_valid(pdata.fifop)) { dev_err(&spi->dev, "fifop gpio is not valid\n"); ret = -EINVAL; goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata->fifop, + ret = devm_gpio_request_one(&spi->dev, pdata.fifop, GPIOF_IN, "fifop"); if (ret) goto err_hw_init; - if (!gpio_is_valid(pdata->sfd)) { + if (!gpio_is_valid(pdata.sfd)) { dev_err(&spi->dev, "sfd gpio is not valid\n"); ret = -EINVAL; goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata->sfd, + ret = devm_gpio_request_one(&spi->dev, pdata.sfd, GPIOF_IN, "sfd"); if (ret) goto err_hw_init; - if (!gpio_is_valid(pdata->reset)) { + if (!gpio_is_valid(pdata.reset)) { dev_err(&spi->dev, "reset gpio is not valid\n"); ret = -EINVAL; goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata->reset, + ret = devm_gpio_request_one(&spi->dev, pdata.reset, GPIOF_OUT_INIT_LOW, "reset"); if (ret) goto err_hw_init; - if (!gpio_is_valid(pdata->vreg)) { + if (!gpio_is_valid(pdata.vreg)) { dev_err(&spi->dev, "vreg gpio is not valid\n"); ret = -EINVAL; goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata->vreg, + ret = devm_gpio_request_one(&spi->dev, pdata.vreg, GPIOF_OUT_INIT_LOW, "vreg"); if (ret) goto err_hw_init; - gpio_set_value(pdata->vreg, HIGH); + gpio_set_value(pdata.vreg, HIGH); usleep_range(100, 150); - gpio_set_value(pdata->reset, HIGH); + gpio_set_value(pdata.reset, HIGH); usleep_range(200, 250); ret = cc2520_hw_init(priv); @@ -947,7 +944,7 @@ static int cc2520_probe(struct spi_device *spi) /* Set up fifop interrupt */ ret = devm_request_irq(&spi->dev, - gpio_to_irq(pdata->fifop), + gpio_to_irq(pdata.fifop), cc2520_fifop_isr, IRQF_TRIGGER_RISING, dev_name(&spi->dev), @@ -959,7 +956,7 @@ static int cc2520_probe(struct spi_device *spi) /* Set up sfd interrupt */ ret = devm_request_irq(&spi->dev, - gpio_to_irq(pdata->sfd), + gpio_to_irq(pdata.sfd), cc2520_sfd_isr, IRQF_TRIGGER_FALLING, dev_name(&spi->dev), From f0b7d43c8a28155f50adb087a563cfc97566e477 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Tue, 17 Mar 2015 16:25:46 -0400 Subject: [PATCH 58/59] cc2520: Add support for CC2591 amplifier. The TI CC2521 is an RF power amplifier that is designed to interface with the CC2520. Conveniently, it directly interfaces with the CC2520 and does not require any pins to be connected to a microcontroller/processor. Adding a CC2591 increases the CC2520's range, which is useful for border router and other wall-powered applications. Using the CC2591 with the CC2520 requires configuring the CC2520 GPIOs that are connected to the CC2591 to correctly set the CC2591 into TX and RX modes. Further, TI recommends that the CC2520_TXPOWER and CC2520_AGCCTRL1 registers are set differently to maximize the CC2591's performance. These settings are covered in TI Application Note AN065. This patch adds an optional `amplified` field to the cc2520 entry in the device tree. If present, the CC2520 will be configured to operate with a CC2591. The expected pin mapping is: CC2520 GPIO0 --> CC2591 EN CC2520 GPIO5 --> CC2591 PAEN Signed-off-by: Brad Campbell Acked-by: Varka Bhadram Signed-off-by: Marcel Holtmann --- .../bindings/net/ieee802154/cc2520.txt | 4 ++ drivers/net/ieee802154/cc2520.c | 55 ++++++++++++++++--- include/linux/spi/cc2520.h | 1 + 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt index 0071883c08d8..fb6d49f184ed 100644 --- a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt +++ b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt @@ -13,11 +13,15 @@ Required properties: - cca-gpio: GPIO spec for the CCA pin - vreg-gpio: GPIO spec for the VREG pin - reset-gpio: GPIO spec for the RESET pin +Optional properties: + - amplified: include if the CC2520 is connected to a CC2591 amplifier + Example: cc2520@0 { compatible = "ti,cc2520"; reg = <0>; spi-max-frequency = <4000000>; + amplified; pinctrl-names = "default"; pinctrl-0 = <&cc2520_cape_pins>; fifo-gpio = <&gpio1 18 0>; diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index 233b6c6017d4..f833b8bb6663 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -738,6 +738,8 @@ static int cc2520_get_platform_data(struct spi_device *spi, pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); + pdata->amplified = of_property_read_bool(np, "amplified"); + return 0; } @@ -746,6 +748,11 @@ static int cc2520_hw_init(struct cc2520_private *priv) u8 status = 0, state = 0xff; int ret; int timeout = 100; + struct cc2520_platform_data pdata; + + ret = cc2520_get_platform_data(priv->spi, &pdata); + if (ret) + goto err_ret; ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); if (ret) @@ -768,11 +775,47 @@ static int cc2520_hw_init(struct cc2520_private *priv) dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); - /* Registers default value: section 28.1 in Datasheet */ - ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); - if (ret) - goto err_ret; + /* If the CC2520 is connected to a CC2591 amplifier, we must both + * configure GPIOs on the CC2520 to correctly configure the CC2591 + * and change a couple settings of the CC2520 to work with the + * amplifier. See section 8 page 17 of TI application note AN065. + * http://www.ti.com/lit/an/swra229a/swra229a.pdf + */ + if (pdata.amplified) { + ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF9); + if (ret) + goto err_ret; + ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1); + if (ret) + goto err_ret; + } else { + ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); + if (ret) + goto err_ret; + } + + /* Registers default value: section 28.1 in Datasheet */ ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); if (ret) goto err_ret; @@ -797,10 +840,6 @@ static int cc2520_hw_init(struct cc2520_private *priv) if (ret) goto err_ret; - ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); - if (ret) - goto err_ret; - ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); if (ret) goto err_ret; diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h index 85b8ee67e937..e741e8baad92 100644 --- a/include/linux/spi/cc2520.h +++ b/include/linux/spi/cc2520.h @@ -21,6 +21,7 @@ struct cc2520_platform_data { int sfd; int reset; int vreg; + bool amplified; }; #endif From ea6edfbcefec1fcfdb826a1d5a054f402dfbfb24 Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Thu, 19 Mar 2015 21:23:40 +0800 Subject: [PATCH 59/59] mac802154: fix typo in header guard Signed-off-by: Nicolas Iooss Fixes: b6eea9ca354a ("mac802154: introduce driver-ops header") Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann --- net/mac802154/driver-ops.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h index 98180a9fff4a..a0533357b9ea 100644 --- a/net/mac802154/driver-ops.h +++ b/net/mac802154/driver-ops.h @@ -1,4 +1,4 @@ -#ifndef __MAC802154_DRVIER_OPS +#ifndef __MAC802154_DRIVER_OPS #define __MAC802154_DRIVER_OPS #include @@ -220,4 +220,4 @@ drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) return local->ops->set_promiscuous_mode(&local->hw, on); } -#endif /* __MAC802154_DRVIER_OPS */ +#endif /* __MAC802154_DRIVER_OPS */