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