|  | /* | 
|  | * | 
|  | *  BlueZ - Bluetooth protocol stack for Linux | 
|  | * | 
|  | *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved. | 
|  | * | 
|  | * | 
|  | *  This library is free software; you can redistribute it and/or | 
|  | *  modify it under the terms of the GNU Lesser General Public | 
|  | *  License as published by the Free Software Foundation; either | 
|  | *  version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | *  This library 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 | 
|  | *  Lesser General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU Lesser General Public | 
|  | *  License along with this library; 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 <ctype.h> | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #if defined(ANDROID) | 
|  | #include <sys/capability.h> | 
|  | #endif | 
|  |  | 
|  | #include "lib/bluetooth.h" | 
|  | #include "lib/hci.h" | 
|  | #include "lib/mgmt.h" | 
|  |  | 
|  | #include "src/shared/mainloop.h" | 
|  | #include "src/shared/btsnoop.h" | 
|  | #include "src/log.h" | 
|  |  | 
|  | #define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log" | 
|  |  | 
|  | static struct btsnoop *snoop = NULL; | 
|  | static uint8_t monitor_buf[BTSNOOP_MAX_PACKET_SIZE]; | 
|  | static int monitor_fd = -1; | 
|  |  | 
|  | static void signal_callback(int signum, void *user_data) | 
|  | { | 
|  | switch (signum) { | 
|  | case SIGINT: | 
|  | case SIGTERM: | 
|  | mainloop_quit(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint32_t get_flags_from_opcode(uint16_t opcode) | 
|  | { | 
|  | switch (opcode) { | 
|  | case BTSNOOP_OPCODE_NEW_INDEX: | 
|  | case BTSNOOP_OPCODE_DEL_INDEX: | 
|  | break; | 
|  | case BTSNOOP_OPCODE_COMMAND_PKT: | 
|  | return 0x02; | 
|  | case BTSNOOP_OPCODE_EVENT_PKT: | 
|  | return 0x03; | 
|  | case BTSNOOP_OPCODE_ACL_TX_PKT: | 
|  | return 0x00; | 
|  | case BTSNOOP_OPCODE_ACL_RX_PKT: | 
|  | return 0x01; | 
|  | case BTSNOOP_OPCODE_SCO_TX_PKT: | 
|  | case BTSNOOP_OPCODE_SCO_RX_PKT: | 
|  | break; | 
|  | case BTSNOOP_OPCODE_OPEN_INDEX: | 
|  | case BTSNOOP_OPCODE_CLOSE_INDEX: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return 0xff; | 
|  | } | 
|  |  | 
|  | static void data_callback(int fd, uint32_t events, void *user_data) | 
|  | { | 
|  | unsigned char control[32]; | 
|  | struct mgmt_hdr hdr; | 
|  | struct msghdr msg; | 
|  | struct iovec iov[2]; | 
|  |  | 
|  | if (events & (EPOLLERR | EPOLLHUP)) { | 
|  | mainloop_remove_fd(monitor_fd); | 
|  | return; | 
|  | } | 
|  |  | 
|  | iov[0].iov_base = &hdr; | 
|  | iov[0].iov_len = MGMT_HDR_SIZE; | 
|  | iov[1].iov_base = monitor_buf; | 
|  | iov[1].iov_len = sizeof(monitor_buf); | 
|  |  | 
|  | memset(&msg, 0, sizeof(msg)); | 
|  | msg.msg_iov = iov; | 
|  | msg.msg_iovlen = 2; | 
|  | msg.msg_control = control; | 
|  | msg.msg_controllen = sizeof(control); | 
|  |  | 
|  | while (true) { | 
|  | struct cmsghdr *cmsg; | 
|  | struct timeval *tv = NULL; | 
|  | struct timeval ctv; | 
|  | uint16_t opcode, index, pktlen; | 
|  | uint32_t flags; | 
|  | ssize_t len; | 
|  |  | 
|  | len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT); | 
|  | if (len < 0) | 
|  | break; | 
|  |  | 
|  | if (len < MGMT_HDR_SIZE) | 
|  | break; | 
|  |  | 
|  | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; | 
|  | cmsg = CMSG_NXTHDR(&msg, cmsg)) { | 
|  | if (cmsg->cmsg_level != SOL_SOCKET) | 
|  | continue; | 
|  |  | 
|  | if (cmsg->cmsg_type == SCM_TIMESTAMP) { | 
|  | memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); | 
|  | tv = &ctv; | 
|  | } | 
|  | } | 
|  |  | 
|  | opcode = btohs(hdr.opcode); | 
|  | index  = btohs(hdr.index); | 
|  | pktlen = btohs(hdr.len); | 
|  |  | 
|  | if (index) | 
|  | continue; | 
|  |  | 
|  | flags = get_flags_from_opcode(opcode); | 
|  | if (flags != 0xff) | 
|  | btsnoop_write(snoop, tv, flags, 0, monitor_buf, pktlen); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int open_monitor(const char *path) | 
|  | { | 
|  | struct sockaddr_hci addr; | 
|  | int opt = 1; | 
|  |  | 
|  | snoop = btsnoop_create(path, BTSNOOP_FORMAT_HCI); | 
|  | if (!snoop) | 
|  | return -1; | 
|  |  | 
|  | monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); | 
|  | if (monitor_fd < 0) | 
|  | goto failed; | 
|  |  | 
|  | memset(&addr, 0, sizeof(addr)); | 
|  | addr.hci_family = AF_BLUETOOTH; | 
|  | addr.hci_dev = HCI_DEV_NONE; | 
|  | addr.hci_channel = HCI_CHANNEL_MONITOR; | 
|  |  | 
|  | if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) | 
|  | goto failed_close; | 
|  |  | 
|  | if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) | 
|  | < 0) | 
|  | goto failed_close; | 
|  |  | 
|  | mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | failed_close: | 
|  | close(monitor_fd); | 
|  | monitor_fd = -1; | 
|  |  | 
|  | failed: | 
|  | btsnoop_unref(snoop); | 
|  | snoop = NULL; | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static void close_monitor(void) | 
|  | { | 
|  | btsnoop_unref(snoop); | 
|  | snoop = NULL; | 
|  |  | 
|  | close(monitor_fd); | 
|  | monitor_fd = -1; | 
|  | } | 
|  |  | 
|  | static void set_capabilities(void) | 
|  | { | 
|  | #if defined(ANDROID) | 
|  | struct __user_cap_header_struct header; | 
|  | struct __user_cap_data_struct cap; | 
|  |  | 
|  | header.version = _LINUX_CAPABILITY_VERSION; | 
|  | header.pid = 0; | 
|  |  | 
|  | /* | 
|  | * CAP_NET_RAW: for snooping | 
|  | * CAP_DAC_READ_SEARCH: override path search permissions | 
|  | */ | 
|  | cap.effective = cap.permitted = | 
|  | CAP_TO_MASK(CAP_NET_RAW) | | 
|  | CAP_TO_MASK(CAP_DAC_READ_SEARCH); | 
|  | cap.inheritable = 0; | 
|  |  | 
|  | /* TODO: Move to cap_set_proc once bionic support it */ | 
|  | if (capset(&header, &cap) < 0) | 
|  | exit(EXIT_FAILURE); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | const char *path; | 
|  | sigset_t mask; | 
|  |  | 
|  | __btd_log_init(NULL, 0); | 
|  |  | 
|  | DBG(""); | 
|  |  | 
|  | set_capabilities(); | 
|  |  | 
|  | if (argc > 1) | 
|  | path = argv[1]; | 
|  | else | 
|  | path = DEFAULT_SNOOP_FILE; | 
|  |  | 
|  | mainloop_init(); | 
|  |  | 
|  | sigemptyset(&mask); | 
|  | sigaddset(&mask, SIGINT); | 
|  | sigaddset(&mask, SIGTERM); | 
|  |  | 
|  | mainloop_set_signal(&mask, signal_callback, NULL, NULL); | 
|  |  | 
|  | if (!strcmp(DEFAULT_SNOOP_FILE, path)) | 
|  | rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old"); | 
|  |  | 
|  | if (open_monitor(path) < 0) { | 
|  | error("bluetoothd_snoop: start failed"); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  |  | 
|  | info("bluetoothd_snoop: started"); | 
|  |  | 
|  | mainloop_run(); | 
|  |  | 
|  | close_monitor(); | 
|  |  | 
|  | info("bluetoothd_snoop: stopped"); | 
|  |  | 
|  | __btd_log_cleanup(); | 
|  |  | 
|  | return EXIT_SUCCESS; | 
|  | } |