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