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