1e25b118aSDominic Spill /* -*- c -*- */ 2e25b118aSDominic Spill /* 3e25b118aSDominic Spill * Copyright 2007 - 2012 Mike Ryan, Dominic Spill, Michael Ossmann 4e25b118aSDominic Spill * Copyright 2005, 2006 Free Software Foundation, Inc. 5e25b118aSDominic Spill * 6e25b118aSDominic Spill * This file is part of libbtbb 7e25b118aSDominic Spill * 8e25b118aSDominic Spill * This program is free software; you can redistribute it and/or modify 9e25b118aSDominic Spill * it under the terms of the GNU General Public License as published by 10e25b118aSDominic Spill * the Free Software Foundation; either version 2, or (at your option) 11e25b118aSDominic Spill * any later version. 12e25b118aSDominic Spill * 13e25b118aSDominic Spill * This program is distributed in the hope that it will be useful, 14e25b118aSDominic Spill * but WITHOUT ANY WARRANTY; without even the implied warranty of 15e25b118aSDominic Spill * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16e25b118aSDominic Spill * GNU General Public License for more details. 17e25b118aSDominic Spill * 18e25b118aSDominic Spill * You should have received a copy of the GNU General Public License 19e25b118aSDominic Spill * along with libbtbb; see the file COPYING. If not, write to 20e25b118aSDominic Spill * the Free Software Foundation, Inc., 51 Franklin Street, 21e25b118aSDominic Spill * Boston, MA 02110-1301, USA. 22e25b118aSDominic Spill */ 23e25b118aSDominic Spill 24e25b118aSDominic Spill #ifdef HAVE_CONFIG_H 25e25b118aSDominic Spill #include "config.h" 26e25b118aSDominic Spill #endif 27e25b118aSDominic Spill 288f3e7eeaSChristopher Kilgour #include "btbb.h" 29e25b118aSDominic Spill #include "bluetooth_le_packet.h" 30e25b118aSDominic Spill #include <ctype.h> 318f3e7eeaSChristopher Kilgour #include <string.h> 32e25b118aSDominic Spill 33e25b118aSDominic Spill /* string representations of advertising packet type */ 34e25b118aSDominic Spill static const char *ADV_TYPE_NAMES[] = { 35e25b118aSDominic Spill "ADV_IND", "ADV_DIRECT_IND", "ADV_NONCONN_IND", "SCAN_REQ", 36e25b118aSDominic Spill "SCAN_RSP", "CONNECT_REQ", "ADV_SCAN_IND", 37e25b118aSDominic Spill }; 38e25b118aSDominic Spill 39e25b118aSDominic Spill /* source clock accuracy in a connect packet */ 40e25b118aSDominic Spill static const char *CONNECT_SCA[] = { 41e25b118aSDominic Spill "251 ppm to 500 ppm", "151 ppm to 250 ppm", "101 ppm to 150 ppm", 42e25b118aSDominic Spill "76 ppm to 100 ppm", "51 ppm to 75 ppm", "31 ppm to 50 ppm", 43e25b118aSDominic Spill "21 ppm to 30 ppm", "0 ppm to 20 ppm", 44e25b118aSDominic Spill }; 45e25b118aSDominic Spill 46e25b118aSDominic Spill // count of objects in an array, shamelessly stolen from Chrome 47e25b118aSDominic Spill #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 48e25b118aSDominic Spill 49*0a637dcaSDominic Spill static uint8_t count_bits(uint32_t n) 50*0a637dcaSDominic Spill { 51*0a637dcaSDominic Spill uint8_t i = 0; 52*0a637dcaSDominic Spill for (i = 0; n != 0; i++) 53*0a637dcaSDominic Spill n &= n - 1; 54*0a637dcaSDominic Spill return i; 55*0a637dcaSDominic Spill } 56*0a637dcaSDominic Spill 578f3e7eeaSChristopher Kilgour static int aa_access_channel_off_by_one(const uint32_t aa) { 588f3e7eeaSChristopher Kilgour int retval = 0; 59*0a637dcaSDominic Spill if(count_bits(aa ^ LE_ADV_AA) == 1) { 608f3e7eeaSChristopher Kilgour retval = 1; 618f3e7eeaSChristopher Kilgour } 628f3e7eeaSChristopher Kilgour return retval; 638f3e7eeaSChristopher Kilgour } 64e25b118aSDominic Spill 658f3e7eeaSChristopher Kilgour /* 668f3e7eeaSChristopher Kilgour * A helper function for filtering bogus packets on data channels. 678f3e7eeaSChristopher Kilgour * 688f3e7eeaSChristopher Kilgour * If a candidate capture packet is random noise we would expect its 698f3e7eeaSChristopher Kilgour * Access Address to be a randomly distributed 32-bit number. An 708f3e7eeaSChristopher Kilgour * exhaustive software analysis reveals that of 4294967296 possible 718f3e7eeaSChristopher Kilgour * 32-bit Access Address values, 2900629660 (67.5%) are acceptable and 728f3e7eeaSChristopher Kilgour * 1394337636 (32.5%) are invalid. This function will identify which 738f3e7eeaSChristopher Kilgour * category a candidate Access Address falls into by returning the 748f3e7eeaSChristopher Kilgour * number of offenses contained. 758f3e7eeaSChristopher Kilgour * 768f3e7eeaSChristopher Kilgour * Refer to BT 4.x, Vol 6, Par B, Section 2.1.2. 778f3e7eeaSChristopher Kilgour * 788f3e7eeaSChristopher Kilgour * The Access Address in data channel packets meet the 798f3e7eeaSChristopher Kilgour * following requirements: 808f3e7eeaSChristopher Kilgour * - It shall have no more than six consecutive zeros or ones. 818f3e7eeaSChristopher Kilgour * - It shall not be the advertising channel packets’ Access Address. 828f3e7eeaSChristopher Kilgour * - It shall not be a sequence that differs from the advertising channel packets’ 838f3e7eeaSChristopher Kilgour * Access Address by only one bit. 848f3e7eeaSChristopher Kilgour * - It shall not have all four octets equal. 858f3e7eeaSChristopher Kilgour * - It shall have no more than 24 transitions. 868f3e7eeaSChristopher Kilgour * - It shall have a minimum of two transitions in the most significant six bits. 878f3e7eeaSChristopher Kilgour */ 888f3e7eeaSChristopher Kilgour static int aa_data_channel_offenses(const uint32_t aa) { 898f3e7eeaSChristopher Kilgour int retval = 0, transitions = 0; 908f3e7eeaSChristopher Kilgour unsigned shift, odd = (unsigned) (aa & 1); 918f3e7eeaSChristopher Kilgour uint8_t aab3, aab2, aab1, aab0 = (uint8_t) (aa & 0xff); 92e25b118aSDominic Spill 938f3e7eeaSChristopher Kilgour const uint8_t EIGHT_BIT_TRANSITIONS_EVEN[256] = { 948f3e7eeaSChristopher Kilgour 0, 2, 2, 2, 2, 4, 2, 2, 2, 4, 4, 4, 2, 4, 2, 2, 958f3e7eeaSChristopher Kilgour 2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2, 968f3e7eeaSChristopher Kilgour 2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4, 978f3e7eeaSChristopher Kilgour 2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2, 988f3e7eeaSChristopher Kilgour 2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4, 998f3e7eeaSChristopher Kilgour 4, 6, 6, 6, 6, 8, 6, 6, 4, 6, 6, 6, 4, 6, 4, 4, 1008f3e7eeaSChristopher Kilgour 2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4, 1018f3e7eeaSChristopher Kilgour 2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2, 1028f3e7eeaSChristopher Kilgour 1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3, 1038f3e7eeaSChristopher Kilgour 3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3, 1048f3e7eeaSChristopher Kilgour 3, 5, 5, 5, 5, 7, 5, 5, 5, 7, 7, 7, 5, 7, 5, 5, 1058f3e7eeaSChristopher Kilgour 3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3, 1068f3e7eeaSChristopher Kilgour 1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3, 1078f3e7eeaSChristopher Kilgour 3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3, 1088f3e7eeaSChristopher Kilgour 1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3, 1098f3e7eeaSChristopher Kilgour 1, 3, 3, 3, 3, 5, 3, 3, 1, 3, 3, 3, 1, 3, 1, 1 1108f3e7eeaSChristopher Kilgour }; 111e25b118aSDominic Spill 1128f3e7eeaSChristopher Kilgour const uint8_t EIGHT_BIT_TRANSITIONS_ODD[256] = { 1138f3e7eeaSChristopher Kilgour 1, 1, 3, 1, 3, 3, 3, 1, 3, 3, 5, 3, 3, 3, 3, 1, 1148f3e7eeaSChristopher Kilgour 3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1, 1158f3e7eeaSChristopher Kilgour 3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3, 1168f3e7eeaSChristopher Kilgour 3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1, 1178f3e7eeaSChristopher Kilgour 3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3, 1188f3e7eeaSChristopher Kilgour 5, 5, 7, 5, 7, 7, 7, 5, 5, 5, 7, 5, 5, 5, 5, 3, 1198f3e7eeaSChristopher Kilgour 3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3, 1208f3e7eeaSChristopher Kilgour 3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1, 1218f3e7eeaSChristopher Kilgour 2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2, 1228f3e7eeaSChristopher Kilgour 4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2, 1238f3e7eeaSChristopher Kilgour 4, 4, 6, 4, 6, 6, 6, 4, 6, 6, 8, 6, 6, 6, 6, 4, 1248f3e7eeaSChristopher Kilgour 4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2, 1258f3e7eeaSChristopher Kilgour 2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2, 1268f3e7eeaSChristopher Kilgour 4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2, 1278f3e7eeaSChristopher Kilgour 2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2, 1288f3e7eeaSChristopher Kilgour 2, 2, 4, 2, 4, 4, 4, 2, 2, 2, 4, 2, 2, 2, 2, 0 1298f3e7eeaSChristopher Kilgour }; 1308f3e7eeaSChristopher Kilgour 1318f3e7eeaSChristopher Kilgour transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab0] : EIGHT_BIT_TRANSITIONS_EVEN[aab0] ); 1328f3e7eeaSChristopher Kilgour odd = (unsigned) (aab0 & 0x80); 1338f3e7eeaSChristopher Kilgour aab1 = (uint8_t) (aa >> 8); 1348f3e7eeaSChristopher Kilgour transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab1] : EIGHT_BIT_TRANSITIONS_EVEN[aab1] ); 1358f3e7eeaSChristopher Kilgour odd = (unsigned) (aab1 & 0x80); 1368f3e7eeaSChristopher Kilgour aab2 = (uint8_t) (aa >> 16); 1378f3e7eeaSChristopher Kilgour transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab2] : EIGHT_BIT_TRANSITIONS_EVEN[aab2] ); 1388f3e7eeaSChristopher Kilgour odd = (unsigned) (aab2 & 0x80); 1398f3e7eeaSChristopher Kilgour aab3 = (uint8_t) (aa >> 24); 1408f3e7eeaSChristopher Kilgour transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab3] : EIGHT_BIT_TRANSITIONS_EVEN[aab3] ); 1418f3e7eeaSChristopher Kilgour 1428f3e7eeaSChristopher Kilgour /* consider excessive transitions as offenses */ 1438f3e7eeaSChristopher Kilgour if (transitions > 24) { 1448f3e7eeaSChristopher Kilgour retval += (transitions - 24); 1458f3e7eeaSChristopher Kilgour } 1468f3e7eeaSChristopher Kilgour 1478f3e7eeaSChristopher Kilgour const uint8_t AA_MSB6_ALLOWED[64] = { 1488f3e7eeaSChristopher Kilgour 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1498f3e7eeaSChristopher Kilgour 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1508f3e7eeaSChristopher Kilgour 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1518f3e7eeaSChristopher Kilgour 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0 1528f3e7eeaSChristopher Kilgour }; 1538f3e7eeaSChristopher Kilgour 1548f3e7eeaSChristopher Kilgour /* consider excessive transitions in the 6 MSBs as an offense */ 1558f3e7eeaSChristopher Kilgour retval += (1 - AA_MSB6_ALLOWED[aab3>>2]); 1568f3e7eeaSChristopher Kilgour 1578f3e7eeaSChristopher Kilgour /* consider all bytes as being equal an offense */ 1588f3e7eeaSChristopher Kilgour retval += (((aab0 == aab1) && (aab0 == aab2) && (aab0 == aab3)) ? 1 : 0); 1598f3e7eeaSChristopher Kilgour 1608f3e7eeaSChristopher Kilgour /* access-channel address and off-by-ones are illegal */ 161*0a637dcaSDominic Spill retval += ((aa == LE_ADV_AA) ? 1 : 0); 1628f3e7eeaSChristopher Kilgour retval += aa_access_channel_off_by_one(aa); 1638f3e7eeaSChristopher Kilgour 1648f3e7eeaSChristopher Kilgour /* inspect nibble triples for insufficient bit transitions */ 1658f3e7eeaSChristopher Kilgour for(shift=0; shift<=20; shift+=4) { 1668f3e7eeaSChristopher Kilgour uint16_t twelvebits = (uint16_t) ((aa >> shift) & 0xfff); 1678f3e7eeaSChristopher Kilgour switch( twelvebits ) { 1688f3e7eeaSChristopher Kilgour /* seven consecutive zeroes */ 1698f3e7eeaSChristopher Kilgour case 0x080: case 0x180: case 0x280: case 0x380: case 0x480: 1708f3e7eeaSChristopher Kilgour case 0x580: case 0x680: case 0x780: case 0x880: case 0x980: 1718f3e7eeaSChristopher Kilgour case 0xa80: case 0xb80: case 0xc80: case 0xd80: case 0xe80: 1728f3e7eeaSChristopher Kilgour case 0xf80: case 0x101: case 0x301: case 0x501: case 0x701: 1738f3e7eeaSChristopher Kilgour case 0x901: case 0xb01: case 0xd01: case 0xf01: case 0x202: 1748f3e7eeaSChristopher Kilgour case 0x602: case 0xa02: case 0xe02: case 0x203: case 0x603: 1758f3e7eeaSChristopher Kilgour case 0xa03: case 0xe03: case 0x404: case 0xc04: case 0x405: 1768f3e7eeaSChristopher Kilgour case 0xc05: case 0x406: case 0xc06: case 0x407: case 0xc07: 1778f3e7eeaSChristopher Kilgour case 0x808: case 0x809: case 0x80a: case 0x80b: case 0x80c: 1788f3e7eeaSChristopher Kilgour case 0x80d: case 0x80e: case 0x80f: case 0x010: case 0x011: 1798f3e7eeaSChristopher Kilgour case 0x012: case 0x013: case 0x014: case 0x015: case 0x016: 1808f3e7eeaSChristopher Kilgour case 0x017: case 0x018: case 0x019: case 0x01a: case 0x01b: 1818f3e7eeaSChristopher Kilgour case 0x01c: case 0x01d: case 0x01e: case 0x01f: 1828f3e7eeaSChristopher Kilgour /* eight consecutive zeroes */ 1838f3e7eeaSChristopher Kilgour case 0x100: case 0x300: case 0x500: case 0x700: case 0x900: 1848f3e7eeaSChristopher Kilgour case 0xb00: case 0xd00: case 0xf00: case 0x201: case 0x601: 1858f3e7eeaSChristopher Kilgour case 0xa01: case 0xe01: case 0x402: case 0xc02: case 0x403: 1868f3e7eeaSChristopher Kilgour case 0xc03: case 0x804: case 0x805: case 0x806: case 0x807: 1878f3e7eeaSChristopher Kilgour case 0x008: case 0x009: case 0x00a: case 0x00b: case 0x00c: 1888f3e7eeaSChristopher Kilgour case 0x00d: case 0x00e: case 0x00f: 1898f3e7eeaSChristopher Kilgour /* nine consecutive zeroes */ 1908f3e7eeaSChristopher Kilgour case 0xe00: case 0xc01: case 0x802: case 0x803: case 0x004: 1918f3e7eeaSChristopher Kilgour case 0x005: case 0x006: case 0x007: 1928f3e7eeaSChristopher Kilgour /* ten consecutive zeroes */ 1938f3e7eeaSChristopher Kilgour case 0x400: case 0xc00: case 0x801: case 0x002: case 0x003: 1948f3e7eeaSChristopher Kilgour /* eleven consecutive zeroes */ 1958f3e7eeaSChristopher Kilgour case 0x800: case 0x001: 1968f3e7eeaSChristopher Kilgour /* twelve consecutive zeroes */ 1978f3e7eeaSChristopher Kilgour case 0x000: 1988f3e7eeaSChristopher Kilgour /* seven consecutive ones */ 1998f3e7eeaSChristopher Kilgour case 0x07f: case 0x0fe: case 0x2fe: case 0x4fe: case 0x6fe: 2008f3e7eeaSChristopher Kilgour case 0x8fe: case 0xafe: case 0xcfe: case 0xefe: case 0x1fc: 2018f3e7eeaSChristopher Kilgour case 0x5fc: case 0x9fc: case 0xdfc: case 0x1fd: case 0x5fd: 2028f3e7eeaSChristopher Kilgour case 0x9fd: case 0xdfd: case 0x3f8: case 0xbf8: case 0x3f9: 2038f3e7eeaSChristopher Kilgour case 0xbf9: case 0x3fa: case 0xbfa: case 0x3fb: case 0xbfb: 2048f3e7eeaSChristopher Kilgour case 0x7f4: case 0x7f5: case 0x7f6: case 0x7f7: case 0xfe0: 2058f3e7eeaSChristopher Kilgour /* eight consecutive ones */ 2068f3e7eeaSChristopher Kilgour case 0x0ff: case 0x2ff: case 0x4ff: case 0x6ff: case 0x8ff: 2078f3e7eeaSChristopher Kilgour case 0xaff: case 0xcff: case 0xeff: case 0x1fe: case 0x5fe: 2088f3e7eeaSChristopher Kilgour case 0x9fe: case 0xdfe: case 0x3fc: case 0xbfc: case 0x3fd: 2098f3e7eeaSChristopher Kilgour case 0xbfd: case 0x7f8: case 0x7f9: case 0x7fa: case 0x7fb: 2108f3e7eeaSChristopher Kilgour case 0xff0: case 0xff1: case 0xff2: case 0xff3: case 0xff4: 2118f3e7eeaSChristopher Kilgour case 0xff5: case 0xff6: case 0xff7: 2128f3e7eeaSChristopher Kilgour /* nine consecutive ones */ 2138f3e7eeaSChristopher Kilgour case 0x1ff: case 0x5ff: case 0x9ff: case 0xdff: case 0x3fe: 2148f3e7eeaSChristopher Kilgour case 0xbfe: case 0x7fc: case 0x7fd: case 0xff8: case 0xff9: 2158f3e7eeaSChristopher Kilgour case 0xffa: case 0xffb: 2168f3e7eeaSChristopher Kilgour /* ten consecutive ones */ 2178f3e7eeaSChristopher Kilgour case 0x3ff: case 0xbff: case 0x7fe: case 0xffc: case 0xffd: 2188f3e7eeaSChristopher Kilgour /* eleven consecutive ones */ 2198f3e7eeaSChristopher Kilgour case 0x7ff: case 0xffe: 2208f3e7eeaSChristopher Kilgour /* all ones */ 2218f3e7eeaSChristopher Kilgour case 0xfff: 2228f3e7eeaSChristopher Kilgour retval++; 2238f3e7eeaSChristopher Kilgour break; 2248f3e7eeaSChristopher Kilgour default: 2258f3e7eeaSChristopher Kilgour break; 226e25b118aSDominic Spill } 227e25b118aSDominic Spill } 228e25b118aSDominic Spill 2298f3e7eeaSChristopher Kilgour return retval; 230e25b118aSDominic Spill } 231e25b118aSDominic Spill 2328f3e7eeaSChristopher Kilgour lell_packet * 2338f3e7eeaSChristopher Kilgour lell_packet_new(void) 2348f3e7eeaSChristopher Kilgour { 2358f3e7eeaSChristopher Kilgour lell_packet *pkt = (lell_packet *)calloc(1, sizeof(lell_packet)); 2368f3e7eeaSChristopher Kilgour pkt->refcount = 1; 2378f3e7eeaSChristopher Kilgour return pkt; 2388f3e7eeaSChristopher Kilgour } 2398f3e7eeaSChristopher Kilgour 2408f3e7eeaSChristopher Kilgour void 2418f3e7eeaSChristopher Kilgour lell_packet_ref(lell_packet *pkt) 2428f3e7eeaSChristopher Kilgour { 2438f3e7eeaSChristopher Kilgour pkt->refcount++; 2448f3e7eeaSChristopher Kilgour } 2458f3e7eeaSChristopher Kilgour 2468f3e7eeaSChristopher Kilgour void 2478f3e7eeaSChristopher Kilgour lell_packet_unref(lell_packet *pkt) 2488f3e7eeaSChristopher Kilgour { 2498f3e7eeaSChristopher Kilgour pkt->refcount--; 2508f3e7eeaSChristopher Kilgour if (pkt->refcount == 0) 2518f3e7eeaSChristopher Kilgour free(pkt); 2528f3e7eeaSChristopher Kilgour } 2538f3e7eeaSChristopher Kilgour 2548f3e7eeaSChristopher Kilgour static uint8_t le_channel_index(uint16_t phys_channel) { 255e25b118aSDominic Spill uint8_t ret; 256e25b118aSDominic Spill if (phys_channel == 2402) { 257e25b118aSDominic Spill ret = 37; 258e25b118aSDominic Spill } else if (phys_channel < 2426) { // 0 - 10 259e25b118aSDominic Spill ret = (phys_channel - 2404) / 2; 260e25b118aSDominic Spill } else if (phys_channel == 2426) { 261e25b118aSDominic Spill ret = 38; 262e25b118aSDominic Spill } else if (phys_channel < 2480) { // 11 - 36 263e25b118aSDominic Spill ret = 11 + (phys_channel - 2428) / 2; 264e25b118aSDominic Spill } else { 265e25b118aSDominic Spill ret = 39; 266e25b118aSDominic Spill } 267e25b118aSDominic Spill return ret; 268e25b118aSDominic Spill } 269e25b118aSDominic Spill 2708f3e7eeaSChristopher Kilgour void lell_allocate_and_decode(const uint8_t *stream, uint16_t phys_channel, uint32_t clk100ns, lell_packet **pkt) 2718f3e7eeaSChristopher Kilgour { 2728f3e7eeaSChristopher Kilgour *pkt = lell_packet_new( ); 2738f3e7eeaSChristopher Kilgour memcpy((*pkt)->symbols, stream, MAX_LE_SYMBOLS); 2748f3e7eeaSChristopher Kilgour 2758f3e7eeaSChristopher Kilgour (*pkt)->channel_idx = le_channel_index(phys_channel); 2768f3e7eeaSChristopher Kilgour (*pkt)->channel_k = (phys_channel-2402)/2; 2778f3e7eeaSChristopher Kilgour (*pkt)->clk100ns = clk100ns; 2788f3e7eeaSChristopher Kilgour 2798f3e7eeaSChristopher Kilgour (*pkt)->access_address = 0; 2808f3e7eeaSChristopher Kilgour (*pkt)->access_address |= (*pkt)->symbols[0]; 2818f3e7eeaSChristopher Kilgour (*pkt)->access_address |= (*pkt)->symbols[1] << 8; 2828f3e7eeaSChristopher Kilgour (*pkt)->access_address |= (*pkt)->symbols[2] << 16; 2838f3e7eeaSChristopher Kilgour (*pkt)->access_address |= (*pkt)->symbols[3] << 24; 2848f3e7eeaSChristopher Kilgour 2858f3e7eeaSChristopher Kilgour if (lell_packet_is_data(*pkt)) { 2868f3e7eeaSChristopher Kilgour // data PDU 2878f3e7eeaSChristopher Kilgour (*pkt)->length = (*pkt)->symbols[5] & 0x1f; 2888f3e7eeaSChristopher Kilgour (*pkt)->access_address_offenses = aa_data_channel_offenses((*pkt)->access_address); 2898f3e7eeaSChristopher Kilgour (*pkt)->flags.as_bits.access_address_ok = (*pkt)->access_address_offenses ? 0 : 1; 2908f3e7eeaSChristopher Kilgour } else { 2918f3e7eeaSChristopher Kilgour // advertising PDU 2928f3e7eeaSChristopher Kilgour (*pkt)->length = (*pkt)->symbols[5] & 0x3f; 2938f3e7eeaSChristopher Kilgour (*pkt)->adv_type = (*pkt)->symbols[4] & 0xf; 2948f3e7eeaSChristopher Kilgour (*pkt)->adv_tx_add = (*pkt)->symbols[4] & 0x40 ? 1 : 0; 2958f3e7eeaSChristopher Kilgour (*pkt)->adv_rx_add = (*pkt)->symbols[4] & 0x80 ? 1 : 0; 2968f3e7eeaSChristopher Kilgour (*pkt)->flags.as_bits.access_address_ok = ((*pkt)->access_address == 0x8e89bed6); 2978f3e7eeaSChristopher Kilgour (*pkt)->access_address_offenses = (*pkt)->flags.as_bits.access_address_ok ? 0 : 2988f3e7eeaSChristopher Kilgour (aa_access_channel_off_by_one((*pkt)->access_address) ? 1 : 32); 2998f3e7eeaSChristopher Kilgour } 3008f3e7eeaSChristopher Kilgour } 3018f3e7eeaSChristopher Kilgour 3028f3e7eeaSChristopher Kilgour unsigned lell_packet_is_data(const lell_packet *pkt) 3038f3e7eeaSChristopher Kilgour { 3048f3e7eeaSChristopher Kilgour return (unsigned) (pkt->channel_idx < 37); 3058f3e7eeaSChristopher Kilgour } 3068f3e7eeaSChristopher Kilgour 3078f3e7eeaSChristopher Kilgour uint32_t lell_get_access_address(const lell_packet *pkt) 3088f3e7eeaSChristopher Kilgour { 3098f3e7eeaSChristopher Kilgour return pkt->access_address; 3108f3e7eeaSChristopher Kilgour } 3118f3e7eeaSChristopher Kilgour 3128f3e7eeaSChristopher Kilgour unsigned lell_get_access_address_offenses(const lell_packet *pkt) 3138f3e7eeaSChristopher Kilgour { 3148f3e7eeaSChristopher Kilgour return pkt->access_address_offenses; 3158f3e7eeaSChristopher Kilgour } 3168f3e7eeaSChristopher Kilgour 3178f3e7eeaSChristopher Kilgour unsigned lell_get_channel_index(const lell_packet *pkt) 3188f3e7eeaSChristopher Kilgour { 3198f3e7eeaSChristopher Kilgour return pkt->channel_idx; 3208f3e7eeaSChristopher Kilgour } 3218f3e7eeaSChristopher Kilgour 3228f3e7eeaSChristopher Kilgour unsigned lell_get_channel_k(const lell_packet *pkt) 3238f3e7eeaSChristopher Kilgour { 3248f3e7eeaSChristopher Kilgour return pkt->channel_k; 3258f3e7eeaSChristopher Kilgour } 3268f3e7eeaSChristopher Kilgour 3278f3e7eeaSChristopher Kilgour const char * lell_get_adv_type_str(const lell_packet *pkt) 3288f3e7eeaSChristopher Kilgour { 3298f3e7eeaSChristopher Kilgour if (lell_packet_is_data(pkt)) 330e25b118aSDominic Spill return NULL; 3318f3e7eeaSChristopher Kilgour if (pkt->adv_type < COUNT_OF(ADV_TYPE_NAMES)) 3328f3e7eeaSChristopher Kilgour return ADV_TYPE_NAMES[pkt->adv_type]; 333e25b118aSDominic Spill return "UNKNOWN"; 334e25b118aSDominic Spill } 335e25b118aSDominic Spill 3368f3e7eeaSChristopher Kilgour static void _dump_addr(const char *name, const uint8_t *buf, int offset, int random) { 337e25b118aSDominic Spill int i; 338e25b118aSDominic Spill printf(" %s%02x", name, buf[offset+5]); 339e25b118aSDominic Spill for (i = 4; i >= 0; --i) 340e25b118aSDominic Spill printf(":%02x", buf[offset+i]); 341e25b118aSDominic Spill printf(" (%s)\n", random ? "random" : "public"); 342e25b118aSDominic Spill } 343e25b118aSDominic Spill 3448f3e7eeaSChristopher Kilgour static void _dump_8(const char *name, const uint8_t *buf, int offset) { 345e25b118aSDominic Spill printf(" %s%02x (%d)\n", name, buf[offset], buf[offset]); 346e25b118aSDominic Spill } 347e25b118aSDominic Spill 3488f3e7eeaSChristopher Kilgour static void _dump_16(const char *name, const uint8_t *buf, int offset) { 349e25b118aSDominic Spill uint16_t val = buf[offset+1] << 8 | buf[offset]; 350e25b118aSDominic Spill printf(" %s%04x (%d)\n", name, val, val); 351e25b118aSDominic Spill } 352e25b118aSDominic Spill 3538f3e7eeaSChristopher Kilgour static void _dump_24(const char *name, const uint8_t *buf, int offset) { 354e25b118aSDominic Spill uint16_t val = buf[offset+2] << 16 | buf[offset+1] << 8 | buf[offset]; 355e25b118aSDominic Spill printf(" %s%06x\n", name, val); 356e25b118aSDominic Spill } 357e25b118aSDominic Spill 3588f3e7eeaSChristopher Kilgour static void _dump_32(const char *name, const uint8_t *buf, int offset) { 359e25b118aSDominic Spill uint32_t val = buf[offset+3] << 24 | 360e25b118aSDominic Spill buf[offset+2] << 16 | 361e25b118aSDominic Spill buf[offset+1] << 8 | 362e25b118aSDominic Spill buf[offset+0]; 363e25b118aSDominic Spill printf(" %s%08x\n", name, val); 364e25b118aSDominic Spill } 365e25b118aSDominic Spill 3668f3e7eeaSChristopher Kilgour static void _dump_uuid(const uint8_t *uuid) { 367e25b118aSDominic Spill int i; 368e25b118aSDominic Spill for (i = 0; i < 4; ++i) 369e25b118aSDominic Spill printf("%02x", uuid[i]); 370e25b118aSDominic Spill printf("-"); 371e25b118aSDominic Spill for (i = 4; i < 6; ++i) 372e25b118aSDominic Spill printf("%02x", uuid[i]); 373e25b118aSDominic Spill printf("-"); 374e25b118aSDominic Spill for (i = 6; i < 8; ++i) 375e25b118aSDominic Spill printf("%02x", uuid[i]); 376e25b118aSDominic Spill printf("-"); 377e25b118aSDominic Spill for (i = 8; i < 10; ++i) 378e25b118aSDominic Spill printf("%02x", uuid[i]); 379e25b118aSDominic Spill printf("-"); 380e25b118aSDominic Spill for (i = 10; i < 16; ++i) 381e25b118aSDominic Spill printf("%02x", uuid[i]); 382e25b118aSDominic Spill } 383e25b118aSDominic Spill 384e25b118aSDominic Spill // Refer to pg 1735 of Bluetooth Core Spec 4.0 3858f3e7eeaSChristopher Kilgour static void _dump_scan_rsp_data(const uint8_t *buf, int len) { 386e25b118aSDominic Spill int pos = 0; 387e25b118aSDominic Spill int sublen, i; 388e25b118aSDominic Spill uint8_t type; 389e25b118aSDominic Spill uint16_t val; 390e25b118aSDominic Spill char *cval; 391e25b118aSDominic Spill 392e25b118aSDominic Spill while (pos < len) { 393e25b118aSDominic Spill sublen = buf[pos]; 394e25b118aSDominic Spill ++pos; 395e25b118aSDominic Spill if (pos + sublen > len) { 396e25b118aSDominic Spill printf("Error: attempt to read past end of buffer (%d + %d > %d)\n", pos, sublen, len); 397e25b118aSDominic Spill return; 398e25b118aSDominic Spill } 399e25b118aSDominic Spill if (sublen == 0) { 400e25b118aSDominic Spill printf("Early return due to 0 length\n"); 401e25b118aSDominic Spill return; 402e25b118aSDominic Spill } 403e25b118aSDominic Spill type = buf[pos]; 404e25b118aSDominic Spill printf(" Type %02x", type); 405e25b118aSDominic Spill switch (type) { 406e25b118aSDominic Spill case 0x01: 407e25b118aSDominic Spill printf(" (Flags)\n"); 408e25b118aSDominic Spill printf(" "); 409e25b118aSDominic Spill for (i = 0; i < 8; ++i) 410e25b118aSDominic Spill printf("%d", buf[pos+1] & (1 << (7-i)) ? 1 : 0); 411e25b118aSDominic Spill printf("\n"); 412e25b118aSDominic Spill break; 41345000095SMike Ryan case 0x06: 41445000095SMike Ryan printf(" (128-bit Service UUIDs, more available)\n"); 41545000095SMike Ryan goto print128; 416e25b118aSDominic Spill case 0x07: 417e25b118aSDominic Spill printf(" (128-bit Service UUIDs)\n"); 41845000095SMike Ryan print128: 419e25b118aSDominic Spill if ((sublen - 1) % 16 == 0) { 420e25b118aSDominic Spill uint8_t uuid[16]; 421e25b118aSDominic Spill for (i = 0; i < sublen - 1; ++i) { 422e25b118aSDominic Spill uuid[15 - (i % 16)] = buf[pos+1+i]; 423e25b118aSDominic Spill if ((i & 15) == 15) { 424e25b118aSDominic Spill printf(" "); 425e25b118aSDominic Spill _dump_uuid(uuid); 426e25b118aSDominic Spill printf("\n"); 427e25b118aSDominic Spill } 428e25b118aSDominic Spill } 429e25b118aSDominic Spill } 430e25b118aSDominic Spill else { 431e25b118aSDominic Spill printf("Wrong length (%d, must be divisible by 16)\n", sublen-1); 432e25b118aSDominic Spill } 433e25b118aSDominic Spill break; 434e25b118aSDominic Spill case 0x09: 435e25b118aSDominic Spill printf(" (Complete Local Name)\n"); 436e25b118aSDominic Spill printf(" "); 437e25b118aSDominic Spill for (i = 1; i < sublen; ++i) 438e25b118aSDominic Spill printf("%c", isprint(buf[pos+i]) ? buf[pos+i] : '.'); 439e25b118aSDominic Spill printf("\n"); 440e25b118aSDominic Spill break; 441e25b118aSDominic Spill case 0x0a: 442e25b118aSDominic Spill printf(" (Tx Power Level)\n"); 443e25b118aSDominic Spill printf(" "); 444e25b118aSDominic Spill if (sublen-1 == 1) { 445e25b118aSDominic Spill cval = (char *)&buf[pos+1]; 446e25b118aSDominic Spill printf("%d dBm\n", *cval); 447e25b118aSDominic Spill } else { 448e25b118aSDominic Spill printf("Wrong length (%d, should be 1)\n", sublen-1); 449e25b118aSDominic Spill } 450e25b118aSDominic Spill break; 451e25b118aSDominic Spill case 0x12: 452e25b118aSDominic Spill printf(" (Slave Connection Interval Range)\n"); 453e25b118aSDominic Spill printf(" "); 454e25b118aSDominic Spill if (sublen-1 == 4) { 455e25b118aSDominic Spill val = (buf[pos+2] << 8) | buf[pos+1]; 456e25b118aSDominic Spill printf("(%0.2f, ", val * 1.25); 457e25b118aSDominic Spill val = (buf[pos+4] << 8) | buf[pos+3]; 458e25b118aSDominic Spill printf("%0.2f) ms\n", val * 1.25); 459e25b118aSDominic Spill } 460e25b118aSDominic Spill else { 461e25b118aSDominic Spill printf("Wrong length (%d, should be 4)\n", sublen-1); 462e25b118aSDominic Spill } 463e25b118aSDominic Spill break; 464e25b118aSDominic Spill case 0x16: 465e25b118aSDominic Spill printf(" (Service Data)\n"); 466e25b118aSDominic Spill printf(" "); 467e25b118aSDominic Spill if (sublen-1 >= 2) { 468e25b118aSDominic Spill val = (buf[pos+2] << 8) | buf[pos+1]; 469e25b118aSDominic Spill printf("UUID: %02x", val); 470e25b118aSDominic Spill if (sublen-1 > 2) { 471e25b118aSDominic Spill printf(", Additional:"); 472e25b118aSDominic Spill for (i = 3; i < sublen; ++i) 473e25b118aSDominic Spill printf(" %02x", buf[pos+i]); 474e25b118aSDominic Spill } 475e25b118aSDominic Spill printf("\n"); 476e25b118aSDominic Spill } 477e25b118aSDominic Spill else { 478e25b118aSDominic Spill printf("Wrong length (%d, should be >= 2)\n", sublen-1); 479e25b118aSDominic Spill } 480e25b118aSDominic Spill break; 481e25b118aSDominic Spill default: 482e25b118aSDominic Spill printf("\n"); 483e25b118aSDominic Spill printf(" "); 484e25b118aSDominic Spill for (i = 1; i < sublen; ++i) 485e25b118aSDominic Spill printf(" %02x", buf[pos+i]); 486e25b118aSDominic Spill printf("\n"); 487e25b118aSDominic Spill } 488e25b118aSDominic Spill pos += sublen; 489e25b118aSDominic Spill } 490e25b118aSDominic Spill } 491e25b118aSDominic Spill 4928f3e7eeaSChristopher Kilgour void lell_print(const lell_packet *pkt) 4938f3e7eeaSChristopher Kilgour { 494387b6603SIvan Krasin int i, opcode; 4958f3e7eeaSChristopher Kilgour if (lell_packet_is_data(pkt)) { 4968f3e7eeaSChristopher Kilgour int llid = pkt->symbols[4] & 0x3; 497e25b118aSDominic Spill static const char *llid_str[] = { 498e25b118aSDominic Spill "Reserved", 499e25b118aSDominic Spill "LL Data PDU / empty or L2CAP continuation", 500e25b118aSDominic Spill "LL Data PDU / L2CAP start", 501e25b118aSDominic Spill "LL Control PDU", 502e25b118aSDominic Spill }; 503e25b118aSDominic Spill 5048f3e7eeaSChristopher Kilgour printf("Data / AA %08x (%s) / %2d bytes\n", pkt->access_address, 5058f3e7eeaSChristopher Kilgour pkt->flags.as_bits.access_address_ok ? "valid" : "invalid", 5068f3e7eeaSChristopher Kilgour pkt->length); 5078f3e7eeaSChristopher Kilgour printf(" Channel Index: %d\n", pkt->channel_idx); 508e25b118aSDominic Spill printf(" LLID: %d / %s\n", llid, llid_str[llid]); 5098f3e7eeaSChristopher Kilgour printf(" NESN: %d SN: %d MD: %d\n", (pkt->symbols[4] >> 2) & 1, 5108f3e7eeaSChristopher Kilgour (pkt->symbols[4] >> 3) & 1, 5118f3e7eeaSChristopher Kilgour (pkt->symbols[4] >> 4) & 1); 512387b6603SIvan Krasin switch (llid) { 513387b6603SIvan Krasin case 3: // LL Control PDU 5148f3e7eeaSChristopher Kilgour opcode = pkt->symbols[6]; 515387b6603SIvan Krasin static const char *opcode_str[] = { 516387b6603SIvan Krasin "LL_CONNECTION_UPDATE_REQ", 517387b6603SIvan Krasin "LL_CHANNEL_MAP_REQ", 518387b6603SIvan Krasin "LL_TERMINATE_IND", 519387b6603SIvan Krasin "LL_ENC_REQ", 520387b6603SIvan Krasin "LL_ENC_RSP", 521387b6603SIvan Krasin "LL_START_ENC_REQ", 522387b6603SIvan Krasin "LL_START_ENC_RSP", 523387b6603SIvan Krasin "LL_UNKNOWN_RSP", 524387b6603SIvan Krasin "LL_FEATURE_REQ", 525387b6603SIvan Krasin "LL_FEATURE_RSP", 526387b6603SIvan Krasin "LL_PAUSE_ENC_REQ", 527387b6603SIvan Krasin "LL_PAUSE_ENC_RSP", 528387b6603SIvan Krasin "LL_VERSION_IND", 529387b6603SIvan Krasin "LL_REJECT_IND", 530b3394904SMike Ryan "LL_SLAVE_FEATURE_REQ", 531b3394904SMike Ryan "LL_CONNECTION_PARAM_REQ", 532b3394904SMike Ryan "LL_CONNECTION_PARAM_RSP", 533b3394904SMike Ryan "LL_REJECT_IND_EXT", 534b3394904SMike Ryan "LL_PING_REQ", 535b3394904SMike Ryan "LL_PING_RSP", 536387b6603SIvan Krasin "Reserved for Future Use", 537387b6603SIvan Krasin }; 538ba100f3dSMike Ryan printf(" Opcode: %d / %s\n", opcode, opcode_str[(opcode<0x14)?opcode:0x14]); 539387b6603SIvan Krasin break; 540387b6603SIvan Krasin default: 541387b6603SIvan Krasin break; 542387b6603SIvan Krasin } 543e25b118aSDominic Spill } else { 5448f3e7eeaSChristopher Kilgour printf("Advertising / AA %08x (%s)/ %2d bytes\n", pkt->access_address, 5458f3e7eeaSChristopher Kilgour pkt->flags.as_bits.access_address_ok ? "valid" : "invalid", 5468f3e7eeaSChristopher Kilgour pkt->length); 5478f3e7eeaSChristopher Kilgour printf(" Channel Index: %d\n", pkt->channel_idx); 5488f3e7eeaSChristopher Kilgour printf(" Type: %s\n", lell_get_adv_type_str(pkt)); 549e25b118aSDominic Spill 5508f3e7eeaSChristopher Kilgour switch(pkt->adv_type) { 551e25b118aSDominic Spill case ADV_IND: 5528f3e7eeaSChristopher Kilgour _dump_addr("AdvA: ", pkt->symbols, 6, pkt->adv_tx_add); 5538f3e7eeaSChristopher Kilgour if (pkt->length-6 > 0) { 554e25b118aSDominic Spill printf(" AdvData:"); 5558f3e7eeaSChristopher Kilgour for (i = 0; i < pkt->length - 6; ++i) 5568f3e7eeaSChristopher Kilgour printf(" %02x", pkt->symbols[12+i]); 557e25b118aSDominic Spill printf("\n"); 5588f3e7eeaSChristopher Kilgour _dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6); 559e25b118aSDominic Spill } 560e25b118aSDominic Spill break; 561e25b118aSDominic Spill case SCAN_REQ: 5628f3e7eeaSChristopher Kilgour _dump_addr("ScanA: ", pkt->symbols, 6, pkt->adv_tx_add); 5638f3e7eeaSChristopher Kilgour _dump_addr("AdvA: ", pkt->symbols, 12, pkt->adv_rx_add); 564e25b118aSDominic Spill break; 565e25b118aSDominic Spill case SCAN_RSP: 5668f3e7eeaSChristopher Kilgour _dump_addr("AdvA: ", pkt->symbols, 6, pkt->adv_tx_add); 567e25b118aSDominic Spill printf(" ScanRspData:"); 5688f3e7eeaSChristopher Kilgour for (i = 0; i < pkt->length - 6; ++i) 5698f3e7eeaSChristopher Kilgour printf(" %02x", pkt->symbols[12+i]); 570e25b118aSDominic Spill printf("\n"); 5718f3e7eeaSChristopher Kilgour _dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6); 572e25b118aSDominic Spill break; 573e25b118aSDominic Spill case CONNECT_REQ: 5748f3e7eeaSChristopher Kilgour _dump_addr("InitA: ", pkt->symbols, 6, pkt->adv_tx_add); 5758f3e7eeaSChristopher Kilgour _dump_addr("AdvA: ", pkt->symbols, 12, pkt->adv_rx_add); 5768f3e7eeaSChristopher Kilgour _dump_32("AA: ", pkt->symbols, 18); 5778f3e7eeaSChristopher Kilgour _dump_24("CRCInit: ", pkt->symbols, 22); 5788f3e7eeaSChristopher Kilgour _dump_8("WinSize: ", pkt->symbols, 25); 5798f3e7eeaSChristopher Kilgour _dump_16("WinOffset: ", pkt->symbols, 26); 5808f3e7eeaSChristopher Kilgour _dump_16("Interval: ", pkt->symbols, 28); 5818f3e7eeaSChristopher Kilgour _dump_16("Latency: ", pkt->symbols, 30); 5828f3e7eeaSChristopher Kilgour _dump_16("Timeout: ", pkt->symbols, 32); 583e25b118aSDominic Spill 584e25b118aSDominic Spill printf(" ChM:"); 585e25b118aSDominic Spill for (i = 0; i < 5; ++i) 5868f3e7eeaSChristopher Kilgour printf(" %02x", pkt->symbols[34+i]); 587e25b118aSDominic Spill printf("\n"); 588e25b118aSDominic Spill 5898f3e7eeaSChristopher Kilgour printf(" Hop: %d\n", pkt->symbols[39] & 0x1f); 590e25b118aSDominic Spill printf(" SCA: %d, %s\n", 5918f3e7eeaSChristopher Kilgour pkt->symbols[39] >> 5, 5928f3e7eeaSChristopher Kilgour CONNECT_SCA[pkt->symbols[39] >> 5]); 593e25b118aSDominic Spill break; 594e25b118aSDominic Spill } 595e25b118aSDominic Spill } 596e25b118aSDominic Spill 597e25b118aSDominic Spill printf("\n"); 598e25b118aSDominic Spill printf(" Data: "); 5998f3e7eeaSChristopher Kilgour for (i = 6; i < 6 + pkt->length; ++i) 6008f3e7eeaSChristopher Kilgour printf(" %02x", pkt->symbols[i]); 601e25b118aSDominic Spill printf("\n"); 602e25b118aSDominic Spill 603e25b118aSDominic Spill printf(" CRC: "); 604e25b118aSDominic Spill for (i = 0; i < 3; ++i) 6058f3e7eeaSChristopher Kilgour printf(" %02x", pkt->symbols[6 + pkt->length + i]); 606e25b118aSDominic Spill printf("\n"); 607e25b118aSDominic Spill } 608