2014-05-30 16:15:53

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH 1/4] shared/btsnoop: Add btsnoop_create_fd

This patch adds btsnoop_create_fd function which allows to create
btsnoop on existing file descriptor instead of creating new file like
btsnoop_create does.
---
src/shared/btsnoop.c | 47 ++++++++++++++++++++++++++++++++++++-----------
src/shared/btsnoop.h | 1 +
2 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index 17a872c..da993c5 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -128,12 +128,45 @@ failed:
return NULL;
}

-struct btsnoop *btsnoop_create(const char *path, uint32_t type)
+static bool btsnoop_init(struct btsnoop *btsnoop, uint32_t type)
{
- struct btsnoop *btsnoop;
struct btsnoop_hdr hdr;
ssize_t written;

+ btsnoop->type = type;
+ btsnoop->index = 0xffff;
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(btsnoop_version);
+ hdr.type = htobe32(btsnoop->type);
+
+ written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+
+ return written == BTSNOOP_HDR_SIZE;
+}
+
+struct btsnoop *btsnoop_create_fd(int fd, uint32_t type)
+{
+ struct btsnoop *btsnoop;
+
+ btsnoop = calloc(1, sizeof(*btsnoop));
+ if (!btsnoop)
+ return NULL;
+
+ btsnoop->fd = fd;
+
+ if (!btsnoop_init(btsnoop, type)) {
+ free(btsnoop);
+ return NULL;
+ }
+
+ return btsnoop_ref(btsnoop);
+}
+
+struct btsnoop *btsnoop_create(const char *path, uint32_t type)
+{
+ struct btsnoop *btsnoop;
+
btsnoop = calloc(1, sizeof(*btsnoop));
if (!btsnoop)
return NULL;
@@ -145,15 +178,7 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t type)
return NULL;
}

- btsnoop->type = type;
- btsnoop->index = 0xffff;
-
- memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
- hdr.version = htobe32(btsnoop_version);
- hdr.type = htobe32(btsnoop->type);
-
- written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
- if (written < 0) {
+ if (!btsnoop_init(btsnoop, type)) {
close(btsnoop->fd);
free(btsnoop);
return NULL;
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index 2c55d02..9e6b854 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -55,6 +55,7 @@ struct btsnoop;

struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
struct btsnoop *btsnoop_create(const char *path, uint32_t type);
+struct btsnoop *btsnoop_create_fd(int fd, uint32_t type);

struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
void btsnoop_unref(struct btsnoop *btsnoop);
--
1.9.3



2014-05-30 16:15:56

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH 4/4] tools/hcidump: Add support to read snoop via ADB

Reading from ADB requires bluetoothd-snoop service to be running on
target device (i.e. HCI logging needs to be turned on in case of KitKat
devices).

Without optional argument, "-d" or "--read-adb" will connect to default
device and use default port (4330). Optional argument can be in form:
<serialno>:<port>

Any of <serialno> and <port> can be omitted and default value will be
used, i.e. --read-adb=ABCD will read from device with serial ABCD and
port 4330 while --read-adb=:1234 will read from default device and port
1234.
---
tools/hcidump.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 165 insertions(+), 2 deletions(-)

diff --git a/tools/hcidump.c b/tools/hcidump.c
index 37a9f00..3dff7d6 100644
--- a/tools/hcidump.c
+++ b/tools/hcidump.c
@@ -38,6 +38,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
+#include <arpa/inet.h>

#include "parser/parser.h"
#include "parser/sdp.h"
@@ -53,7 +54,8 @@ enum {
READ,
WRITE,
PPPDUMP,
- AUDIO
+ AUDIO,
+ ADB,
};

/* Default options */
@@ -522,6 +524,153 @@ static int open_file(char *file, int mode, unsigned long flags)
return fd;
}

