From 98e70c425c51618752109b8bae017a4ab1e92c54 Mon Sep 17 00:00:00 2001 From: Jing Ma Date: Tue, 15 Dec 2020 00:36:33 +0800 Subject: [PATCH] [thread-cert] add case 9.2.3 ActiveDatasetGet using pktverify (#5949) --- .../Cert_9_2_03_ActiveDatasetGet.py | 268 ++++++++++++++++++ tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/node.py | 48 +--- tests/scripts/thread-cert/pktverify/consts.py | 3 + 4 files changed, 283 insertions(+), 38 deletions(-) create mode 100755 tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py diff --git a/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py b/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py new file mode 100755 index 000000000..78b547a7d --- /dev/null +++ b/tests/scripts/thread-cert/Cert_9_2_03_ActiveDatasetGet.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2020, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import unittest + +import mesh_cop +import thread_cert +from pktverify.consts import MLE_DATA_RESPONSE, MGMT_ACTIVE_GET_URI, NM_CHANNEL_TLV, NM_COMMISSIONER_ID_TLV, NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV, NM_BORDER_AGENT_LOCATOR_TLV, NM_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_NETWORK_MESH_LOCAL_PREFIX_TLV, NM_PSKC_TLV, NM_SCAN_DURATION, NM_ENERGY_LIST_TLV, NM_ACTIVE_TIMESTAMP_TLV, NM_CHANNEL_MASK_TLV, NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_MASTER_KEY_TLV, NM_SECURITY_POLICY_TLV, LEADER_ALOC +from pktverify.packet_verifier import PacketVerifier +from pktverify.null_field import nullField + +COMMISSIONER = 1 +LEADER = 2 + +# Test Purpose and Description: +# ----------------------------- +# The purpose of this test case is to verify Leader's and active Commissioner's behavior via +# MGMT_ACTIVE_GET request and response +# +# Test Topology: +# ------------- +# Commissioner +# | +# Leader +# +# DUT Types: +# ---------- +# Leader +# Commissioner + + +class Cert_9_2_03_ActiveDatasetGet(thread_cert.TestCase): + SUPPORT_NCP = False + + TOPOLOGY = { + COMMISSIONER: { + 'name': 'COMMISSIONER', + 'mode': 'rdn', + 'panid': 0xface, + 'router_selection_jitter': 1, + 'allowlist': [LEADER] + }, + LEADER: { + 'name': 'LEADER', + 'mode': 'rdn', + 'panid': 0xface, + 'router_selection_jitter': 1, + 'allowlist': [COMMISSIONER] + }, + } + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[COMMISSIONER].start() + self.simulator.go(5) + self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router') + self.simulator.get_messages_sent_by(LEADER) + + self.collect_rlocs() + self.collect_rloc16s() + + leader_rloc = self.nodes[LEADER].get_rloc() + + self.nodes[COMMISSIONER].commissioner_start() + self.simulator.go(3) + + self.nodes[COMMISSIONER].send_mgmt_active_get() + self.simulator.go(5) + + self.nodes[COMMISSIONER].send_mgmt_active_get( + leader_rloc, + [mesh_cop.TlvType.CHANNEL_MASK, mesh_cop.TlvType.NETWORK_MESH_LOCAL_PREFIX, mesh_cop.TlvType.NETWORK_NAME]) + self.simulator.go(5) + + self.nodes[COMMISSIONER].send_mgmt_active_get(leader_rloc, [ + mesh_cop.TlvType.CHANNEL, mesh_cop.TlvType.NETWORK_MESH_LOCAL_PREFIX, mesh_cop.TlvType.NETWORK_NAME, + mesh_cop.TlvType.SCAN_DURATION, mesh_cop.TlvType.ENERGY_LIST + ]) + self.simulator.go(5) + + commissioner_rloc = self.nodes[COMMISSIONER].get_rloc() + self.assertTrue(self.nodes[COMMISSIONER].ping(leader_rloc)) + self.simulator.go(1) + self.assertTrue(self.nodes[LEADER].ping(commissioner_rloc)) + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + LEADER = pv.vars['LEADER'] + LEADER_RLOC = pv.vars['LEADER_RLOC'] + COMMISSIONER = pv.vars['COMMISSIONER'] + COMMISSIONER_RLOC = pv.vars['COMMISSIONER_RLOC'] + + # Step 1: Ensure topology is formed correctly + pv.verify_attached('COMMISSIONER', 'LEADER') + + # Step 2: Commissioner sends a MGMT_ACTIVE_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/ag + # CoAP Payload + # - get all Active Operational Dataset parameters + _pkt = pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_ACTIVE_GET_URI).\ + filter(lambda p: p.coap.tlv.type is nullField).\ + must_next() + + # Step 3: Leader sends a MGMT_ACTIVE_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # (entire Active Operational Dataset) + # Active Timestamp TLV + # Channel TLV + # Channel Mask TLV + # Extended PAN ID TLV + # Network Mesh-Local Prefix TLV + # Network Master Key TLV + # Network Name TLV + # PAN ID TLV + # PSKc TLV + # Security Policy TLV + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_ACTIVE_GET_URI).\ + filter(lambda p: { + NM_ACTIVE_TIMESTAMP_TLV, + NM_CHANNEL_TLV, + NM_CHANNEL_MASK_TLV, + NM_EXTENDED_PAN_ID_TLV, + NM_NETWORK_MESH_LOCAL_PREFIX_TLV, + NM_NETWORK_MASTER_KEY_TLV, + NM_NETWORK_NAME_TLV, + NM_PAN_ID_TLV, + NM_PSKC_TLV, + NM_SECURITY_POLICY_TLV + } == set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 4: Commissioner sends a MGMT_ACTIVE_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/ag + # CoAP Payload + # Channel Mask TLV + # Network Mesh-Local Prefix TLV + # Network Name TLV + pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_ACTIVE_GET_URI).\ + filter(lambda p: { + NM_CHANNEL_MASK_TLV, + NM_NETWORK_MESH_LOCAL_PREFIX_TLV, + NM_NETWORK_NAME_TLV + } <= set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 5: Leader sends a MGMT_ACTIVE_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # Channel Mask TLV + # Network Mesh-Local Prefix TLV + # Network Name TLV + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_ACTIVE_GET_URI).\ + filter(lambda p: { + NM_CHANNEL_MASK_TLV, + NM_NETWORK_MESH_LOCAL_PREFIX_TLV, + NM_NETWORK_NAME_TLV + } == set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 6: Commissioner sends a MGMT_ACTIVE_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/ag + # CoAP Payload + # Channel TLV + # Network Mesh-Local Prefix TLV + # Network Name TLV + # Scan Duration TLV (not allowed TLV) + # Energy List TLV (not allowed TLV) + pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_ACTIVE_GET_URI).\ + filter(lambda p: { + NM_CHANNEL_TLV, + NM_NETWORK_MESH_LOCAL_PREFIX_TLV, + NM_NETWORK_NAME_TLV, + NM_SCAN_DURATION, + NM_ENERGY_LIST_TLV + } <= set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 7: Leader sends a MGMT_ACTIVE_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # Channel TLV + # Network Mesh-Local Prefix TLV + # Network Name TLV + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_ACTIVE_GET_URI).\ + filter(lambda p: { + NM_CHANNEL_TLV, + NM_NETWORK_MESH_LOCAL_PREFIX_TLV, + NM_NETWORK_NAME_TLV, + } == set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 8: Verify connectivity by sending an ICMPv6 Echo Request to the + # DUT mesh local address + _pkt = pkts.filter_ping_request().\ + filter_ipv6_src_dst(COMMISSIONER_RLOC, LEADER_RLOC).\ + must_next() + pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ + filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + must_next() + + _pkt = pkts.filter_ping_request().\ + filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + must_next() + pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ + filter_ipv6_src_dst(COMMISSIONER_RLOC, LEADER_RLOC).\ + must_next() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 1a1bf8a64..b7f0e5046 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -111,6 +111,7 @@ EXTRA_DIST = \ Cert_8_2_02_JoinerRouter.py \ Cert_9_2_01_MGMTCommissionerGet.py \ Cert_9_2_02_MGMTCommissionerSet.py \ + Cert_9_2_03_ActiveDatasetGet.py \ Cert_9_2_04_ActiveDataset.py \ Cert_9_2_07_DelayTimer.py \ Cert_9_2_08_PersistentDatasets.py \ @@ -292,6 +293,7 @@ check_SCRIPTS = \ Cert_8_2_02_JoinerRouter.py \ Cert_9_2_01_MGMTCommissionerGet.py \ Cert_9_2_02_MGMTCommissionerSet.py \ + Cert_9_2_03_ActiveDatasetGet.py \ Cert_9_2_04_ActiveDataset.py \ Cert_9_2_07_DelayTimer.py \ Cert_9_2_08_PersistentDatasets.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index c03853dd8..53703bb45 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1506,48 +1506,20 @@ class NodeImpl: 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 ' + def send_mgmt_active_get(self, addr='', tlvs=[]): + cmd = 'dataset mgmtgetcommand active' - if active_timestamp is not None: - cmd += 'activetimestamp %d ' % active_timestamp + if addr != '': + cmd += ' address ' + cmd += addr - 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 + if len(tlvs) != 0: + tlv_str = ''.join('%02x' % tlv for tlv in tlvs) + cmd += ' -x ' + cmd += tlv_str self.send_command(cmd) + self._expect('Done') def send_mgmt_pending_set( self, diff --git a/tests/scripts/thread-cert/pktverify/consts.py b/tests/scripts/thread-cert/pktverify/consts.py index c22694b90..e357c3d94 100644 --- a/tests/scripts/thread-cert/pktverify/consts.py +++ b/tests/scripts/thread-cert/pktverify/consts.py @@ -56,6 +56,8 @@ ALL_MPL_FORWARDERS_MA = Ipv6Addr('ff03::fc') LINK_LOCAL_PREFIX = Bytes("fe80") DEFAULT_MESH_LOCAL_PREFIX = Bytes("fd00:0db8:0000:0000") +LEADER_ALOC_IID = Bytes("0000:00ff:fe00:fc00") +LEADER_ALOC = Ipv6Addr(DEFAULT_MESH_LOCAL_PREFIX + LEADER_ALOC_IID) # WPAN CMDs WPAN_DATA_REQUEST = 4 @@ -189,6 +191,7 @@ NM_STATE_TLV = 16 NM_JOINER_UDP_PORT_TLV = 18 NM_DELAY_TIMER_TLV = 52 NM_CHANNEL_MASK_TLV = 53 +NM_SCAN_DURATION = 56 NM_ENERGY_LIST_TLV = 57 NM_DISCOVERY_REQUEST_TLV = 128 NM_DISCOVERY_RESPONSE_TLV = 129