OSDN Git Service

monitor: Add support for decoding link layer packets
authorMarcel Holtmann <marcel@holtmann.org>
Sat, 24 Aug 2013 03:35:15 +0000 (20:35 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Sat, 24 Aug 2013 03:35:15 +0000 (20:35 -0700)
Makefile.tools
monitor/bt.h
monitor/ll.c
monitor/ll.h

index 816cdc0..b2c272b 100644 (file)
@@ -24,6 +24,7 @@ monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
                                        monitor/l2cap.h monitor/l2cap.c \
                                        monitor/uuid.h monitor/uuid.c \
                                        monitor/sdp.h monitor/sdp.c \
+                                       monitor/crc.h monitor/crc.c \
                                        monitor/ll.h monitor/ll.c
 monitor_btmon_LDADD = lib/libbluetooth-internal.la
 endif
index 269d76d..c2d7921 100644 (file)
 
 #include <stdint.h>
 
+struct bt_ll_hdr {
+       uint8_t  preamble;
+       uint32_t access_addr;
+} __attribute__ ((packed));
+
 #define BT_H4_CMD_PKT  0x01
 #define BT_H4_ACL_PKT  0x02
 #define BT_H4_SCO_PKT  0x03
index 95fed72..41bba1a 100644 (file)
 #include <config.h>
 #endif
 
+#include <bluetooth/bluetooth.h>
+
+#include "display.h"
 #include "packet.h"
+#include "crc.h"
+#include "bt.h"
 #include "ll.h"
 
+#define COLOR_OPCODE           COLOR_MAGENTA
+#define COLOR_OPCODE_UNKNOWN   COLOR_WHITE_BG
+
+#define MAX_CHANNEL 16
+
+struct channel_data {
+       uint32_t access_addr;
+       uint32_t crc_init;
+};
+
+static struct channel_data channel_list[MAX_CHANNEL];
+
+static void set_crc_init(uint32_t access_addr, uint32_t crc_init)
+{
+       int i;
+
+       for (i = 0; i < MAX_CHANNEL; i++) {
+               if (channel_list[i].access_addr == 0x00000000) {
+                       channel_list[i].access_addr = access_addr;
+                       channel_list[i].crc_init = crc_init;
+                       break;
+               }
+       }
+}
+
+static uint32_t get_crc_init(uint32_t access_addr)
+{
+       int i;
+
+       for (i = 0; i < MAX_CHANNEL; i++) {
+               if (channel_list[i].access_addr == access_addr)
+                       return channel_list[i].crc_init;
+       }
+
+       return 0x00000000;
+}
+
+static void advertising_packet(const void *data, uint8_t size)
+{
+       const uint8_t *ptr = data;
+       uint8_t pdu_type, length, win_size;
+       bool tx_add, rx_add;
+       uint32_t access_addr, crc_init;
+       uint16_t win_offset, interval, latency, timeout;
+       const char *str;
+
+       if (size < 2) {
+               print_text(COLOR_ERROR, "packet too short");
+               packet_hexdump(data, size);
+               return;
+       }
+
+       pdu_type = ptr[0] & 0x0f;
+       tx_add = !!(ptr[0] & 0x40);
+       rx_add = !!(ptr[0] & 0x80);
+       length = ptr[1] & 0x3f;
+
+       switch (pdu_type) {
+       case 0x00:
+               str = "ADV_IND";
+               break;
+       case 0x01:
+               str = "ADV_DIRECT_IND";
+               break;
+       case 0x02:
+               str = "ADV_NONCONN_IND";
+               break;
+       case 0x03:
+               str = "SCAN_REQ";
+               break;
+       case 0x04:
+               str = "SCAN_RSP";
+               break;
+       case 0x05:
+               str = "CONNECT_REQ";
+               break;
+       case 0x06:
+               str = "ADV_SCAN_IND";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Type: %s (0x%2.2x)", str, pdu_type);
+       print_field("TxAdd: %u", tx_add);
+       print_field("RxAdd: %u", rx_add);
+       print_field("Length: %u", length);
+
+       if (length != size - 2) {
+               print_text(COLOR_ERROR, "packet size mismatch");
+               packet_hexdump(data + 2, size - 2);
+               return;
+       }
+
+       switch (pdu_type) {
+       case 0x00:      /* ADV_IND */
+       case 0x02:      /* AVD_NONCONN_IND */
+       case 0x06:      /* ADV_SCAN_IND */
+       case 0x04:      /* SCAN_RSP */
+               if (length < 6) {
+                       print_text(COLOR_ERROR, "payload too short");
+                       packet_hexdump(data + 2, length);
+                       return;
+               }
+
+               packet_print_addr("Advertiser address", data + 2, tx_add);
+               packet_print_ad(data + 8, length - 6);
+               break;
+
+       case 0x01:      /* ADV_DIRECT_IND */
+               if (length < 12) {
+                       print_text(COLOR_ERROR, "payload too short");
+                       packet_hexdump(data + 2, length);
+                       return;
+               }
+
+               packet_print_addr("Advertiser address", data + 2, tx_add);
+               packet_print_addr("Inititator address", data + 8, rx_add);
+               break;
+
+       case 0x03:      /* SCAN_REQ */
+               if (length < 12) {
+                       print_text(COLOR_ERROR, "payload too short");
+                       packet_hexdump(data + 2, length);
+                       return;
+               }
+
+               packet_print_addr("Scanner address", data + 2, tx_add);
+               packet_print_addr("Advertiser address", data + 8, rx_add);
+               break;
+
+       case 0x05:      /* CONNECT_REQ */
+               if (length < 34) {
+                       print_text(COLOR_ERROR, "payload too short");
+                       packet_hexdump(data + 2, length);
+                       return;
+               }
+
+               packet_print_addr("Inititator address", data + 2, tx_add);
+               packet_print_addr("Advertiser address", data + 8, rx_add);
+
+               access_addr = ptr[14] | ptr[15] << 8 |
+                                       ptr[16] << 16 | ptr[17] << 24;
+               crc_init = ptr[18] | ptr[19] << 8 | ptr[20] << 16;
+
+               print_field("Access address: 0x%8.8x", access_addr);
+               print_field("CRC init: 0x%6.6x", crc_init);
+
+               set_crc_init(access_addr, crc24_bit_reverse(crc_init));
+
+               win_size = ptr[21];
+               win_offset = ptr[22] | ptr[23] << 8;
+               interval = ptr[24] | ptr[25] << 8;
+               latency = ptr[26] | ptr[27] << 8;
+               timeout = ptr[28] | ptr[29] << 8;
+
+               print_field("Transmit window size: %u", win_size);
+               print_field("Transmit window offset: %u", win_offset);
+               print_field("Connection interval: %u", interval);
+               print_field("Connection slave latency: %u", latency);
+               print_field("Connection supervision timeout: %u", timeout);
+
+               packet_hexdump(data + 30, length - 28);
+               break;
+
+       default:
+               packet_hexdump(data + 2, length);
+               break;
+       }
+}
+
+static void data_packet(const void *data, uint8_t size)
+{
+       const uint8_t *ptr = data;
+       uint8_t llid, length;
+       bool nesn, sn, md;
+       const char *str;
+
+       if (size < 2) {
+               print_text(COLOR_ERROR, "packet too short");
+               packet_hexdump(data, size);
+               return;
+       }
+
+       llid = ptr[0] & 0x03;
+       nesn = !!(ptr[0] & 0x04);
+       sn = !!(ptr[0] & 0x08);
+       md = !!(ptr[0] & 0x10);
+       length = ptr[1] & 0x1f;
+
+       switch (llid) {
+       case 0x01:
+               if (length > 0)
+                       str = "Continuation fragement of L2CAP message";
+               else
+                       str = "Empty message";
+               break;
+       case 0x02:
+               str = "Start of L2CAP message";
+               break;
+       case 0x03:
+               str = "Control";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("LLID: %s (0x%2.2x)", str, llid);
+       print_field("Next expected sequence number: %u", nesn);
+       print_field("Sequence number: %u", sn);
+       print_field("More data: %u", md);
+       print_field("Length: %u", length);
+
+       switch (llid) {
+       case 0x03:
+               llcp_packet(data + 2, size - 2);
+               break;
+
+       default:
+               packet_hexdump(data + 2, size - 2);
+               break;
+       }
+}
+
 void ll_packet(uint16_t frequency, const void *data, uint8_t size)
 {
-       packet_hexdump(data, size);
+       const struct bt_ll_hdr *hdr = data;
+       uint8_t channel = (frequency - 2402) / 2;
+       uint32_t access_addr;
+       char access_str[12];
+       const char *channel_label, *channel_color;
+       const uint8_t *pdu_data;
+       uint8_t pdu_len;
+       uint32_t pdu_crc, crc, crc_init;
+
+       if (size < sizeof(*hdr)) {
+               print_text(COLOR_ERROR, "packet missing header");
+               packet_hexdump(data, size);
+               return;
+       }
+
+       if (size < sizeof(*hdr) + 3) {
+               print_text(COLOR_ERROR, "packet missing checksum");
+               packet_hexdump(data, size);
+               return;
+       }
+
+       if (hdr->preamble != 0xaa && hdr->preamble != 0x55) {
+               print_text(COLOR_ERROR, "invalid preamble");
+               packet_hexdump(data, size);
+               return;
+       }
+
+       access_addr = btohl(hdr->access_addr);
+
+       pdu_data = data + sizeof(*hdr);
+       pdu_len = size - sizeof(*hdr) - 3;
+
+       pdu_crc = pdu_data[pdu_len + 0] | (pdu_data[pdu_len + 1] << 8) |
+                                               (pdu_data[pdu_len + 2] << 16);
+
+       if (access_addr == 0x8e89bed6) {
+               channel_label = "Advertising channel: ";
+               channel_color = COLOR_MAGENTA;
+       } else {
+               channel_label = "Data channel: ";
+               channel_color = COLOR_CYAN;
+       }
+
+       sprintf(access_str, "0x%8.8x", access_addr);
+
+       print_indent(6, channel_color, channel_label, access_str, COLOR_OFF,
+               " (channel %d) len %d crc 0x%6.6x", channel, pdu_len, pdu_crc);
+
+       if (access_addr == 0x8e89bed6)
+               crc_init = 0xaaaaaa;
+       else
+               crc_init = get_crc_init(access_addr);
+
+       if (crc_init) {
+               crc = crc24_calculate(crc_init, pdu_data, pdu_len);
+
+               if (crc != pdu_crc) {
+                       print_text(COLOR_ERROR, "invalid checksum");
+                       packet_hexdump(pdu_data, pdu_len);
+                       return;
+               }
+       } else
+               print_text(COLOR_ERROR, "unknown access address");
+
+       if (access_addr == 0x8e89bed6)
+               advertising_packet(pdu_data, pdu_len);
+       else
+               data_packet(pdu_data, pdu_len);
+}
+
+struct llcp_data {
+       uint8_t opcode;
+       const char *str;
+       void (*func) (const void *data, uint8_t size);
+       uint8_t size;
+       bool fixed;
+};
+
+static const struct llcp_data llcp_table[] = {
+       { 0x00, "LL_CONNECTION_UPDATE_REQ" },
+       { 0x01, "LL_CHANNEL_MAP_REQ" },
+       { 0x02, "LL_TERMINATE_IND" },
+       { 0x03, "LL_ENC_REQ" },
+       { 0x04, "LL_ENC_RSP" },
+       { 0x05, "LL_START_ENC_REQ" },
+       { 0x06, "LL_START_ENC_RSP" },
+       { 0x07, "LL_UNKNOWN_RSP" },
+       { 0x08, "LL_FEATURE_REQ" },
+       { 0x09, "LL_FEATURE_RSP" },
+       { 0x0a, "LL_PAUSE_ENC_REQ" },
+       { 0x0b, "LL_PAUSE_ENC_RSP" },
+       { 0x0c, "LL_VERSION_IND" },
+       { 0x0d, "LL_REJECT_IND" },
+       { }
+};
+
+void llcp_packet(const void *data, uint8_t size)
+{
+       uint8_t opcode = ((const uint8_t *) data)[0];
+       const struct llcp_data *llcp_data = NULL;
+       const char *opcode_color, *opcode_str;
+       int i;
+
+       for (i = 0; llcp_table[i].str; i++) {
+               if (llcp_table[i].opcode == opcode) {
+                       llcp_data = &llcp_table[i];
+                       break;
+               }
+       }
+
+       if (llcp_data) {
+               if (llcp_data->func)
+                       opcode_color = COLOR_OPCODE;
+               else
+                       opcode_color = COLOR_OPCODE_UNKNOWN;
+               opcode_str = llcp_data->str;
+       } else {
+               opcode_color = COLOR_OPCODE_UNKNOWN;
+               opcode_str = "Unknown";
+       }
+
+       print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+                                               " (0x%2.2x)", opcode);
+
+       if (!llcp_data || !llcp_data->func) {
+               packet_hexdump(data + 1, size - 1);
+               return;
+       }
+
+       if (llcp_data->fixed) {
+               if (size - 1 != llcp_data->size) {
+                       print_text(COLOR_ERROR, "invalid packet size");
+                       packet_hexdump(data + 1, size - 1);
+                       return;
+               }
+       } else {
+               if (size - 1 < llcp_data->size) {
+                       print_text(COLOR_ERROR, "too short packet");
+                       packet_hexdump(data + 1, size - 1);
+                       return;
+               }
+       }
+
+       llcp_data->func(data + 1, size - 1);
 }
index 496bc22..17d934b 100644 (file)
@@ -25,3 +25,4 @@
 #include <stdint.h>
 
 void ll_packet(uint16_t frequency, const void *data, uint8_t size);
+void llcp_packet(const void *data, uint8_t size);