+static int send_adb(int fd, const char *cmd)
+{
+ char buf[128];
+ unsigned int len;
+ struct iovec iov[2];
+
+ len = strlen(cmd);
+ sprintf(buf, "%04X", len);
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = (void *) cmd;
+ iov[1].iov_len = len;
+
+ if (writev(fd, iov, 2) < 0)
+ return -1;
+
+ if (recv(fd, buf, sizeof(buf), 0) < 0)
+ return -1;
+
+ /* OKAY means we're fine */
+ if (!memcmp("OKAY", buf, 4))
+ return 0;
+
+ /*
+ * failure comes in format FAIL<4hex><msg> where <4hex> is length of
+ * <msg> written as hexadecimal string (4 chars)
+ */
+ if (sscanf(buf, "FAIL%04X", &len) > 0) {
+ buf[8 + len] = '\0';
+ fprintf(stderr, "ADB: %s\n", &buf[8]);
+ } else {
+ fprintf(stderr, "ADB: unknown failure");
+ }
+
+ return -1;
+}
+
+static int connect_adb(const char *serial, int port)
+{
+ struct sockaddr_in addr;
+ char cmd[128];
+ int fd;
+
+ fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ perror("Failed to open TCP client socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = htons(5037); /* default ADB port */
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to connect TCP client socket");
+ close(fd);
+ return -1;
+ }
+
+ /* first need to switch socket to adb daemon on device */
+ if (serial)
+ sprintf(cmd, "host:transport:%s", serial);
+ else
+ strcpy(cmd, "host:transport-any");
+ if (send_adb(fd, cmd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ /* next need to switch socket to tcp socket */
+ sprintf(cmd, "tcp:%d", port ? port : 4330);
+ if (send_adb(fd, cmd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static void parse_adb(const char *device, char **serial, int *port)
+{
+ const char *p;
+
+ *serial = NULL;
+ *port = 0;
+
+ if (!device)
+ return;
+
+ p = strrchr(device, ':');
+ if (!p) {
+ *serial = strdup(device);
+ return;
+ }
+
+ if (p - device)
+ *serial = strndup(device, p - device);
+ *port = atoi(&p[1]);
+}
+
+static int open_adb(const char *device)
+{
+ unsigned char buf[BTSNOOP_HDR_SIZE];
+ struct btsnoop_hdr *hdr = (struct btsnoop_hdr *) buf;
+ int fd, len;
+ char *serial;
+ int port;
+
+ parse_adb(device, &serial, &port);
+
+ fd = connect_adb(serial, port);
+ if (fd < 0)
+ return -1;
+
+ free(serial);
+
+ len = read(fd, buf, BTSNOOP_HDR_SIZE);
+ if (len != BTSNOOP_HDR_SIZE)
+ return -1;
+
+ if (memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id)))
+ return -1;
+
+ parser.flags |= DUMP_BTSNOOP;
+
+ btsnoop_version = be32toh(hdr->version);
+ btsnoop_type = be32toh(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 &&
+ btsnoop_type != 2001) {
+ fprintf(stderr, "Unsupported BTSnoop datalink type\n");
+ exit(1);
+ }
+
+ return fd;
+}
+
static int open_socket(int dev, unsigned long flags)
{
struct sockaddr_hci addr;
@@ -624,6 +773,7 @@ static void usage(void)
" -m, --manufacturer=compid Default manufacturer\n"
" -w, --save-dump=file Save dump to a file\n"
" -r, --read-dump=file Read dump from a file\n"
+ " -d, --read-adb=dev Read dump via ADB\n"
" -t, --ts Display time stamps\n"
" -a, --ascii Dump data in ascii\n"
" -x, --hex Dump data in hex\n"
@@ -650,6 +800,7 @@ static struct option main_options[] = {
{ "manufacturer", 1, 0, 'm' },
{ "save-dump", 1, 0, 'w' },
{ "read-dump", 1, 0, 'r' },
+ { "read-adb", 2, 0, 'd' },
{ "timestamp", 0, 0, 't' },
{ "ascii", 0, 0, 'a' },
{ "hex", 0, 0, 'x' },
@@ -679,7 +830,7 @@ int main(int argc, char *argv[])
uint16_t obex_port;

while ((opt = getopt_long(argc, argv,
- "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv",
+ "i:l:p:m:w:r:d::taxXRC:H:O:P:S:D:A:Yhv",
main_options, NULL)) != -1) {
switch(opt) {
case 'i':
@@ -711,6 +862,11 @@ int main(int argc, char *argv[])
dump_file = strdup(optarg);
break;

+ case 'd':
+ mode = ADB;
+ dump_file = optarg ? strdup(optarg) : NULL;
+ break;
+
case 't':
flags |= DUMP_TSTAMP;
break;
@@ -817,6 +973,13 @@ int main(int argc, char *argv[])
process_frames(device, open_socket(device, flags),
open_file(dump_file, mode, flags), flags);
break;
+
+ case ADB:
+ flags |= DUMP_VERBOSE;
+ init_parser(flags, filter, defpsm, defcompid,
+ pppdump_fd, audio_fd);
+ read_dump(open_adb(dump_file));
+ break;
}

return 0;
--
1.9.3


2014-05-30 16:15:55

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH 3/4] android/snoop: Add support for TCP clients

This patch adds support to write snoop file to client connected via
TCP. Default listen port is 4330 which is used by Bluedroid for similar
purpose.
---
android/bluetoothd-snoop.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 122 insertions(+), 1 deletion(-)

diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 57f97f4..25b9c97 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -32,6 +32,9 @@
#if defined(ANDROID)
#include <sys/capability.h>
#endif
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
@@ -41,12 +44,15 @@
#include "src/shared/btsnoop.h"

#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log"
+#define DEFAULT_TCP_PORT 4330

#define MAX_PACKET_SIZE (1486 + 4)

static struct btsnoop *snoop = NULL;
+static struct btsnoop *tcp_snoop = NULL;
static uint8_t monitor_buf[MAX_PACKET_SIZE];
static int monitor_fd = -1;
+static int server_fd = -1;

static void signal_callback(int signum, void *user_data)
{
@@ -137,8 +143,10 @@ static void data_callback(int fd, uint32_t events, void *user_data)
continue;

flags = get_flags_from_opcode(opcode);
- if (flags != 0xff)
+ if (flags != 0xff) {
btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+ btsnoop_write(tcp_snoop, tv, flags, monitor_buf, pktlen);
+ }
}
}

@@ -215,6 +223,115 @@ static void set_capabilities(void)
#endif
}

+static void tcp_callback(int fd, uint32_t events, void *user_data)
+{
+ if (events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+ mainloop_remove_fd(fd);
+
+ free(tcp_snoop);
+ tcp_snoop = NULL;
+
+ printf("TCP client disconnected\n");
+ }
+}
+
+static void accept_tcp(int fd, uint32_t events, void *user_data)
+{
+ struct sockaddr_un addr;
+ socklen_t len;
+ int nfd;
+
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_remove_fd(server_fd);
+ return;
+ }
+
+ nfd = accept(server_fd, (struct sockaddr *) &addr, &len);
+ if (nfd < 0) {
+ perror("Failed to accept incoming connection\n");
+ return;
+ }
+
+ /* We only support one client connected */
+ if (tcp_snoop) {
+ fprintf(stderr, "TCP client already connected\n");
+ close(nfd);
+ return;
+ }
+
+ if (mainloop_add_fd(nfd, EPOLLIN | EPOLLRDHUP, tcp_callback,
+ NULL, NULL) < 0) {
+ fprintf(stderr, "Failed to setup watch on client\n");
+ close(nfd);
+ return;
+ }
+
+ tcp_snoop = btsnoop_create_fd(nfd, BTSNOOP_TYPE_HCI);
+ if (!tcp_snoop) {
+ fprintf(stderr, "Failed to create snoop\n");
+ close(nfd);
+ return;
+ }
+
+ printf("TCP client connected\n");
+}
+
+static int open_tcp(void)
+{
+ struct sockaddr_in addr;
+ int fd, opt = 1;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("Failed to open server socket\n");
+ return -1;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = htons(DEFAULT_TCP_PORT);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind server socket\n");
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 5) < 0) {
+ perror("Failed to listen server socket\n");
+ close(fd);
+ return -1;
+ }
+
+ if (mainloop_add_fd(fd, EPOLLIN, accept_tcp, NULL, NULL) < 0) {
+ fprintf(stderr, "Failed to setup watch on server socket\n");
+ close(fd);
+ return -1;
+ }
+
+ server_fd = fd;
+
+ printf("TCP server ready\n");
+
+ return 0;
+}
+
+static void close_tcp(void)
+{
+ close(server_fd);
+
+ mainloop_remove_fd(server_fd);
+
+ server_fd = -1;
+}
+
int main(int argc, char *argv[])
{
const char *path;
@@ -243,8 +360,12 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}

