From 234562a577c80750dafa542a2c1897fbc5e39f17 Mon Sep 17 00:00:00 2001 From: Jing Ma Date: Tue, 15 Dec 2020 00:34:39 +0800 Subject: [PATCH] [thread-cert] add case 9.2.1 MGMTCommissionerGet using pktverify (#5952) --- .../Cert_9_2_01_MGMTCommissionerGet.py | 282 ++++++++++++++++++ tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/node.py | 7 + tests/scripts/thread-cert/pktverify/consts.py | 1 + 4 files changed, 292 insertions(+) create mode 100755 tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py diff --git a/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py b/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py new file mode 100755 index 000000000..4e6870dea --- /dev/null +++ b/tests/scripts/thread-cert/Cert_9_2_01_MGMTCommissionerGet.py @@ -0,0 +1,282 @@ +#!/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 thread_cert +from pktverify.consts import MLE_DATA_RESPONSE, MGMT_COMMISSIONER_GET_URI, NM_CHANNEL_TLV, NM_COMMISSIONER_ID_TLV, NM_COMMISSIONER_SESSION_ID_TLV, NM_STEERING_DATA_TLV, NM_BORDER_AGENT_LOCATOR_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV, SOURCE_ADDRESS_TLV, NWD_COMMISSIONING_DATA_TLV, NM_PAN_ID_TLV, NM_NETWORK_NAME_TLV +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_COMMISSIONER_GET request and response +# +# Test Topology: +# ------------- +# Commissioner +# | +# Leader +# +# DUT Types: +# ---------- +# Leader +# Commissioner + + +class Cert_9_2_01_MGMTCommissionerGet(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_leader_aloc(LEADER) + self.collect_rlocs() + self.collect_rloc16s() + + self.nodes[COMMISSIONER].commissioner_start() + self.simulator.go(3) + + self.nodes[COMMISSIONER].commissioner_mgmtget() + self.simulator.go(5) + + self.nodes[COMMISSIONER].commissioner_mgmtget('0b08') + self.simulator.go(5) + + self.nodes[COMMISSIONER].commissioner_mgmtget('0b01') + self.simulator.go(5) + + self.nodes[COMMISSIONER].commissioner_mgmtget('0903') + self.simulator.go(5) + + leader_rloc = self.nodes[LEADER].get_rloc() + 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_ALOC = pv.vars['LEADER_ALOC'] + 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_COMMISSIONER_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/cg + # CoAP Payload + # - get all Commissioner Dataset parameters + _pkt = pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: p.coap.tlv.type is nullField).\ + must_next() + + # Step 3: Leader sends a MGMT_COMMISSIONER_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # (entire Commissioner Dataset) + # Border Agent Locator TLV + # Commissioner Session ID TLV + # Steering Data TLV + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_COMMISSIONER_SESSION_ID_TLV, + NM_BORDER_AGENT_LOCATOR_TLV, + NM_STEERING_DATA_TLV + } == set(p.thread_meshcop.tlv.type) and\ + p.thread_meshcop.tlv.ba_locator is not nullField and\ + p.thread_meshcop.tlv.commissioner_sess_id is not nullField and\ + p.thread_meshcop.tlv.steering_data is not nullField + ).\ + must_next() + + # Step 4: Commissioner sends a MGMT_COMMISSIONER_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/cg + # CoAP Payload + # Commissioner Session ID TLV + # Steering Data TLV + pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_COMMISSIONER_SESSION_ID_TLV, + NM_STEERING_DATA_TLV + } <= set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 5: Leader sends a MGMT_COMMISSIONER_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # Encoded values for the requested Commissioner Dataset parameters + # Commissioner Session ID TLV + # Steering Data TLV + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_COMMISSIONER_SESSION_ID_TLV, + NM_STEERING_DATA_TLV + } == set(p.thread_meshcop.tlv.type) and\ + p.thread_meshcop.tlv.commissioner_sess_id is not nullField and\ + p.thread_meshcop.tlv.steering_data is not nullField + ).\ + must_next() + + # Step 6: Commissioner sends a MGMT_COMMISSIONER_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/cg + # CoAP Payload + # Commissioner Session ID TLV + # PAN ID TLV + pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_COMMISSIONER_SESSION_ID_TLV, + NM_PAN_ID_TLV + } <= set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 7: Leader sends a MGMT_COMMISSIONER_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # Encoded values for the requested Commissioner Dataset parameters + # Commissioner Session ID TLV + # (PAN ID TLV in Get TLV is ignored) + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_COMMISSIONER_SESSION_ID_TLV + } == set(p.thread_meshcop.tlv.type) and\ + p.thread_meshcop.tlv.commissioner_sess_id is not nullField and\ + p.thread_meshcop.tlv.pan_id is nullField + ).\ + must_next() + + # Step 8: Commissioner sends a MGMT_COMMISSIONER_GET.req to Leader Anycast + # or Routing Locator: + # CoAP Request URI + # CON POST coap://:MM/c/cg + # CoAP Payload + # Border Agent Locator TLV + # Network Name TLV + pkts.filter_wpan_src64(COMMISSIONER).\ + filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ + filter_coap_request(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_BORDER_AGENT_LOCATOR_TLV, + NM_NETWORK_NAME_TLV + } <= set(p.thread_meshcop.tlv.type) + ).\ + must_next() + + # Step 9: Leader sends a MGMT_COMMISSIONER_GET.rsp to Commissioner with + # the following format: + # CoAP Response Code + # 2.04 Changed + # CoAP Payload + # Encoded values for the requested Commissioner Dataset parameters + # Border Agent Locator TLV + # (Network Name TLV in Get TLV is ignored) + pkts.filter_ipv6_src_dst(LEADER_RLOC, COMMISSIONER_RLOC).\ + filter_coap_ack(MGMT_COMMISSIONER_GET_URI).\ + filter(lambda p: { + NM_BORDER_AGENT_LOCATOR_TLV + } == set(p.thread_meshcop.tlv.type) and\ + p.thread_meshcop.tlv.ba_locator is not nullField and\ + p.thread_meshcop.tlv.net_name is nullField + ).\ + must_next() + + # Step 10: 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 c7da02c24..1a1bf8a64 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -109,6 +109,7 @@ EXTRA_DIST = \ Cert_8_1_02_Commissioning.py \ Cert_8_2_01_JoinerRouter.py \ Cert_8_2_02_JoinerRouter.py \ + Cert_9_2_01_MGMTCommissionerGet.py \ Cert_9_2_02_MGMTCommissionerSet.py \ Cert_9_2_04_ActiveDataset.py \ Cert_9_2_07_DelayTimer.py \ @@ -289,6 +290,7 @@ check_SCRIPTS = \ Cert_8_1_02_Commissioning.py \ Cert_8_2_01_JoinerRouter.py \ Cert_8_2_02_JoinerRouter.py \ + Cert_9_2_01_MGMTCommissionerGet.py \ Cert_9_2_02_MGMTCommissionerSet.py \ Cert_9_2_04_ActiveDataset.py \ Cert_9_2_07_DelayTimer.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 3ab12236f..c03853dd8 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1824,6 +1824,13 @@ class NodeImpl: self._expect('coaps response', timeout=timeout) + def commissioner_mgmtget(self, tlvs_binary=None): + cmd = 'commissioner mgmtget' + if tlvs_binary is not None: + cmd += ' -x %s' % tlvs_binary + self.send_command(cmd) + self._expect('Done') + def commissioner_mgmtset(self, tlvs_binary): cmd = 'commissioner mgmtset -x %s' % tlvs_binary self.send_command(cmd) diff --git a/tests/scripts/thread-cert/pktverify/consts.py b/tests/scripts/thread-cert/pktverify/consts.py index e0e991a6d..c22694b90 100644 --- a/tests/scripts/thread-cert/pktverify/consts.py +++ b/tests/scripts/thread-cert/pktverify/consts.py @@ -107,6 +107,7 @@ MGMT_ACTIVE_GET_URI = '/c/ag' MGMT_ACTIVE_SET_URI = '/c/as' MGMT_PENDING_SET_URI = '/c/ps' MGMT_DATASET_CHANGED_URI = '/c/dc' +MGMT_COMMISSIONER_GET_URI = '/c/cg' DIAG_GET_URI = '/d/dg' DIAG_RST_URI = '/d/dr' MGMT_COMMISSIONER_SET_URI = '/c/cs'