aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/hci_sock.c
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2012-02-20 14:34:38 -0500
committerJohan Hedberg <johan.hedberg@intel.com>2012-02-20 16:03:24 -0500
commitcd82e61c110a36e398323e422896fcfe05879fed (patch)
tree42cbe44d8915e82febf10420100c45219d610b83 /net/bluetooth/hci_sock.c
parent040030ef7d907107e6489b39da518bdf94136d68 (diff)
Bluetooth: Add support for HCI monitor channel
The HCI monitor channel can be used to monitor all packets and events from the Bluetooth subsystem. The monitor is not bound to any specific HCI device and allows even capturing multiple devices at the same time. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Diffstat (limited to 'net/bluetooth/hci_sock.c')
-rw-r--r--net/bluetooth/hci_sock.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 14727cb43f63..213697d23771 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -48,9 +48,12 @@
48 48
49#include <net/bluetooth/bluetooth.h> 49#include <net/bluetooth/bluetooth.h>
50#include <net/bluetooth/hci_core.h> 50#include <net/bluetooth/hci_core.h>
51#include <net/bluetooth/hci_mon.h>
51 52
52static bool enable_mgmt; 53static bool enable_mgmt;
53 54
55static atomic_t monitor_promisc = ATOMIC_INIT(0);
56
54/* ----- HCI socket interface ----- */ 57/* ----- HCI socket interface ----- */
55 58
56static inline int hci_test_bit(int nr, void *addr) 59static inline int hci_test_bit(int nr, void *addr)
@@ -189,6 +192,174 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
189 read_unlock(&hci_sk_list.lock); 192 read_unlock(&hci_sk_list.lock);
190} 193}
191 194
195/* Send frame to monitor socket */
196void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
197{
198 struct sock *sk;
199 struct hlist_node *node;
200 struct sk_buff *skb_copy = NULL;
201 __le16 opcode;
202
203 if (!atomic_read(&monitor_promisc))
204 return;
205
206 BT_DBG("hdev %p len %d", hdev, skb->len);
207
208 switch (bt_cb(skb)->pkt_type) {
209 case HCI_COMMAND_PKT:
210 opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT);
211 break;
212 case HCI_EVENT_PKT:
213 opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT);
214 break;
215 case HCI_ACLDATA_PKT:
216 if (bt_cb(skb)->incoming)
217 opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT);
218 else
219 opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT);
220 break;
221 case HCI_SCODATA_PKT:
222 if (bt_cb(skb)->incoming)
223 opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT);
224 else
225 opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT);
226 break;
227 default:
228 return;
229 }
230
231 read_lock(&hci_sk_list.lock);
232
233 sk_for_each(sk, node, &hci_sk_list.head) {
234 struct sk_buff *nskb;
235
236 if (sk->sk_state != BT_BOUND)
237 continue;
238
239 if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
240 continue;
241
242 if (!skb_copy) {
243 struct hci_mon_hdr *hdr;
244
245 /* Create a private copy with headroom */
246 skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC);
247 if (!skb_copy)
248 continue;
249
250 /* Put header before the data */
251 hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
252 hdr->opcode = opcode;
253 hdr->index = cpu_to_le16(hdev->id);
254 hdr->len = cpu_to_le16(skb->len);
255 }
256
257 nskb = skb_clone(skb_copy, GFP_ATOMIC);
258 if (!nskb)
259 continue;
260
261 if (sock_queue_rcv_skb(sk, nskb))
262 kfree_skb(nskb);
263 }
264
265 read_unlock(&hci_sk_list.lock);
266
267 kfree_skb(skb_copy);
268}
269
270static void send_monitor_event(struct sk_buff *skb)
271{
272 struct sock *sk;
273 struct hlist_node *node;
274
275 BT_DBG("len %d", skb->len);
276
277 read_lock(&hci_sk_list.lock);
278
279 sk_for_each(sk, node, &hci_sk_list.head) {
280 struct sk_buff *nskb;
281
282 if (sk->sk_state != BT_BOUND)
283 continue;
284
285 if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
286 continue;
287
288 nskb = skb_clone(skb, GFP_ATOMIC);
289 if (!nskb)
290 continue;
291
292 if (sock_queue_rcv_skb(sk, nskb))
293 kfree_skb(nskb);
294 }
295
296 read_unlock(&hci_sk_list.lock);
297}
298
299static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
300{
301 struct hci_mon_hdr *hdr;
302 struct hci_mon_new_index *ni;
303 struct sk_buff *skb;
304 __le16 opcode;
305
306 switch (event) {
307 case HCI_DEV_REG:
308 skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC);
309 if (!skb)
310 return NULL;
311
312 ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
313 ni->type = hdev->dev_type;
314 ni->bus = hdev->bus;
315 bacpy(&ni->bdaddr, &hdev->bdaddr);
316 memcpy(ni->name, hdev->name, 8);
317
318 opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX);
319 break;
320
321 case HCI_DEV_UNREG:
322 skb = bt_skb_alloc(0, GFP_ATOMIC);
323 if (!skb)
324 return NULL;
325
326 opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX);
327 break;
328
329 default:
330 return NULL;
331 }
332
333 __net_timestamp(skb);
334
335 hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE);
336 hdr->opcode = opcode;
337 hdr->index = cpu_to_le16(hdev->id);
338 hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
339
340 return skb;
341}
342
343static void send_monitor_replay(struct sock *sk)
344{
345 struct hci_dev *hdev;
346
347 read_lock(&hci_dev_list_lock);
348
349 list_for_each_entry(hdev, &hci_dev_list, list) {
350 struct sk_buff *skb;
351
352 skb = create_monitor_event(hdev, HCI_DEV_REG);
353 if (!skb)
354 continue;
355
356 if (sock_queue_rcv_skb(sk, skb))
357 kfree_skb(skb);
358 }
359
360 read_unlock(&hci_dev_list_lock);
361}
362
192/* Generate internal stack event */ 363/* Generate internal stack event */
193static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) 364static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
194{ 365{
@@ -223,6 +394,17 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
223 394
224 BT_DBG("hdev %s event %d", hdev->name, event); 395 BT_DBG("hdev %s event %d", hdev->name, event);
225 396
397 /* Send event to monitor */
398 if (atomic_read(&monitor_promisc)) {
399 struct sk_buff *skb;
400
401 skb = create_monitor_event(hdev, event);
402 if (skb) {
403 send_monitor_event(skb);
404 kfree_skb(skb);
405 }
406 }
407
226 /* Send event to sockets */ 408 /* Send event to sockets */
227 ev.event = event; 409 ev.event = event;
228 ev.dev_id = hdev->id; 410 ev.dev_id = hdev->id;
@@ -262,6 +444,9 @@ static int hci_sock_release(struct socket *sock)
262 444
263 hdev = hci_pi(sk)->hdev; 445 hdev = hci_pi(sk)->hdev;
264 446
447 if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR)
448 atomic_dec(&monitor_promisc);
449
265 bt_sock_unlink(&hci_sk_list, sk); 450 bt_sock_unlink(&hci_sk_list, sk);
266 451
267 if (hdev) { 452 if (hdev) {
@@ -474,6 +659,22 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
474 set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags); 659 set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags);
475 break; 660 break;
476 661
662 case HCI_CHANNEL_MONITOR:
663 if (haddr.hci_dev != HCI_DEV_NONE) {
664 err = -EINVAL;
665 goto done;
666 }
667
668 if (!capable(CAP_NET_RAW)) {
669 err = -EPERM;
670 goto done;
671 }
672
673 send_monitor_replay(sk);
674
675 atomic_inc(&monitor_promisc);
676 break;
677
477 default: 678 default:
478 err = -EINVAL; 679 err = -EINVAL;
479 goto done; 680 goto done;
@@ -578,6 +779,9 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
578 case HCI_CHANNEL_RAW: 779 case HCI_CHANNEL_RAW:
579 hci_sock_cmsg(sk, msg, skb); 780 hci_sock_cmsg(sk, msg, skb);
580 break; 781 break;
782 case HCI_CHANNEL_MONITOR:
783 sock_recv_timestamp(msg, sk, skb);
784 break;
581 } 785 }
582 786
583 skb_free_datagram(sk, skb); 787 skb_free_datagram(sk, skb);
@@ -612,6 +816,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
612 case HCI_CHANNEL_CONTROL: 816 case HCI_CHANNEL_CONTROL:
613 err = mgmt_control(sk, msg, len); 817 err = mgmt_control(sk, msg, len);
614 goto done; 818 goto done;
819 case HCI_CHANNEL_MONITOR:
820 err = -EOPNOTSUPP;
821 goto done;
615 default: 822 default:
616 err = -EINVAL; 823 err = -EINVAL;
617 goto done; 824 goto done;