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