/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>
#include <getopt.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <netinet/in.h>
#include "parser/parser.h"
static volatile sig_atomic_t __io_canceled = 0;
static void sig_hup(int sig)
{
}
static void sig_term(int sig)
{
__io_canceled = 1;
}
static int read_revision(int dd, char *revision, int size)
{
struct hci_request rq;
unsigned char req[] = { 0x07 };
unsigned char buf[46];
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_VENDOR_CMD;
rq.ocf = 0x000e;
rq.cparam = req;
rq.clen = sizeof(req);
rq.rparam = &buf;
rq.rlen = sizeof(buf);
if (hci_send_req(dd, &rq, 1000) < 0)
return -1;
if (buf[0] > 0) {
errno = EIO;
return -1;
}
if (revision)
strncpy(revision, (char *) (buf + 1), size);
return 0;
}
static int enable_sniffer(int dd, uint8_t enable)
{
struct hci_request rq;
unsigned char req[] = { 0x00, enable };
unsigned char buf[1];
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_VENDOR_CMD;
rq.ocf = 0x000e;
rq.cparam = req;
rq.clen = sizeof(req);
rq.rparam = &buf;
rq.rlen = sizeof(buf);
if (hci_send_req(dd, &rq, 1000) < 0)
return -1;
if (buf[0] > 0) {
errno = EIO;
return -1;
}
return 0;
}
static int enable_sync(int dd, uint8_t enable, bdaddr_t *bdaddr)
{
struct hci_request rq;
unsigned char req[] = { 0x01, enable,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfa, 0x00 };
memcpy(req + 2, bdaddr, 6);
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_VENDOR_CMD;
rq.ocf = 0x000e;
rq.cparam = req;
rq.clen = sizeof(req);
hci_send_req(dd, &rq, 1000);
return 0;
}
static char *type2str(uint8_t type)
{
switch (type) {
case 0x00:
return "NULL";
case 0x01:
return "POLL";
case 0x02:
return "FHS";
case 0x03:
return "DM1";
case 0x04:
return "DH1";
case 0x05:
return "HV1";
case 0x06:
return "HV2";
case 0x07:
return "HV3";
case 0x08:
return "DV";
case 0x09:
return "AUX1";
case 0x0a:
return "DM3";
case 0x0b:
return "DH3";
case 0x0c:
return "EV4";
case 0x0d:
return "EV5";
case 0x0e:
return "DM5";
case 0x0f:
return "DH5";
case 0xff:
return "ID";
default:
return "UNK";
}
}
static void decode(unsigned char *buf, int count)
{
struct frame frm;
uint8_t id, status, channel;
uint16_t num, len;
uint32_t time;
uint8_t type, addr, temp, hdr;
uint8_t flow, arqn, seqn, hec, llid, pflow;
uint16_t plen;
if (count < 7)
return;
id = buf[0];
num = ntohs(bt_get_unaligned((uint16_t *) (buf + 1)));
len = btohs(bt_get_unaligned((uint16_t *) (buf + 3)));
status = buf[5];
time = ntohl(bt_get_unaligned((uint32_t *) (buf + 6)));
channel = buf[10];
if (len < 8)
return;
type = (len < 7) ? 0xff : bt_get_unaligned((uint8_t *) (buf + 11));
if (type < 2)
return;
p_indent(-1, NULL);
memset(&frm, 0, sizeof(frm));
frm.data = buf + 12;
frm.data_len = count - 12;
frm.ptr = frm.data;
frm.len = frm.data_len;
frm.in = 0;
frm.master = 0;
frm.handle = 0;
frm.flags = 0;
p_indent(0, &frm);
printf("BPA: id %d num %d status 0x%02x time %d channel %2d len %d\n",
id, num, status, time, channel, len - 6);
if (type < 3) {
printf(" %s\n", type2str(type));
raw_dump(1, &frm);
return;
}
addr = bt_get_unaligned((uint8_t *) (buf + 12));
temp = bt_get_unaligned((uint8_t *) (buf + 13));
flow = (temp & 0x04) >> 2;
arqn = (temp & 0x02) >> 1;
seqn = (temp & 0x01);
hec = bt_get_unaligned((uint8_t *) (buf + 14));
hdr = bt_get_unaligned((uint8_t *) (buf + 20));
plen = ((hdr & 0x10) >> 4) | ((hdr & 0x08) >> 2) | (hdr & 0x04) | ((hdr & 0x02) << 2) | ((hdr & 0x01) << 4);
pflow = ((hdr & 0x20) >> 5);
llid = ((hdr & 0x80) >> 7) | ((hdr & 0x40) >> 5);
hdr = bt_get_unaligned((uint8_t *) (buf + 21));
plen = plen | ((hdr & 0x80) >> 2) | (hdr & 0x40) | ((hdr & 0x20) << 2) | ((hdr & 0x08) << 4);
p_indent(0, &frm);
printf("%s: addr 0x%02x flow %d arqn %d seqn %d hec 0x%02x llid %d pflow %d plen %d\n",
type2str(type), addr, flow, arqn, seqn, hec, llid, pflow, plen);
if (type == 0x03 && llid == 3) {
memset(&frm, 0, sizeof(frm));
frm.data = buf + 22;
frm.data_len = plen;
frm.ptr = frm.data;
frm.len = frm.data_len;
frm.in = 0;
frm.master = 1;
frm.handle = 0;
frm.flags = llid;
lmp_dump(1, &frm);
return;
}
raw_dump(1, &frm);
}
static void process_frames(int dev)
{
struct sigaction sa;
struct hci_filter flt;
unsigned char *buf;
int dd, size = 2048;
buf = malloc(size);
if (!buf) {
fprintf(stderr, "Can't allocate buffer for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
return;
}
dd = hci_open_dev(dev);
if (dd < 0) {
fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
dev, strerror(errno), errno);
free(buf);
return;
}
hci_filter_clear(&flt);
hci_filter_set_ptype(HCI_VENDOR_PKT, &flt);
hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
hci_filter_set_event(EVT_VENDOR, &flt);
if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
fprintf(stderr, "Can't set filter for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
hci_close_dev(dd);
free(buf);
return;
}
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sa.sa_handler = sig_term;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = sig_hup;
sigaction(SIGHUP, &sa, NULL);
while (!__io_canceled) {
int len;
len = read(dd, buf, size);
if (len < 0)
break;
if (len < 2)
continue;
if (buf[0] == 0x04 && buf[1] == 0xff) {
if (buf[3] == 0x02) {
switch (buf[4]) {
case 0x00:
printf("Waiting for synchronization...\n");
break;
case 0x08:
printf("Synchronization lost\n");
__io_canceled = 1;
break;
default:
printf("Unknown event 0x%02x\n", buf[4]);
break;
}
}
}
if (buf[0] != 0xff)
continue;
decode(buf + 1, len - 1);
}
hci_close_dev(dd);
free(buf);
}
static void usage(void)
{
printf("bpasniff - Utility for the BPA 100/105 sniffers\n\n");
printf("Usage:\n"
"\tbpasniff [-i <dev>] <master-bdaddr>\n");
}
static struct option main_options[] = {
{ "help", 0, 0, 'h' },
{ "device", 1, 0, 'i' },
{ 0, 0, 0, 0}
};
int main(int argc, char *argv[])
{
struct hci_dev_info di;
struct hci_version ver;
char rev[46];
bdaddr_t bdaddr;
int dd, opt, dev = 0;
bacpy(&bdaddr, BDADDR_ANY);
while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
switch (opt) {
case 'i':
dev = hci_devid(optarg);
if (dev < 0) {
perror("Invalid device");
exit(1);
}
break;
case 'h':
default:
usage();
exit(0);
}
}
argc -= optind;
argv += optind;
optind = 0;
argc -= optind;
argv += optind;
optind = 0;
if (argc < 1) {
usage();
exit(1);
}
str2ba(argv[0], &bdaddr);
dd = hci_open_dev(dev);
if (dd < 0) {
fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
dev, strerror(errno), errno);
exit(1);
}
if (hci_devinfo(dev, &di) < 0) {
fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
hci_close_dev(dd);
exit(1);
}
if (hci_read_local_version(dd, &ver, 1000) < 0) {
fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
hci_close_dev(dd);
exit(1);
}
if (ver.manufacturer != 12) {
fprintf(stderr, "Can't find sniffer at hci%d: %s (%d)\n",
dev, strerror(ENOSYS), ENOSYS);
hci_close_dev(dd);
exit(1);
}
if (read_revision(dd, rev, sizeof(rev)) < 0) {
fprintf(stderr, "Can't read revision info for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
hci_close_dev(dd);
exit(1);
}
printf("%s\n", rev);
if (enable_sniffer(dd, 0x01) < 0) {
fprintf(stderr, "Can't enable sniffer for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
hci_close_dev(dd);
exit(1);
}
if (enable_sync(dd, 0x01, &bdaddr) < 0) {
fprintf(stderr, "Can't enable sync for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
enable_sniffer(dd, 0x00);
hci_close_dev(dd);
exit(1);
}
init_parser(DUMP_EXT | DUMP_VERBOSE, ~0L, 0, DEFAULT_COMPID, -1, -1);
process_frames(dev);
if (enable_sync(dd, 0x00, &bdaddr) < 0) {
fprintf(stderr, "Can't disable sync for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
enable_sniffer(dd, 0x00);
hci_close_dev(dd);
exit(1);
}
if (enable_sniffer(dd, 0x00) < 0) {
fprintf(stderr, "Can't disable sniffer for hci%d: %s (%d)\n",
dev, strerror(errno), errno);
hci_close_dev(dd);
exit(1);
}
hci_close_dev(dd);
return 0;
}