1335640Shselasky/*
2335640Shselasky * Copyright (c) 2014 Michal Labedzki for Tieto Corporation
3335640Shselasky * All rights reserved.
4335640Shselasky *
5335640Shselasky * Redistribution and use in source and binary forms, with or without
6335640Shselasky * modification, are permitted provided that the following conditions
7335640Shselasky * are met:
8335640Shselasky *
9335640Shselasky * 1. Redistributions of source code must retain the above copyright
10335640Shselasky * notice, this list of conditions and the following disclaimer.
11335640Shselasky * 2. Redistributions in binary form must reproduce the above copyright
12335640Shselasky * notice, this list of conditions and the following disclaimer in the
13335640Shselasky * documentation and/or other materials provided with the distribution.
14335640Shselasky * 3. The name of the author may not be used to endorse or promote
15335640Shselasky * products derived from this software without specific prior written
16335640Shselasky * permission.
17335640Shselasky *
18335640Shselasky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19335640Shselasky * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20335640Shselasky * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21335640Shselasky * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22335640Shselasky * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23335640Shselasky * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24335640Shselasky * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25335640Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26335640Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27335640Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28335640Shselasky * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29335640Shselasky *
30335640Shselasky */
31335640Shselasky
32335640Shselasky#ifdef HAVE_CONFIG_H
33335640Shselasky#include <config.h>
34335640Shselasky#endif
35335640Shselasky
36335640Shselasky#include <errno.h>
37335640Shselasky#include <stdint.h>
38335640Shselasky#include <stdlib.h>
39335640Shselasky#include <string.h>
40335640Shselasky
41335640Shselasky#include <bluetooth/bluetooth.h>
42335640Shselasky#include <bluetooth/hci.h>
43335640Shselasky
44335640Shselasky#include "pcap/bluetooth.h"
45335640Shselasky#include "pcap-int.h"
46335640Shselasky
47335640Shselasky#include "pcap-bt-monitor-linux.h"
48335640Shselasky
49335640Shselasky#define BT_CONTROL_SIZE 32
50335640Shselasky#define INTERFACE_NAME "bluetooth-monitor"
51335640Shselasky
52335640Shselasky/*
53335640Shselasky * Fields and alignment must match the declaration in the Linux kernel 3.4+.
54335640Shselasky * See struct hci_mon_hdr in include/net/bluetooth/hci_mon.h.
55335640Shselasky */
56335640Shselaskystruct hci_mon_hdr {
57335640Shselasky    uint16_t opcode;
58335640Shselasky    uint16_t index;
59335640Shselasky    uint16_t len;
60335640Shselasky} __attribute__((packed));
61335640Shselasky
62335640Shselaskyint
63335640Shselaskybt_monitor_findalldevs(pcap_if_list_t *devlistp, char *err_str)
64335640Shselasky{
65335640Shselasky    int         ret = 0;
66335640Shselasky
67335640Shselasky    /*
68335640Shselasky     * Bluetooth is a wireless technology.
69335640Shselasky     *
70335640Shselasky     * This is a device to monitor all Bluetooth interfaces, so
71335640Shselasky     * there's no notion of "connected" or "disconnected", any
72335640Shselasky     * more than there's a notion of "connected" or "disconnected"
73335640Shselasky     * for the "any" device.
74335640Shselasky     */
75335640Shselasky    if (add_dev(devlistp, INTERFACE_NAME,
76335640Shselasky                PCAP_IF_WIRELESS|PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE,
77335640Shselasky                "Bluetooth Linux Monitor", err_str) == NULL)
78335640Shselasky    {
79335640Shselasky        ret = -1;
80335640Shselasky    }
81335640Shselasky
82335640Shselasky    return ret;
83335640Shselasky}
84335640Shselasky
85335640Shselaskystatic int
86335640Shselaskybt_monitor_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *user)
87335640Shselasky{
88335640Shselasky    struct cmsghdr *cmsg;
89335640Shselasky    struct msghdr msg;
90335640Shselasky    struct iovec  iv[2];
91335640Shselasky    ssize_t ret;
92335640Shselasky    struct pcap_pkthdr pkth;
93335640Shselasky    pcap_bluetooth_linux_monitor_header *bthdr;
94335640Shselasky    u_char *pktd;
95335640Shselasky    struct hci_mon_hdr hdr;
96335640Shselasky
97335640Shselasky    pktd = (u_char *)handle->buffer + BT_CONTROL_SIZE;
98335640Shselasky    bthdr = (pcap_bluetooth_linux_monitor_header*)(void *)pktd;
99335640Shselasky
100335640Shselasky    iv[0].iov_base = &hdr;
101335640Shselasky    iv[0].iov_len = sizeof(hdr);
102335640Shselasky    iv[1].iov_base = pktd + sizeof(pcap_bluetooth_linux_monitor_header);
103335640Shselasky    iv[1].iov_len = handle->snapshot;
104335640Shselasky
105335640Shselasky    memset(&pkth.ts, 0, sizeof(pkth.ts));
106335640Shselasky    memset(&msg, 0, sizeof(msg));
107335640Shselasky    msg.msg_iov = iv;
108335640Shselasky    msg.msg_iovlen = 2;
109335640Shselasky    msg.msg_control = handle->buffer;
110335640Shselasky    msg.msg_controllen = BT_CONTROL_SIZE;
111335640Shselasky
112335640Shselasky    do {
113335640Shselasky        ret = recvmsg(handle->fd, &msg, 0);
114335640Shselasky        if (handle->break_loop)
115335640Shselasky        {
116335640Shselasky            handle->break_loop = 0;
117335640Shselasky            return -2;
118335640Shselasky        }
119335640Shselasky    } while ((ret == -1) && (errno == EINTR));
120335640Shselasky
121335640Shselasky    if (ret < 0) {
122335640Shselasky        pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
123335640Shselasky            errno, "Can't receive packet");
124335640Shselasky        return -1;
125335640Shselasky    }
126335640Shselasky
127335640Shselasky    pkth.caplen = ret - sizeof(hdr) + sizeof(pcap_bluetooth_linux_monitor_header);
128335640Shselasky    pkth.len = pkth.caplen;
129335640Shselasky
130335640Shselasky    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
131335640Shselasky        if (cmsg->cmsg_level != SOL_SOCKET) continue;
132335640Shselasky
133335640Shselasky        if (cmsg->cmsg_type == SCM_TIMESTAMP) {
134335640Shselasky            memcpy(&pkth.ts, CMSG_DATA(cmsg), sizeof(pkth.ts));
135335640Shselasky        }
136335640Shselasky    }
137335640Shselasky
138335640Shselasky    bthdr->adapter_id = htons(hdr.index);
139335640Shselasky    bthdr->opcode = htons(hdr.opcode);
140335640Shselasky
141335640Shselasky    if (handle->fcode.bf_insns == NULL ||
142335640Shselasky        bpf_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) {
143335640Shselasky        callback(user, &pkth, pktd);
144335640Shselasky        return 1;
145335640Shselasky    }
146335640Shselasky    return 0;   /* didn't pass filter */
147335640Shselasky}
148335640Shselasky
149335640Shselaskystatic int
150335640Shselaskybt_monitor_inject(pcap_t *handle, const void *buf _U_, size_t size _U_)
151335640Shselasky{
152356341Scy    pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
153356341Scy        "Packet injection is not supported yet on Bluetooth monitor devices");
154335640Shselasky    return -1;
155335640Shselasky}
156335640Shselasky
157335640Shselaskystatic int
158335640Shselaskybt_monitor_setdirection(pcap_t *p, pcap_direction_t d)
159335640Shselasky{
160335640Shselasky    p->direction = d;
161335640Shselasky    return 0;
162335640Shselasky}
163335640Shselasky
164335640Shselaskystatic int
165335640Shselaskybt_monitor_stats(pcap_t *handle _U_, struct pcap_stat *stats)
166335640Shselasky{
167335640Shselasky    stats->ps_recv = 0;
168335640Shselasky    stats->ps_drop = 0;
169335640Shselasky    stats->ps_ifdrop = 0;
170335640Shselasky
171335640Shselasky    return 0;
172335640Shselasky}
173335640Shselasky
174335640Shselaskystatic int
175335640Shselaskybt_monitor_activate(pcap_t* handle)
176335640Shselasky{
177335640Shselasky    struct sockaddr_hci addr;
178335640Shselasky    int err = PCAP_ERROR;
179335640Shselasky    int opt;
180335640Shselasky
181335640Shselasky    if (handle->opt.rfmon) {
182335640Shselasky        /* monitor mode doesn't apply here */
183335640Shselasky        return PCAP_ERROR_RFMON_NOTSUP;
184335640Shselasky    }
185335640Shselasky
186335640Shselasky    /*
187335640Shselasky     * Turn a negative snapshot value (invalid), a snapshot value of
188335640Shselasky     * 0 (unspecified), or a value bigger than the normal maximum
189335640Shselasky     * value, into the maximum allowed value.
190335640Shselasky     *
191335640Shselasky     * If some application really *needs* a bigger snapshot
192335640Shselasky     * length, we should just increase MAXIMUM_SNAPLEN.
193335640Shselasky     */
194335640Shselasky    if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN)
195335640Shselasky        handle->snapshot = MAXIMUM_SNAPLEN;
196335640Shselasky
197335640Shselasky    handle->bufsize = BT_CONTROL_SIZE + sizeof(pcap_bluetooth_linux_monitor_header) + handle->snapshot;
198335640Shselasky    handle->linktype = DLT_BLUETOOTH_LINUX_MONITOR;
199335640Shselasky
200335640Shselasky    handle->read_op = bt_monitor_read;
201335640Shselasky    handle->inject_op = bt_monitor_inject;
202335640Shselasky    handle->setfilter_op = install_bpf_program; /* no kernel filtering */
203335640Shselasky    handle->setdirection_op = bt_monitor_setdirection;
204335640Shselasky    handle->set_datalink_op = NULL; /* can't change data link type */
205335640Shselasky    handle->getnonblock_op = pcap_getnonblock_fd;
206335640Shselasky    handle->setnonblock_op = pcap_setnonblock_fd;
207335640Shselasky    handle->stats_op = bt_monitor_stats;
208335640Shselasky
209335640Shselasky    handle->fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
210335640Shselasky    if (handle->fd < 0) {
211335640Shselasky        pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
212335640Shselasky            errno, "Can't create raw socket");
213335640Shselasky        return PCAP_ERROR;
214335640Shselasky    }
215335640Shselasky
216335640Shselasky    handle->buffer = malloc(handle->bufsize);
217335640Shselasky    if (!handle->buffer) {
218335640Shselasky        pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
219335640Shselasky            errno, "Can't allocate dump buffer");
220335640Shselasky        goto close_fail;
221335640Shselasky    }
222335640Shselasky
223335640Shselasky    /* Bind socket to the HCI device */
224335640Shselasky    addr.hci_family = AF_BLUETOOTH;
225335640Shselasky    addr.hci_dev = HCI_DEV_NONE;
226335640Shselasky    addr.hci_channel = HCI_CHANNEL_MONITOR;
227335640Shselasky
228335640Shselasky    if (bind(handle->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
229335640Shselasky        pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
230335640Shselasky            errno, "Can't attach to interface");
231335640Shselasky        goto close_fail;
232335640Shselasky    }
233335640Shselasky
234335640Shselasky    opt = 1;
235335640Shselasky    if (setsockopt(handle->fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
236335640Shselasky        pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
237335640Shselasky            errno, "Can't enable time stamp");
238335640Shselasky        goto close_fail;
239335640Shselasky    }
240335640Shselasky
241335640Shselasky    handle->selectable_fd = handle->fd;
242335640Shselasky
243335640Shselasky    return 0;
244335640Shselasky
245335640Shselaskyclose_fail:
246335640Shselasky    pcap_cleanup_live_common(handle);
247335640Shselasky    return err;
248335640Shselasky}
249335640Shselasky
250335640Shselaskypcap_t *
251335640Shselaskybt_monitor_create(const char *device, char *ebuf, int *is_ours)
252335640Shselasky{
253335640Shselasky    pcap_t      *p;
254335640Shselasky    const char  *cp;
255335640Shselasky
256335640Shselasky    cp = strrchr(device, '/');
257335640Shselasky    if (cp == NULL)
258335640Shselasky        cp = device;
259335640Shselasky
260335640Shselasky    if (strcmp(cp, INTERFACE_NAME) != 0) {
261335640Shselasky        *is_ours = 0;
262335640Shselasky        return NULL;
263335640Shselasky    }
264335640Shselasky
265335640Shselasky    *is_ours = 1;
266335640Shselasky    p = pcap_create_common(ebuf, 0);
267335640Shselasky    if (p == NULL)
268335640Shselasky        return NULL;
269335640Shselasky
270335640Shselasky    p->activate_op = bt_monitor_activate;
271335640Shselasky
272335640Shselasky    return p;
273335640Shselasky}
274