def test_prop_get(self): """ Unit test of SpinelCodec.prop_get_value. """ mock_stream = MockStream({ # Request: Response "810236": "810636ffff", # get panid = 65535 "810243": "81064300", # get state = detached "81025e": "81065e0f", # mode = 0xF "810202": "8106024f50454e54485245414400", # get version "810247": "81064705000000", # get keysequence "810244": "8106444f70656e54687265616400", # get networkname }) nodeid = 1 use_hdlc = False wpan_api = WpanApi(mock_stream, nodeid, use_hdlc) for prop_id, truth_value in self.VECTOR.iteritems(): value = wpan_api.prop_get_value(prop_id) self.failUnless(value == truth_value)
def test_prop_get(self): """ Unit test of SpinelCodec.prop_get_value. """ mock_stream = MockStream({ # Request: Response "810236": "810636ffff", # get panid = 65535 "810243": "81064300", # get state = detached "81025e": "81065e0f", # mode = 0xF "810202": "8106024f50454e54485245414400", # get version "810247": "81064705000000", # get keysequence "810244": "8106444f70656e54687265616400", # get networkname }) nodeid = 1 use_hdlc = False wpan_api = WpanApi(mock_stream, nodeid, use_hdlc) for prop_id, truth_value in self.VECTOR.iteritems(): value = wpan_api.prop_get_value(prop_id) # print "value "+util.hexify_str(value) # print "truth "+util.hexify_str(truth_value) self.failUnless(value == truth_value)
class NCPTransport(): '''A CoAP Toolkit compatible transport''' CFG_KEY_CHANNEL = 'channel' CFG_KEY_PANID = 'panid' CFG_KEY_RESET = 'reset' CFG_KEY_MASTERKEY = 'masterkey' def __init__(self, port, stream_descriptor, config=None): self._port = port self._stream_descriptor = stream_descriptor.split(":") self._config = config if config is not None else self.get_default_config( ) self._attached = False self._udp6_parser = spinel.ipv6.IPv6PacketFactory( ulpf={ 17: spinel.ipv6.UDPDatagramFactory( udp_header_factory=spinel.ipv6.UDPHeaderFactory(), dst_port_factories={ port: spinel.ipv6.UDPBytesPayloadFactory() }), }) self._receivers = [] @staticmethod def _propid_to_str(propid): for name, value in SPINEL.__dict__.items(): if (name.startswith('PROP_') and value == propid): return name def _set_property(self, *args): for propid, value, py_format in args: logger.debug("Setting property %s to %s", self.__class__._propid_to_str(propid), str(value)) result = self._wpan.prop_set_value(propid, value, py_format) if (result is None): raise Exception("Failed to set property {}".format( self.__class__._propid_to_str(propid))) else: logger.debug("Done") def _get_property(self, *args): return self._wpan.prop_get_value(SPINEL.PROP_LAST_STATUS) def _attach_to_network(self): props = [ (SPINEL.PROP_IPv6_ICMP_PING_OFFLOAD, 1, 'B'), (SPINEL.PROP_THREAD_RLOC16_DEBUG_PASSTHRU, 1, 'B'), (SPINEL.PROP_PHY_CHAN, self._config[self.CFG_KEY_CHANNEL], 'H'), (SPINEL.PROP_MAC_15_4_PANID, self._config[self.CFG_KEY_PANID], 'H'), (SPINEL.PROP_NET_MASTER_KEY, self._config[self.CFG_KEY_MASTERKEY], '16s'), (SPINEL.PROP_NET_IF_UP, 1, 'B'), (SPINEL.PROP_NET_STACK_UP, 1, 'B'), ] self._set_property(*props) while True: role = self._wpan.prop_get_value(SPINEL.PROP_NET_ROLE) if (role != 0): self._attached = True return True time.sleep(1) return False def _wpan_receive(self, prop, value, tid): consumed = False if prop == SPINEL.PROP_STREAM_NET: consumed = True try: pkt = self._udp6_parser.parse(io.BytesIO(value), spinel.common.MessageInfo()) endpoint = collections.namedtuple('endpoint', 'addr port') payload = pkt.upper_layer_protocol.payload.to_bytes() src = endpoint(pkt.ipv6_header.source_address, pkt.upper_layer_protocol.header.src_port) dst = endpoint(pkt.ipv6_header.destination_address, pkt.upper_layer_protocol.header.dst_port) for receiver in self._receivers: receiver.receive(payload, src, dst) except RuntimeError: pass except Exception as e: logging.exception(e) else: logger.warn( "Unexpected property received (PROP_ID: {})".format(prop)) return consumed def _build_udp_datagram(self, saddr, sport, daddr, dport, payload): return spinel.ipv6.IPv6Packet( spinel.ipv6.IPv6Header(saddr, daddr), spinel.ipv6.UDPDatagram(spinel.ipv6.UDPHeader(sport, dport), spinel.ipv6.UDPBytesPayload(payload))) @classmethod def get_default_config(cls): return { cls.CFG_KEY_CHANNEL: 11, cls.CFG_KEY_PANID: 0xabcd, cls.CFG_KEY_RESET: True, cls.CFG_KEY_MASTERKEY: util.hex_to_bytes("00112233445566778899aabbccddeeff") } def add_ip_address(self, ipaddr): valid = 1 preferred = 1 flags = 0 prefix_len = 64 prefix = ipaddress.IPv6Interface(str(ipaddr)) arr = prefix.ip.packed arr += self._wpan.encode_fields('CLLC', prefix_len, valid, preferred, flags) self._wpan.prop_insert_value(SPINEL.PROP_IPV6_ADDRESS_TABLE, arr, str(len(arr)) + 's') logger.debug("Added") def print_addresses(self): logger.info("NCP Thread IPv6 addresses:") for addr in self._wpan.get_ipaddrs(): logger.info(str(addr)) def send(self, payload, dest): if (dest.addr.is_multicast): rloc16 = self._wpan.prop_get_value(SPINEL.PROP_THREAD_RLOC16) # Create an IPv6 Thread RLOC address from mesh-local prefix and RLOC16 MAC address. src_addr = ipaddress.ip_address(self._ml_prefix + b'\x00\x00\x00\xff\xfe\x00' + struct.pack('>H', rloc16)) else: src_addr = self._ml_eid logger.debug("Sending datagram {} {} {} {}".format( src_addr, self._port, dest.addr, dest.port)) try: datagram = self._build_udp_datagram(src_addr, self._port, dest.addr, dest.port, payload) except Exception as e: logging.exception(e) self._wpan.ip_send(datagram.to_bytes()) def register_receiver(self, callback): '''Registers a receiver, that will get all the data received from the transport. The callback function shall be in format: receive_callback(src_addr, src_port, dest_port, dest_addr, payload)''' self._receivers.append(callback) def remove_receiver(self, callback): '''Remove a receiver callback''' self._receivers.remove(callback) def open(self): '''Opens transport for communication.''' self._stream = StreamOpen(self._stream_descriptor[0], self._stream_descriptor[1], False) # FIXME: remove node id from constructor after WpanAPI is refactored self._wpan = WpanApi(self._stream, 666) self._wpan.queue_register(SPINEL.HEADER_DEFAULT) self._wpan.queue_register(SPINEL.HEADER_ASYNC) self._wpan.callback_register(SPINEL.PROP_STREAM_NET, self._wpan_receive) if (self._config[NCPTransport.CFG_KEY_RESET] ) and not self._wpan.cmd_reset(): raise Exception( 'Failed to reset NCP. Please flash connectivity firmware.') logger.info('Attaching to the network') if (not self._attach_to_network()): logger.error("Failed to attach to the network") raise Exception('Unable to attach') self._ml_eid = ipaddress.ip_address( self._wpan.prop_get_value(SPINEL.PROP_IPV6_ML_ADDR)) self._ml_prefix = self._wpan.prop_get_value(SPINEL.PROP_IPV6_ML_PREFIX) logger.info("Done") self.print_addresses() def close(self): '''Closes transport for communication.''' self._wpan.cmd_reset() self._stream.close()
class NCPTransport(): '''A CoAP Toolkit compatible transport''' CFG_KEY_CHANNEL = 'channel' CFG_KEY_PANID = 'panid' CFG_KEY_RESET = 'reset' CFG_KEY_MASTERKEY = 'masterkey' def __init__(self, port, stream_descriptor, config = None): self._port = port self._stream_descriptor = stream_descriptor.split(":") self._config = config if config is not None else self.get_default_config() self._attached = False self._udp6_parser = spinel.ipv6.IPv6PacketFactory( ulpf = { 17: spinel.ipv6.UDPDatagramFactory( udp_header_factory = spinel.ipv6.UDPHeaderFactory(), dst_port_factories = { port: spinel.ipv6.UDPBytesPayloadFactory() } ), }) self._receivers = [] @staticmethod def _propid_to_str(propid): for name, value in SPINEL.__dict__.iteritems(): if (name.startswith('PROP_') and value == propid): return name def _set_property(self, *args): for propid, value, py_format in args: logger.debug("Setting property %s to %s", self.__class__._propid_to_str(propid), str(value)) result = self._wpan.prop_set_value(propid, value, py_format) if (result is None): raise Exception("Failed to set property {}".format(self.__class__._propid_to_str(propid))) else: logger.debug("Done") def _get_property(self, *args): return self._wpan.prop_get_value(SPINEL.PROP_LAST_STATUS) def _attach_to_network(self): props = [ (SPINEL.PROP_IPv6_ICMP_PING_OFFLOAD, 1, 'B'), (SPINEL.PROP_THREAD_RLOC16_DEBUG_PASSTHRU, 1, 'B'), (SPINEL.PROP_PHY_CHAN, self._config[self.CFG_KEY_CHANNEL], 'H'), (SPINEL.PROP_MAC_15_4_PANID, self._config[self.CFG_KEY_PANID], 'H'), (SPINEL.PROP_NET_MASTER_KEY, self._config[self.CFG_KEY_MASTERKEY], '16s'), (SPINEL.PROP_NET_IF_UP, 1, 'B'), (SPINEL.PROP_NET_STACK_UP, 1, 'B'), ] self._set_property(*props) while True: role = self._wpan.prop_get_value(SPINEL.PROP_NET_ROLE) if (role != 0): self._attached = True return True time.sleep(1) return False def _wpan_receive(self, prop, value, tid): consumed = False if prop == SPINEL.PROP_STREAM_NET: consumed = True try: pkt = self._udp6_parser.parse(io.BytesIO(value), spinel.common.MessageInfo()) endpoint = collections.namedtuple('endpoint', 'addr port') payload = str(pkt.upper_layer_protocol.payload.to_bytes()) src = endpoint(pkt.ipv6_header.source_address, pkt.upper_layer_protocol.header.src_port) dst = endpoint(pkt.ipv6_header.destination_address, pkt.upper_layer_protocol.header.dst_port) for receiver in self._receivers: receiver.receive(payload, src, dst) except RuntimeError: pass except Exception as e: logging.exception(e) else: logger.warn("Unexpected property received (PROP_ID: {})".format(prop)) return consumed def _build_udp_datagram(self, saddr, sport, daddr, dport, payload): return spinel.ipv6.IPv6Packet(spinel.ipv6.IPv6Header(saddr, daddr), spinel.ipv6.UDPDatagram( spinel.ipv6.UDPHeader(sport, dport), spinel.ipv6.UDPBytesPayload(payload))) @classmethod def get_default_config(cls): return {cls.CFG_KEY_CHANNEL: 11, cls.CFG_KEY_PANID: 0xabcd, cls.CFG_KEY_RESET: True, cls.CFG_KEY_MASTERKEY: util.hex_to_bytes("00112233445566778899aabbccddeeff")} def add_ip_address(self, ipaddr): valid = 1 preferred = 1 flags = 0 prefix_len = 64 prefix = ipaddress.IPv6Interface(unicode(ipaddr)) arr = prefix.ip.packed arr += self._wpan.encode_fields('CLLC', prefix_len, valid, preferred, flags) self._wpan.prop_insert_value(SPINEL.PROP_IPV6_ADDRESS_TABLE, arr, str(len(arr)) + 's') logger.debug("Added") def print_addresses(self): logger.info("NCP Thread IPv6 addresses:") for addr in self._wpan.get_ipaddrs(): logger.info(unicode(addr)) def send(self, payload, dest): if (dest.addr.is_multicast): rloc16 = self._wpan.prop_get_value(SPINEL.PROP_THREAD_RLOC16) # Create an IPv6 Thread RLOC address from mesh-local prefix and RLOC16 MAC address. src_addr = ipaddress.ip_address(self._ml_prefix + '\x00\x00\x00\xff\xfe\x00' + struct.pack('>H', rloc16)) else: src_addr = self._ml_eid logger.debug("Sending datagram {} {} {} {}".format(src_addr, self._port, dest.addr, dest.port)) try: datagram = self._build_udp_datagram(src_addr, self._port, dest.addr, dest.port, payload) except Exception as e: logging.exception(e) self._wpan.ip_send(str(datagram.to_bytes())) def register_receiver(self, callback): '''Registers a reciever, that will get all the data received from the transport. The callback function shall be in format: receive_callback(src_addr, src_port, dest_port, dest_addr, payload)''' self._receivers.append(callback) def remove_receiver(self, callback): '''Remove a receiver callback''' self._receivers.remove(callback) def open(self): '''Opens transport for communication.''' self._stream = StreamOpen(self._stream_descriptor[0], self._stream_descriptor[1], False) # FIXME: remove node id from constructor after WpanAPI is refactored self._wpan = WpanApi(self._stream, 666) self._wpan.queue_register(SPINEL.HEADER_DEFAULT) self._wpan.queue_register(SPINEL.HEADER_ASYNC) self._wpan.callback_register(SPINEL.PROP_STREAM_NET, self._wpan_receive) if (self._config[NCPTransport.CFG_KEY_RESET]) and not self._wpan.cmd_reset(): raise Exception('Failed to reset NCP. Please flash connectivity firmware.') logger.info('Attaching to the network') if (not self._attach_to_network()): logger.error("Failed to attach to the network") raise Exception('Unable to attach') self._ml_eid = ipaddress.ip_address(self._wpan.prop_get_value(SPINEL.PROP_IPV6_ML_ADDR)) self._ml_prefix = self._wpan.prop_get_value(SPINEL.PROP_IPV6_ML_PREFIX) logger.info("Done") self.print_addresses() def close(self): '''Closes transport for communication.''' self._wpan.cmd_reset() self._stream.close()
def main(): parser = argparse.ArgumentParser() parser.add_argument('rcp', type=str, help='path to rcp executable') args = parser.parse_args() stream = StreamOpen('p', f'{args.rcp} 1', False, 115200, False) wpan_api = WpanApi(stream, 1) assert wpan_api.prop_set_value(SPINEL.PROP_PHY_ENABLED, 1) == 1 ext_addr = b'\x00\x11\x22\x33\x44\x55\x66\x77' assert wpan_api.prop_set_value(SPINEL.PROP_PHY_CHAN, 17) == 17 assert wpan_api.prop_set_value(SPINEL.PROP_MAC_15_4_SADDR, 0x1234, 'H') == 0x1234 assert wpan_api.prop_set_value(SPINEL.PROP_MAC_15_4_LADDR, ext_addr, '8s') == ext_addr assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SRC_MATCH_ENABLED, 1) == 1 while wpan_api.prop_insert_value( SPINEL.PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, ext_addr, '8s') == ext_addr: ext_addr = (int.from_bytes(ext_addr, 'little') + 1).to_bytes( 8, 'little') short_addr = 0x2345 while wpan_api.prop_insert_value(SPINEL.PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, short_addr, 'H') == short_addr: short_addr += 1 assert wpan_api.prop_set_value(SPINEL.PROP_MAC_FILTER_MODE, SPINEL.MAC_FILTER_MODE_PROMISCUOUS) in { SPINEL.MAC_FILTER_MODE_PROMISCUOUS, SPINEL.MAC_FILTER_MODE_MONITOR, } assert wpan_api.prop_set_value(SPINEL.PROP_MAC_RAW_STREAM_ENABLED, 1) == 1 assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SCAN_MASK, 11) == [11] assert wpan_api.prop_set_value( SPINEL.PROP_MAC_SCAN_STATE, SPINEL.SCAN_STATE_ENERGY) == SPINEL.SCAN_STATE_ENERGY wpan_api.cmd_send(SPINEL.CMD_RESET) assert wpan_api.prop_get_value(SPINEL.PROP_PHY_ENABLED) == 0 assert wpan_api.prop_set_value(SPINEL.PROP_PHY_ENABLED, 1) == 1 assert wpan_api.prop_get_value(SPINEL.PROP_PHY_CHAN) == 11 assert wpan_api.prop_get_value(SPINEL.PROP_MAC_15_4_SADDR) == 0xfffe assert wpan_api.prop_get_value(SPINEL.PROP_MAC_15_4_LADDR) == b'\x00' * 8 assert wpan_api.prop_get_value(SPINEL.PROP_MAC_SRC_MATCH_ENABLED) == 0 assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SRC_MATCH_ENABLED, 1) == 1 assert wpan_api.prop_insert_value( SPINEL.PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, ext_addr, '8s') == ext_addr assert wpan_api.prop_insert_value( SPINEL.PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, short_addr, 'H') == short_addr assert wpan_api.prop_get_value( SPINEL.PROP_MAC_FILTER_MODE) == SPINEL.MAC_FILTER_MDOE_NORMAL assert wpan_api.prop_get_value(SPINEL.PROP_MAC_RAW_STREAM_ENABLED) == 0 assert wpan_api.prop_get_value( SPINEL.PROP_MAC_SCAN_STATE) == SPINEL.SCAN_STATE_IDLE