1*1fbe4564SMatthias Ringwald#!/usr/bin/env python 2*1fbe4564SMatthias Ringwald# 3*1fbe4564SMatthias Ringwald# Simulate network of Bluetooth Controllers 4*1fbe4564SMatthias Ringwald# 5*1fbe4564SMatthias Ringwald# Each simulated controller has an HCI H4 interface 6*1fbe4564SMatthias Ringwald# Network configuration will be stored in a YAML file or similar 7*1fbe4564SMatthias Ringwald# 8*1fbe4564SMatthias Ringwald# Copyright 2017 BlueKitchen GmbH 9*1fbe4564SMatthias Ringwald# 10*1fbe4564SMatthias Ringwald 11*1fbe4564SMatthias Ringwald 12*1fbe4564SMatthias Ringwaldimport os 13*1fbe4564SMatthias Ringwaldimport pty 14*1fbe4564SMatthias Ringwaldimport select 15*1fbe4564SMatthias Ringwaldimport subprocess 16*1fbe4564SMatthias Ringwaldimport sys 17*1fbe4564SMatthias Ringwaldimport bisect 18*1fbe4564SMatthias Ringwaldimport time 19*1fbe4564SMatthias Ringwald 20*1fbe4564SMatthias Ringwaldfrom Crypto.Cipher import AES 21*1fbe4564SMatthias Ringwaldfrom Crypto import Random 22*1fbe4564SMatthias Ringwald 23*1fbe4564SMatthias Ringwalddef little_endian_read_16(buffer, pos): 24*1fbe4564SMatthias Ringwald return ord(buffer[pos]) + (ord(buffer[pos+1]) << 8) 25*1fbe4564SMatthias Ringwald 26*1fbe4564SMatthias Ringwalddef as_hex(data): 27*1fbe4564SMatthias Ringwald str_list = [] 28*1fbe4564SMatthias Ringwald for byte in data: 29*1fbe4564SMatthias Ringwald str_list.append("{0:02x} ".format(ord(byte))) 30*1fbe4564SMatthias Ringwald return ''.join(str_list) 31*1fbe4564SMatthias Ringwald 32*1fbe4564SMatthias Ringwaldadv_type_names = ['ADV_IND', 'ADV_DIRECT_IND_HIGH', 'ADV_SCAN_IND', 'ADV_NONCONN_IND', 'ADV_DIRECT_IND_LOW'] 33*1fbe4564SMatthias Ringwaldtimers_timeouts = [] 34*1fbe4564SMatthias Ringwaldtimers_callbacks = [] 35*1fbe4564SMatthias Ringwald 36*1fbe4564SMatthias Ringwaldclass H4Parser: 37*1fbe4564SMatthias Ringwald 38*1fbe4564SMatthias Ringwald def __init__(self): 39*1fbe4564SMatthias Ringwald self.packet_type = "NONE" 40*1fbe4564SMatthias Ringwald self.reset() 41*1fbe4564SMatthias Ringwald 42*1fbe4564SMatthias Ringwald def set_packet_handler(self, handler): 43*1fbe4564SMatthias Ringwald self.handler = handler 44*1fbe4564SMatthias Ringwald 45*1fbe4564SMatthias Ringwald def reset(self): 46*1fbe4564SMatthias Ringwald self.bytes_to_read = 1 47*1fbe4564SMatthias Ringwald self.buffer = '' 48*1fbe4564SMatthias Ringwald self.state = "H4_W4_PACKET_TYPE" 49*1fbe4564SMatthias Ringwald 50*1fbe4564SMatthias Ringwald def parse(self, data): 51*1fbe4564SMatthias Ringwald self.buffer += data 52*1fbe4564SMatthias Ringwald self.bytes_to_read -= 1 53*1fbe4564SMatthias Ringwald if self.bytes_to_read == 0: 54*1fbe4564SMatthias Ringwald if self.state == "H4_W4_PACKET_TYPE": 55*1fbe4564SMatthias Ringwald self.buffer = '' 56*1fbe4564SMatthias Ringwald if data == chr(1): 57*1fbe4564SMatthias Ringwald # cmd 58*1fbe4564SMatthias Ringwald self.packet_type = "CMD" 59*1fbe4564SMatthias Ringwald self.state = "W4_CMD_HEADER" 60*1fbe4564SMatthias Ringwald self.bytes_to_read = 3 61*1fbe4564SMatthias Ringwald if data == chr(2): 62*1fbe4564SMatthias Ringwald # acl 63*1fbe4564SMatthias Ringwald self.packet_type = "ACL" 64*1fbe4564SMatthias Ringwald self.state = "W4_ACL_HEADER" 65*1fbe4564SMatthias Ringwald self.bytes_to_read = 4 66*1fbe4564SMatthias Ringwald return 67*1fbe4564SMatthias Ringwald if self.state == "W4_CMD_HEADER": 68*1fbe4564SMatthias Ringwald self.bytes_to_read = ord(self.buffer[2]) 69*1fbe4564SMatthias Ringwald self.state = "H4_W4_PAYLOAD" 70*1fbe4564SMatthias Ringwald if self.bytes_to_read > 0: 71*1fbe4564SMatthias Ringwald return 72*1fbe4564SMatthias Ringwald # fall through to handle payload len = 0 73*1fbe4564SMatthias Ringwald if self.state == "W4_ACL_HEADER": 74*1fbe4564SMatthias Ringwald self.bytes_to_read = little_endian_read_16(buffer, 2) 75*1fbe4564SMatthias Ringwald self.state = "H4_W4_PAYLOAD" 76*1fbe4564SMatthias Ringwald if self.bytes_to_read > 0: 77*1fbe4564SMatthias Ringwald return 78*1fbe4564SMatthias Ringwald # fall through to handle payload len = 0 79*1fbe4564SMatthias Ringwald if self.state == "H4_W4_PAYLOAD": 80*1fbe4564SMatthias Ringwald self.handler(self.packet_type, self.buffer) 81*1fbe4564SMatthias Ringwald self.reset() 82*1fbe4564SMatthias Ringwald return 83*1fbe4564SMatthias Ringwald 84*1fbe4564SMatthias Ringwaldclass HCIController: 85*1fbe4564SMatthias Ringwald 86*1fbe4564SMatthias Ringwald def __init__(self): 87*1fbe4564SMatthias Ringwald self.fd = -1 88*1fbe4564SMatthias Ringwald self.random = Random.new() 89*1fbe4564SMatthias Ringwald self.name = 'BTstack Mesh Simulator' 90*1fbe4564SMatthias Ringwald self.bd_addr = 'aaaaaa' 91*1fbe4564SMatthias Ringwald self.parser = H4Parser() 92*1fbe4564SMatthias Ringwald self.parser.set_packet_handler(self.packet_handler) 93*1fbe4564SMatthias Ringwald self.adv_enabled = 0 94*1fbe4564SMatthias Ringwald self.adv_type = 0 95*1fbe4564SMatthias Ringwald self.adv_interval_min = 0 96*1fbe4564SMatthias Ringwald self.adv_interval_max = 0 97*1fbe4564SMatthias Ringwald self.adv_data = '' 98*1fbe4564SMatthias Ringwald self.scan_enabled = False 99*1fbe4564SMatthias Ringwald 100*1fbe4564SMatthias Ringwald def parse(self, data): 101*1fbe4564SMatthias Ringwald self.parser.parse(data) 102*1fbe4564SMatthias Ringwald 103*1fbe4564SMatthias Ringwald def set_fd(self,fd): 104*1fbe4564SMatthias Ringwald self.fd = fd 105*1fbe4564SMatthias Ringwald 106*1fbe4564SMatthias Ringwald def set_bd_addr(self, bd_addr): 107*1fbe4564SMatthias Ringwald self.bd_addr = bd_addr 108*1fbe4564SMatthias Ringwald 109*1fbe4564SMatthias Ringwald def set_name(self, name): 110*1fbe4564SMatthias Ringwald self.name = name 111*1fbe4564SMatthias Ringwald 112*1fbe4564SMatthias Ringwald def set_adv_handler(self, adv_handler, adv_handler_context): 113*1fbe4564SMatthias Ringwald self.adv_handler = adv_handler 114*1fbe4564SMatthias Ringwald self.adv_handler_context = adv_handler_context 115*1fbe4564SMatthias Ringwald 116*1fbe4564SMatthias Ringwald def is_scanning(self): 117*1fbe4564SMatthias Ringwald return self.scan_enabled 118*1fbe4564SMatthias Ringwald 119*1fbe4564SMatthias Ringwald def emit_command_complete(self, opcode, result): 120*1fbe4564SMatthias Ringwald # type, event, len, num commands, opcode, result 121*1fbe4564SMatthias Ringwald os.write(self.fd, '\x04\x0e' + chr(3 + len(result)) + chr(1) + chr(opcode & 255) + chr(opcode >> 8) + result) 122*1fbe4564SMatthias Ringwald 123*1fbe4564SMatthias Ringwald def emit_adv_report(self, event_type, rssi): 124*1fbe4564SMatthias Ringwald # type, event, len, Subevent_Code, Num_Reports, Event_Type[i], Address_Type[i], Address[i], Length[i], Data[i], RSSI[i] 125*1fbe4564SMatthias Ringwald event = '\x04\x3e' + chr(12 + len(self.adv_data)) + chr(2) + chr(1) + chr(event_type) + chr(0) + self.bd_addr[::-1] + chr(len(self.adv_data)) + self.adv_data + chr(rssi) 126*1fbe4564SMatthias Ringwald self.adv_handler(self.adv_handler_context, event) 127*1fbe4564SMatthias Ringwald 128*1fbe4564SMatthias Ringwald def handle_set_adv_enable(self, enable): 129*1fbe4564SMatthias Ringwald self.adv_enabled = enable 130*1fbe4564SMatthias Ringwald print('Node %s adv enable %u' % (self.name, self.adv_enabled)) 131*1fbe4564SMatthias Ringwald if self.adv_enabled: 132*1fbe4564SMatthias Ringwald add_timer(1, self.handle_adv_timer, self) 133*1fbe4564SMatthias Ringwald else: 134*1fbe4564SMatthias Ringwald remove_timer(self.handle_adv_timer, self) 135*1fbe4564SMatthias Ringwald 136*1fbe4564SMatthias Ringwald def handle_set_adv_data(self, data): 137*1fbe4564SMatthias Ringwald self.adv_data = data 138*1fbe4564SMatthias Ringwald print('Node %s adv data %s' % (self.name, as_hex(self.adv_data))) 139*1fbe4564SMatthias Ringwald 140*1fbe4564SMatthias Ringwald def handle_set_adv_params(self, interval_min, interval_max, adv_type): 141*1fbe4564SMatthias Ringwald self.adv_interval_min = interval_min * 0.625 142*1fbe4564SMatthias Ringwald self.adv_interval_max = interval_max * 0.625 143*1fbe4564SMatthias Ringwald self.adv_type = adv_type 144*1fbe4564SMatthias Ringwald print('Node %s adv interval min/max %u/%u ms, type %s' % (self.name, self.adv_interval_min, self.adv_interval_max, adv_type_names[self.adv_type])) 145*1fbe4564SMatthias Ringwald 146*1fbe4564SMatthias Ringwald def handle_adv_timer(self, context): 147*1fbe4564SMatthias Ringwald if self.adv_enabled: 148*1fbe4564SMatthias Ringwald self.emit_adv_report(0, 0) 149*1fbe4564SMatthias Ringwald add_timer(self.adv_interval_min, self.handle_adv_timer, self) 150*1fbe4564SMatthias Ringwald 151*1fbe4564SMatthias Ringwald def packet_handler(self, packet_type, packet): 152*1fbe4564SMatthias Ringwald opcode = little_endian_read_16(packet, 0) 153*1fbe4564SMatthias Ringwald # print ("%s, opcode 0x%04x" % (self.name, opcode)) 154*1fbe4564SMatthias Ringwald if opcode == 0x0c03: 155*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 156*1fbe4564SMatthias Ringwald return 157*1fbe4564SMatthias Ringwald if opcode == 0x1001: 158*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00\x10\x00\x06\x86\x1d\x06\x0a\x00\x86\x1d') 159*1fbe4564SMatthias Ringwald return 160*1fbe4564SMatthias Ringwald if opcode == 0x0c14: 161*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00' + self.name) 162*1fbe4564SMatthias Ringwald return 163*1fbe4564SMatthias Ringwald if opcode == 0x1002: 164*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00\xff\xff\xff\x03\xfe\xff\xff\xff\xff\xff\xff\xff\xf3\x0f\xe8\xfe\x3f\xf7\x83\xff\x1c\x00\x00\x00\x61\xf7\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 165*1fbe4564SMatthias Ringwald return 166*1fbe4564SMatthias Ringwald if opcode == 0x1009: 167*1fbe4564SMatthias Ringwald # read bd_addr 168*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00' + self.bd_addr[::-1]) 169*1fbe4564SMatthias Ringwald return 170*1fbe4564SMatthias Ringwald if opcode == 0x1005: 171*1fbe4564SMatthias Ringwald # read buffer size 172*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00\x36\x01\x40\x0a\x00\x08\x00') 173*1fbe4564SMatthias Ringwald return 174*1fbe4564SMatthias Ringwald if opcode == 0x1003: 175*1fbe4564SMatthias Ringwald # read local supported features 176*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00\xff\xff\x8f\xfe\xf8\xff\x5b\x87') 177*1fbe4564SMatthias Ringwald return 178*1fbe4564SMatthias Ringwald if opcode == 0x0c01: 179*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 180*1fbe4564SMatthias Ringwald return 181*1fbe4564SMatthias Ringwald if opcode == 0x2002: 182*1fbe4564SMatthias Ringwald # le read buffer size 183*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00\x00\x00\x00') 184*1fbe4564SMatthias Ringwald return 185*1fbe4564SMatthias Ringwald if opcode == 0x200f: 186*1fbe4564SMatthias Ringwald # read whitelist size 187*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00\x19') 188*1fbe4564SMatthias Ringwald return 189*1fbe4564SMatthias Ringwald if opcode == 0x200b: 190*1fbe4564SMatthias Ringwald # set scan parameters 191*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 192*1fbe4564SMatthias Ringwald return 193*1fbe4564SMatthias Ringwald if opcode == 0x200c: 194*1fbe4564SMatthias Ringwald # set scan enabled 195*1fbe4564SMatthias Ringwald self.scan_enabled = ord(packet[3]) 196*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 197*1fbe4564SMatthias Ringwald return 198*1fbe4564SMatthias Ringwald if opcode == 0x0c6d: 199*1fbe4564SMatthias Ringwald # write le host supported 200*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 201*1fbe4564SMatthias Ringwald return 202*1fbe4564SMatthias Ringwald if opcode == 0x2017: 203*1fbe4564SMatthias Ringwald # LE Encrypt - key 16, data 16 204*1fbe4564SMatthias Ringwald key = packet[18:2:-1] 205*1fbe4564SMatthias Ringwald data = packet[35:18:-1] 206*1fbe4564SMatthias Ringwald cipher = AES.new(key) 207*1fbe4564SMatthias Ringwald result = cipher.encrypt(data) 208*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, result[::-1]) 209*1fbe4564SMatthias Ringwald return 210*1fbe4564SMatthias Ringwald if opcode == 0x2018: 211*1fbe4564SMatthias Ringwald # LE Rand 212*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00' + self.random.read(8)) 213*1fbe4564SMatthias Ringwald return 214*1fbe4564SMatthias Ringwald if opcode == 0x2006: 215*1fbe4564SMatthias Ringwald # Set Adv Params 216*1fbe4564SMatthias Ringwald self.handle_set_adv_params(little_endian_read_16(packet,3), little_endian_read_16(packet,5), ord(packet[6])) 217*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 218*1fbe4564SMatthias Ringwald return 219*1fbe4564SMatthias Ringwald if opcode == 0x2008: 220*1fbe4564SMatthias Ringwald # Set Adv Data 221*1fbe4564SMatthias Ringwald len = ord(packet[3]) 222*1fbe4564SMatthias Ringwald self.handle_set_adv_data(packet[4:4+len]) 223*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 224*1fbe4564SMatthias Ringwald return 225*1fbe4564SMatthias Ringwald if opcode == 0x200a: 226*1fbe4564SMatthias Ringwald # Set Adv Enable 227*1fbe4564SMatthias Ringwald self.handle_set_adv_enable(ord(packet[3])) 228*1fbe4564SMatthias Ringwald self.emit_command_complete(opcode, '\x00') 229*1fbe4564SMatthias Ringwald return 230*1fbe4564SMatthias Ringwald print("Opcode 0x%0x not handled!" % opcode) 231*1fbe4564SMatthias Ringwald 232*1fbe4564SMatthias Ringwaldclass Node: 233*1fbe4564SMatthias Ringwald 234*1fbe4564SMatthias Ringwald def __init__(self): 235*1fbe4564SMatthias Ringwald self.name = 'node' 236*1fbe4564SMatthias Ringwald self.master = -1 237*1fbe4564SMatthias Ringwald self.slave = -1 238*1fbe4564SMatthias Ringwald self.slave_ttyname = '' 239*1fbe4564SMatthias Ringwald self.controller = HCIController() 240*1fbe4564SMatthias Ringwald 241*1fbe4564SMatthias Ringwald def set_name(self, name): 242*1fbe4564SMatthias Ringwald self.controller.set_name(name) 243*1fbe4564SMatthias Ringwald self.name = name 244*1fbe4564SMatthias Ringwald 245*1fbe4564SMatthias Ringwald def get_name(self): 246*1fbe4564SMatthias Ringwald return self.name 247*1fbe4564SMatthias Ringwald 248*1fbe4564SMatthias Ringwald def set_bd_addr(self, bd_addr): 249*1fbe4564SMatthias Ringwald self.controller.set_bd_addr(bd_addr) 250*1fbe4564SMatthias Ringwald 251*1fbe4564SMatthias Ringwald def start_process(self): 252*1fbe4564SMatthias Ringwald print('Node: %s' % self.name) 253*1fbe4564SMatthias Ringwald (self.master, self.slave) = pty.openpty() 254*1fbe4564SMatthias Ringwald self.slave_ttyname = os.ttyname(self.slave) 255*1fbe4564SMatthias Ringwald print('- tty %s' % self.slave_ttyname) 256*1fbe4564SMatthias Ringwald print('- fd %u' % self.master) 257*1fbe4564SMatthias Ringwald self.controller.set_fd(self.master) 258*1fbe4564SMatthias Ringwald subprocess.Popen(['./mesh', '-d', self.slave_ttyname]) 259*1fbe4564SMatthias Ringwald 260*1fbe4564SMatthias Ringwald def get_master(self): 261*1fbe4564SMatthias Ringwald return self.master 262*1fbe4564SMatthias Ringwald 263*1fbe4564SMatthias Ringwald def parse(self, c): 264*1fbe4564SMatthias Ringwald self.controller.parse(c) 265*1fbe4564SMatthias Ringwald 266*1fbe4564SMatthias Ringwald def set_adv_handler(self, adv_handler, adv_handler_context): 267*1fbe4564SMatthias Ringwald self.controller.set_adv_handler(adv_handler, adv_handler_context) 268*1fbe4564SMatthias Ringwald 269*1fbe4564SMatthias Ringwald def inject_packet(self, event): 270*1fbe4564SMatthias Ringwald os.write(self.master, event) 271*1fbe4564SMatthias Ringwald 272*1fbe4564SMatthias Ringwald def is_scanning(self): 273*1fbe4564SMatthias Ringwald return self.controller.is_scanning() 274*1fbe4564SMatthias Ringwald 275*1fbe4564SMatthias Ringwalddef get_time_millis(): 276*1fbe4564SMatthias Ringwald return int(round(time.time() * 1000)) 277*1fbe4564SMatthias Ringwald 278*1fbe4564SMatthias Ringwalddef add_timer(timeout_ms, callback, context): 279*1fbe4564SMatthias Ringwald global timers_timeouts 280*1fbe4564SMatthias Ringwald global timers_callbacks 281*1fbe4564SMatthias Ringwald 282*1fbe4564SMatthias Ringwald timeout = get_time_millis() + timeout_ms; 283*1fbe4564SMatthias Ringwald pos = bisect.bisect(timers_timeouts, timeout) 284*1fbe4564SMatthias Ringwald timers_timeouts.insert(pos, timeout) 285*1fbe4564SMatthias Ringwald timers_callbacks.insert(pos, (callback, context)) 286*1fbe4564SMatthias Ringwald 287*1fbe4564SMatthias Ringwalddef remove_timer(callback, context): 288*1fbe4564SMatthias Ringwald if (callback, context) in timers_callbacks: 289*1fbe4564SMatthias Ringwald indices = [timers_callbacks.index(t) for t in timers_callbacks if t[0] == callback and t[1] == context] 290*1fbe4564SMatthias Ringwald index = indices[0] 291*1fbe4564SMatthias Ringwald timers_callbacks.pop(index) 292*1fbe4564SMatthias Ringwald timers_timeouts.pop(index) 293*1fbe4564SMatthias Ringwald 294*1fbe4564SMatthias Ringwalddef run(nodes): 295*1fbe4564SMatthias Ringwald # create map fd -> node 296*1fbe4564SMatthias Ringwald nodes_by_fd = { node.get_master():node for node in nodes} 297*1fbe4564SMatthias Ringwald read_fds = nodes_by_fd.keys() 298*1fbe4564SMatthias Ringwald while True: 299*1fbe4564SMatthias Ringwald # process expired timers 300*1fbe4564SMatthias Ringwald time_ms = get_time_millis() 301*1fbe4564SMatthias Ringwald while len(timers_timeouts) and timers_timeouts[0] < time_ms: 302*1fbe4564SMatthias Ringwald timers_timeouts.pop(0) 303*1fbe4564SMatthias Ringwald (callback,context) = timers_callbacks.pop(0) 304*1fbe4564SMatthias Ringwald callback(context) 305*1fbe4564SMatthias Ringwald # timer timers_timeouts? 306*1fbe4564SMatthias Ringwald if len(timers_timeouts): 307*1fbe4564SMatthias Ringwald timeout = (timers_timeouts[0] - time_ms) / 1000.0 308*1fbe4564SMatthias Ringwald (read_ready, write_ready, exception_ready) = select.select(read_fds,[],[], timeout) 309*1fbe4564SMatthias Ringwald else: 310*1fbe4564SMatthias Ringwald (read_ready, write_ready, exception_ready) = select.select(read_fds,[],[]) 311*1fbe4564SMatthias Ringwald for fd in read_ready: 312*1fbe4564SMatthias Ringwald node = nodes_by_fd[fd] 313*1fbe4564SMatthias Ringwald c = os.read(fd, 1) 314*1fbe4564SMatthias Ringwald node.parse(c) 315*1fbe4564SMatthias Ringwald 316*1fbe4564SMatthias Ringwalddef adv_handler(src_node, event): 317*1fbe4564SMatthias Ringwald global nodes 318*1fbe4564SMatthias Ringwald # print('adv from %s' % src_node.get_name()) 319*1fbe4564SMatthias Ringwald for dst_node in nodes: 320*1fbe4564SMatthias Ringwald if src_node == dst_node: 321*1fbe4564SMatthias Ringwald continue 322*1fbe4564SMatthias Ringwald if not dst_node.is_scanning(): 323*1fbe4564SMatthias Ringwald continue 324*1fbe4564SMatthias Ringwald print('Adv %s -> %s - %s' % (src_node.get_name(), dst_node.get_name(), as_hex(event[14:-1]))) 325*1fbe4564SMatthias Ringwald dst_node.inject_packet(event) 326*1fbe4564SMatthias Ringwald 327*1fbe4564SMatthias Ringwald# parse configuration file passed in via cmd line args 328*1fbe4564SMatthias Ringwald# TODO 329*1fbe4564SMatthias Ringwald 330*1fbe4564SMatthias Ringwaldnode1 = Node() 331*1fbe4564SMatthias Ringwaldnode1.set_name('node_1') 332*1fbe4564SMatthias Ringwaldnode1.set_bd_addr('aaaaaa') 333*1fbe4564SMatthias Ringwaldnode1.set_adv_handler(adv_handler, node1) 334*1fbe4564SMatthias Ringwaldnode1.start_process() 335*1fbe4564SMatthias Ringwald 336*1fbe4564SMatthias Ringwaldnode2 = Node() 337*1fbe4564SMatthias Ringwaldnode2.set_name('node_2') 338*1fbe4564SMatthias Ringwaldnode2.set_bd_addr('bbbbbb') 339*1fbe4564SMatthias Ringwaldnode2.set_adv_handler(adv_handler, node2) 340*1fbe4564SMatthias Ringwaldnode2.start_process() 341*1fbe4564SMatthias Ringwald 342*1fbe4564SMatthias Ringwaldnodes = [node1, node2] 343*1fbe4564SMatthias Ringwald 344*1fbe4564SMatthias Ringwaldrun(nodes) 345