+ open_tcp();
+
mainloop_run();

+ close_tcp();
+
close_monitor();

return EXIT_SUCCESS;
--
1.9.3


2014-05-30 16:15:54

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH 2/4] shared/btsnoop: Use writev in btsnoop_write

This patch makes btsnoop_write to use writev so there's single write
instead of two. It's useful when file descriptor is e.g. TCP socket
rather than regular file.
---
src/shared/btsnoop.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index da993c5..59f8c65 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -32,6 +32,7 @@
#include <string.h>
#include <arpa/inet.h>
#include <sys/stat.h>
+#include <sys/uio.h>

#include "src/shared/btsnoop.h"

@@ -225,6 +226,7 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
struct btsnoop_pkt pkt;
uint64_t ts;
ssize_t written;
+ struct iovec iov[2];

if (!btsnoop || !tv)
return false;
@@ -237,17 +239,14 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
pkt.drops = htobe32(0);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);

- written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
- if (written < 0)
- return false;
+ iov[0].iov_base = &pkt;
+ iov[0].iov_len = BTSNOOP_PKT_SIZE;
+ iov[1].iov_base = (void *) data;
+ iov[1].iov_len = size;

- if (data && size > 0) {
- written = write(btsnoop->fd, data, size);
- if (written < 0)
- return false;
- }
+ written = writev(btsnoop->fd, iov, 2);

- return true;
+ return written >= 0;
}

static uint32_t get_flags_from_opcode(uint16_t opcode)
--
1.9.3