diff --git a/tests/scripts/thread-cert/Cert_9_2_04_ActiveDataset.py b/tests/scripts/thread-cert/Cert_9_2_04_ActiveDataset.py index d831c6dcb..e1d66997f 100755 --- a/tests/scripts/thread-cert/Cert_9_2_04_ActiveDataset.py +++ b/tests/scripts/thread-cert/Cert_9_2_04_ActiveDataset.py @@ -30,6 +30,8 @@ import unittest import thread_cert +from pktverify.consts import MLE_CHILD_ID_RESPONSE, MGMT_ACTIVE_SET_URI, MGMT_ACTIVE_GET_URI +from pktverify.packet_verifier import PacketVerifier COMMISSIONER = 1 LEADER = 2 @@ -40,22 +42,26 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): TOPOLOGY = { COMMISSIONER: { + 'name': 'COMMISSIONER', 'active_dataset': { 'timestamp': 10, 'panid': 0xface, - 'master_key': '000102030405060708090a0b0c0d0e0f' + 'master_key': '00112233445566778899aabbccddeeff' }, 'mode': 'rsdn', - 'router_selection_jitter': 1 + 'router_selection_jitter': 1, + 'allowlist': [LEADER] }, LEADER: { + 'name': 'LEADER', 'active_dataset': { 'timestamp': 10, 'panid': 0xface, - 'master_key': '000102030405060708090a0b0c0d0e0f' + 'master_key': '00112233445566778899aabbccddeeff' }, 'mode': 'rsdn', - 'router_selection_jitter': 1 + 'router_selection_jitter': 1, + 'allowlist': [COMMISSIONER] }, } @@ -71,21 +77,26 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): self.nodes[COMMISSIONER].commissioner_start() self.simulator.go(3) + self.collect_rlocs() + self.collect_leader_aloc(LEADER) + # Step 2 self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=101, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000000', network_name='GRL', ) self.simulator.go(3) self.assertEqual(self.nodes[LEADER].get_network_name(), 'GRL') + self.nodes[COMMISSIONER].send_mgmt_active_get() + self.simulator.go(3) # Step 6 # Attempt to set Channel TLV self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=102, channel=18, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000001', network_name='threadcert', ) @@ -96,7 +107,7 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Attempt to set Mesh Local Prefix TLV self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=103, - channel_mask=0x001ffee0, + channel_mask=0x7fff800, extended_panid='000db70000000000', mesh_local='fd00:0db7::', network_name='UL', @@ -108,11 +119,11 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Attempt to set Network Master Key TLV self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=104, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000000', - master_key='00112233445566778899aabbccddeeff', + master_key='ffeeddccbbaa99887766554433221100', mesh_local='fd00:0db7::', - network_name='UL', + network_name='GRL', ) self.simulator.go(3) self.assertEqual(self.nodes[LEADER].get_network_name(), 'GRL') @@ -121,7 +132,7 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Attempt to set PAN ID TLV self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=105, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000000', master_key='00112233445566778899aabbccddeeff', mesh_local='fd00:0db7::', @@ -135,7 +146,7 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Invalid Commissioner Session ID self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=106, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000000', network_name='UL', binary='0b02abcd', @@ -147,7 +158,7 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Old Active Timestamp self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=101, - channel_mask=0x001fffe0, + channel_mask=0x01fff800, extended_panid='000db70000000000', network_name='UL', ) @@ -158,7 +169,7 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Unexpected Steering Data TLV self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=107, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000000', network_name='UL', binary='0806113320440000', @@ -170,7 +181,7 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): # Undefined TLV self.nodes[COMMISSIONER].send_mgmt_active_set( active_timestamp=108, - channel_mask=0x001fffe0, + channel_mask=0x7fff800, extended_panid='000db70000000000', network_name='GRL', binary='8202aa55', @@ -182,6 +193,131 @@ class Cert_9_2_04_ActiveDataset(thread_cert.TestCase): for ipaddr in ipaddrs: self.assertTrue(self.nodes[LEADER].ping(ipaddr)) + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + LEADER = pv.vars['LEADER'] + LEADER_RLOC = pv.vars['LEADER_RLOC'] + LEADER_ALOC = pv.vars['LEADER_ALOC'] + COMMISSIONER = pv.vars['COMMISSIONER'] + COMMISSIONER_RLOC = pv.vars['COMMISSIONER_RLOC'] + + # Step 1: Ensure the topology is formed correctly + pkts.filter_wpan_src64(LEADER).filter_wpan_dst64(COMMISSIONER).filter_mle_cmd( + MLE_CHILD_ID_RESPONSE).must_next() + + # Step 2: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.xpan_id == '000db70000000000' and p.thread_meshcop.tlv.net_name == 'GRL' + and p.thread_meshcop.tlv.chan_mask_mask == '001fffe0' and p.thread_meshcop.tlv.active_tstamp == 101) + + # Step 3: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == 1) + + # Step 4: Commissioner sends MGMT_ACTIVE_GET.req to Leader + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_GET_URI).must_next() + + # Step 5: The Leader MUST send MGMT_ACTIVE_GET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_GET_URI + ).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 101 and p.thread_meshcop.tlv.xpan_id == '000db70000000000' + and p.thread_meshcop.tlv.net_name == 'GRL' and p.thread_meshcop.tlv.chan_mask_mask == '001fffe0') + + # Step 6: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 102 and p.thread_meshcop.tlv.xpan_id == + '000db70000000001' and p.thread_meshcop.tlv.net_name == 'threadcert' and p.thread_meshcop.tlv. + chan_mask_mask == '001fffe0' and p.thread_meshcop.tlv.channel == 18) + + # Step 7: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == -1) + + # Step 8: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 103 and p.thread_meshcop.tlv.xpan_id == + '000db70000000000' and p.thread_meshcop.tlv.net_name == 'UL' and p.thread_meshcop.tlv.chan_mask_mask == + '001fffe0' and p.thread_meshcop.tlv.ml_prefix == 'fd000db700000000') + + # Step 9: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == -1) + + # Step 10: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 104 and p.thread_meshcop.tlv.xpan_id == + '000db70000000000' and p.thread_meshcop.tlv.net_name == 'GRL' and p.thread_meshcop.tlv.master_key == + 'ffeeddccbbaa99887766554433221100' and p.thread_meshcop.tlv.chan_mask_mask == '001fffe0' and p. + thread_meshcop.tlv.ml_prefix == 'fd000db700000000') + + # Step 11: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == -1) + + # Step 12: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 105 and p.thread_meshcop.tlv.xpan_id == + '000db70000000000' and p.thread_meshcop.tlv.net_name == 'UL' and p.thread_meshcop.tlv.master_key == + '00112233445566778899aabbccddeeff' and p.thread_meshcop.tlv.pan_id == 0xafce and p.thread_meshcop.tlv. + chan_mask_mask == '001fffe0' and p.thread_meshcop.tlv.ml_prefix == 'fd000db700000000') + + # Step 13: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == -1) + + # Step 14: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 106 and p.thread_meshcop.tlv.xpan_id == + '000db70000000000' and p.thread_meshcop.tlv.net_name == 'UL' and p.thread_meshcop.tlv. + commissioner_sess_id == 0xabcd and p.thread_meshcop.tlv.chan_mask_mask == '001fffe0') + + # Step 15: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == -1) + + # Step 16: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC + ).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 101 and p.thread_meshcop.tlv.xpan_id == '000db70000000000' + and p.thread_meshcop.tlv.net_name == 'UL' and p.thread_meshcop.tlv.chan_mask_mask == '001fff80') + + # Step 17: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == -1) + + # Step 18: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 107 and p.thread_meshcop.tlv.xpan_id == + '000db70000000000' and p.thread_meshcop.tlv.net_name == 'UL' and p.thread_meshcop.tlv.steering_data == + [0x113320440000] and p.thread_meshcop.tlv.chan_mask_mask == '001fffe0') + + # Step 19: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == 1) + + # Step 20: Commissioner sends MGMT_ACTIVE_SET.req to Leader RLOC or Leader Anycast Locator + pkts.filter_wpan_src64(COMMISSIONER).filter_ipv6_2dsts( + LEADER_RLOC, LEADER_ALOC).filter_coap_request(MGMT_ACTIVE_SET_URI).must_next().must_verify( + lambda p: p.thread_meshcop.tlv.active_tstamp == 108 and p.thread_meshcop.tlv.xpan_id == + '000db70000000000' and p.thread_meshcop.tlv.net_name == 'GRL' and p.thread_meshcop.tlv.unknown == + 'aa55' and p.thread_meshcop.tlv.chan_mask_mask == '001fffe0') + + # Step 21: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner + pkts.filter_wpan_src64(LEADER).filter_ipv6_dst(COMMISSIONER_RLOC).filter_coap_ack( + MGMT_ACTIVE_SET_URI).must_next().must_verify(lambda p: p.thread_meshcop.tlv.state == 1) + if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 5cfd075b0..45cf9b8fe 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1248,6 +1248,49 @@ class Node: self.send_command(cmd) self._expect('Done') + def send_mgmt_active_get( + self, + active_timestamp=None, + channel=None, + channel_mask=None, + extended_panid=None, + panid=None, + master_key=None, + mesh_local=None, + network_name=None, + binary=None, + ): + cmd = 'dataset mgmtgetcommand active ' + + if active_timestamp is not None: + cmd += 'activetimestamp %d ' % active_timestamp + + if channel is not None: + cmd += 'channel %d ' % channel + + if channel_mask is not None: + cmd += 'channelmask %d ' % channel_mask + + if extended_panid is not None: + cmd += 'extpanid %s ' % extended_panid + + if panid is not None: + cmd += 'panid %d ' % panid + + if master_key is not None: + cmd += 'masterkey %s ' % master_key + + if mesh_local is not None: + cmd += 'localprefix %s ' % mesh_local + + if network_name is not None: + cmd += 'networkname %s ' % self._escape_escapable(network_name) + + if binary is not None: + cmd += 'binary %s ' % binary + + self.send_command(cmd) + def send_mgmt_pending_set( self, pending_timestamp=None, diff --git a/tests/scripts/thread-cert/pktverify/layer_fields.py b/tests/scripts/thread-cert/pktverify/layer_fields.py index 3a7701d69..45905b5fc 100644 --- a/tests/scripts/thread-cert/pktverify/layer_fields.py +++ b/tests/scripts/thread-cert/pktverify/layer_fields.py @@ -27,6 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. # import sys +import time, datetime from typing import Any, Union from pyshark.packet.fields import LayerFieldsContainer, LayerField @@ -64,10 +65,15 @@ def _auto(v: Union[LayerFieldsContainer, LayerField]): if ':' in dv and '::' not in dv and dv.replace(':', '') == rv: # '88:00', '8800' return int(rv, 16) - if dv.endswith(' CST'): - # e.x. 'Jan 1, 1970 08:00:00.000000000 CST', '0000000000000000' - # todo: check if the time is valid - return int(rv, 16) + # timestamp: 'Jan 1, 1970 08:00:00.000000000 CST', '0000000000000000' + # convert to seconds from 1970, ignore the nanosecond for now since + # there are integer seconds applied in the test cases + try: + time_str = datetime.datetime.strptime(dv, "%b %d, %Y %H:%M:%S.%f000 %Z") + time_in_sec = time.mktime(time_str.utctimetuple()) + return int(time_in_sec) + except (ValueError, TypeError): + pass try: int(rv, 16) @@ -520,7 +526,7 @@ _LAYER_FIELDS = { "thread_meshcop.tlv.chan_mask": _str, # from ble 'thread_meshcop.tlv.chan_mask_page': _auto, 'thread_meshcop.tlv.chan_mask_len': _auto, - 'thread_meshcop.tlv.chan_mask_mask': _auto, + 'thread_meshcop.tlv.chan_mask_mask': _bytes, 'thread_meshcop.tlv.pan_id': _auto, 'thread_meshcop.tlv.xpan_id': _bytes, 'thread_meshcop.tlv.ml_prefix': _bytes, diff --git a/tests/scripts/thread-cert/pktverify/packet_filter.py b/tests/scripts/thread-cert/pktverify/packet_filter.py index 4b99d2449..2db7bdb20 100644 --- a/tests/scripts/thread-cert/pktverify/packet_filter.py +++ b/tests/scripts/thread-cert/pktverify/packet_filter.py @@ -443,6 +443,11 @@ class PacketFilter(object): assert isinstance(addr, (str, Ipv6Addr)) return self.filter(lambda p: p.ipv6.dst == addr, **kwargs) + def filter_ipv6_2dsts(self, addr1, addr2, **kwargs): + assert isinstance(addr1, (str, Ipv6Addr)) + assert isinstance(addr2, (str, Ipv6Addr)) + return self.filter(lambda p: p.ipv6.dst == addr1 or p.ipv6.dst == addr2, **kwargs) + def filter_ipv6_src_dst(self, src_addr, dst_addr, **kwargs): assert isinstance(src_addr, (str, Ipv6Addr)) assert isinstance(dst_addr, (str, Ipv6Addr)) diff --git a/tests/scripts/thread-cert/pktverify/packet_verifier.py b/tests/scripts/thread-cert/pktverify/packet_verifier.py index c10bfb749..d923b9c26 100644 --- a/tests/scripts/thread-cert/pktverify/packet_verifier.py +++ b/tests/scripts/thread-cert/pktverify/packet_verifier.py @@ -148,6 +148,9 @@ class PacketVerifier(object): key = self.test_info.get_node_name(i) + '_RLOC' self._vars[key] = rloc + if self.test_info.leader_aloc: + self._vars['LEADER_ALOC'] = self.test_info.leader_aloc + for k, v in self.test_info.extra_vars.items(): assert k not in self._vars, k logging.info("add extra var: %s = %s", k, v) diff --git a/tests/scripts/thread-cert/pktverify/test_info.py b/tests/scripts/thread-cert/pktverify/test_info.py index 5969de632..c44f52ab5 100644 --- a/tests/scripts/thread-cert/pktverify/test_info.py +++ b/tests/scripts/thread-cert/pktverify/test_info.py @@ -54,6 +54,7 @@ class TestInfo(object): self.rlocs = {int(k): Ipv6Addr(v) for k, v in test_info.get('rlocs', {}).items()} self.rloc16s = self._convert_hex_values(self._convert_keys_to_ints(test_info.get('rloc16s', {}))) self.extra_vars = test_info.get('extra_vars', {}) + self.leader_aloc = Ipv6Addr(test_info.get('leader_aloc')) if 'leader_aloc' in test_info else '' def __str__(self): macs = dict(self.extaddrs) diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index 46bb09428..38500db81 100644 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -269,6 +269,13 @@ class TestCase(NcpSupportMixin, unittest.TestCase): for i, node in self.nodes.items(): test_info['rlocs'][i] = node.get_rloc() + def collect_leader_aloc(self, node): + if not self._do_packet_verification: + return + + test_info = self._test_info + test_info['leader_aloc'] = self.nodes[node].get_addr_leader_aloc() + def collect_extra_vars(self, **vars): if not self._do_packet_verification: return