hcidump.c revision be9be844106d677ec77acee68935bc9e66772123
1/*
2 *
3 *  Bluetooth packet analyzer - HCIdump
4 *
5 *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
6 *  Copyright (C) 2003-2004  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 *
23 *
24 *  $Id$
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <stdio.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <termios.h>
37#include <string.h>
38#include <getopt.h>
39#include <pwd.h>
40
41#include <sys/types.h>
42#include <sys/socket.h>
43#include <sys/ioctl.h>
44#include <sys/uio.h>
45#include <sys/stat.h>
46#include <netinet/in.h>
47
48#include <bluetooth/bluetooth.h>
49#include <bluetooth/hci.h>
50#include <bluetooth/hci_lib.h>
51
52#include "parser/parser.h"
53#include "parser/sdp.h"
54
55#define SNAP_LEN HCI_MAX_FRAME_SIZE
56
57/* Modes */
58enum {
59	PARSE,
60	READ,
61	WRITE
62};
63
64/* Default options */
65static int  device;
66static int  snap_len = SNAP_LEN;
67static int  defpsm = 0;
68static int  defcompid = DEFAULT_COMPID;
69static int  mode = PARSE;
70static long flags;
71static long filter;
72static char *dump_file;
73
74struct dump_hdr {
75	uint16_t	len;
76	uint8_t		in;
77	uint8_t		pad;
78	uint32_t	ts_sec;
79	uint32_t	ts_usec;
80} __attribute__ ((packed));
81#define DUMP_HDR_SIZE (sizeof(struct dump_hdr))
82
83static inline int read_n(int fd, char *buf, int len)
84{
85	register int t = 0, w;
86
87	while (len > 0) {
88		if ((w = read(fd, buf, len)) < 0) {
89			if (errno == EINTR || errno == EAGAIN)
90				continue;
91			return -1;
92		}
93		if (!w)
94			return 0;
95		len -= w; buf += w; t += w;
96	}
97	return t;
98}
99
100static inline int write_n(int fd, char *buf, int len)
101{
102	register int t = 0, w;
103
104	while (len > 0) {
105		if ((w = write(fd, buf, len)) < 0) {
106			if (errno == EINTR || errno == EAGAIN)
107				continue;
108			return -1;
109		}
110		if (!w)
111			return 0;
112		len -= w; buf += w; t += w;
113	}
114	return t;
115}
116
117static void process_frames(int dev, int sock, int file)
118{
119	struct cmsghdr *cmsg;
120	struct msghdr msg;
121	struct iovec  iv;
122	struct dump_hdr *dh;
123	struct frame frm;
124	char *buf, *ctrl;
125
126	if (snap_len < SNAP_LEN)
127		snap_len = SNAP_LEN;
128
129	if (!(buf = malloc(snap_len + DUMP_HDR_SIZE))) {
130		perror("Can't allocate data buffer");
131		exit(1);
132	}
133
134	dh = (void *) buf;
135	frm.data = buf + DUMP_HDR_SIZE;
136
137	if (!(ctrl = malloc(100))) {
138		perror("Can't allocate control buffer");
139		exit(1);
140	}
141
142	printf("device: hci%d snap_len: %d filter: 0x%lx\n",
143		dev, snap_len, filter);
144
145	memset(&msg, 0, sizeof(msg));
146
147	while (1) {
148		iv.iov_base = frm.data;
149		iv.iov_len  = snap_len;
150
151		msg.msg_iov = &iv;
152		msg.msg_iovlen = 1;
153		msg.msg_control = ctrl;
154		msg.msg_controllen = 100;
155
156		if ((frm.data_len = recvmsg(sock, &msg, 0)) < 0) {
157			perror("Receive failed");
158			exit(1);
159		}
160
161		/* Process control message */
162		frm.in = 0;
163		cmsg = CMSG_FIRSTHDR(&msg);
164		while (cmsg) {
165			switch (cmsg->cmsg_type) {
166			case HCI_CMSG_DIR:
167				frm.in = *((int *)CMSG_DATA(cmsg));
168				break;
169			case HCI_CMSG_TSTAMP:
170				frm.ts = *((struct timeval *)CMSG_DATA(cmsg));
171				break;
172			}
173			cmsg = CMSG_NXTHDR(&msg, cmsg);
174		}
175
176		frm.ptr = frm.data;
177		frm.len = frm.data_len;
178
179		switch (mode) {
180		case WRITE:
181			/* Save dump */
182			dh->len = htobs(frm.data_len);
183			dh->in  = frm.in;
184			dh->ts_sec  = htobl(frm.ts.tv_sec);
185			dh->ts_usec = htobl(frm.ts.tv_usec);
186			if (write_n(file, buf, frm.data_len + DUMP_HDR_SIZE) < 0) {
187				perror("Write error");
188				exit(1);
189			}
190			break;
191
192		default:
193			/* Parse and print */
194			parse(&frm);
195			break;
196		}
197	}
198}
199
200static void read_dump(int file)
201{
202	struct dump_hdr dh;
203	struct frame frm;
204	int err;
205
206	if (!(frm.data = malloc(HCI_MAX_FRAME_SIZE))) {
207		perror("Can't allocate data buffer");
208		exit(1);
209	}
210
211	while (1) {
212		if ((err = read_n(file, (void *) &dh, DUMP_HDR_SIZE)) < 0)
213			goto failed;
214		if (!err) return;
215
216		frm.data_len = btohs(dh.len);
217
218		if ((err = read_n(file, frm.data, frm.data_len)) < 0)
219			goto failed;
220		if (!err) return;
221
222		frm.ptr = frm.data;
223		frm.len = frm.data_len;
224		frm.in  = dh.in;
225		frm.ts.tv_sec  = btohl(dh.ts_sec);
226		frm.ts.tv_usec = btohl(dh.ts_usec);
227
228		parse(&frm);
229	}
230
231failed:
232	perror("Read failed");
233	exit(1);
234}
235
236static int open_file(char *file, int mode)
237{
238	int f, flags;
239
240	if (mode == WRITE)
241		flags = O_WRONLY | O_CREAT | O_APPEND;
242	else
243		flags = O_RDONLY;
244
245	if ((f = open(file, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
246		perror("Can't open output file");
247		exit(1);
248	}
249	return f;
250}
251
252static int open_socket(int dev)
253{
254	struct sockaddr_hci addr;
255	struct hci_filter flt;
256	int sk, opt;
257
258	/* Create HCI socket */
259	if ((sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
260		perror("Can't create HCI socket");
261		exit(1);
262	}
263
264	opt = 1;
265	if (setsockopt(sk, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
266		perror("Can't enable data direction info");
267		exit(1);
268	}
269
270	opt = 1;
271	if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
272		perror("Can't enable time stamp");
273		exit(1);
274	}
275
276	/* Setup filter */
277	hci_filter_clear(&flt);
278	hci_filter_all_ptypes(&flt);
279	hci_filter_all_events(&flt);
280	if (setsockopt(sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
281		perror("Can't set HCI filter");
282		exit(1);
283	}
284
285	/* Bind socket to the HCI device */
286	addr.hci_family = AF_BLUETOOTH;
287	addr.hci_dev = dev;
288	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
289		printf("Can't attach to device hci%d. %s(%d)\n",
290					dev, strerror(errno), errno);
291		exit(1);
292	}
293
294	return sk;
295}
296
297static struct {
298	char *name;
299	int  flag;
300} filters[] = {
301	{ "hci",	FILT_HCI	},
302	{ "sco",	FILT_SCO	},
303	{ "l2cap",	FILT_L2CAP	},
304	{ "rfcomm",	FILT_RFCOMM	},
305	{ "sdp",	FILT_SDP	},
306	{ "bnep",	FILT_BNEP	},
307	{ "cmtp",	FILT_CMTP	},
308	{ "hidp",	FILT_HIDP	},
309	{ "hcrp",	FILT_HCRP	},
310	{ "avdtp",	FILT_AVDTP	},
311	{ "capi",	FILT_CAPI	},
312	{ 0 }
313};
314
315static void parse_filter(int argc, char **argv)
316{
317	int i,n;
318
319	for (i = 0; i < argc; i++) {
320		for (n = 0; filters[n].name; n++) {
321			if (!strcmp(filters[n].name, argv[i])) {
322				filter |= filters[n].flag;
323				break;
324			}
325		}
326	}
327}
328
329static void usage(void)
330{
331	printf(
332	"Usage: hcidump [OPTION...] [filter]\n"
333	"  -i, --device=hci_dev       HCI device\n"
334	"  -s, --snap-len=len         Snap len (in bytes)\n"
335	"  -p, --psm=psm              Default PSM\n"
336	"  -m, --manufacturer=compid  Default manufacturer\n"
337	"  -w, --save-dump=file       Save dump to a file\n"
338	"  -r, --read-dump=file       Read dump from a file\n"
339	"  -t, --ts                   Display time stamps\n"
340	"  -a, --ascii                Dump data in ascii\n"
341	"  -x, --hex                  Dump data in hex\n"
342	"  -X, --ext                  Dump data in hex and ascii\n"
343	"  -R, --raw                  Raw mode\n"
344	"  -C, --cmtp=psm             PSM for CMTP\n"
345	"  -H, --hcrp=psm             PSM for HCRP\n"
346	"  -?, --help                 Give this help list\n"
347	"      --usage                Give a short usage message\n"
348	);
349}
350
351static struct option main_options[] = {
352	{ "device",		1, 0, 'i' },
353	{ "snap-len",		1, 0, 's' },
354	{ "psm",		1, 0, 'p' },
355	{ "manufacturer",	1, 0, 'm' },
356	{ "save-dump",		1, 0, 'w' },
357	{ "read-dump",		1, 0, 'r' },
358	{ "ts",			0, 0, 't' },
359	{ "ascii",		0, 0, 'a' },
360	{ "hex",		0, 0, 'x' },
361	{ "ext",		0, 0, 'X' },
362	{ "raw",		0, 0, 'R' },
363	{ "cmtp",		1, 0, 'C' },
364	{ "hcrp",		1, 0, 'H' },
365	{ "help",		0, 0, 'h' },
366	{ 0 }
367};
368
369int main(int argc, char *argv[])
370{
371	int opt;
372
373	printf("HCIDump - HCI packet analyzer ver %s\n", VERSION);
374
375	while ((opt=getopt_long(argc, argv, "i:s:p:m:w:r:taxXRC:H:h", main_options, NULL)) != -1) {
376		switch(opt) {
377		case 'i':
378			device = atoi(optarg + 3);
379			break;
380
381		case 's':
382			snap_len = atoi(optarg);
383			break;
384
385		case 'p':
386			defpsm = atoi(optarg);
387			break;
388
389		case 'm':
390			defcompid = atoi(optarg);
391			break;
392
393		case 'w':
394			mode = WRITE;
395			dump_file = strdup(optarg);
396			break;
397
398		case 'r':
399			mode = READ;
400			dump_file = strdup(optarg);
401			break;
402
403		case 't':
404			flags |= DUMP_TSTAMP;
405			break;
406
407		case 'a':
408			flags |= DUMP_ASCII;
409			break;
410
411		case 'x':
412			flags |= DUMP_HEX;
413			break;
414
415		case 'X':
416			flags |= DUMP_EXT;
417			break;
418
419		case 'R':
420			flags |= DUMP_RAW;
421			break;
422
423		case 'C':
424			set_proto(0, atoi(optarg), SDP_UUID_CMTP);
425			break;
426
427		case 'H':
428			set_proto(0, atoi(optarg), SDP_UUID_HARDCOPY_CONTROL_CHANNEL);
429			break;
430
431		case 'h':
432		default:
433			usage();
434			exit(0);
435		}
436	}
437
438	argc -= optind;
439	argv += optind;
440	optind = 0;
441
442	if (argc > 0)
443		parse_filter(argc, argv);
444
445	/* Default settings */
446	if (!filter)
447		filter = ~0L;
448
449	switch (mode) {
450	case PARSE:
451		init_parser(flags, filter, defpsm, defcompid);
452		process_frames(device, open_socket(device), -1);
453		break;
454
455	case WRITE:
456		process_frames(device, open_socket(device), open_file(dump_file, mode));
457		break;
458
459	case READ:
460		init_parser(flags, filter, defpsm, defcompid);
461		read_dump(open_file(dump_file, mode));
462		break;
463	}
464
465	return 0;
466}
467