async def render_post(self, request): # Incoming TLVs parsing logging.info('in %s ntf: %s' % (URI.A_AE, ThreadTLV.sub_tlvs_str(request.payload))) # Message not handled by Secondary BBR if not 'primary' in db.get('bbr_status'): return COAP_NO_RESPONSE # Find sub TLVs dua = None eid = None value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: dua = ipaddress.IPv6Address(bytes(value)) value = ThreadTLV.get_value(request.payload, TLV.A_ML_EID) if value: eid = value.hex() if not dua or not eid: return COAP_NO_RESPONSE # Don't process notifications for different prefixes than DUA dua_prefix = ipaddress.IPv6Address(db.get('prefix').split('/')[0]) if dua.packed[:8] != dua_prefix.packed[:8]: return COAP_NO_RESPONSE # Remove entry if it's registered with different EID entry_eid, _, dad = DUA_HNDLR.find_eid(dua.compressed) if not dad and entry_eid != eid: DUA_HNDLR.remove_entry(dua=dua) return COAP_NO_RESPONSE
def _get_b_ba_params(payload): '''Extract paramters from a BB.ans or PRO_BB.ntf payload''' dua = None eid = None rloc16 = None elapsed = None net_name = None value = ThreadTLV.get_value(payload, TLV.A_TARGET_EID) if value: dua = ipaddress.IPv6Address(bytes(value)).compressed value = ThreadTLV.get_value(payload, TLV.A_ML_EID) if value: eid = value.hex() value = ThreadTLV.get_value(payload, TLV.A_RLOC16) if value: rloc16 = value.hex() value = ThreadTLV.get_value(payload, TLV.A_TIME_SINCE_LAST_TRANSACTION) if value: elapsed = struct.unpack('!I', value)[0] value = ThreadTLV.get_value(payload, TLV.A_NETWORK_NAME) if value: net_name = struct.unpack('%ds' % len(value), value)[0].decode() return dua, eid, rloc16, elapsed, net_name
async def render_post(self, request): req_dua = None status = DMStatus.ST_UNSPEC # Incoming TLVs parsing logging.info('in %s req: %s' % (URI.N_DR, ThreadTLV.sub_tlvs_str(request.payload))) # BBR Primary/Secondary status if 'primary' not in db.get('bbr_status'): status = DMStatus.ST_NOT_PRI elif len(DUA_HNDLR.entries) >= DUA_LIMIT: status = DMStatus.ST_RES_SHRT else: dua = None eid = None elapsed = 0 # ML-EID TLV value = ThreadTLV.get_value(request.payload, TLV.A_ML_EID) if value: eid = value.hex() # Target EID TLV value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: try: req_dua = bytes(value) dua = ipaddress.IPv6Address(req_dua).compressed except: status = DMStatus.ST_INV_ADDR # Time Since Last Transaction TLV value = ThreadTLV.get_value(request.payload, TLV.A_TIME_SINCE_LAST_TRANSACTION) if value: elapsed = struct.unpack('!I', value)[0] if eid and dua: # Thread Harness may force response status if db.get('dua_next_status'): status = int(db.get('dua_next_status')) db.set('dua_next_status', '') elif DUA_HNDLR.reg_update(eid, dua, elapsed): status = DMStatus.ST_SUCESS else: # Duplication detected (resource shortage not contemplated) status = DMStatus.ST_DUP_ADDR # Fill and return the response payload = ThreadTLV(t=TLV.A_STATUS, l=1, v=[status]).array() if req_dua: payload += ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=req_dua).array() logging.info( 'out %s rsp: %s' % (URI.N_DR, ThreadTLV.sub_tlvs_str(payload))) return aiocoap.Message(code=Code.CHANGED, payload=payload)
async def render_post(self, request): # Incoming TLVs parsing logging.info('in %s qry: %s' % (URI.A_AQ, ThreadTLV.sub_tlvs_str(request.payload))) # Message not handled by Secondary BBR if not 'primary' in db.get('bbr_status'): return COAP_NO_RESPONSE # Find sub TLVs dua = None value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: dua = ipaddress.IPv6Address(bytes(value)) if not dua: return COAP_NO_RESPONSE # Don't process requests for different prefixes than DUA dua_prefix = ipaddress.IPv6Address(db.get('prefix').split('/')[0]) if dua.packed[:8] != dua_prefix.packed[:8]: return COAP_NO_RESPONSE # Obtain the RLOC16 from the source's RLOC rloc16 = ipaddress.IPv6Address(request.remote.sockaddr[0]).packed[-2:] # TODO: mantain a cache # Propagate Address Query to the Backbone await DUA_HNDLR.send_bb_query(DUA_HNDLR.coap_client, dua, rloc16) return COAP_NO_RESPONSE
def _parse_active_dataset(self, payload): # No response to /c/ag if payload is None or b'': db.set('dongle_secpol', '0') # Response present else: value = ThreadTLV.get_value(payload, TLV.C_CHANNEL) if value: db.set('dongle_channel', int(value[2])) value = ThreadTLV.get_value(payload, TLV.C_PAN_ID) if value: db.set('dongle_panid', '0x' + ''.join('%02x' % byte for byte in value)) value = ThreadTLV.get_value(payload, TLV.C_EXTENDED_PAN_ID) if value: db.set('dongle_xpanid', '0x' + ''.join('%02x' % byte for byte in value)) value = ThreadTLV.get_value(payload, TLV.C_NETWORK_NAME) if value: db.set('dongle_netname', ''.join('%c' % byte for byte in value)) value = ThreadTLV.get_value(payload, TLV.C_NETWORK_MESH_LOCAL_PREFIX) if value: prefix_bytes = bytes(value) + bytes(8) prefix_addr = ipaddress.IPv6Address(prefix_bytes) db.set('dongle_prefix', prefix_addr.compressed + '/64') value = ThreadTLV.get_value(payload, TLV.C_ACTIVE_TIMESTAMP) value = ThreadTLV.get_value(payload, TLV.C_SECURITY_POLICY) if value: db.set('dongle_secpol', value.hex())
def _parse_net_data(self, tlvs): is_pbbr = False value = ThreadTLV.get_value(tlvs, TLV.D_NETWORK_DATA) if value: for tlv in ThreadTLV.sub_tlvs(value): type_ = tlv.type >> 1 if type_ is TLV.N_SERVICE: # Detect BBR Dataset encoding if (tlv.value[0] >> 7 and tlv.value[1] is 1 and tlv.value[2] is 1): server_tlvs = ThreadTLV.sub_tlvs(tlv.value[3:]) '''BBR is primary if there is only one Server TLV in the BBR Dataset and the RLOC16 is the same as ours''' if len(server_tlvs) == 1: node_rloc = ipaddress.IPv6Address( db.get('dongle_rloc')).packed if node_rloc[14:16] == server_tlvs[0].value[0:2]: is_pbbr = True elif type_ is TLV.N_PREFIX: if db.get('prefix_dhcp') and not db.get('dhcp_aloc'): # Detect DHCPv6 Agent ALOC length = math.ceil(tlv.value[1] / 8) byt_prefix = tlv.value[2:2 + length] + bytes(length) int_prefix = int.from_bytes(byt_prefix, byteorder='big') str_prefix = ipaddress.IPv6Address( int_prefix).compressed str_prefix += '/%s' % tlv.value[1] if str_prefix == db.get('prefix'): # This is the prefix that we announced for subtlv in ThreadTLV.sub_tlvs( tlv.value[(length + 2):]): # TODO: verify that there is a Border Router TLV # matching our RLOC16 and DHCP flag if subtlv.type >> 1 is TLV.N_6LOWPAN_ID: cid = subtlv.value[0] & 0x0f rloc = db.get('dongle_rloc') aloc = list( ipaddress.IPv6Address(rloc).packed) aloc[14] = 0xfc aloc[15] = cid aloc = ipaddress.IPv6Address( bytes(aloc)).compressed db.set('dhcp_aloc', aloc) # Listen to the DHCP ALOC which is going to be # used by BR MTD children netmap(aloc, rloc) if is_pbbr: if 'primary' not in db.get('bbr_status'): logging.info('Setting this BBR as Primary') db.set('bbr_status', 'primary') else: if 'secondary' not in db.get('bbr_status'): logging.info('Setting this BBR as Secondary') db.set('bbr_status', 'secondary')
async def render_post(self, request): # Incoming TLVs parsing logging.info('in %s qry: %s' % (URI.B_BQ, ThreadTLV.sub_tlvs_str(request.payload))) # Message not handled by Secondary BBR if not 'primary' in db.get('bbr_status'): return COAP_NO_RESPONSE dua = None value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: dua = ipaddress.IPv6Address(bytes(value)).compressed rloc16 = ThreadTLV.get_value(request.payload, TLV.A_RLOC16) if not dua: return COAP_NO_RESPONSE # Send BB.ans to the requester src_addr = request.remote.sockaddr[0] await DUA_HNDLR.send_bb_ans(src_addr, dua, rloc16=rloc16) return COAP_NO_RESPONSE
async def render_post(self, request): # Incoming TLVs parsing logging.info('in %s req: %s' % (URI.B_BMR, ThreadTLV.sub_tlvs_str(request.payload))) # Primary BBR shouldn't receive this message if not 'primary' in db.get('bbr_status'): return COAP_NO_RESPONSE # IPv6 Addresses TLV addrs = [] value = ThreadTLV.get_value(request.payload, TLV.A_IPV6_ADDRESSES) if value: _, addrs = Res_N_MR._parse_addrs(value) # Timeout TLV timeout = ThreadTLV.get_value(request.payload, TLV.A_TIMEOUT) # Register valid addresses if addrs and timeout: MCAST_HNDLR.reg_update(addrs, timeout) return COAP_NO_RESPONSE
async def periodic(self): # Check internet connection ''' ping = int( str( bash('ping -c 1 -s 0 -I %s -q 8.8.8.8 > /dev/null ; echo $?' % db.get('exterior_ifname')))) self.br_internet_access = 'online' if ping is 0 else 'offline' ''' # Diags response = await self.petitioner.con_request(self.br_permanent_addr, DEFS.PORT_MM, URI.D_DG, PET_DIAGS) if not response: return # Save BR RLOC16 rloc16 = ThreadTLV.get_value(response, TLV.D_MAC_ADDRESS) # TODO: update dongle_rloc and Linux address if rloc16: self.br_rloc16 = '%02x%02x' % (rloc16[0], rloc16[1]) # More requests if changes found in the network or if some time has # passed current_time = _epoch_ms() if response != self.last_diags or current_time > (self.last_time + NODE_INACTIVE_MS): self.last_diags = response self.last_time = current_time self._parse_diags(response) # Network Data get response = await self.petitioner.con_request( self.br_permanent_addr, DEFS.PORT_MM, URI.D_DG, PET_NET_DATA) self._parse_net_data(response) # Active Data Set get response = await self.petitioner.con_request( self.br_permanent_addr, DEFS.PORT_MM, URI.C_AG, PET_ACT_DATASET) self._parse_active_dataset(response) # Update nodes info # TODO: this is commented not to generate noise in the test captures '''
async def periodic(self): # Network visualization not needed in the Thread Harness if kibra.__harness__: return # Check internet connection access = NETWORK.internet_access() self.br_internet_access = 'online' if access else 'offline' # Diags response = await self.petitioner.con_request(self.br_permanent_addr, THREAD.DEFS.PORT_MM, THREAD.URI.D_DG, PET_DIAGS) if not response: return # Save BR RLOC16 rloc16 = ThreadTLV.get_value(response, THREAD.TLV.D_MAC_ADDRESS) if rloc16: self.br_rloc16 = '%02x%02x' % (rloc16[0], rloc16[1]) # More requests if changes found in the network or if some time has # passed current_time = _epoch_ms() if response != self.last_diags or current_time > (self.last_time + NODE_INACTIVE_MS): self.last_diags = response self.last_time = current_time self._parse_diags(response) # Update nodes info for rloc16 in self.nodes_list: if rloc16 == self.br_rloc16: continue node_rloc = THREAD.get_rloc_from_short(db.get('ncp_prefix'), rloc16) response = await self.petitioner.con_request( node_rloc, THREAD.DEFS.PORT_MM, THREAD.URI.D_DG, PET_DIAGS) self._parse_diags(response) time.sleep(0.2) self._mark_old_nodes()
def _parse_diags(self, tlvs): now = _epoch_ms() json_node_info = {} json_node_info['roles'] = [] json_node_info['routes'] = [] json_node_info['addresses'] = [] json_node_info['children'] = [] leader_rloc16 = None # Address16 TLV value = ThreadTLV.get_value(tlvs, THREAD.TLV.D_MAC_ADDRESS) if value: json_node_info['rloc16'] = '%02x%02x' % (value[0], value[1]) if value[1] == 0: json_node_info['roles'].append('router') else: json_node_info['roles'].append('end-device') else: return # Route 64 TLV value = ThreadTLV.get_value(tlvs, THREAD.TLV.D_ROUTE64) if value: router_id_mask = bin(int.from_bytes(value[1:9], byteorder='big')) router_ids = [ 63 - i for i, v in enumerate(router_id_mask[:1:-1]) if int(v) ][::-1] qualities = value[9:] for router_id in router_ids: if not qualities: break router_quality = int(qualities.pop(0)) q_out = (router_quality & 0xC0) >> 6 q_in = (router_quality & 0x30) >> 4 cost = router_quality & 0x0F if q_in is not 0 and q_out is not 0: json_router_info = {} json_router_info['id'] = '%u' % router_id json_router_info['target'] = '%04x' % (router_id << 10) json_router_info['inCost'] = '%u' % q_in json_node_info['routes'].append(json_router_info) if json_router_info['target'] not in self.nodes_list: self.nodes_list.append(json_router_info['target']) elif q_in is 0 and q_out is 0 and cost is 1: json_node_info['id'] = '%u' % router_id # Leader Data TLV value = ThreadTLV.get_value(tlvs, THREAD.TLV.D_LEADER_DATA) if value: leader_rloc16 = '%04x' % (value[7] << 10) # IPv6 Address List TLV value = ThreadTLV.get_value(tlvs, THREAD.TLV.D_IPV6_ADRESS_LIST) if value: addresses = [value[i:i + 16] for i in range(0, len(value), 16)] for addr in addresses: str_addr = ipaddress.IPv6Address( int.from_bytes(addr, byteorder='big')).compressed json_node_info['addresses'].append(str_addr) # Now process child info, because json_node_info['rloc16'] is needed # Child Table TLV value = ThreadTLV.get_value(tlvs, THREAD.TLV.D_CHILD_TABLE) if value: children = [value[i:i + 3] for i in range(0, len(value), 3)] for child in children: json_child_info = {} rloc_high = bytearray.fromhex(json_node_info['rloc16'])[0] rloc_high |= child[0] & 0x01 json_child_info['rloc16'] = '%02x%02x' % (rloc_high, child[1]) json_child_info['timeout'] = '%u' % ( child[0] >> 3) # TODO: convert to seconds json_node_info['children'].append(json_child_info) # Update other informations if leader_rloc16 and json_node_info['rloc16'] in leader_rloc16: json_node_info['roles'].append('leader') if json_node_info['rloc16'] in self.br_rloc16: json_node_info['roles'].append('border-router') json_node_info['internetAccess'] = self.br_internet_access json_node_info['active'] = 'yes' json_node_info['lastSeen'] = now # Add node to database self._add_node(json_node_info) # Add children to database for child in json_node_info['children']: independent_child = copy.deepcopy(child) independent_child['roles'] = ['end-device'] independent_child['active'] = 'yes' independent_child['lastSeen'] = now self._add_node(independent_child)
async def render_post(self, request): # Incoming TLVs parsing logging.info('in %s ans: %s' % (URI.B_BA, ThreadTLV.sub_tlvs_str(request.payload))) # Message not handled by Secondary BBR if not 'primary' in db.get('bbr_status'): return COAP_NO_RESPONSE dua = None rloc16 = None eid = None elapsed = None net_name = None value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: dua = ipaddress.IPv6Address(bytes(value)).compressed value = ThreadTLV.get_value(request.payload, TLV.A_RLOC16) if value is not None: rloc16 = value.hex() value = ThreadTLV.get_value(request.payload, TLV.A_ML_EID) if value: eid = value.hex() value = ThreadTLV.get_value(request.payload, TLV.A_TIME_SINCE_LAST_TRANSACTION) if value: elapsed = struct.unpack('!I', value)[0] value = ThreadTLV.get_value(request.payload, TLV.A_NETWORK_NAME) if value: net_name = struct.unpack('%ds' % len(value), value)[0].decode() # Check if all required TLVs are present if None in (dua, eid, elapsed, net_name): return COAP_NO_RESPONSE logging.info( 'BB.ans: DUA=%s, ML-EID=%s, Time=%d, Net Name=%s, RLOC16=%s' % (dua, eid, elapsed, net_name, rloc16)) # See if its response to DAD or ADDR_QRY if not rloc16: entry_eid, entry_elapsed, dad = DUA_HNDLR.find_eid(dua) if not entry_eid: # Nothing to do for this EID return COAP_NO_RESPONSE elif dad is True: if entry_elapsed < elapsed: # This DUA is still registered somewhere else DUA_HNDLR.duplicated_found(dua, delete=False) # Send PRO_BB.ntf asyncio.ensure_future(DUA_HNDLR.send_pro_bb_ntf(dua)) else: # Duplication detected during DAD DUA_HNDLR.duplicated_found(dua, delete=True) # Send ADDR_ERR.ntf asyncio.ensure_future( DUA_HNDLR.send_addr_err(dua, entry_eid, eid)) else: # This DUA has been registered somewhere else more recently # Remove silently DUA_HNDLR.remove_entry(dua=dua) else: # Send ADDR_NTF.ans bbr_rloc16 = ipaddress.IPv6Address( db.get('dongle_rloc')).packed[-2:] # If this BBR dongle originated the addr_qry, send addr_ntf to its # link local address if rloc16 == bbr_rloc16: dst = db.get('dongle_ll') else: dst = NETWORK.get_rloc_from_short( db.get('dongle_prefix'), rloc16) asyncio.ensure_future( DUA_HNDLR.send_addr_ntf_ans( dst, dua, eid=eid, rloc16=bbr_rloc16, elapsed=elapsed)) # ACK if request.mtype == aiocoap.NON: return COAP_NO_RESPONSE else: return aiocoap.Message(mtype=Type.ACK, code=Code.CHANGED)
async def render_post(self, request): status = DMStatus.ST_UNSPEC good_addrs = [] bad_addrs = [] # Incoming TLVs parsing in_pload = ThreadTLV.sub_tlvs_str(request.payload) logging.info('in %s req: %s' % (URI.N_MR, in_pload)) # BBR Primary/Secondary status if not 'primary' in db.get('bbr_status'): status = DMStatus.ST_NOT_PRI else: timeout = None comm_sid = None # IPv6 Addresses TLV addrs_value = ThreadTLV.get_value(request.payload, TLV.A_IPV6_ADDRESSES) if addrs_value: status, good_addrs, bad_addrs = Res_N_MR._parse_addrs( addrs_value) # Timeout TLV timeout = ThreadTLV.get_value(request.payload, TLV.A_TIMEOUT) # Commissioner Session ID TLV comm_sid = ThreadTLV.get_value(request.payload, TLV.A_COMMISSIONER_SESSION_ID) # Register valid addresses if good_addrs: if timeout and comm_sid: addr_tout = timeout else: addr_tout = db.get('mlr_timeout') or DEFS.MIN_MLR_TIMEOUT reg_addrs = [] reg_addrs_bytes = [] for addr_bytes in good_addrs: reg_addrs.append( ipaddress.IPv6Address(addr_bytes).compressed) reg_addrs_bytes += addr_bytes MCAST_HNDLR.reg_update(reg_addrs, addr_tout) # Send BMLR.ntf ipv6_addressses_tlv = ThreadTLV( t=TLV.A_IPV6_ADDRESSES, l=16 * len(good_addrs), v=reg_addrs_bytes) timeout_tlv = ThreadTLV( t=TLV.A_TIMEOUT, l=4, v=struct.pack('!I', addr_tout)) payload = ipv6_addressses_tlv.array() + timeout_tlv.array() dst = '%s%%%s' % (db.get('all_network_bbrs'), db.get('exterior_ifname')) client = CoapClient() await client.non_request(dst, DEFS.PORT_BB, URI.B_BMR, payload) client.stop() # Fill and return the response out_pload = ThreadTLV(t=TLV.A_STATUS, l=1, v=[status]).array() addrs_payload = [] for elem in bad_addrs: addrs_payload += elem if bad_addrs: out_pload += ThreadTLV( t=TLV.A_IPV6_ADDRESSES, l=16 * len(bad_addrs), v=bytes(addrs_payload)).array() logging.info( 'out %s rsp: %s' % (URI.N_MR, ThreadTLV.sub_tlvs_str(out_pload))) return aiocoap.Message(code=Code.CHANGED, payload=out_pload)
async def render_post(self, request): req_dua = None status = DMStatus.ST_UNSPEC # Incoming TLVs parsing logging.info('in %s req: %s' % (URI.N_DR, ThreadTLV.sub_tlvs_str(request.payload))) # BBR Primary/Secondary status if not 'primary' in db.get('bbr_status'): status = DMStatus.ST_NOT_PRI elif len(DUA_HNDLR.entries) >= DUA_LIMIT: status = DMStatus.ST_RES_SHRT else: dua = None eid = None elapsed = 0 # Only used for sending ADDR_ERR.ntf in case of DAD finds duplicate src_rloc = request.remote.sockaddr[0] # ML-EID TLV value = ThreadTLV.get_value(request.payload, TLV.A_ML_EID) if value: eid = value.hex() # Target EID TLV value = ThreadTLV.get_value(request.payload, TLV.A_TARGET_EID) if value: try: req_dua = bytes(value) dua = ipaddress.IPv6Address(req_dua).compressed except: status = DMStatus.ST_INV_ADDR # Time Since Last Transaction TLV value = ThreadTLV.get_value(request.payload, TLV.A_TIME_SINCE_LAST_TRANSACTION) if value: elapsed = struct.unpack('!I', value)[0] if eid and dua: # Thread Harness may force response status if kibra.__harness__ and eid == db.get('dua_next_status_eid'): status = int(db.get('dua_next_status')) db.set('dua_next_status_eid', '') db.set('dua_next_status', '') # DUA-TC-17 step 48 if status == 500: return aiocoap.Message(code=Code.INTERNAL_SERVER_ERROR) elif DUA_HNDLR.reg_update(src_rloc, eid, dua, elapsed): status = DMStatus.ST_SUCESS else: # Duplication detected status = DMStatus.ST_DUP_ADDR # Fill and return the response payload = ThreadTLV(t=TLV.A_STATUS, l=1, v=[status]).array() if req_dua: payload += ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=req_dua).array() logging.info('out %s rsp: %s' % (URI.N_DR, ThreadTLV.sub_tlvs_str(payload))) return aiocoap.Message(code=Code.CHANGED, payload=payload)
async def render_post(self, request): status = DMStatus.ST_UNSPEC good_addrs = [] bad_addrs = [] # Incoming TLVs parsing in_pload = ThreadTLV.sub_tlvs_str(request.payload) logging.info('in %s req: %s' % (URI.N_MR, in_pload)) # Thread Harness may force response status mlr_next_status = db.get('mlr_next_status') if kibra.__harness__ and mlr_next_status: status = int(mlr_next_status) db.set('mlr_next_status', '') # Include bad addresses for resources shortage status if status == DMStatus.ST_NOT_PRI: # IPv6 Addresses TLV addrs_value = ThreadTLV.get_value(request.payload, TLV.A_IPV6_ADDRESSES) if addrs_value: _, good, bad = Res_N_MR.parse_addrs(addrs_value) bad_addrs.append(good) bad_addrs.append(bad) # BBR Primary/Secondary status elif not 'primary' in db.get('bbr_status'): status = DMStatus.ST_NOT_PRI # Resources shortage elif len(MCAST_HNDLR.maddrs) >= MULTICAST_LIMIT: status = DMStatus.ST_RES_SHRT # Normal registration else: timeout = None comm_sid = None # IPv6 Addresses TLV addrs_value = ThreadTLV.get_value(request.payload, TLV.A_IPV6_ADDRESSES) if addrs_value: status, good_addrs, bad_addrs = Res_N_MR.parse_addrs( addrs_value) # Timeout TLV timeout = ThreadTLV.get_value(request.payload, TLV.A_TIMEOUT) # Commissioner Session ID TLV comm_sid = ThreadTLV.get_value(request.payload, TLV.A_COMMISSIONER_SESSION_ID) # Register valid addresses if good_addrs: if timeout and comm_sid: addr_tout = struct.unpack('!I', timeout)[0] else: addr_tout = db.get('mlr_timeout') or DEFS.MIN_MLR_TIMEOUT reg_addrs = [] reg_addrs_bytes = [] for addr_bytes in good_addrs: reg_addrs.append( ipaddress.IPv6Address(addr_bytes).compressed) reg_addrs_bytes += addr_bytes MCAST_HNDLR.reg_update(reg_addrs, addr_tout) # Send BMLR.ntf ipv6_addressses_tlv = ThreadTLV(t=TLV.A_IPV6_ADDRESSES, l=16 * len(good_addrs), v=reg_addrs_bytes) timeout_tlv = ThreadTLV(t=TLV.A_TIMEOUT, l=4, v=struct.pack('!I', addr_tout)) net_name = db.get('ncp_netname').encode() network_name_tlv = ThreadTLV(t=TLV.A_NETWORK_NAME, l=len(net_name), v=net_name) payload = (ipv6_addressses_tlv.array() + timeout_tlv.array() + network_name_tlv.array()) dst = '%s%%%s' % (db.get('all_network_bbrs'), db.get('exterior_ifname')) await MCAST_HNDLR.coap_client.non_request( dst, DEFS.PORT_BB, URI.B_BMR, payload) # Fill and return the response out_pload = ThreadTLV(t=TLV.A_STATUS, l=1, v=[status]).array() addrs_payload = [] for elem in bad_addrs: addrs_payload += elem if bad_addrs: out_pload += ThreadTLV(t=TLV.A_IPV6_ADDRESSES, l=16 * len(bad_addrs), v=bytes(addrs_payload)).array() logging.info('out %s rsp: %s' % (URI.N_MR, ThreadTLV.sub_tlvs_str(out_pload))) return aiocoap.Message(code=Code.CHANGED, payload=out_pload)