📄 hcidump.c
字号:
/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com> * Copyright (C) 2003-2007 Marcel Holtmann <marcel@holtmann.org> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <getopt.h>#include <sys/poll.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <arpa/inet.h>#include <netinet/in.h>#include <netdb.h>#include "parser/parser.h"#include "parser/sdp.h"#if __BYTE_ORDER == __LITTLE_ENDIANstatic inline uint64_t ntoh64(uint64_t n){ uint64_t h; uint64_t tmp = ntohl(n & 0x00000000ffffffff); h = ntohl(n >> 32); h |= tmp << 32; return h;}#elif __BYTE_ORDER == __BIG_ENDIAN#define ntoh64(x) (x)#else#error "Unknown byte order"#endif#define hton64(x) ntoh64(x)#define SNAP_LEN HCI_MAX_FRAME_SIZE#define DEFAULT_PORT "10839";/* Modes */enum { PARSE, READ, WRITE, RECEIVE, SEND, SERVER, PPPDUMP, AUDIO};/* Default options */static int snap_len = SNAP_LEN;static int mode = PARSE;static int permcheck = 1;static int noappend = 0;static char *dump_file = NULL;static char *pppdump_file = NULL;static char *audio_file = NULL;static char *dump_addr;static char *dump_port = DEFAULT_PORT;static int af = AF_UNSPEC;struct hcidump_hdr { uint16_t len; uint8_t in; uint8_t pad; uint32_t ts_sec; uint32_t ts_usec;} __attribute__ ((packed));#define HCIDUMP_HDR_SIZE (sizeof(struct hcidump_hdr))struct btsnoop_hdr { uint8_t id[8]; /* Identification Pattern */ uint32_t version; /* Version Number = 1 */ uint32_t type; /* Datalink Type */} __attribute__ ((packed));#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))struct btsnoop_pkt { uint32_t size; /* Original Length */ uint32_t len; /* Included Length */ uint32_t flags; /* Packet Flags */ uint32_t drops; /* Cumulative Drops */ uint64_t ts; /* Timestamp microseconds */ uint8_t data[0]; /* Packet Data */} __attribute__ ((packed));#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };static uint32_t btsnoop_version = 0;static uint32_t btsnoop_type = 0;struct pktlog_hdr { uint32_t len; uint64_t ts; uint8_t type;} __attribute__ ((packed));#define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr))static inline int read_n(int fd, char *buf, int len){ int t = 0, w; while (len > 0) { if ((w = read(fd, buf, len)) < 0) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (!w) return 0; len -= w; buf += w; t += w; } return t;}static inline int write_n(int fd, char *buf, int len){ int t = 0, w; while (len > 0) { if ((w = write(fd, buf, len)) < 0) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (!w) return 0; len -= w; buf += w; t += w; } return t;}static int process_frames(int dev, int sock, int fd, unsigned long flags){ struct cmsghdr *cmsg; struct msghdr msg; struct iovec iv; struct hcidump_hdr *dh; struct btsnoop_pkt *dp; struct frame frm; struct pollfd fds[2]; int nfds = 0; char *buf, *ctrl; int len, hdr_size = HCIDUMP_HDR_SIZE; if (sock < 0) return -1; if (mode == SERVER) flags |= DUMP_BTSNOOP; if (snap_len < SNAP_LEN) snap_len = SNAP_LEN; if (flags & DUMP_BTSNOOP) hdr_size = BTSNOOP_PKT_SIZE; buf = malloc(snap_len + hdr_size); if (!buf) { perror("Can't allocate data buffer"); return -1; } dh = (void *) buf; dp = (void *) buf; frm.data = buf + hdr_size; ctrl = malloc(100); if (!ctrl) { free(buf); perror("Can't allocate control buffer"); return -1; } if (dev == HCI_DEV_NONE) printf("system: "); else printf("device: hci%d ", dev); printf("snap_len: %d filter: 0x%lx\n", snap_len, parser.filter); memset(&msg, 0, sizeof(msg)); if (mode == SERVER) { struct btsnoop_hdr *hdr = (void *) buf; btsnoop_version = 1; btsnoop_type = 1002; memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id)); hdr->version = htonl(btsnoop_version); hdr->type = htonl(btsnoop_type); printf("btsnoop version: %d datalink type: %d\n", btsnoop_version, btsnoop_type); len = write(fd, buf, BTSNOOP_HDR_SIZE); if (len < 0) { perror("Can't create dump header"); return -1; } if (len != BTSNOOP_HDR_SIZE) { fprintf(stderr, "Header size mismatch\n"); return -1; } fds[nfds].fd = fd; fds[nfds].events = POLLIN; fds[nfds].revents = 0; nfds++; } fds[nfds].fd = sock; fds[nfds].events = POLLIN; fds[nfds].revents = 0; nfds++; while (1) { int i, n = poll(fds, nfds, -1); if (n <= 0) continue; for (i = 0; i < nfds; i++) { if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) { if (fds[i].fd == sock) printf("device: disconnected\n"); else printf("client: disconnect\n"); return 0; } } if (mode == SERVER) { len = recv(fd, buf, snap_len, MSG_DONTWAIT); if (len == 0) { printf("client: disconnect\n"); return 0; } if (len < 0 && errno != EAGAIN && errno != EINTR) { perror("Connection read failure"); return -1; } } iv.iov_base = frm.data; iv.iov_len = snap_len; msg.msg_iov = &iv; msg.msg_iovlen = 1; msg.msg_control = ctrl; msg.msg_controllen = 100; len = recvmsg(sock, &msg, MSG_DONTWAIT); if (len < 0) { if (errno == EAGAIN || errno == EINTR) continue; perror("Receive failed"); return -1; } /* Process control message */ frm.data_len = len; frm.dev_id = dev; frm.in = 0; frm.pppdump_fd = parser.pppdump_fd; frm.audio_fd = parser.audio_fd; cmsg = CMSG_FIRSTHDR(&msg); while (cmsg) { switch (cmsg->cmsg_type) { case HCI_CMSG_DIR: frm.in = *((int *) CMSG_DATA(cmsg)); break; case HCI_CMSG_TSTAMP: frm.ts = *((struct timeval *) CMSG_DATA(cmsg)); break; } cmsg = CMSG_NXTHDR(&msg, cmsg); } frm.ptr = frm.data; frm.len = frm.data_len; switch (mode) { case WRITE: case SEND: case SERVER: /* Save or send dump */ if (flags & DUMP_BTSNOOP) { uint64_t ts; uint8_t pkt_type = ((uint8_t *) frm.data)[0]; dp->size = htonl(frm.data_len); dp->len = dp->size; dp->flags = ntohl(frm.in & 0x01); dp->drops = 0; ts = (frm.ts.tv_sec - 946684800ll) * 1000000ll + frm.ts.tv_usec; dp->ts = hton64(ts + 0x00E03AB44A676000ll); if (pkt_type == HCI_COMMAND_PKT || pkt_type == HCI_EVENT_PKT) dp->flags |= ntohl(0x02); } else { dh->len = htobs(frm.data_len); dh->in = frm.in; dh->ts_sec = htobl(frm.ts.tv_sec); dh->ts_usec = htobl(frm.ts.tv_usec); } if (write_n(fd, buf, frm.data_len + hdr_size) < 0) { perror("Write error"); return -1; } break; default: /* Parse and print */ parse(&frm); break; } } return 0;}static void read_dump(int fd){ struct hcidump_hdr dh; struct btsnoop_pkt dp; struct pktlog_hdr ph; struct frame frm; uint8_t pkt_type; int err; frm.data = malloc(HCI_MAX_FRAME_SIZE); if (!frm.data) { perror("Can't allocate data buffer"); exit(1); } while (1) { if (parser.flags & DUMP_PKTLOG) err = read_n(fd, (void *) &ph, PKTLOG_HDR_SIZE); else if (parser.flags & DUMP_BTSNOOP) err = read_n(fd, (void *) &dp, BTSNOOP_PKT_SIZE); else err = read_n(fd, (void *) &dh, HCIDUMP_HDR_SIZE); if (err < 0) goto failed; if (!err) return; if (parser.flags & DUMP_PKTLOG) { switch (ph.type) { case 0x00: ((uint8_t *) frm.data)[0] = HCI_COMMAND_PKT; frm.in = 0; break; case 0x01: ((uint8_t *) frm.data)[0] = HCI_EVENT_PKT; frm.in = 1; break; case 0x02: ((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT; frm.in = 0; break; case 0x03: ((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT; frm.in = 1; break; default: lseek(fd, ntohl(ph.len) - 9, SEEK_CUR); continue; } frm.data_len = ntohl(ph.len) - 8; err = read_n(fd, frm.data + 1, frm.data_len - 1); } else if (parser.flags & DUMP_BTSNOOP) { switch (btsnoop_type) { case 1001: if (ntohl(dp.flags) & 0x02) { if (ntohl(dp.flags) & 0x01) pkt_type = HCI_EVENT_PKT; else pkt_type = HCI_COMMAND_PKT; } else pkt_type = HCI_ACLDATA_PKT; ((uint8_t *) frm.data)[0] = pkt_type; frm.data_len = ntohl(dp.len) + 1; err = read_n(fd, frm.data + 1, frm.data_len - 1); break; case 1002: frm.data_len = ntohl(dp.len); err = read_n(fd, frm.data, frm.data_len); break; } } else { frm.data_len = btohs(dh.len); err = read_n(fd, frm.data, frm.data_len); } if (err < 0) goto failed; if (!err) return; frm.ptr = frm.data; frm.len = frm.data_len; if (parser.flags & DUMP_PKTLOG) { uint64_t ts; ts = ntoh64(ph.ts); frm.ts.tv_sec = ts >> 32; frm.ts.tv_usec = ts & 0xffffffff; } else if (parser.flags & DUMP_BTSNOOP) { uint64_t ts; frm.in = ntohl(dp.flags) & 0x01; ts = ntoh64(dp.ts) - 0x00E03AB44A676000ll; frm.ts.tv_sec = (ts / 1000000ll) + 946684800ll; frm.ts.tv_usec = ts % 1000000ll; } else { frm.in = dh.in; frm.ts.tv_sec = btohl(dh.ts_sec); frm.ts.tv_usec = btohl(dh.ts_usec); } parse(&frm); }failed: perror("Read failed"); exit(1);}static int open_file(char *file, int mode, unsigned long flags){ unsigned char buf[BTSNOOP_HDR_SIZE]; struct btsnoop_hdr *hdr = (struct btsnoop_hdr *) buf; int fd, len, open_flags; if (mode == WRITE || mode == PPPDUMP || mode == AUDIO) { if (noappend || flags & DUMP_BTSNOOP) open_flags = O_WRONLY | O_CREAT | O_TRUNC; else open_flags = O_WRONLY | O_CREAT | O_APPEND; } else open_flags = O_RDONLY; fd = open(file, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { perror("Can't open dump file"); exit(1); } if (mode == READ) { len = read(fd, buf, BTSNOOP_HDR_SIZE); if (len != BTSNOOP_HDR_SIZE) { lseek(fd, 0, SEEK_SET); return fd; } if (!memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id))) { parser.flags |= DUMP_BTSNOOP; btsnoop_version = ntohl(hdr->version); btsnoop_type = ntohl(hdr->type); printf("btsnoop version: %d datalink type: %d\n", btsnoop_version, btsnoop_type); if (btsnoop_version != 1) { fprintf(stderr, "Unsupported BTSnoop version\n"); exit(1); } if (btsnoop_type != 1001 && btsnoop_type != 1002) { fprintf(stderr, "Unsupported BTSnoop datalink type\n"); exit(1); } } else { if (buf[0] == 0x00 && buf[1] == 0x00) { parser.flags |= DUMP_PKTLOG; printf("packet logger data format\n"); } parser.flags &= ~DUMP_BTSNOOP; lseek(fd, 0, SEEK_SET); return fd; } } else { if (flags & DUMP_BTSNOOP) { btsnoop_version = 1; btsnoop_type = 1002; memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id)); hdr->version = htonl(btsnoop_version); hdr->type = htonl(btsnoop_type); printf("btsnoop version: %d datalink type: %d\n", btsnoop_version, btsnoop_type); len = write(fd, buf, BTSNOOP_HDR_SIZE); if (len < 0) { perror("Can't create dump header"); exit(1); } if (len != BTSNOOP_HDR_SIZE) { fprintf(stderr, "Header size mismatch\n"); exit(1); } } } return fd;}static int open_socket(int dev, unsigned long flags){ struct sockaddr_hci addr; struct hci_filter flt; struct hci_dev_info di; int sk, dd, opt; if (permcheck && dev != HCI_DEV_NONE) { dd = hci_open_dev(dev); if (dd < 0) { perror("Can't open device"); return -1; } if (hci_devinfo(dev, &di) < 0) { perror("Can't get device info"); return -1; } opt = hci_test_bit(HCI_RAW, &di.flags); if (ioctl(dd, HCISETRAW, opt) < 0) { if (errno == EACCES) { perror("Can't access device"); return -1; } } hci_close_dev(dd); } /* Create HCI socket */ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (sk < 0) { perror("Can't create raw socket");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -