1#!/usr/bin/env python3 2# BlueKitchen GmbH (c) 2019 3 4# primitive dump for PacketLogger format 5 6# APPLE PacketLogger 7# typedef struct { 8# uint32_t len; 9# uint32_t ts_sec; 10# uint32_t ts_usec; 11# uint8_t type; // 0xfc for note 12# } 13 14#define BLUETOOTH_DATA_TYPE_PB_ADV 0x29 // PB-ADV 15#define BLUETOOTH_DATA_TYPE_MESH_MESSAGE 0x2A // Mesh Message 16#define BLUETOOTH_DATA_TYPE_MESH_BEACON 0x2B // Mesh Beacon 17 18import re 19import sys 20import time 21import datetime 22import struct 23from mesh_crypto import * 24 25# state 26netkeys = {} 27appkeys = {} 28devkey = b'' 29ivi = b'\x00' 30segmented_messages = {} 31 32access_messages = { 33# Foundation 340x0000: 'Config Appkey Add', 350x0001: 'Config Appkey Update', 360x0002: 'Config Composition Data Status', 370x0003: 'Config Model Publication Set', 380x0004: 'Config Health Current Status', 390x0005: 'Config Health Fault Status', 400x0006: 'Config Heartbeat Publication Status', 410x8000: 'Config Appkey Delete', 420x8001: 'Config Appkey Get', 430x8002: 'Config Appkey List', 440x8003: 'Config Appkey Status', 450x8004: 'Config Health Attention Get', 460x8005: 'Config Health Attention Set', 470x8006: 'Config Health Attention Set Unacknowledged', 480x8007: 'Config Health Attention Status', 490x8008: 'Config Composition Data Get', 500x8009: 'Config Beacon Get', 510x800a: 'Config Beacon Set', 520x800b: 'Config Beacon Status', 530x800c: 'Config Default Ttl Get', 540x800d: 'Config Default Ttl Set', 550x800e: 'Config Default Ttl Status', 560x800f: 'Config Friend Get', 570x8010: 'Config Friend Set', 580x8011: 'Config Friend Status', 590x8012: 'Config Gatt Proxy Get', 600x8013: 'Config Gatt Proxy Set', 610x8014: 'Config Gatt Proxy Status', 620x8015: 'Config Key Refresh Phase Get', 630x8016: 'Config Key Refresh Phase Set', 640x8017: 'Config Key Refresh Phase Status', 650x8018: 'Config Model Publication Get', 660x8019: 'Config Model Publication Status', 670x801a: 'Config Model Publication Virtual Address Set', 680x801b: 'Config Model Subscription Add', 690x801c: 'Config Model Subscription Delete', 700x801d: 'Config Model Subscription Delete All', 710x801e: 'Config Model Subscription Overwrite', 720x801f: 'Config Model Subscription Status', 730x8020: 'Config Model Subscription Virtual Address Add', 740x8021: 'Config Model Subscription Virtual Address Delete', 750x8022: 'Config Model Subscription Virtual Address Overwrite', 760x8023: 'Config Network Transmit Get', 770x8024: 'Config Network Transmit Set', 780x8025: 'Config Network Transmit Status', 790x8026: 'Config Relay Get', 800x8027: 'Config Relay Set', 810x8028: 'Config Relay Status', 820x8029: 'Config Sig Model Subscription Get', 830x802a: 'Config Sig Model Subscription List', 840x802b: 'Config Vendor Model Subscription Get', 850x802c: 'Config Vendor Model Subscription List', 860x802d: 'Config Low Power Node Poll Timeout Get', 870x802e: 'Config Low Power Node Poll Timeout Status', 880x802f: 'Config Health Fault Clear', 890x8030: 'Config Health Fault Clear Unacknowledged', 900x8031: 'Config Health Fault Get', 910x8032: 'Config Health Fault Test', 920x8033: 'Config Health Fault Test Unacknowledged', 930x8034: 'Config Health Period Get', 940x8035: 'Config Health Period Set', 950x8036: 'Config Health Period Set Unacknowledged', 960x8037: 'Config Health Period Status', 970x8038: 'Config Heartbeat Publication Get', 980x8039: 'Config Heartbeat Publication Set', 990x803a: 'Config Heartbeat Subscription Get', 1000x803b: 'Config Heartbeat Subscription Set', 1010x803c: 'Config Heartbeat Subscription Status', 1020x803d: 'Config Model App Bind', 1030x803e: 'Config Model App Status', 1040x803f: 'Config Model App Unbind', 1050x8040: 'Config Netkey Add', 1060x8041: 'Config Netkey Delete', 1070x8042: 'Config Netkey Get', 1080x8043: 'Config Netkey List', 1090x8044: 'Config Netkey Status', 1100x8045: 'Config Netkey Update', 1110x8046: 'Config Node Identity Get', 1120x8047: 'Config Node Identity Set', 1130x8048: 'Config Node Identity Status', 1140x8049: 'Config Node Reset', 1150x804a: 'Config Node Reset Status', 1160x804b: 'Config Sig Model App Get', 1170x804c: 'Config Sig Model App List', 1180x804d: 'Config Vendor Model App Get', 1190x804e: 'Config Vendor Model App List', 120# Generic 1210x8201: 'Generic On Off Get', 1220x8202: 'Generic On Off Set', 1230x8203: 'Generic On Off Set Unacknowledged', 1240x8204: 'Generic On Off Status', 1250x8205: 'Generic Level Get', 1260x8206: 'Generic Level Set', 1270x8207: 'Generic Level Set Unacknowledged', 1280x8208: 'Generic Level Status', 1290x8209: 'Generic Delta Set', 1300x820A: 'Generic Delta Set Unacknowledged', 1310x820B: 'Generic Move Set', 1320x820C: 'Generic Move Set Unacknowledged', 133} 134 135# quick hack to convert Access message opcode + names 136tmp_messages =[ 137# 'GENERIC_ON_OFF_GET :0x8201', 138] 139for message in tmp_messages: 140 parts = message.split(':') 141 print ("%s: '%s'," % (parts[1].strip(),parts[0].strip().title().replace('_',' '))) 142 143# helpers 144def read_net_32_from_file(f): 145 data = f.read(4) 146 if len(data) < 4: 147 return -1 148 return struct.unpack('>I', data)[0] 149 150def as_hex(data): 151 return ''.join(["{0:02x} ".format(byte) for byte in data]) 152 153def as_big_endian32(value): 154 return struct.pack('>I', value) 155 156def read_net_16(data): 157 return struct.unpack('>H', data)[0] 158 159def read_net_24(data): 160 return data[0] << 16 | struct.unpack('>H', data[1:3])[0] 161 162def read_net_32(data): 163 return struct.unpack('>I', data)[0] 164 165# log engine - simple pretty printer 166max_indent = 10 167def log_pdu(pdu, indent = 0, in_hide_properties = []): 168 hide_properties = list(in_hide_properties) 169 spaces = ' ' * indent 170 print(spaces + "%-20s %s" % (pdu.type, pdu.summary)) 171 if indent >= max_indent: 172 return 173 for property in pdu.properties: 174 if property.key in hide_properties: 175 continue 176 if isinstance( property.value, int): 177 print (spaces + "|%20s: 0x%x (%u)" % (property.key, property.value, property.value)) 178 elif isinstance( property.value, bytes): 179 print (spaces + "|%20s: %s" % (property.key, as_hex(property.value))) 180 else: 181 print (spaces + "|%20s: %s" % (property.key, str(property.value))) 182 hide_properties.append(property.key) 183 print (spaces + '| data: ' + as_hex(pdu.data)) 184 print (spaces + '----') 185 for origin in pdu.origins: 186 log_pdu(origin, indent + 1, hide_properties) 187 188# classes 189 190class network_key(object): 191 def __init__(self, index, netkey): 192 self.index = index 193 self.netkey = netkey 194 (self.nid, self.encryption_key, self.privacy_key) = k2(netkey, b'\x00') 195 196 def __repr__(self): 197 return ("NetKey-%04x %s: NID %02x Encryption %s Privacy %s" % (self.index, self.netkey.hex(), self.nid, self.encryption_key.hex(), self.privacy_key.hex())) 198 199class application_key(object): 200 def __init__(self, index, appkey): 201 self.index = index 202 self.appkey = appkey 203 self.aid = k4(self.appkey) 204 205 def __repr__(self): 206 return ("AppKey-%04x %s: AID %02x" % (self.index, self.appkey.hex(), self.aid)) 207 208class property(object): 209 def __init__(self, key, value): 210 self.key = key 211 self.value = value 212 213class layer_pdu(object): 214 def __init__(self, pdu_type, pdu_data): 215 self.summary = '' 216 self.src = None 217 self.dst = None 218 self.type = pdu_type 219 self.data = pdu_data 220 self.origins = [] 221 self.properties = [] 222 223 def add_property(self, key, value): 224 self.properties.append(property(key, value)) 225 226class network_pdu(layer_pdu): 227 def __init__(self, pdu_data): 228 super().__init__("Network(unencrpyted)", pdu_data) 229 230 # parse pdu 231 self.ivi = (self.data[1] & 0x80) >> 7 232 self.nid = self.data[0] & 0x7f 233 self.ctl = (self.data[1] & 0x80) == 0x80 234 self.ttl = self.data[1] & 0x7f 235 self.seq = read_net_24(self.data[2:5]) 236 self.src = read_net_16(self.data[5:7]) 237 self.dst = read_net_16(self.data[7:9]) 238 self.lower_transport = self.data[9:] 239 240 # set properties 241 self.add_property('ivi', self.ivi) 242 self.add_property('nid', self.nid) 243 self.add_property('ctl', self.ctl) 244 self.add_property('ttl', self.ttl) 245 self.add_property('seq', self.seq) 246 self.add_property('src', self.src) 247 self.add_property('dst', self.dst) 248 self.add_property('lower_transport', self.lower_transport) 249 250class lower_transport_pdu(layer_pdu): 251 def __init__(self, network_pdu): 252 super().__init__('Lower Transport', network_pdu.lower_transport) 253 254 # inherit properties 255 self.ctl = network_pdu.ctl 256 self.seq = network_pdu.seq 257 self.src = network_pdu.src 258 self.dst = network_pdu.dst 259 self.add_property('ctl', self.ctl) 260 self.add_property('seq', self.seq) 261 self.add_property('src', self.src) 262 self.add_property('dst', self.dst) 263 264 # parse pdu and set propoerties 265 self.seg = (self.data[0] & 0x80) == 0x80 266 self.add_property('seg', self.seg) 267 self.szmic = False 268 if self.ctl: 269 self.opcode = self.data[0] & 0x7f 270 self.add_property('opcode', self.opcode) 271 else: 272 self.aid = self.data[0] & 0x3f 273 self.add_property('aid', self.aid) 274 self.akf = self.data[0] & 0x40 == 0x040 275 self.add_property('akf', self.akf) 276 if self.seg: 277 if not self.ctl: 278 self.szmic = self.data[1] & 0x80 == 0x80 279 self.add_property('szmic', self.szmic) 280 temp_12 = struct.unpack('>H', self.data[1:3])[0] 281 self.seq_zero = (temp_12 >> 2) & 0x1fff 282 self.add_property('seq_zero', self.seq_zero) 283 temp_23 = struct.unpack('>H', self.data[2:4])[0] 284 self.seg_o = (temp_23 >> 5) & 0x1f 285 self.add_property('seg_o', self.seg_o) 286 self.seg_n = temp_23 & 0x1f 287 self.add_property('seg_n', self.seg_n) 288 self.segment = self.data[4:] 289 self.add_property('segment', self.segment) 290 else: 291 self.seq_auth = self.seq 292 self.upper_transport = self.data[1:] 293 self.add_property('upper_transport', self.upper_transport) 294 295class upper_transport_pdu(layer_pdu): 296 def __init__(self, segment): 297 if segment.ctl: 298 super().__init__('Segmented Control', b'') 299 else: 300 super().__init__('Segmented Transport', b'') 301 self.ctl = segment.ctl 302 self.src = segment.src 303 self.dst = segment.dst 304 self.seq = segment.seq 305 self.akf = segment.akf 306 self.aid = segment.aid 307 self.szmic = segment.szmic 308 self.seg_n = segment.seg_n 309 self.seq_zero = segment.seq_zero 310 self.direction = segment.direction 311 # TODO handle seq_zero overrun 312 self.seq_auth = segment.seq & 0xffffe000 | segment.seq_zero 313 self.add_property('seq_auth', self.seq_auth) 314 self.missing = (1 << (segment.seg_n+1)) - 1 315 self.data = b'' 316 self.processed = False 317 self.origins = [] 318 if self.ctl: 319 self.segment_len = 8 320 else: 321 self.segment_len = 12 322 323 self.add_property('src', self.src) 324 self.add_property('dst', self.dst) 325 self.add_property('aid', self.aid) 326 self.add_property('akf', self.akf) 327 self.add_property('segment_len', self.segment_len) 328 self.add_property('dir', self.direction) 329 330 def add_segment(self, network_pdu): 331 self.origins.append(network_pdu) 332 self.missing &= ~ (1 << network_pdu.seg_o) 333 if network_pdu.seg_o == self.seg_n: 334 # last segment, set len 335 self.len = (self.seg_n * self.segment_len) + len(network_pdu.segment) 336 if len(self.data) == 0 and self.complete(): 337 self.reassemble() 338 339 def complete(self): 340 return self.missing == 0 341 342 def reassemble(self): 343 self.data = bytearray(self.len) 344 missing = (1 << (self.seg_n+1)) - 1 345 for pdu in self.origins: 346 if pdu.ctl: 347 continue 348 # copy data 349 pos = pdu.seg_o * self.segment_len 350 self.data[pos:pos+len(pdu.segment)] = pdu.segment 351 # done? 352 missing &= ~ (1 << pdu.seg_o) 353 if missing == 0: 354 break 355 356class access_pdu(layer_pdu): 357 def __init__(self, lower_pdu, data): 358 super().__init__('Access', b'') 359 self.src = lower_pdu.src 360 self.dst = lower_pdu.dst 361 self.akf = lower_pdu.akf 362 self.aid = lower_pdu.aid 363 self.seq_auth = lower_pdu.seq_auth 364 self.data = data 365 self.direction = lower_pdu.direction 366 self.add_property('src', self.src) 367 self.add_property('dst', self.dst) 368 self.add_property('akf', self.akf) 369 self.add_property('aid', self.aid) 370 self.add_property('seq_auth', self.seq_auth) 371 self.add_property('dir', self.direction) 372 373 upper_bits = data[0] >> 6 374 if upper_bits == 0 or upper_bits == 1: 375 self.opcode = data[0] 376 self.opcode_len = 1 377 elif upper_bits == 2: 378 self.opcode = read_net_16(data[0:2]) 379 self.opcode_len = 2 380 elif upper_bits == 3: 381 self.opcode = read_net_24(data[0:3]) 382 self.opcode_len = 3 383 self.add_property('opcode', self.opcode) 384 self.params = data[self.opcode_len:] 385 self.add_property('params', self.params) 386 if self.opcode in access_messages: 387 self.summary = access_messages[self.opcode] 388 389def segmented_message_tag(src, direction): 390 tag = str(src) + direction 391 return tag 392 393def segmented_message_for_pdu(pdu): 394 tag = segmented_message_tag(pdu.src, pdu.direction) 395 if tag in segmented_messages: 396 seg_message = segmented_messages[tag] 397 # check seq zero 398 if pdu.seq_zero == seg_message.seq_zero: 399 return seg_message 400 # print("new segmented message: src %04x, seq_zero %04x" % (pdu.src, pdu.seq_zero)) 401 seg_message = upper_transport_pdu(pdu) 402 segmented_messages[tag] = seg_message 403 return seg_message 404 405def mesh_set_iv_index(iv_index): 406 global ivi 407 ivi = iv_index 408 print ("IV-Index: " + as_big_endian32(ivi).hex()) 409 410# key management 411def mesh_add_netkey(index, netkey): 412 key = network_key(index, netkey) 413 print (key) 414 netkeys[index] = key 415 416def mesh_network_keys_for_nid(nid): 417 for (index, key) in netkeys.items(): 418 if key.nid == nid: 419 yield key 420 421def mesh_set_device_key(key): 422 global devkey 423 print ("DevKey: " + key.hex()) 424 devkey = key 425 426def mesh_add_application_key(index, appkey): 427 key = application_key(index, appkey) 428 print (key) 429 appkeys[index] = key 430 431def mesh_application_keys_for_aid(aid): 432 for (index, key) in appkeys.items(): 433 if key.aid == aid: 434 yield key 435 436def mesh_transport_nonce(pdu, nonce_type): 437 if pdu.szmic: 438 aszmic = 0x80 439 else: 440 aszmic = 0x00 441 return bytes( [nonce_type, aszmic, pdu.seq_auth >> 16, (pdu.seq_auth >> 8) & 0xff, pdu.seq_auth & 0xff, pdu.src >> 8, pdu.src & 0xff, pdu.dst >> 8, pdu.dst & 0xff]) + as_big_endian32(ivi) 442 443def mesh_application_nonce(pdu): 444 return mesh_transport_nonce(pdu, 0x01) 445 446def mesh_device_nonce(pdu): 447 return mesh_transport_nonce(pdu, 0x02) 448 449def mesh_upper_transport_decrypt(message, data): 450 if message.szmic: 451 trans_mic_len = 8 452 else: 453 trans_mic_len = 4 454 ciphertext = data[:-trans_mic_len] 455 trans_mic = data[-trans_mic_len:] 456 decrypted = None 457 if message.akf: 458 nonce = mesh_application_nonce(message) 459 # print( as_hex(nonce)) 460 for key in mesh_application_keys_for_aid(message.aid): 461 decrypted = aes_ccm_decrypt(key.appkey, nonce, ciphertext, b'', trans_mic_len, trans_mic) 462 if decrypted != None: 463 break 464 else: 465 nonce = mesh_device_nonce(message) 466 decrypted = aes_ccm_decrypt(devkey, nonce, ciphertext, b'', trans_mic_len, trans_mic) 467 return decrypted 468 469def mesh_process_control(control_pdu): 470 # TODO decode control message 471 # TODO add Seg Ack to sender access message origins 472 control_pdu.opcode = control_pdu.data[0] 473 control_pdu.add_property('opcode', control_pdu.opcode) 474 control_pdu.obo = (control_pdu.data[1] & 0x80) >> 7 475 control_pdu.add_property('obo', control_pdu.obo) 476 temp_12 = read_net_16(control_pdu.data[1:3]) 477 control_pdu.seq_zero = (temp_12 >> 2) & 0x1fff 478 control_pdu.add_property('seq_zero', control_pdu.seq_zero) 479 control_pdu.block_ack = read_net_32(control_pdu.data[3:7]) 480 control_pdu.add_property('block_ack', control_pdu.block_ack) 481 482 # try to add it to access message 483 inverse_direction = 'RX' 484 if control_pdu.direction == 'RX': 485 inverse_direcgtion = 'TX' 486 tag = segmented_message_tag(control_pdu.dst, inverse_direction) 487 if tag in segmented_messages: 488 seg_message = segmented_messages[tag] 489 seg_message.origins.append(control_pdu) 490 else: 491 log_pdu(control_pdu, 0, []) 492 493def mesh_process_access(access_pdu): 494 log_pdu(access_pdu, 0, []) 495 496def mesh_process_network_pdu_tx(network_pdu_encrypted): 497 498 # network layer - decrypt pdu 499 nid = network_pdu_encrypted.data[0] & 0x7f 500 for key in mesh_network_keys_for_nid(nid): 501 network_pdu_decrypted_data = network_decrypt(network_pdu_encrypted.data, as_big_endian32(ivi), key.encryption_key, key.privacy_key) 502 if network_pdu_decrypted_data != None: 503 break 504 if network_pdu_decrypted_data == None: 505 network_pdu_encrypted.summary = 'No encryption key found' 506 log_pdu(network_pdu_encrypted, 0, []) 507 return 508 509 # decrypted network pdu 510 network_pdu_decrypted = network_pdu(network_pdu_decrypted_data) 511 network_pdu_decrypted.direction = network_pdu_encrypted.direction 512 network_pdu_decrypted.add_property('dir', network_pdu_decrypted.direction) 513 network_pdu_decrypted.origins.append(network_pdu_encrypted) 514 515 # print("network pdu (enc)" + network_pdu_encrypted.data.hex()) 516 # print("network pdu (dec)" + network_pdu_decrypted_data.hex()) 517 518 # lower transport - reassemble 519 lower_transport = lower_transport_pdu(network_pdu_decrypted) 520 lower_transport.direction = network_pdu_decrypted.direction 521 lower_transport.add_property('dir', lower_transport.direction) 522 lower_transport.origins.append(network_pdu_decrypted) 523 524 if lower_transport.seg: 525 message = segmented_message_for_pdu(lower_transport) 526 message.add_segment(lower_transport) 527 if not message.complete(): 528 return 529 if message.processed: 530 return 531 532 message.processed = True 533 if message.ctl: 534 mesh_process_control(message) 535 else: 536 access_payload = mesh_upper_transport_decrypt(message, message.data) 537 if access_payload == None: 538 message.summary = 'No encryption key found' 539 log_pdu(message, 0, []) 540 else: 541 access = access_pdu(message, access_payload) 542 access.direction = message.direction 543 access.origins.append(message) 544 mesh_process_access(access) 545 546 else: 547 # print("lower_transport.ctl = " + str(lower_transport.ctl)) 548 if lower_transport.ctl: 549 control = layer_pdu('Unsegmented Control', lower_transport.data) 550 control.direction = lower_transport.direction 551 control.seq = lower_transport.seq 552 control.src = lower_transport.src 553 control.dst = lower_transport.dst 554 control.ctl = True 555 control.add_property('seq', lower_transport.seq) 556 control.add_property('src', lower_transport.src) 557 control.add_property('dst', lower_transport.dst) 558 control.origins.append(lower_transport) 559 mesh_process_control(control) 560 else: 561 access_payload = mesh_upper_transport_decrypt(lower_transport, lower_transport.upper_transport) 562 if access_payload == None: 563 lower_transport.summary = 'No encryption key found' 564 log_pdu(lower_transport, 0, []) 565 else: 566 access = access_pdu(lower_transport, access_payload) 567 access.add_property('seq_auth', lower_transport.seq) 568 access.direction = lower_transport.direction 569 access.origins.append(lower_transport) 570 mesh_process_access(access) 571 572 573def mesh_process_beacon_pdu(adv_pdu): 574 log_pdu(adv_pdu, 0, []) 575 576def mesh_process_adv(adv_pdu): 577 ad_len = adv_pdu.data[0] - 1 578 ad_type = adv_pdu.data[1] 579 if ad_type == 0x2A: 580 network_pdu_encrypted = layer_pdu("Network(encrypted)", adv_data[2:2+ad_len]) 581 network_pdu_encrypted.add_property('ivi', adv_data[2] >> 7) 582 network_pdu_encrypted.add_property('nid', adv_data[2] & 0x7f) 583 network_pdu_encrypted.add_property('dir', adv_pdu.direction) 584 network_pdu_encrypted.direction = adv_pdu.direction 585 network_pdu_encrypted.origins.append(adv_pdu) 586 mesh_process_network_pdu_tx(network_pdu_encrypted) 587 if ad_type == 0x2b: 588 beacon_pdu = layer_pdu("Beacon", adv_data[2:2+ad_len]) 589 beacon_pdu.origins.append(adv_pdu) 590 mesh_process_beacon_pdu(beacon_pdu) 591 592def mesh_log_completed(): 593 # log left-overs 594 print("\n\nLOG COMPLETE - unfinished segmented messages:") 595 for tag in segmented_messages: 596 message = segmented_messages[tag] 597 if message.processed: 598 continue 599 log_pdu(message, 0, []) 600 601if len(sys.argv) == 1: 602 print ('Dump Mesh PacketLogger file') 603 print ('Copyright 2019, BlueKitchen GmbH') 604 print ('') 605 print ('Usage: ' + sys.argv[0] + 'hci_dump.pklg') 606 exit(0) 607 608infile = sys.argv[1] 609 610with open (infile, 'rb') as fin: 611 pos = 0 612 while True: 613 payload_length = read_net_32_from_file(fin) 614 if payload_length < 0: 615 break 616 ts_sec = read_net_32_from_file(fin) 617 ts_usec = read_net_32_from_file(fin) 618 type = ord(fin.read(1)) 619 packet_len = payload_length - 9; 620 if (packet_len > 66000): 621 print ("Error parsing pklg at offset %u (%x)." % (pos, pos)) 622 break 623 624 packet = fin.read(packet_len) 625 pos = pos + 4 + payload_length 626 # time = "[%s.%03u] " % (datetime.datetime.fromtimestamp(ts_sec).strftime("%Y-%m-%d %H:%M:%S"), ts_usec / 1000) 627 628 if type == 0: 629 # CMD 630 if packet[0] != 0x08: 631 continue 632 if packet[1] != 0x20: 633 continue 634 adv_data = packet[4:] 635 adv_pdu = layer_pdu("ADV(TX)", adv_data) 636 adv_pdu.add_property('dir', 'TX') 637 adv_pdu.direction = 'TX' 638 mesh_process_adv(adv_pdu) 639 640 elif type == 1: 641 # EVT 642 event = packet[0] 643 if event != 0x3e: 644 continue 645 event_len = packet[1] 646 if event_len < 14: 647 continue 648 adv_data = packet[13:-1] 649 adv_pdu = layer_pdu("ADV(RX)", adv_data) 650 adv_pdu.add_property('dir', 'RX') 651 adv_pdu.direction = 'RX' 652 mesh_process_adv(adv_pdu) 653 654 elif type == 0xfc: 655 # LOG 656 log = packet.decode("utf-8") 657 parts = re.match('mesh-iv-index: (.*)', log) 658 if parts and len(parts.groups()) == 1: 659 mesh_set_iv_index(int(parts.groups()[0], 16)) 660 continue 661 parts = re.match('mesh-devkey: (.*)', log) 662 if parts and len(parts.groups()) == 1: 663 mesh_set_device_key(bytes.fromhex(parts.groups()[0])) 664 continue 665 parts = re.match('mesh-appkey-(.*): (.*)', log) 666 if parts and len(parts.groups()) == 2: 667 mesh_add_application_key(int(parts.groups()[0], 16), bytes.fromhex(parts.groups()[1])) 668 continue 669 parts = re.match('mesh-netkey-(.*): (.*)', log) 670 if parts and len(parts.groups()) == 2: 671 mesh_add_netkey(int(parts.groups()[0], 16), bytes.fromhex(parts.groups()[1])) 672 continue 673 674 mesh_log_completed() 675