mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-26 21:54:33 +08:00
1348 lines
27 KiB
C
1348 lines
27 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2004-2011 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 <ctype.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include "parser.h"
|
|
#include "lib/hci.h"
|
|
#include "lib/hci_lib.h"
|
|
|
|
#define LMP_U8(frm) (get_u8(frm))
|
|
#define LMP_U16(frm) (btohs(htons(get_u16(frm))))
|
|
#define LMP_U32(frm) (btohl(htonl(get_u32(frm))))
|
|
|
|
static enum {
|
|
IN_RAND,
|
|
COMB_KEY_M,
|
|
COMB_KEY_S,
|
|
AU_RAND_M,
|
|
AU_RAND_S,
|
|
SRES_M,
|
|
SRES_S,
|
|
} pairing_state = IN_RAND;
|
|
|
|
static struct {
|
|
uint8_t in_rand[16];
|
|
uint8_t comb_key_m[16];
|
|
uint8_t comb_key_s[16];
|
|
uint8_t au_rand_m[16];
|
|
uint8_t au_rand_s[16];
|
|
uint8_t sres_m[4];
|
|
uint8_t sres_s[4];
|
|
} pairing_data;
|
|
|
|
static inline void pairing_data_dump(void)
|
|
{
|
|
int i;
|
|
|
|
p_indent(6, NULL);
|
|
printf("IN_RAND ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", pairing_data.in_rand[i]);
|
|
printf("\n");
|
|
|
|
p_indent(6, NULL);
|
|
printf("COMB_KEY ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", pairing_data.comb_key_m[i]);
|
|
printf(" (M)\n");
|
|
|
|
p_indent(6, NULL);
|
|
printf("COMB_KEY ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", pairing_data.comb_key_s[i]);
|
|
printf(" (S)\n");
|
|
|
|
p_indent(6, NULL);
|
|
printf("AU_RAND ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", pairing_data.au_rand_m[i]);
|
|
printf(" SRES ");
|
|
for (i = 0; i < 4; i++)
|
|
printf("%2.2x", pairing_data.sres_m[i]);
|
|
printf(" (M)\n");
|
|
|
|
p_indent(6, NULL);
|
|
printf("AU_RAND ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", pairing_data.au_rand_s[i]);
|
|
printf(" SRES ");
|
|
for (i = 0; i < 4; i++)
|
|
printf("%2.2x", pairing_data.sres_s[i]);
|
|
printf(" (S)\n");
|
|
}
|
|
|
|
static inline void in_rand(struct frame *frm)
|
|
{
|
|
uint8_t *val = frm->ptr;
|
|
|
|
memcpy(pairing_data.in_rand, val, 16);
|
|
pairing_state = COMB_KEY_M;
|
|
}
|
|
|
|
static inline void comb_key(struct frame *frm)
|
|
{
|
|
uint8_t *val = frm->ptr;
|
|
|
|
switch (pairing_state) {
|
|
case COMB_KEY_M:
|
|
memcpy(pairing_data.comb_key_m, val, 16);
|
|
pairing_state = COMB_KEY_S;
|
|
break;
|
|
case COMB_KEY_S:
|
|
memcpy(pairing_data.comb_key_s, val, 16);
|
|
pairing_state = AU_RAND_M;
|
|
break;
|
|
default:
|
|
pairing_state = IN_RAND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void au_rand(struct frame *frm)
|
|
{
|
|
uint8_t *val = frm->ptr;
|
|
|
|
switch (pairing_state) {
|
|
case AU_RAND_M:
|
|
memcpy(pairing_data.au_rand_m, val, 16);
|
|
pairing_state = SRES_M;
|
|
break;
|
|
case AU_RAND_S:
|
|
memcpy(pairing_data.au_rand_s, val, 16);
|
|
pairing_state = SRES_S;
|
|
break;
|
|
default:
|
|
pairing_state = IN_RAND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void sres(struct frame *frm)
|
|
{
|
|
uint8_t *val = frm->ptr;
|
|
|
|
switch (pairing_state) {
|
|
case SRES_M:
|
|
memcpy(pairing_data.sres_m, val, 4);
|
|
pairing_state = AU_RAND_S;
|
|
break;
|
|
case SRES_S:
|
|
memcpy(pairing_data.sres_s, val, 4);
|
|
pairing_state = IN_RAND;
|
|
pairing_data_dump();
|
|
break;
|
|
default:
|
|
pairing_state = IN_RAND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static char *opcode2str(uint16_t opcode)
|
|
{
|
|
switch (opcode) {
|
|
case 1:
|
|
return "name_req";
|
|
case 2:
|
|
return "name_res";
|
|
case 3:
|
|
return "accepted";
|
|
case 4:
|
|
return "not_accepted";
|
|
case 5:
|
|
return "clkoffset_req";
|
|
case 6:
|
|
return "clkoffset_res";
|
|
case 7:
|
|
return "detach";
|
|
case 8:
|
|
return "in_rand";
|
|
case 9:
|
|
return "comb_key";
|
|
case 10:
|
|
return "unit_key";
|
|
case 11:
|
|
return "au_rand";
|
|
case 12:
|
|
return "sres";
|
|
case 13:
|
|
return "temp_rand";
|
|
case 14:
|
|
return "temp_key";
|
|
case 15:
|
|
return "encryption_mode_req";
|
|
case 16:
|
|
return "encryption_key_size_req";
|
|
case 17:
|
|
return "start_encryption_req";
|
|
case 18:
|
|
return "stop_encryption_req";
|
|
case 19:
|
|
return "switch_req";
|
|
case 20:
|
|
return "hold";
|
|
case 21:
|
|
return "hold_req";
|
|
case 22:
|
|
return "sniff";
|
|
case 23:
|
|
return "sniff_req";
|
|
case 24:
|
|
return "unsniff_req";
|
|
case 25:
|
|
return "park_req";
|
|
case 26:
|
|
return "park";
|
|
case 27:
|
|
return "set_broadcast_scan_window";
|
|
case 28:
|
|
return "modify_beacon";
|
|
case 29:
|
|
return "unpark_BD_ADDR_req";
|
|
case 30:
|
|
return "unpark_PM_ADDR_req";
|
|
case 31:
|
|
return "incr_power_req";
|
|
case 32:
|
|
return "decr_power_req";
|
|
case 33:
|
|
return "max_power";
|
|
case 34:
|
|
return "min_power";
|
|
case 35:
|
|
return "auto_rate";
|
|
case 36:
|
|
return "preferred_rate";
|
|
case 37:
|
|
return "version_req";
|
|
case 38:
|
|
return "version_res";
|
|
case 39:
|
|
return "feature_req";
|
|
case 40:
|
|
return "feature_res";
|
|
case 41:
|
|
return "quality_of_service";
|
|
case 42:
|
|
return "quality_of_service_req";
|
|
case 43:
|
|
return "SCO_link_req";
|
|
case 44:
|
|
return "remove_SCO_link_req";
|
|
case 45:
|
|
return "max_slot";
|
|
case 46:
|
|
return "max_slot_req";
|
|
case 47:
|
|
return "timing_accuracy_req";
|
|
case 48:
|
|
return "timing_accuracy_res";
|
|
case 49:
|
|
return "setup_complete";
|
|
case 50:
|
|
return "use_semi_permanent_key";
|
|
case 51:
|
|
return "host_connection_req";
|
|
case 52:
|
|
return "slot_offset";
|
|
case 53:
|
|
return "page_mode_req";
|
|
case 54:
|
|
return "page_scan_mode_req";
|
|
case 55:
|
|
return "supervision_timeout";
|
|
case 56:
|
|
return "test_activate";
|
|
case 57:
|
|
return "test_control";
|
|
case 58:
|
|
return "encryption_key_size_mask_req";
|
|
case 59:
|
|
return "encryption_key_size_mask_res";
|
|
case 60:
|
|
return "set_AFH";
|
|
case 61:
|
|
return "encapsulated_header";
|
|
case 62:
|
|
return "encapsulated_payload";
|
|
case 63:
|
|
return "simple_pairing_confirm";
|
|
case 64:
|
|
return "simple_pairing_number";
|
|
case 65:
|
|
return "DHkey_check";
|
|
case 127 + (1 << 7):
|
|
return "accepted_ext";
|
|
case 127 + (2 << 7):
|
|
return "not_accepted_ext";
|
|
case 127 + (3 << 7):
|
|
return "features_req_ext";
|
|
case 127 + (4 << 7):
|
|
return "features_res_ext";
|
|
case 127 + (11 << 7):
|
|
return "packet_type_table_req";
|
|
case 127 + (12 << 7):
|
|
return "eSCO_link_req";
|
|
case 127 + (13 << 7):
|
|
return "remove_eSCO_link_req";
|
|
case 127 + (16 << 7):
|
|
return "channel_classification_req";
|
|
case 127 + (17 << 7):
|
|
return "channel_classification";
|
|
case 127 + (21 << 7):
|
|
return "sniff_subrating_req";
|
|
case 127 + (22 << 7):
|
|
return "sniff_subrating_res";
|
|
case 127 + (23 << 7):
|
|
return "pause_encryption_req";
|
|
case 127 + (24 << 7):
|
|
return "resume_encryption_req";
|
|
case 127 + (25 << 7):
|
|
return "IO_capability_req";
|
|
case 127 + (26 << 7):
|
|
return "IO_capability_res";
|
|
case 127 + (27 << 7):
|
|
return "numeric_comparison_failed";
|
|
case 127 + (28 << 7):
|
|
return "passkey_failed";
|
|
case 127 + (29 << 7):
|
|
return "oob_failed";
|
|
case 127 + (30 << 7):
|
|
return "keypress_notification";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static inline void name_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t offset = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("name offset %d\n", offset);
|
|
}
|
|
|
|
static inline void name_res_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t offset = LMP_U8(frm);
|
|
uint8_t length = LMP_U8(frm);
|
|
uint8_t *name = frm->ptr;
|
|
int i, size;
|
|
|
|
frm->ptr += 14;
|
|
frm->len -= 14;
|
|
|
|
p_indent(level, frm);
|
|
printf("name offset %d\n", offset);
|
|
|
|
p_indent(level, frm);
|
|
printf("name length %d\n", length);
|
|
|
|
size = length - offset;
|
|
if (size > 14)
|
|
size = 14;
|
|
|
|
p_indent(level, frm);
|
|
printf("name fragment '");
|
|
for (i = 0; i < size; i++)
|
|
if (isprint(name[i]))
|
|
printf("%c", name[i]);
|
|
else
|
|
printf(".");
|
|
printf("'\n");
|
|
}
|
|
|
|
static inline void accepted_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t opcode = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("op code %d (%s)\n", opcode, opcode2str(opcode));
|
|
}
|
|
|
|
static inline void not_accepted_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t opcode = LMP_U8(frm);
|
|
uint8_t error = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("op code %d (%s)\n", opcode, opcode2str(opcode));
|
|
|
|
p_indent(level, frm);
|
|
printf("error code 0x%2.2x\n", error);
|
|
}
|
|
|
|
static inline void clkoffset_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t clkoffset = LMP_U16(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("clock offset 0x%4.4x\n", clkoffset);
|
|
}
|
|
|
|
static inline void detach_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t error = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("error code 0x%2.2x\n", error);
|
|
}
|
|
|
|
static inline void random_number_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *number = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
|
|
p_indent(level, frm);
|
|
printf("random number ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", number[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void key_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *key = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
|
|
p_indent(level, frm);
|
|
printf("key ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", key[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void auth_resp_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *resp = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 4;
|
|
frm->ptr -= 4;
|
|
|
|
p_indent(level, frm);
|
|
printf("authentication response ");
|
|
for (i = 0; i < 4; i++)
|
|
printf("%2.2x", resp[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void encryption_mode_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t mode = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("encryption mode %d\n", mode);
|
|
}
|
|
|
|
static inline void encryption_key_size_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t keysize = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("key size %d\n", keysize);
|
|
}
|
|
|
|
static inline void switch_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint32_t instant = LMP_U32(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("switch instant 0x%4.4x\n", instant);
|
|
}
|
|
|
|
static inline void hold_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t time = LMP_U16(frm);
|
|
uint32_t instant = LMP_U32(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("hold time 0x%4.4x\n", time);
|
|
|
|
p_indent(level, frm);
|
|
printf("hold instant 0x%4.4x\n", instant);
|
|
}
|
|
|
|
static inline void sniff_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t timing = LMP_U8(frm);
|
|
uint16_t dsniff = LMP_U16(frm);
|
|
uint16_t tsniff = LMP_U16(frm);
|
|
uint16_t attempt = LMP_U16(frm);
|
|
uint16_t timeout = LMP_U16(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("timing control flags 0x%2.2x\n", timing);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_sniff %d T_sniff %d\n", dsniff, tsniff);
|
|
|
|
p_indent(level, frm);
|
|
printf("sniff attempt %d\n", attempt);
|
|
|
|
p_indent(level, frm);
|
|
printf("sniff timeout %d\n", timeout);
|
|
}
|
|
|
|
static inline void park_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t timing = LMP_U8(frm);
|
|
uint16_t db = LMP_U16(frm);
|
|
uint16_t tb = LMP_U16(frm);
|
|
uint8_t nb = LMP_U8(frm);
|
|
uint8_t xb = LMP_U8(frm);
|
|
uint8_t pmaddr = LMP_U8(frm);
|
|
uint8_t araddr = LMP_U8(frm);
|
|
uint8_t nbsleep = LMP_U8(frm);
|
|
uint8_t dbsleep = LMP_U8(frm);
|
|
uint8_t daccess = LMP_U8(frm);
|
|
uint8_t taccess = LMP_U8(frm);
|
|
uint8_t nslots = LMP_U8(frm);
|
|
uint8_t npoll = LMP_U8(frm);
|
|
uint8_t access = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("timing control flags 0x%2.2x\n", timing);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_B %d T_B %d N_B %d X_B %d\n", db, tb, nb, xb);
|
|
|
|
p_indent(level, frm);
|
|
printf("PM_ADDR %d AR_ADDR %d\n", pmaddr, araddr);
|
|
|
|
p_indent(level, frm);
|
|
printf("N_Bsleep %d D_Bsleep %d\n", nbsleep, dbsleep);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_access %d T_access %d\n", daccess, taccess);
|
|
|
|
p_indent(level, frm);
|
|
printf("N_acc-slots %d N_poll %d\n", nslots, npoll);
|
|
|
|
p_indent(level, frm);
|
|
printf("M_access %d\n", access & 0x0f);
|
|
|
|
p_indent(level, frm);
|
|
printf("access scheme 0x%2.2x\n", access >> 4);
|
|
}
|
|
|
|
static inline void modify_beacon_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t timing = LMP_U8(frm);
|
|
uint16_t db = LMP_U16(frm);
|
|
uint16_t tb = LMP_U16(frm);
|
|
uint8_t nb = LMP_U8(frm);
|
|
uint8_t xb = LMP_U8(frm);
|
|
uint8_t daccess = LMP_U8(frm);
|
|
uint8_t taccess = LMP_U8(frm);
|
|
uint8_t nslots = LMP_U8(frm);
|
|
uint8_t npoll = LMP_U8(frm);
|
|
uint8_t access = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("timing control flags 0x%2.2x\n", timing);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_B %d T_B %d N_B %d X_B %d\n", db, tb, nb, xb);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_access %d T_access %d\n", daccess, taccess);
|
|
|
|
p_indent(level, frm);
|
|
printf("N_acc-slots %d N_poll %d\n", nslots, npoll);
|
|
|
|
p_indent(level, frm);
|
|
printf("M_access %d\n", access & 0x0f);
|
|
|
|
p_indent(level, frm);
|
|
printf("access scheme 0x%2.2x\n", access >> 4);
|
|
}
|
|
|
|
static inline void power_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t val = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("future use 0x%2.2x\n", val);
|
|
}
|
|
|
|
static inline void preferred_rate_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t rate = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("data rate 0x%2.2x\n", rate);
|
|
|
|
p_indent(level, frm);
|
|
printf("Basic: ");
|
|
|
|
printf("%suse FEC, ", rate & 0x01 ? "do not " : "");
|
|
|
|
switch ((rate >> 1) & 0x03) {
|
|
case 0x00:
|
|
printf("no packet-size preference\n");
|
|
break;
|
|
case 0x01:
|
|
printf("use 1-slot packets\n");
|
|
break;
|
|
case 0x02:
|
|
printf("use 3-slot packets\n");
|
|
break;
|
|
case 0x03:
|
|
printf("use 5-slot packets\n");
|
|
break;
|
|
}
|
|
|
|
p_indent(level, frm);
|
|
printf("EDR: ");
|
|
|
|
switch ((rate >> 3) & 0x03) {
|
|
case 0x00:
|
|
printf("use DM1 packets, ");
|
|
break;
|
|
case 0x01:
|
|
printf("use 2 Mbps packets, ");
|
|
break;
|
|
case 0x02:
|
|
printf("use 3 Mbps packets, ");
|
|
break;
|
|
case 0x03:
|
|
printf("reserved, \n");
|
|
break;
|
|
}
|
|
|
|
switch ((rate >> 5) & 0x03) {
|
|
case 0x00:
|
|
printf("no packet-size preference\n");
|
|
break;
|
|
case 0x01:
|
|
printf("use 1-slot packets\n");
|
|
break;
|
|
case 0x02:
|
|
printf("use 3-slot packets\n");
|
|
break;
|
|
case 0x03:
|
|
printf("use 5-slot packets\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void version_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t ver = LMP_U8(frm);
|
|
uint16_t compid = LMP_U16(frm);
|
|
uint16_t subver = LMP_U16(frm);
|
|
char *tmp;
|
|
|
|
p_indent(level, frm);
|
|
tmp = lmp_vertostr(ver);
|
|
printf("VersNr %d (%s)\n", ver, tmp);
|
|
bt_free(tmp);
|
|
|
|
p_indent(level, frm);
|
|
printf("CompId %d (%s)\n", compid, bt_compidtostr(compid));
|
|
|
|
p_indent(level, frm);
|
|
printf("SubVersNr %d\n", subver);
|
|
}
|
|
|
|
static inline void features_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *features = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 8;
|
|
frm->len -= 8;
|
|
|
|
p_indent(level, frm);
|
|
printf("features");
|
|
for (i = 0; i < 8; i++)
|
|
printf(" 0x%2.2x", features[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void set_afh_dump(int level, struct frame *frm)
|
|
{
|
|
uint32_t instant = LMP_U32(frm);
|
|
uint8_t mode = LMP_U8(frm);
|
|
uint8_t *map = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 10;
|
|
frm->len -= 10;
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH_instant 0x%04x\n", instant);
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH_mode %d\n", mode);
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH_channel_map 0x");
|
|
for (i = 0; i < 10; i++)
|
|
printf("%2.2x", map[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void encapsulated_header_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t major = LMP_U8(frm);
|
|
uint8_t minor = LMP_U8(frm);
|
|
uint8_t length = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("major type %d minor type %d payload length %d\n",
|
|
major, minor, length);
|
|
|
|
if (major == 1 && minor == 1) {
|
|
p_indent(level, frm);
|
|
printf("P-192 Public Key\n");
|
|
}
|
|
}
|
|
|
|
static inline void encapsulated_payload_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *value = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
|
|
p_indent(level, frm);
|
|
printf("data ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", value[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void simple_pairing_confirm_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *value = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
|
|
p_indent(level, frm);
|
|
printf("commitment value ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", value[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void simple_pairing_number_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *value = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
|
|
p_indent(level, frm);
|
|
printf("nounce value ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", value[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void dhkey_check_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *value = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
|
|
p_indent(level, frm);
|
|
printf("confirmation value ");
|
|
for (i = 0; i < 16; i++)
|
|
printf("%2.2x", value[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void accepted_ext_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t opcode = LMP_U8(frm) + (LMP_U8(frm) << 7);
|
|
|
|
p_indent(level, frm);
|
|
printf("op code %d/%d (%s)\n", opcode & 0x7f, opcode >> 7, opcode2str(opcode));
|
|
}
|
|
|
|
static inline void not_accepted_ext_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t opcode = LMP_U8(frm) + (LMP_U8(frm) << 7);
|
|
uint8_t error = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("op code %d/%d (%s)\n", opcode & 0x7f, opcode >> 7, opcode2str(opcode));
|
|
|
|
p_indent(level, frm);
|
|
printf("error code 0x%2.2x\n", error);
|
|
}
|
|
|
|
static inline void features_ext_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t page = LMP_U8(frm);
|
|
uint8_t max = LMP_U8(frm);
|
|
uint8_t *features = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 8;
|
|
frm->len -= 8;
|
|
|
|
p_indent(level, frm);
|
|
printf("features page %d\n", page);
|
|
|
|
p_indent(level, frm);
|
|
printf("max supported page %d\n", max);
|
|
|
|
p_indent(level, frm);
|
|
printf("extended features");
|
|
for (i = 0; i < 8; i++)
|
|
printf(" 0x%2.2x", features[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void quality_of_service_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t interval = LMP_U16(frm);
|
|
uint8_t nbc = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("poll interval %d\n", interval);
|
|
|
|
p_indent(level, frm);
|
|
printf("N_BC %d\n", nbc);
|
|
}
|
|
|
|
static inline void sco_link_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t handle = LMP_U8(frm);
|
|
uint8_t timing = LMP_U8(frm);
|
|
uint8_t dsco = LMP_U8(frm);
|
|
uint8_t tsco = LMP_U8(frm);
|
|
uint8_t packet = LMP_U8(frm);
|
|
uint8_t airmode = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("SCO handle %d\n", handle);
|
|
|
|
p_indent(level, frm);
|
|
printf("timing control flags 0x%2.2x\n", timing);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_SCO %d T_SCO %d\n", dsco, tsco);
|
|
|
|
p_indent(level, frm);
|
|
printf("SCO packet 0x%2.2x\n", packet);
|
|
|
|
p_indent(level, frm);
|
|
printf("air mode 0x%2.2x\n", airmode);
|
|
}
|
|
|
|
static inline void remove_sco_link_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t handle = LMP_U8(frm);
|
|
uint8_t error = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("SCO handle %d\n", handle);
|
|
|
|
p_indent(level, frm);
|
|
printf("error code 0x%2.2x\n", error);
|
|
}
|
|
|
|
static inline void max_slots_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t slots = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("max slots %d\n", slots);
|
|
}
|
|
|
|
static inline void timing_accuracy_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t drift = LMP_U8(frm);
|
|
uint8_t jitter = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("drift %d\n", drift);
|
|
|
|
p_indent(level, frm);
|
|
printf("jitter %d\n", jitter);
|
|
}
|
|
|
|
static inline void slot_offset_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t offset = LMP_U16(frm);
|
|
char addr[18];
|
|
|
|
p_ba2str((bdaddr_t *) frm->ptr, addr);
|
|
|
|
p_indent(level, frm);
|
|
printf("slot offset %d\n", offset);
|
|
|
|
p_indent(level, frm);
|
|
printf("BD_ADDR %s\n", addr);
|
|
}
|
|
|
|
static inline void page_mode_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t scheme = LMP_U8(frm);
|
|
uint8_t settings = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("page scheme %d\n", scheme);
|
|
|
|
p_indent(level, frm);
|
|
printf("page scheme settings %d\n", settings);
|
|
}
|
|
|
|
static inline void supervision_timeout_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t timeout = LMP_U16(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("supervision timeout %d\n", timeout);
|
|
}
|
|
|
|
static inline void test_control_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t scenario = LMP_U8(frm);
|
|
uint8_t hopping = LMP_U8(frm);
|
|
uint8_t txfreq = LMP_U8(frm);
|
|
uint8_t rxfreq = LMP_U8(frm);
|
|
uint8_t power = LMP_U8(frm);
|
|
uint8_t poll = LMP_U8(frm);
|
|
uint8_t packet = LMP_U8(frm);
|
|
uint16_t length = LMP_U16(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("test scenario %d\n", scenario);
|
|
|
|
p_indent(level, frm);
|
|
printf("hopping mode %d\n", hopping);
|
|
|
|
p_indent(level, frm);
|
|
printf("TX frequency %d\n", txfreq);
|
|
|
|
p_indent(level, frm);
|
|
printf("RX frequency %d\n", rxfreq);
|
|
|
|
p_indent(level, frm);
|
|
printf("power control mode %d\n", power);
|
|
|
|
p_indent(level, frm);
|
|
printf("poll period %d\n", poll);
|
|
|
|
p_indent(level, frm);
|
|
printf("poll period %d\n", poll);
|
|
|
|
p_indent(level, frm);
|
|
printf("packet type 0x%2.2x\n", packet);
|
|
|
|
p_indent(level, frm);
|
|
printf("length of test data %d\n", length);
|
|
}
|
|
|
|
static inline void encryption_key_size_mask_res_dump(int level, struct frame *frm)
|
|
{
|
|
uint16_t mask = LMP_U16(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("key size mask 0x%4.4x\n", mask);
|
|
}
|
|
|
|
static inline void packet_type_table_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t type = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("packet type table %d ", type);
|
|
switch (type) {
|
|
case 0:
|
|
printf("(1Mbps only)\n");
|
|
break;
|
|
case 1:
|
|
printf("(2/3Mbps)\n");
|
|
break;
|
|
default:
|
|
printf("(Reserved)\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void esco_link_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t handle = LMP_U8(frm);
|
|
uint8_t ltaddr = LMP_U8(frm);
|
|
uint8_t timing = LMP_U8(frm);
|
|
uint8_t desco = LMP_U8(frm);
|
|
uint8_t tesco = LMP_U8(frm);
|
|
uint8_t wesco = LMP_U8(frm);
|
|
uint8_t mspkt = LMP_U8(frm);
|
|
uint8_t smpkt = LMP_U8(frm);
|
|
uint16_t mslen = LMP_U16(frm);
|
|
uint16_t smlen = LMP_U16(frm);
|
|
uint8_t airmode = LMP_U8(frm);
|
|
uint8_t negstate = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("eSCO handle %d\n", handle);
|
|
|
|
p_indent(level, frm);
|
|
printf("eSCO LT_ADDR %d\n", ltaddr);
|
|
|
|
p_indent(level, frm);
|
|
printf("timing control flags 0x%2.2x\n", timing);
|
|
|
|
p_indent(level, frm);
|
|
printf("D_eSCO %d T_eSCO %d W_eSCO %d\n", desco, tesco, wesco);
|
|
|
|
p_indent(level, frm);
|
|
printf("eSCO M->S packet type 0x%2.2x length %d\n", mspkt, mslen);
|
|
|
|
p_indent(level, frm);
|
|
printf("eSCO S->M packet type 0x%2.2x length %d\n", smpkt, smlen);
|
|
|
|
p_indent(level, frm);
|
|
printf("air mode 0x%2.2x\n", airmode);
|
|
|
|
p_indent(level, frm);
|
|
printf("negotiation state 0x%2.2x\n", negstate);
|
|
}
|
|
|
|
static inline void remove_esco_link_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t handle = LMP_U8(frm);
|
|
uint8_t error = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("eSCO handle %d\n", handle);
|
|
|
|
p_indent(level, frm);
|
|
printf("error code 0x%2.2x\n", error);
|
|
}
|
|
|
|
static inline void channel_classification_req_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t mode = LMP_U8(frm);
|
|
uint16_t min = LMP_U16(frm);
|
|
uint16_t max = LMP_U16(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH reporting mode %d\n", mode);
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH min interval 0x%4.4x\n", min);
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH max interval 0x%4.4x\n", max);
|
|
}
|
|
|
|
static inline void channel_classification_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t *map = frm->ptr;
|
|
int i;
|
|
|
|
frm->ptr += 10;
|
|
frm->len -= 10;
|
|
|
|
p_indent(level, frm);
|
|
printf("AFH channel classification 0x");
|
|
for (i = 0; i < 10; i++)
|
|
printf("%2.2x", map[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static inline void sniff_subrating_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t subrate = LMP_U8(frm);
|
|
uint16_t timeout = LMP_U16(frm);
|
|
uint32_t instant = LMP_U32(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("max subrate %d\n", subrate);
|
|
|
|
p_indent(level, frm);
|
|
printf("min sniff timeout %d\n", timeout);
|
|
|
|
p_indent(level, frm);
|
|
printf("subrate instant 0x%4.4x\n", instant);
|
|
}
|
|
|
|
static inline void io_capability_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t capability = LMP_U8(frm);
|
|
uint8_t oob_data = LMP_U8(frm);
|
|
uint8_t authentication = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("capability 0x%2.2x oob 0x%2.2x auth 0x%2.2x\n",
|
|
capability, oob_data, authentication);
|
|
}
|
|
|
|
static inline void keypress_notification_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t value = LMP_U8(frm);
|
|
|
|
p_indent(level, frm);
|
|
printf("notification value %d\n", value);
|
|
}
|
|
|
|
void lmp_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t tmp, tid;
|
|
uint16_t opcode;
|
|
|
|
p_indent(level, frm);
|
|
|
|
tmp = LMP_U8(frm);
|
|
tid = tmp & 0x01;
|
|
opcode = (tmp & 0xfe) >> 1;
|
|
if (opcode > 123) {
|
|
tmp = LMP_U8(frm);
|
|
opcode += tmp << 7;
|
|
}
|
|
|
|
printf("LMP(%c): %s(%c): ", frm->master ? 's' : 'r',
|
|
opcode2str(opcode), tid ? 's' : 'm');
|
|
|
|
if (opcode > 123)
|
|
printf("op code %d/%d", opcode & 0x7f, opcode >> 7);
|
|
else
|
|
printf("op code %d", opcode);
|
|
|
|
if (frm->handle > 17)
|
|
printf(" handle %d\n", frm->handle);
|
|
else
|
|
printf("\n");
|
|
|
|
if (!(parser.flags & DUMP_VERBOSE)) {
|
|
raw_dump(level, frm);
|
|
return;
|
|
}
|
|
|
|
switch (opcode) {
|
|
case 1:
|
|
name_req_dump(level + 1, frm);
|
|
return;
|
|
case 2:
|
|
name_res_dump(level + 1, frm);
|
|
return;
|
|
case 3:
|
|
accepted_dump(level + 1, frm);
|
|
return;
|
|
case 4:
|
|
not_accepted_dump(level + 1, frm);
|
|
return;
|
|
case 6:
|
|
clkoffset_dump(level + 1, frm);
|
|
return;
|
|
case 7:
|
|
detach_dump(level + 1, frm);
|
|
return;
|
|
case 8:
|
|
in_rand(frm);
|
|
random_number_dump(level + 1, frm);
|
|
return;
|
|
case 9:
|
|
comb_key(frm);
|
|
random_number_dump(level + 1, frm);
|
|
return;
|
|
case 11:
|
|
au_rand(frm);
|
|
random_number_dump(level + 1, frm);
|
|
return;
|
|
case 12:
|
|
sres(frm);
|
|
auth_resp_dump(level + 1, frm);
|
|
return;
|
|
case 13:
|
|
case 17:
|
|
random_number_dump(level + 1, frm);
|
|
return;
|
|
case 10:
|
|
case 14:
|
|
key_dump(level + 1, frm);
|
|
return;
|
|
case 15:
|
|
encryption_mode_req_dump(level + 1, frm);
|
|
return;
|
|
case 16:
|
|
encryption_key_size_req_dump(level + 1, frm);
|
|
return;
|
|
case 19:
|
|
switch_req_dump(level + 1, frm);
|
|
return;
|
|
case 20:
|
|
case 21:
|
|
hold_dump(level + 1, frm);
|
|
return;
|
|
case 23:
|
|
sniff_req_dump(level + 1, frm);
|
|
return;
|
|
case 25:
|
|
park_req_dump(level + 1, frm);
|
|
return;
|
|
case 28:
|
|
modify_beacon_dump(level + 1, frm);
|
|
return;
|
|
case 31:
|
|
case 32:
|
|
power_req_dump(level + 1, frm);
|
|
return;
|
|
case 36:
|
|
preferred_rate_dump(level + 1, frm);
|
|
return;
|
|
case 37:
|
|
case 38:
|
|
version_dump(level + 1, frm);
|
|
return;
|
|
case 39:
|
|
case 40:
|
|
features_dump(level + 1, frm);
|
|
return;
|
|
case 41:
|
|
case 42:
|
|
quality_of_service_dump(level + 1, frm);
|
|
return;
|
|
case 43:
|
|
sco_link_req_dump(level + 1, frm);
|
|
return;
|
|
case 44:
|
|
remove_sco_link_req_dump(level + 1, frm);
|
|
return;
|
|
case 45:
|
|
case 46:
|
|
max_slots_dump(level + 1, frm);
|
|
return;
|
|
case 48:
|
|
timing_accuracy_dump(level + 1, frm);
|
|
return;
|
|
case 52:
|
|
slot_offset_dump(level + 1, frm);
|
|
return;
|
|
case 53:
|
|
case 54:
|
|
page_mode_dump(level + 1, frm);
|
|
return;
|
|
case 55:
|
|
supervision_timeout_dump(level + 1, frm);
|
|
return;
|
|
case 57:
|
|
test_control_dump(level + 1, frm);
|
|
return;
|
|
case 59:
|
|
encryption_key_size_mask_res_dump(level + 1, frm);
|
|
return;
|
|
case 60:
|
|
set_afh_dump(level + 1, frm);
|
|
return;
|
|
case 61:
|
|
encapsulated_header_dump(level + 1, frm);
|
|
return;
|
|
case 62:
|
|
encapsulated_payload_dump(level + 1, frm);
|
|
return;
|
|
case 63:
|
|
simple_pairing_confirm_dump(level + 1, frm);
|
|
return;
|
|
case 64:
|
|
simple_pairing_number_dump(level + 1, frm);
|
|
return;
|
|
case 65:
|
|
dhkey_check_dump(level + 1, frm);
|
|
return;
|
|
case 5:
|
|
case 18:
|
|
case 24:
|
|
case 33:
|
|
case 34:
|
|
case 35:
|
|
case 47:
|
|
case 49:
|
|
case 50:
|
|
case 51:
|
|
case 56:
|
|
case 58:
|
|
case 127 + (23 << 7):
|
|
case 127 + (24 << 7):
|
|
case 127 + (27 << 7):
|
|
case 127 + (28 << 7):
|
|
case 127 + (29 << 7):
|
|
return;
|
|
case 127 + (1 << 7):
|
|
accepted_ext_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (2 << 7):
|
|
not_accepted_ext_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (3 << 7):
|
|
case 127 + (4 << 7):
|
|
features_ext_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (11 << 7):
|
|
packet_type_table_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (12 << 7):
|
|
esco_link_req_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (13 << 7):
|
|
remove_esco_link_req_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (16 << 7):
|
|
channel_classification_req_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (17 << 7):
|
|
channel_classification_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (21 << 7):
|
|
case 127 + (22 << 7):
|
|
sniff_subrating_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (25 << 7):
|
|
case 127 + (26 << 7):
|
|
io_capability_dump(level + 1, frm);
|
|
return;
|
|
case 127 + (30 << 7):
|
|
keypress_notification_dump(level + 1, frm);
|
|
return;
|
|
}
|
|
|
|
raw_dump(level, frm);
|
|
}
|