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