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 request(self, addr, port, path, mtype, payload=''): '''Client request''' if self.context is None: self.context = await aiocoap.Context.create_client_context() req = aiocoap.Message(code=Code.POST, mtype=mtype, payload=payload) uri = 'coap://[%s]:%u%s' % (addr, port, path) req.set_request_uri(uri=uri, set_uri_host=False) logging.debug('tx: %s %s' % (uri, ThreadTLV.sub_tlvs_str(payload))) try: # Workaround for not waiting a response to a non-confirmable request if mtype == aiocoap.NON: try: await asyncio.wait_for(self.context.request(req).response, timeout=0.001) except asyncio.TimeoutError: return else: response = await self.context.request(req).response except: logging.warn('No response from %s', addr) else: logging.debug('rx: %s %s %s' % (addr, response.code, ThreadTLV.sub_tlvs_str(response.payload))) return response.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_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 send_bb_query(self, client, dua, rloc16=None): dua_bytes = ipaddress.IPv6Address(dua).packed payload = ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=dua_bytes).array() if rloc16: payload += ThreadTLV(t=TLV.A_RLOC16, l=2, v=rloc16).array() dst = '%s%%%s' % (db.get('all_domain_bbrs'), db.get('exterior_ifname')) logging.info( 'out %s qry: %s' % (URI.B_BQ, ThreadTLV.sub_tlvs_str(payload))) await client.non_request(dst, DEFS.PORT_BB, URI.B_BQ, payload)
async def send_addr_err(self, dst, mtype, dua, eid_iid): 'Thread 1.2 5.23.3.6.4' dua_bytes = ipaddress.IPv6Address(dua).packed payload = ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=dua_bytes).array() payload += ThreadTLV(t=TLV.A_ML_EID, l=8, v=bytes.fromhex(eid_iid)).array() logging.info('out %s ntf: %s' % (URI.A_AE, ThreadTLV.sub_tlvs_str(payload))) await self.coap_client.request(dst, DEFS.PORT_MM, URI.A_AE, mtype, payload)
async def render_post(self, request): # Incoming TLVs parsing logging.info('in %s ntf: %s' % (URI.B_BMR, ThreadTLV.sub_tlvs_str(request.payload))) # Primary BBR shouldn't receive this message if not 'secondary' 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) ''' # TODO: mantain a Backup Multicast Listeners Table return COAP_NO_RESPONSE
async def send_addr_err(self, dua, eid_iid, dst_iid): 'Thread 1.2 5.23.3.6.4' dua_bytes = ipaddress.IPv6Address(dua).packed payload = ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=dua_bytes).array() payload += ThreadTLV( t=TLV.A_ML_EID, l=8, v=bytes.fromhex(eid_iid)).array() prefix_bytes = ipaddress.IPv6Address( db.get('dongle_prefix').split('/')[0]).packed dst = ipaddress.IPv6Address(prefix_bytes[0:8] + bytes.fromhex(dst_iid)) logging.info( 'out %s ntf: %s' % (URI.A_AE, ThreadTLV.sub_tlvs_str(payload))) await self.ntf_client.con_request(dst.compressed, DEFS.PORT_MM, URI.A_AE, payload)
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 # TODO: 9.4.8.2.9 Caching DUAs Advertised on the Backbone Link # Check if all required TLVs are present dua, eid, rloc16, elapsed, net_name = _get_b_ba_params(request.payload) 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 we have this DUA in our table src_rloc, entry_eid, _, dad = DUA_HNDLR.find_eid(dua) # 9.4.8.2.8 Receipt of Backbone Answer BB.ans if not rloc16: if not entry_eid: # Nothing to do for this EID return COAP_NO_RESPONSE elif dad is True: if entry_eid == eid: # This DUA is still registered somewhere else, inform others 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(src_rloc, aiocoap.CON, dua, eid)) else: # Send ADDR_NTF.ans bbr_rloc16 = ipaddress.IPv6Address(db.get('ncp_rloc')).packed[-2:] # If this BBR NCP originated the addr_qry, send addr_ntf to its # link local address if rloc16 == bbr_rloc16: dst = db.get('ncp_ll') else: dst = THREAD.get_rloc_from_short(db.get('ncp_prefix'), rloc16) asyncio.ensure_future( DUA_HNDLR.send_addr_ntf_ans(dst, dua, eid=eid, rloc16=bbr_rloc16, elapsed=elapsed)) # ACK return aiocoap.Message(mtype=Type.ACK, code=Code.CHANGED)
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 coap_con_request(): req_str = db.get('coap_req') if req_str: db.set('coap_req', '') req = json.loads(req_str.replace("'", '"')) dst = req.get('dst')[0] prt = int(req.get('prt')[0]) uri = req.get('uri')[0] pld = bytes.fromhex(req.get('pld')[0]) logging.info('out %s ntf: %s' % (uri, ThreadTLV.sub_tlvs_str(pld))) asyncio.ensure_future(DUA_HNDLR.ntf_client.con_request(dst, prt, uri, pld))
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 send_ntf_msg(self, dst, port, uri, mode, dua, eid, elapsed, rloc16=None): # Fill TLVs # Target EID TLV payload = ThreadTLV(t=TLV.A_TARGET_EID, l=16, v=ipaddress.IPv6Address(dua).packed).array() # ML-EID TLV payload += ThreadTLV(t=TLV.A_ML_EID, l=8, v=bytes.fromhex(eid)).array() # RLOV16 TLV if rloc16: payload += ThreadTLV(t=TLV.A_RLOC16, l=2, v=rloc16).array() # Time Since Last Transaction TLV payload += ThreadTLV(t=TLV.A_TIME_SINCE_LAST_TRANSACTION, l=4, v=struct.pack('!I', elapsed)).array() # Network Name TLV net_name = db.get('ncp_netname').encode() payload += ThreadTLV(t=TLV.A_NETWORK_NAME, l=len(net_name), v=net_name).array() logging.info('out %s ans: %s' % (uri, ThreadTLV.sub_tlvs_str(payload))) if mode == aiocoap.CON: await self.coap_client.con_request(dst, port, uri, payload) else: await self.coap_client.non_request(dst, port, uri, payload)
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 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 # TODO: 9.4.8.2.9 Caching DUAs Advertised on the Backbone Link # Check if all required TLVs are present dua, eid, _, elapsed, net_name = _get_b_ba_params(request.payload) if None in (dua, eid, elapsed, net_name): return COAP_NO_RESPONSE logging.info('PRO_BB.ntf: DUA=%s, ML-EID=%s, Time=%d, Net Name=%s' % (dua, eid, elapsed, net_name)) # Se if we have this DUA in our table _, entry_eid, entry_elapsed, _ = DUA_HNDLR.find_eid(dua) # 9.4.8.2.4 Receipt of Proactive Backbone Notification PRO_BB.ntf Multicast if not entry_eid: # TODO: 9.4.8.2.9 Caching DUAs Advertised on the Backbone Link return COAP_NO_RESPONSE elif entry_eid == eid: # Case 1: ML-EID IID matches if entry_elapsed < elapsed: # Send PRO_BB.ntf asyncio.ensure_future(DUA_HNDLR.send_pro_bb_ntf(dua)) else: # This DUA has been registered somewhere else more recently # Remove silently DUA_HNDLR.remove_entry(dua=dua) else: # Case 2: ML-EID IID does not match (that is, duplicate address detected) DUA_HNDLR.remove_entry(dua=dua) # Send ADDR_ERR.ntf # Special KiBRA-KiNOS message that triggers a multicast ADDR_ERR.ntf asyncio.ensure_future( DUA_HNDLR.send_addr_err(db.get('ncp_rloc'), aiocoap.NON, dua, eid)) # No ACK 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): 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)
import kibra.database as db import kibra.iptables as IPTABLES import kibra.network as NETWORK import kibra.thread as THREAD from kibra.coapclient import CoapClient from kibra.ktask import Ktask from kibra.tlv import ThreadTLV VALUES = [ THREAD.TLV.D_MAC_ADDRESS, THREAD.TLV.D_ROUTE64, THREAD.TLV.D_LEADER_DATA, THREAD.TLV.D_IPV6_ADRESS_LIST, THREAD.TLV.D_CHILD_TABLE, ] PET_DIAGS = ThreadTLV(t=THREAD.TLV.D_TYPE_LIST, l=len(VALUES), v=VALUES).array() NODE_INACTIVE_MS = 90000 DIAGS_DB = {} def _epoch_ms(): return int(time.mktime(time.localtime()) * 1000) class DIAGS(Ktask): def __init__(self): Ktask.__init__( self, name='diags',
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)) # 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): # 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)
import time import kibra.database as db import kibra.network as NETWORK from kibra.coapclient import CoapClient from kibra.iptables import netmap from kibra.ktask import Ktask from kibra.shell import bash from kibra.thread import DEFS, TLV, URI from kibra.tlv import ThreadTLV VALUES = [ TLV.D_MAC_ADDRESS, TLV.D_ROUTE64, TLV.D_LEADER_DATA, TLV.D_IPV6_ADRESS_LIST, TLV.D_CHILD_TABLE ] PET_DIAGS = ThreadTLV(t=TLV.D_TYPE_LIST, l=len(VALUES), v=VALUES).array() VALUES = [ TLV.C_CHANNEL, TLV.C_PAN_ID, TLV.C_EXTENDED_PAN_ID, TLV.C_NETWORK_NAME, TLV.C_NETWORK_MESH_LOCAL_PREFIX, TLV.C_ACTIVE_TIMESTAMP, TLV.C_SECURITY_POLICY ] PET_ACT_DATASET = ThreadTLV(t=TLV.C_GET, l=len(VALUES), v=VALUES).array() PET_NET_DATA = ThreadTLV(t=TLV.D_TYPE_LIST, l=1, v=[TLV.D_NETWORK_DATA]).array() NODE_INACTIVE_MS = 90000 DIAGS_DB = {}