btsnoop.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2011-2012 Intel Corporation
  7. * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
  8. *
  9. *
  10. */
  11. #ifdef HAVE_CONFIG_H
  12. #include <config.h>
  13. #endif
  14. #define _GNU_SOURCE
  15. #include <stdio.h>
  16. #include <errno.h>
  17. #include <fcntl.h>
  18. #include <unistd.h>
  19. #include <stdlib.h>
  20. #include <stdint.h>
  21. #include <stdbool.h>
  22. #include <string.h>
  23. #include <getopt.h>
  24. #include <endian.h>
  25. #include <arpa/inet.h>
  26. #include <sys/stat.h>
  27. #include "src/shared/btsnoop.h"
  28. struct btsnoop_hdr {
  29. uint8_t id[8]; /* Identification Pattern */
  30. uint32_t version; /* Version Number = 1 */
  31. uint32_t type; /* Datalink Type */
  32. } __attribute__ ((packed));
  33. #define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
  34. struct btsnoop_pkt {
  35. uint32_t size; /* Original Length */
  36. uint32_t len; /* Included Length */
  37. uint32_t flags; /* Packet Flags */
  38. uint32_t drops; /* Cumulative Drops */
  39. uint64_t ts; /* Timestamp microseconds */
  40. uint8_t data[0]; /* Packet Data */
  41. } __attribute__ ((packed));
  42. #define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
  43. static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
  44. 0x6f, 0x6f, 0x70, 0x00 };
  45. static const uint32_t btsnoop_version = 1;
  46. static int create_btsnoop(const char *path)
  47. {
  48. struct btsnoop_hdr hdr;
  49. ssize_t written;
  50. int fd;
  51. fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
  52. if (fd < 0) {
  53. perror("failed to output file");
  54. return -1;
  55. }
  56. memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
  57. hdr.version = htobe32(btsnoop_version);
  58. hdr.type = htobe32(2001);
  59. written = write(fd, &hdr, BTSNOOP_HDR_SIZE);
  60. if (written < 0) {
  61. perror("failed to write output header");
  62. close(fd);
  63. return -1;
  64. }
  65. return fd;
  66. }
  67. static int open_btsnoop(const char *path, uint32_t *type)
  68. {
  69. struct btsnoop_hdr hdr;
  70. ssize_t len;
  71. int fd;
  72. fd = open(path, O_RDONLY | O_CLOEXEC);
  73. if (fd < 0) {
  74. perror("failed to open input file");
  75. return -1;
  76. }
  77. len = read(fd, &hdr, BTSNOOP_HDR_SIZE);
  78. if (len < 0 || len != BTSNOOP_HDR_SIZE) {
  79. perror("failed to read input header");
  80. close(fd);
  81. return -1;
  82. }
  83. if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
  84. fprintf(stderr, "not a valid btsnoop header\n");
  85. close(fd);
  86. return -1;
  87. }
  88. if (be32toh(hdr.version) != btsnoop_version) {
  89. fprintf(stderr, "invalid btsnoop version\n");
  90. close(fd);
  91. return -1;
  92. }
  93. if (type)
  94. *type = be32toh(hdr.type);
  95. return fd;
  96. }
  97. #define MAX_MERGE 8
  98. static void command_merge(const char *output, int argc, char *argv[])
  99. {
  100. struct btsnoop_pkt input_pkt[MAX_MERGE];
  101. unsigned char buf[2048];
  102. int output_fd, input_fd[MAX_MERGE], num_input = 0;
  103. int i, select_input;
  104. ssize_t len, written;
  105. uint32_t toread, flags;
  106. uint16_t index, opcode;
  107. if (argc > MAX_MERGE) {
  108. fprintf(stderr, "only up to %d files allowed\n", MAX_MERGE);
  109. return;
  110. }
  111. for (i = 0; i < argc; i++) {
  112. uint32_t type;
  113. int fd;
  114. fd = open_btsnoop(argv[i], &type);
  115. if (fd < 0)
  116. break;
  117. if (type != 1002) {
  118. fprintf(stderr, "unsupported link data type %u\n",
  119. type);
  120. close(fd);
  121. break;
  122. }
  123. input_fd[num_input++] = fd;
  124. }
  125. if (num_input != argc) {
  126. fprintf(stderr, "failed to open all input files\n");
  127. goto close_input;
  128. }
  129. output_fd = create_btsnoop(output);
  130. if (output_fd < 0)
  131. goto close_input;
  132. for (i = 0; i < num_input; i++) {
  133. len = read(input_fd[i], &input_pkt[i], BTSNOOP_PKT_SIZE);
  134. if (len < 0 || len != BTSNOOP_PKT_SIZE) {
  135. close(input_fd[i]);
  136. input_fd[i] = -1;
  137. }
  138. }
  139. next_packet:
  140. select_input = -1;
  141. for (i = 0; i < num_input; i++) {
  142. uint64_t ts;
  143. if (input_fd[i] < 0)
  144. continue;
  145. if (select_input < 0) {
  146. select_input = i;
  147. continue;
  148. }
  149. ts = be64toh(input_pkt[i].ts);
  150. if (ts < be64toh(input_pkt[select_input].ts))
  151. select_input = i;
  152. }
  153. if (select_input < 0)
  154. goto close_output;
  155. toread = be32toh(input_pkt[select_input].size);
  156. flags = be32toh(input_pkt[select_input].flags);
  157. len = read(input_fd[select_input], buf, toread);
  158. if (len < 0 || len != (ssize_t) toread) {
  159. close(input_fd[select_input]);
  160. input_fd[select_input] = -1;
  161. goto next_packet;
  162. }
  163. written = htobe32(toread - 1);
  164. input_pkt[select_input].size = written;
  165. input_pkt[select_input].len = written;
  166. switch (buf[0]) {
  167. case 0x01:
  168. opcode = BTSNOOP_OPCODE_COMMAND_PKT;
  169. break;
  170. case 0x02:
  171. if (flags & 0x01)
  172. opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
  173. else
  174. opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
  175. break;
  176. case 0x03:
  177. if (flags & 0x01)
  178. opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
  179. else
  180. opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
  181. break;
  182. case 0x04:
  183. opcode = BTSNOOP_OPCODE_EVENT_PKT;
  184. break;
  185. default:
  186. goto skip_write;
  187. }
  188. index = select_input;
  189. input_pkt[select_input].flags = htobe32((index << 16) | opcode);
  190. written = write(output_fd, &input_pkt[select_input], BTSNOOP_PKT_SIZE);
  191. if (written != BTSNOOP_PKT_SIZE) {
  192. fprintf(stderr, "write of packet header failed\n");
  193. goto close_output;
  194. }
  195. written = write(output_fd, buf + 1, toread - 1);
  196. if (written != (ssize_t) toread - 1) {
  197. fprintf(stderr, "write of packet data failed\n");
  198. goto close_output;
  199. }
  200. skip_write:
  201. len = read(input_fd[select_input],
  202. &input_pkt[select_input], BTSNOOP_PKT_SIZE);
  203. if (len < 0 || len != BTSNOOP_PKT_SIZE) {
  204. close(input_fd[select_input]);
  205. input_fd[select_input] = -1;
  206. }
  207. goto next_packet;
  208. close_output:
  209. close(output_fd);
  210. close_input:
  211. for (i = 0; i < num_input; i++)
  212. close(input_fd[i]);
  213. }
  214. static void command_extract_eir(const char *input)
  215. {
  216. struct btsnoop_pkt pkt;
  217. unsigned char buf[2048];
  218. ssize_t len;
  219. uint32_t type, toread, flags;
  220. uint16_t opcode;
  221. int fd, count = 0;
  222. fd = open_btsnoop(input, &type);
  223. if (fd < 0)
  224. return;
  225. if (type != 2001) {
  226. fprintf(stderr, "unsupported link data type %u\n", type);
  227. close(fd);
  228. return;
  229. }
  230. next_packet:
  231. len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
  232. if (len < 0 || len != BTSNOOP_PKT_SIZE)
  233. goto close_input;
  234. toread = be32toh(pkt.size);
  235. flags = be32toh(pkt.flags);
  236. opcode = flags & 0x00ff;
  237. len = read(fd, buf, toread);
  238. if (len < 0 || len != (ssize_t) toread) {
  239. fprintf(stderr, "failed to read packet data\n");
  240. goto close_input;
  241. }
  242. switch (opcode) {
  243. case BTSNOOP_OPCODE_EVENT_PKT:
  244. /* extended inquiry result event */
  245. if (buf[0] == 0x2f) {
  246. uint8_t *eir_ptr, eir_len, i;
  247. eir_len = buf[1] - 15;
  248. eir_ptr = buf + 17;
  249. if (eir_len < 1 || eir_len > 240)
  250. break;
  251. printf("\t[Extended Inquiry Data with %u bytes]\n",
  252. eir_len);
  253. printf("\t\t");
  254. for (i = 0; i < eir_len; i++) {
  255. printf("0x%02x", eir_ptr[i]);
  256. if (((i + 1) % 8) == 0) {
  257. if (i < eir_len - 1)
  258. printf(",\n\t\t");
  259. } else {
  260. if (i < eir_len - 1)
  261. printf(", ");
  262. }
  263. }
  264. printf("\n");
  265. count++;
  266. }
  267. break;
  268. }
  269. goto next_packet;
  270. close_input:
  271. close(fd);
  272. }
  273. static void command_extract_ad(const char *input)
  274. {
  275. struct btsnoop_pkt pkt;
  276. unsigned char buf[2048];
  277. ssize_t len;
  278. uint32_t type, toread, flags;
  279. uint16_t opcode;
  280. int fd, count = 0;
  281. fd = open_btsnoop(input, &type);
  282. if (fd < 0)
  283. return;
  284. if (type != 2001) {
  285. fprintf(stderr, "unsupported link data type %u\n", type);
  286. close(fd);
  287. return;
  288. }
  289. next_packet:
  290. len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
  291. if (len < 0 || len != BTSNOOP_PKT_SIZE)
  292. goto close_input;
  293. toread = be32toh(pkt.size);
  294. flags = be32toh(pkt.flags);
  295. opcode = flags & 0x00ff;
  296. len = read(fd, buf, toread);
  297. if (len < 0 || len != (ssize_t) toread) {
  298. fprintf(stderr, "failed to read packet data\n");
  299. goto close_input;
  300. }
  301. switch (opcode) {
  302. case BTSNOOP_OPCODE_EVENT_PKT:
  303. /* advertising report */
  304. if (buf[0] == 0x3e && buf[2] == 0x02) {
  305. uint8_t *ad_ptr, ad_len, i;
  306. ad_len = buf[12];
  307. ad_ptr = buf + 13;
  308. if (ad_len < 1 || ad_len > 40)
  309. break;
  310. printf("\t[Advertising Data with %u bytes]\n", ad_len);
  311. printf("\t\t");
  312. for (i = 0; i < ad_len; i++) {
  313. printf("0x%02x", ad_ptr[i]);
  314. if (((i + 1) % 8) == 0) {
  315. if (i < ad_len - 1)
  316. printf(",\n\t\t");
  317. } else {
  318. if (i < ad_len - 1)
  319. printf(", ");
  320. }
  321. }
  322. printf("\n");
  323. count++;
  324. }
  325. break;
  326. }
  327. goto next_packet;
  328. close_input:
  329. close(fd);
  330. }
  331. static const uint8_t conn_complete[] = { 0x04, 0x03, 0x0B, 0x00 };
  332. static const uint8_t disc_complete[] = { 0x04, 0x05, 0x04, 0x00 };
  333. static void command_extract_sdp(const char *input)
  334. {
  335. struct btsnoop_pkt pkt;
  336. unsigned char buf[2048];
  337. ssize_t len;
  338. uint32_t type, toread;
  339. uint16_t current_cid = 0x0000;
  340. uint8_t pdu_buf[512];
  341. uint16_t pdu_len = 0;
  342. bool pdu_first = false;
  343. int fd, count = 0;
  344. fd = open_btsnoop(input, &type);
  345. if (fd < 0)
  346. return;
  347. if (type != 1002) {
  348. fprintf(stderr, "unsupported link data type %u\n", type);
  349. close(fd);
  350. return;
  351. }
  352. next_packet:
  353. len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
  354. if (len < 0 || len != BTSNOOP_PKT_SIZE)
  355. goto close_input;
  356. toread = be32toh(pkt.size);
  357. len = read(fd, buf, toread);
  358. if (len < 0 || len != (ssize_t) toread) {
  359. fprintf(stderr, "failed to read packet data\n");
  360. goto close_input;
  361. }
  362. if (buf[0] == 0x02) {
  363. uint8_t acl_flags;
  364. /* first 4 bytes are handle and data len */
  365. acl_flags = buf[2] >> 4;
  366. /* use only packet with ACL start flag */
  367. if (acl_flags & 0x02) {
  368. if (current_cid == 0x0040 && pdu_len > 0) {
  369. int i;
  370. if (!pdu_first)
  371. printf(",\n");
  372. printf("\t\traw_pdu(");
  373. for (i = 0; i < pdu_len; i++) {
  374. printf("0x%02x", pdu_buf[i]);
  375. if (((i + 1) % 8) == 0) {
  376. if (i < pdu_len - 1)
  377. printf(",\n\t\t\t");
  378. } else {
  379. if (i < pdu_len - 1)
  380. printf(", ");
  381. }
  382. }
  383. printf(")");
  384. pdu_first = false;
  385. }
  386. /* next 4 bytes are data len and cid */
  387. current_cid = buf[8] << 8 | buf[7];
  388. memcpy(pdu_buf, buf + 9, len - 9);
  389. pdu_len = len - 9;
  390. } else if (acl_flags & 0x01) {
  391. memcpy(pdu_buf + pdu_len, buf + 5, len - 5);
  392. pdu_len += len - 5;
  393. }
  394. }
  395. if ((size_t) len > sizeof(conn_complete)) {
  396. if (memcmp(buf, conn_complete, sizeof(conn_complete)) == 0) {
  397. printf("\tdefine_test(\"/test/%u\",\n", ++count);
  398. pdu_first = true;
  399. }
  400. }
  401. if ((size_t) len > sizeof(disc_complete)) {
  402. if (memcmp(buf, disc_complete, sizeof(disc_complete)) == 0) {
  403. printf(");\n");
  404. }
  405. }
  406. goto next_packet;
  407. close_input:
  408. close(fd);
  409. }
  410. static void usage(void)
  411. {
  412. printf("btsnoop trace file handling tool\n"
  413. "Usage:\n");
  414. printf("\tbtsnoop <command> [files]\n");
  415. printf("commands:\n"
  416. "\t-m, --merge <output> Merge multiple btsnoop files\n"
  417. "\t-e, --extract <input> Extract data from btsnoop file\n"
  418. "\t-h, --help Show help options\n");
  419. }
  420. static const struct option main_options[] = {
  421. { "merge", required_argument, NULL, 'm' },
  422. { "extract", required_argument, NULL, 'e' },
  423. { "type", required_argument, NULL, 't' },
  424. { "version", no_argument, NULL, 'v' },
  425. { "help", no_argument, NULL, 'h' },
  426. { }
  427. };
  428. enum { INVALID, MERGE, EXTRACT };
  429. int main(int argc, char *argv[])
  430. {
  431. const char *output_path = NULL;
  432. const char *input_path = NULL;
  433. const char *type = NULL;
  434. unsigned short command = INVALID;
  435. for (;;) {
  436. int opt;
  437. opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
  438. if (opt < 0)
  439. break;
  440. switch (opt) {
  441. case 'm':
  442. command = MERGE;
  443. output_path = optarg;
  444. break;
  445. case 'e':
  446. command = EXTRACT;
  447. input_path = optarg;
  448. break;
  449. case 't':
  450. type = optarg;
  451. break;
  452. case 'v':
  453. printf("%s\n", VERSION);
  454. return EXIT_SUCCESS;
  455. case 'h':
  456. usage();
  457. return EXIT_SUCCESS;
  458. default:
  459. return EXIT_FAILURE;
  460. }
  461. }
  462. switch (command) {
  463. case MERGE:
  464. if (argc - optind < 1) {
  465. fprintf(stderr, "input files required\n");
  466. return EXIT_FAILURE;
  467. }
  468. command_merge(output_path, argc - optind, argv + optind);
  469. break;
  470. case EXTRACT:
  471. if (argc - optind > 0) {
  472. fprintf(stderr, "extra arguments not allowed\n");
  473. return EXIT_FAILURE;
  474. }
  475. if (!type) {
  476. fprintf(stderr, "no extract type specified\n");
  477. return EXIT_FAILURE;
  478. }
  479. if (!strcasecmp(type, "eir"))
  480. command_extract_eir(input_path);
  481. else if (!strcasecmp(type, "ad"))
  482. command_extract_ad(input_path);
  483. else if (!strcasecmp(type, "sdp"))
  484. command_extract_sdp(input_path);
  485. else
  486. fprintf(stderr, "extract type not supported\n");
  487. break;
  488. default:
  489. usage();
  490. return EXIT_FAILURE;
  491. }
  492. return EXIT_SUCCESS;
  493. }