hcidump.c revision a5bbd712e40f7efff7bdbde49d579171500fb76d
1/*
2	HCIDump - HCI packet analyzer
3	Copyright (C) 2000-2001 Maxim Krasnyansky <maxk@qualcomm.com>
4
5	This program is free software; you can redistribute it and/or modify
6	it under the terms of the GNU General Public License version 2 as
7	published by the Free Software Foundation;
8
9	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12	IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
13	OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
14	RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15	NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
16	USE OR PERFORMANCE OF THIS SOFTWARE.
17
18	ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
19	TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
20*/
21
22/*
23 * $Id$
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <termios.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <string.h>
33#include <pwd.h>
34#include <getopt.h>
35
36#include <sys/types.h>
37#include <sys/ioctl.h>
38#include <sys/socket.h>
39#include <sys/uio.h>
40#include <sys/stat.h>
41#include <netinet/in.h>
42
43#include <bluetooth/bluetooth.h>
44#include <bluetooth/hci.h>
45#include <bluetooth/l2cap.h>
46#include <bluetooth/hci_lib.h>
47
48#include "parser/parser.h"
49#include "hcidump.h"
50
51/* Default options */
52static int  device;
53static int  snap_len = SNAP_LEN;
54static int  defpsm = 0;
55static int  mode = PARSE;
56static long flags;
57static long filter;
58static char *dump_file;
59
60static void process_frames(int dev, int sock, int file)
61{
62	struct cmsghdr *cmsg;
63	struct msghdr msg;
64	struct iovec  iv;
65	struct dump_hdr *dh;
66	struct frame frm;
67	char *buf, *ctrl;
68
69	if (snap_len < SNAP_LEN)
70		snap_len = SNAP_LEN;
71
72	if (!(buf = malloc(snap_len + DUMP_HDR_SIZE))) {
73		perror("Can't allocate data buffer");
74		exit(1);
75	}
76	dh = (void *) buf;
77	frm.data = buf + DUMP_HDR_SIZE;
78
79	if (!(ctrl = malloc(100))) {
80		perror("Can't allocate control buffer");
81		exit(1);
82	}
83
84	printf("device: hci%d snap_len: %d filter: 0x%lx\n",
85		dev, snap_len, filter);
86
87	memset(&msg, 0, sizeof(msg));
88
89	while (1) {
90		iv.iov_base = frm.data;
91		iv.iov_len  = snap_len;
92
93		msg.msg_iov = &iv;
94		msg.msg_iovlen = 1;
95		msg.msg_control = ctrl;
96		msg.msg_controllen = 100;
97
98		if ((frm.data_len = recvmsg(sock, &msg, 0)) < 0) {
99			perror("Receive failed");
100			exit(1);
101		}
102
103		/* Process control message */
104		frm.in = 0;
105		cmsg = CMSG_FIRSTHDR(&msg);
106		while (cmsg) {
107			switch (cmsg->cmsg_type) {
108			case HCI_CMSG_DIR:
109				frm.in = *((int *)CMSG_DATA(cmsg));
110				break;
111			case HCI_CMSG_TSTAMP:
112				frm.ts = *((struct timeval *)CMSG_DATA(cmsg));
113				break;
114			}
115			cmsg = CMSG_NXTHDR(&msg, cmsg);
116		}
117
118		frm.ptr = frm.data;
119		frm.len = frm.data_len;
120
121		switch (mode) {
122		case WRITE:
123			/* Save dump */
124			dh->len = htobs(frm.data_len);
125			dh->in  = frm.in;
126			dh->ts_sec  = htobl(frm.ts.tv_sec);
127			dh->ts_usec = htobl(frm.ts.tv_usec);
128			if (write_n(file, buf, frm.data_len + DUMP_HDR_SIZE) < 0) {
129				perror("Write error");
130				exit(1);
131			}
132			break;
133
134		default:
135			/* Parse and print */
136			parse(&frm);
137			break;
138		}
139	}
140}
141
142static void read_dump(int file)
143{
144	struct dump_hdr dh;
145	struct frame frm;
146	int err;
147
148	if (!(frm.data = malloc(HCI_MAX_FRAME_SIZE))) {
149		perror("Can't allocate data buffer");
150		exit(1);
151	}
152
153	while (1) {
154		if ((err = read_n(file, (void *) &dh, DUMP_HDR_SIZE)) < 0)
155			goto failed;
156		if (!err) return;
157
158		frm.data_len = btohs(dh.len);
159
160		if ((err = read_n(file, frm.data, frm.data_len)) < 0)
161			goto failed;
162		if (!err) return;
163
164		frm.ptr = frm.data;
165		frm.len = frm.data_len;
166		frm.in  = dh.in;
167		frm.ts.tv_sec  = btohl(dh.ts_sec);
168		frm.ts.tv_usec = btohl(dh.ts_usec);
169
170		parse(&frm);
171	}
172
173failed:
174	perror("Read failed");
175	exit(1);
176}
177
178static int open_file(char *file, int mode)
179{
180	int f, flags;
181
182	if (mode == WRITE)
183		flags = O_WRONLY | O_CREAT | O_APPEND;
184	else
185		flags = O_RDONLY;
186
187	if ((f = open(file, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
188		perror("Can't open output file");
189		exit(1);
190	}
191	return f;
192}
193
194static int open_socket(int dev)
195{
196	struct sockaddr_hci addr;
197	struct hci_filter flt;
198	int s, opt;
199
200	/* Create HCI socket */
201	if ((s=socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
202		perror("Can't create HCI socket");
203		exit(1);
204	}
205
206	opt = 1;
207	if (setsockopt(s, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
208		perror("Can't enable data direction info");
209		exit(1);
210	}
211
212	opt = 1;
213	if (setsockopt(s, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
214		perror("Can't enable time stamp");
215		exit(1);
216	}
217
218	/* Setup filter */
219	hci_filter_clear(&flt);
220	hci_filter_all_ptypes(&flt);
221	hci_filter_all_events(&flt);
222	if (setsockopt(s, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
223		perror("Can't set HCI filter");
224		exit(1);
225	}
226
227	/* Bind socket to the HCI device */
228	addr.hci_family = AF_BLUETOOTH;
229	addr.hci_dev = dev;
230	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
231		printf("Can't attach to device hci%d. %s(%d)\n",
232					dev, strerror(errno), errno);
233		exit(1);
234	}
235	return s;
236}
237
238static struct {
239	char *name;
240	int  flag;
241} filters[] = {
242	{ "hci",    FILT_HCI    },
243	{ "l2cap",  FILT_L2CAP  },
244	{ "sco",    FILT_SCO    },
245	{ "rfcomm", FILT_RFCOMM },
246	{ "sdp",    FILT_SDP    },
247	{ "bnep",   FILT_BNEP	},
248	{ "cmtp",   FILT_CMTP	},
249	{ 0 }
250};
251
252static void parse_filter(int argc, char **argv)
253{
254	int i,n;
255
256	for (i=0; i<argc; i++) {
257		for (n=0; filters[n].name; n++) {
258			if (!strcmp(filters[n].name, argv[i])) {
259				filter |= filters[n].flag;
260				break;
261			}
262		}
263	}
264}
265
266static void usage(void)
267{
268	printf(
269	"Usage: hcidump [OPTION...] [filter]\n"
270	"  -i, --device=hci_dev       HCI device\n"
271	"  -p, --psm=psm              Default PSM\n"
272	"  -s, --snap-len=len         Snap len (in bytes)\n"
273	"  -r, --read-dump=file       Read dump from a file\n"
274	"  -w, --save-dump=file       Save dump to a file\n"
275	"  -a, --ascii                Dump data in ascii\n"
276	"  -x, --hex                  Dump data in hex\n"
277	"  -R, --raw                  Raw mode\n"
278	"  -t, --ts                   Display time stamps\n"
279	"  -?, --help                 Give this help list\n"
280	"      --usage                Give a short usage message\n"
281	);
282}
283
284static struct option main_options[] = {
285	{"device",	1,0, 'i' },
286	{"snap-len", 	1,0, 's' },
287	{"psm", 	1,0, 'p' },
288	{"save-dump",	1,0, 'w' },
289	{"read-dump",	1,0, 'r' },
290	{"ts", 		0,0, 't' },
291	{"hex", 	0,0, 'x' },
292	{"ascii", 	0,0, 'a' },
293	{"raw", 	0,0, 'R' },
294	{"help", 	0,0, 'h' },
295	{ 0 }
296};
297
298int main(int argc, char *argv[])
299{
300	int opt;
301
302	printf("HCIDump - HCI packet analyzer ver %s\n", VERSION);
303
304        while ((opt=getopt_long(argc, argv, "i:s:p:r:w:xathR", main_options, NULL)) != -1) {
305                switch(opt) {
306		case 'i':
307			device = atoi(optarg+3);
308			break;
309
310		case 'x':
311			flags |= DUMP_HEX;
312			break;
313
314		case 'a':
315			flags |= DUMP_ASCII;
316			break;
317
318		case 's':
319			snap_len = atoi(optarg);
320			break;
321
322		case 'p':
323			defpsm = atoi(optarg);
324			break;
325
326		case 't':
327			flags |= DUMP_TSTAMP;
328			break;
329
330		case 'R':
331			flags |= DUMP_RAW;
332			break;
333
334		case 'r':
335			mode = READ;
336			dump_file = strdup(optarg);
337			break;
338
339		case 'w':
340			mode = WRITE;
341			dump_file = strdup(optarg);
342			break;
343
344                case 'h':
345                default:
346                        usage();
347                        exit(0);
348                }
349        }
350
351        argc -= optind;
352        argv += optind;
353        optind = 0;
354
355
356	if (argc > 0)
357		parse_filter(argc, argv);
358
359	/* Default settings */
360	if (!filter)
361		filter = ~0L;
362
363	switch (mode) {
364	case PARSE:
365		init_parser(flags, filter, defpsm);
366		process_frames(device, open_socket(device), -1);
367		break;
368
369	case WRITE:
370		process_frames(device, open_socket(device), open_file(dump_file, mode));
371		break;
372
373	case READ:
374		init_parser(flags, filter, defpsm);
375		read_dump(open_file(dump_file, mode));
376		break;
377	}
378	return 0;
379}
380