From 15c728ed21996ad4e44bcb0f9167223d7c0eee2a Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Tue, 17 Mar 2026 19:44:45 -0500 Subject: [PATCH] [nexus] add MATN-TC-20 test case for automatic re-registration (#12708) This commit implements the MATN-TC-20 test case in the Nexus simulation framework to verify that a Parent Router handling a multicast registration on behalf of an MTD re-registers the multicast address on behalf of its child before the MLR timeout expires. Key implementation details include: - Implementation of the MATN-TC-20 test scenario in C++ simulating a topology with a Router (DUT), a MED, and two Border Routers (BR_1 as initial Primary BBR, BR_2 as Secondary BBR). - Addition of a Python verification script to validate MLE Child Update Request/Response exchanges and subsequent MLR.req CoAP requests from the DUT to the Primary BBR. - Verification that the DUT automatically re-registers the multicast address when the MLR timeout is updated in the BBR Dataset. - Inclusion of the full test specification as inline comments in both the C++ and Python files, following strict formatting rules. - Registration of the new test case in tests/nexus/CMakeLists.txt and tests/nexus/run_nexus_tests.sh. --- tests/nexus/CMakeLists.txt | 1 + tests/nexus/run_nexus_tests.sh | 1 + tests/nexus/test_1_2_MATN_TC_20.cpp | 366 ++++++++++++++++++ tests/nexus/verify_1_2_MATN_TC_20.py | 286 ++++++++++++++ .../thread-cert/pktverify/layer_fields.py | 1 + 5 files changed, 655 insertions(+) create mode 100644 tests/nexus/test_1_2_MATN_TC_20.cpp create mode 100644 tests/nexus/verify_1_2_MATN_TC_20.py diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index f5e21b9a8..12332ea31 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -246,6 +246,7 @@ ot_nexus_test(1_2_MATN_TC_12 "cert;nexus") ot_nexus_test(1_2_MATN_TC_15 "cert;nexus") ot_nexus_test(1_2_MATN_TC_16 "cert;nexus") ot_nexus_test(1_2_MATN_TC_19 "cert;nexus") +ot_nexus_test(1_2_MATN_TC_20 "cert;nexus") # Misc tests ot_nexus_test(border_admitter "core;nexus") diff --git a/tests/nexus/run_nexus_tests.sh b/tests/nexus/run_nexus_tests.sh index 704c0911a..a0c5c5f8c 100755 --- a/tests/nexus/run_nexus_tests.sh +++ b/tests/nexus/run_nexus_tests.sh @@ -181,6 +181,7 @@ DEFAULT_TESTS=( "1_2_MATN_TC_15" "1_2_MATN_TC_16" "1_2_MATN_TC_19" + "1_2_MATN_TC_20" ) # Use provided arguments or the default test list diff --git a/tests/nexus/test_1_2_MATN_TC_20.cpp b/tests/nexus/test_1_2_MATN_TC_20.cpp new file mode 100644 index 000000000..10927b891 --- /dev/null +++ b/tests/nexus/test_1_2_MATN_TC_20.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2026, 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. + */ + +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" + +namespace ot { +namespace Nexus { + +/** + * Time to advance for a node to form a network and become leader, in milliseconds. + */ +static constexpr uint32_t kFormNetworkTime = 10 * 1000; + +/** + * Time to advance for a node to join as a router, in milliseconds. + */ +static constexpr uint32_t kAttachToRouterTime = 200 * 1000; + +/** + * Time to advance for the network to stabilize, in milliseconds. + */ +static constexpr uint32_t kStabilizationTime = 10 * 1000; + +/** + * Multicast Listener Registration minimum timeout, in seconds. + */ +static constexpr uint32_t kMlrTimeoutMin = 300; + +/** + * Multicast Listener Registration extended timeout, in seconds. + */ +static constexpr uint32_t kMlrTimeoutExtended = kMlrTimeoutMin + 30; + +/** + * Multicast address MA2 (site-local). + */ +static const char kMA2[] = "ff05::1234:777a:1"; + +void TestMatnTc20(void) +{ + /** + * 5.10.16 MATN-TC-20: Automatic re-registration by Parent Router on behalf of MTD + * + * 5.10.16.1 Topology + * - Router (DUT) + * - MED + * - BR_1 + * - BR_2 + * + * 5.10.16.2 Purpose & Description + * The purpose of this test case is to verify that a Parent Router handling a multicast registration on behalf of an + * MTD re-registers the multicast address on behalf of its child, before the MLR timeout expires. + * + * Spec Reference | V1.2 Section + * -----------------|------------- + * Multicast | 5.10.16 + */ + + Core nexus; + + Node &router = nexus.CreateNode(); + Node &med = nexus.CreateNode(); + Node &br1 = nexus.CreateNode(); + Node &br2 = nexus.CreateNode(); + + router.SetName("Router"); + med.SetName("MED"); + br1.SetName("BR_1"); + br2.SetName("BR_2"); + + Ip6::Address ma2; + SuccessOrQuit(ma2.FromString(kMA2)); + + nexus.AdvanceTime(0); + + Instance::SetLogLevel(kLogLevelNote); + + Log("Step 0: Topology formation - BR_1, BR_2. Topology formation - Router (DUT). The DUT is booted and added to " + "the network. Topology formation - MED."); + + /** + * Step 0 + * - Device: N/A + * - Description: Topology formation - BR_1, BR_2. Topology formation - Router (DUT). The DUT is booted and added + * to the network. Topology formation - MED. + * - Pass Criteria: + * - N/A + */ + + /** Use AllowList to specify links between nodes. */ + br1.AllowList(br2); + br1.AllowList(router); + + br2.AllowList(br1); + br2.AllowList(router); + + router.AllowList(br1); + router.AllowList(br2); + router.AllowList(med); + + med.AllowList(router); + + br1.Form(); + nexus.AdvanceTime(kFormNetworkTime); + VerifyOrQuit(br1.Get().IsLeader()); + + br1.Get().Init(1, true); + br1.Get().Init(); + SuccessOrQuit(br1.Get().SetEnabled(true)); + br1.Get().SetEnabled(true); + + br2.Join(br1, Node::kAsFtd); + nexus.AdvanceTime(kAttachToRouterTime); + VerifyOrQuit(br2.Get().IsRouter()); + + br2.Get().Init(2, true); + br2.Get().Init(); + SuccessOrQuit(br2.Get().SetEnabled(true)); + br2.Get().SetEnabled(true); + + router.Join(br1, Node::kAsFtd); + nexus.AdvanceTime(kAttachToRouterTime); + VerifyOrQuit(router.Get().IsRouter()); + + med.Join(router, Node::kAsMed); + nexus.AdvanceTime(kStabilizationTime); + VerifyOrQuit(med.Get().IsChild()); + + Log("Step 2: Harness configures the value of the MLR timeout in the BBR Dataset of the device to be " + "(MLR_TIMEOUT_MIN + 30) seconds and distribute the BBR Dataset."); + + /** + * Step 2 + * - Device: BR_1 + * - Description: Harness configures the value of the MLR timeout in the BBR Dataset of the device to be + * (MLR_TIMEOUT_MIN + 30) seconds and distribute the BBR Dataset. + * - Pass Criteria: + * - N/A + */ + { + BackboneRouter::Config config; + br1.Get().GetConfig(config); + config.mMlrTimeout = kMlrTimeoutExtended; + config.mReregistrationDelay = 1; + SuccessOrQuit(br1.Get().SetConfig(config)); + SuccessOrQuit(br1.Get().AddService(BackboneRouter::Local::kForceRegistration)); + } + + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 3: Automatically distributes the new network data to MED."); + + /** + * Step 3 + * - Device: Router (DUT) + * - Description: Automatically distributes the new network data to MED. + * - Pass Criteria: + * - None. + */ + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 4: Harness instructs the device to register multicast address MA2 with its parent. The device unicasts " + "an MLE Child Update Request to the DUT, where the payload includes a single TLV with single address as " + "below: Address Registration TLV: MA2"); + + /** + * Step 4 + * - Device: MED + * - Description: Harness instructs the device to register multicast address MA2 with its parent. The device + * unicasts an MLE Child Update Request to the DUT, where the payload includes a single TLV with single address + * as below: Address Registration TLV: MA2 + * - Pass Criteria: + * - N/A + */ + SuccessOrQuit(med.Get().SubscribeExternalMulticast(ma2)); + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 5: Automatically responds to the registration request."); + + /** + * Step 5 + * - Device: Router (DUT) + * - Description: Automatically responds to the registration request. + * - Pass Criteria: + * - The DUT MUST unicast an MLE Child Update Response command to MTD, where the payload contains (amongst other + * fields) this TLV with two addresses inside: + * - Address Registration TLV: ML-EID of MTD (M1g) + * - MA2 + */ + /** Handled by AdvanceTime in Step 4 */ + + Log("Step 6: Automatically requests to register multicast address MA2 at BR_1"); + + /** + * Step 6 + * - Device: Router (DUT) + * - Description: Automatically requests to register multicast address MA2 at BR_1 + * - Pass Criteria: + * - The DUT MUST unicast an MLR.req CoAP request to BR_1 as follows: coap://[]:MM/n/mr + * - Where the payload contains: + * - IPv6 Addresses TLV: MA2 + */ + /** Handled by AdvanceTime in Step 4 */ + + Log("Step 7: Before MLR timeout, automatically requests to re-register multicast address, MA2, at BR_1."); + + /** + * Step 7 + * - Device: Router (DUT) + * - Description: Before MLR timeout, automatically requests to re-register multicast address, MA2, at BR_1. + * - Pass Criteria: + * - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + * - Where the payload contains one TLV with at least one address as follows: + * - IPv6 Addresses TLV: MA2 + * - The CoAP request MUST be sent within (MLR_TIMEOUT_MIN + 30) seconds of step 4 + */ + nexus.AdvanceTime(kMlrTimeoutExtended * 1000); + + Log("Step 8: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS."); + + /** + * Step 8 + * - Device: BR_1 + * - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + * - Pass Criteria: + * - N/A + */ + /** Handled by AdvanceTime in Step 7 */ + + Log("Step 9: Before MLR timeout, automatically re-registers for multicast address MA2."); + + /** + * Step 9 + * - Device: Router (DUT) + * - Description: Before MLR timeout, automatically re-registers for multicast address MA2. + * - Pass Criteria: + * - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + * - Where the payload contains one TLV with at least one address as follows: + * - IPv6 Addresses TLV: MA2 + * - The request MUST be sent within (MLR_TIMEOUT_MIN + 30) seconds of step 6, + */ + nexus.AdvanceTime(kMlrTimeoutExtended * 1000); + + Log("Step 10: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS."); + + /** + * Step 10 + * - Device: BR_1 + * - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + * - Pass Criteria: + * - N/A + */ + /** Handled by AdvanceTime in Step 9 */ + + Log("Step 11: Harness configures the value of the device MLR timeout to be MLR_TIMEOUT_MIN seconds and " + "distributes the BBR Dataset."); + + /** + * Step 11 + * - Device: BR_1 + * - Description: Harness configures the value of the device MLR timeout to be MLR_TIMEOUT_MIN seconds and + * distributes the BBR Dataset. + * - Pass Criteria: + * - N/A + */ + { + BackboneRouter::Config config; + br1.Get().GetConfig(config); + config.mMlrTimeout = kMlrTimeoutMin; + config.mReregistrationDelay = 1; + config.mSequenceNumber++; + SuccessOrQuit(br1.Get().SetConfig(config)); + SuccessOrQuit(br1.Get().AddService(BackboneRouter::Local::kForceRegistration)); + } + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 12: Automatically recognizes the BBR Dataset update and re-registers MA2."); + + /** + * Step 12 + * - Device: Router (DUT) + * - Description: Automatically recognizes the BBR Dataset update and re-registers MA2. + * - Pass Criteria: + * - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + * - Where the payload contains at least MA2: + * - IPv6 Addresses TLV: MA2 + */ + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 13: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS."); + + /** + * Step 13 + * - Device: BR_1 + * - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + * - Pass Criteria: + * - N/A + */ + /** Handled by AdvanceTime in Step 12 */ + + Log("Step 14: Before MLR timeout, automatically re-registers for multicast address MA2."); + + /** + * Step 14 + * - Device: Router (DUT) + * - Description: Before MLR timeout, automatically re-registers for multicast address MA2. + * - Pass Criteria: + * - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + * - Where the payload contains at least MA2: + * - IPv6 Addresses TLV: MA2 + * - The request MUST be sent within MLR_TIMEOUT_MIN seconds of step 11, + */ + nexus.AdvanceTime(500 * 1000); + + Log("Step 15: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS."); + + /** + * Step 15 + * - Device: BR_1 + * - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + * - Pass Criteria: + * - N/A + */ + /** Handled by AdvanceTime in Step 14 */ + + nexus.AddTestVar("MA2", kMA2); + nexus.SaveTestInfo("test_1_2_MATN_TC_20.json"); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestMatnTc20(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/verify_1_2_MATN_TC_20.py b/tests/nexus/verify_1_2_MATN_TC_20.py new file mode 100644 index 000000000..f9ff9bcdd --- /dev/null +++ b/tests/nexus/verify_1_2_MATN_TC_20.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026, 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 sys +import os + +# Add the current directory to sys.path to find verify_utils +CUR_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(CUR_DIR) + +import verify_utils +from pktverify import consts +from pktverify.addrs import Ipv6Addr + +ST_MLR_SUCCESS = 0 + + +def verify(pv): + # 5.10.16 MATN-TC-20: Automatic re-registration by Parent Router on behalf of MTD + # + # 5.10.16.1 Topology + # - Router (DUT) + # - MED + # - BR_1 + # - BR_2 + # + # 5.10.16.2 Purpose & Description + # The purpose of this test case is to verify that a Parent Router handling a multicast registration on behalf of an + # MTD re-registers the multicast address on behalf of its child, before the MLR timeout expires. + # + # Spec Reference | V1.2 Section + # ---------------|------------- + # Multicast | 5.10.16 + + pkts = pv.pkts + + ROUTER = pv.vars['Router'] + ROUTER_16 = pv.vars['Router_RLOC16'] + MED = pv.vars['MED'] + MED_16 = pv.vars['MED_RLOC16'] + MED_MLEID = pv.vars['MED_MLEID'] + MED_ADDRS = [Ipv6Addr(a) for a in pv.vars.get('MED_IPADDRS', [MED_MLEID])] + MA2 = Ipv6Addr(pv.vars['MA2']) + BR1_RLOC = pv.vars['BR_1_RLOC'] + BR1_RLOC16 = pv.vars['BR_1_RLOC16'] + PBBR_ALOC = pv.vars.get('PBBR_ALOC', consts.PBBR_ALOC) + + kMlrTimeoutMin = 300 + kMlrTimeoutExtended = kMlrTimeoutMin + 30 + + def has_addr(p, addr): + iid_int = int(addr[8:].hex(), 16) + return any(iid == iid_int for iid in p.mle.tlv.addr_reg_iid) or \ + any(a == addr for a in p.mle.tlv.addr_reg_ipv6) + + # Step 0 + # - Device: N/A + # - Description: Topology formation - BR_1, BR_2. Topology formation - Router (DUT). The DUT is booted and added to + # the network. Topology formation - MED. + # - Pass Criteria: + # - N/A + print("Step 0: Topology formation - BR_1, BR_2. Topology formation - Router (DUT).") + + # Step 2 + # - Device: BR_1 + # - Description: Harness configures the value of the MLR timeout in the BBR Dataset of the device to be + # (MLR_TIMEOUT_MIN + 30) seconds and distribute the BBR Dataset. + # - Pass Criteria: + # - N/A + print("Step 2: Harness configures the value of the MLR timeout in the BBR Dataset.") + + # Step 3 + # - Device: Router (DUT) + # - Description: Automatically distributes the new network data to MED. + # - Pass Criteria: + # - None. + print("Step 3: Automatically distributes the new network data to MED.") + + # Step 4 + # - Device: MED + # - Description: Harness instructs the device to register multicast address MA2 with its parent. The device + # unicasts an MLE Child Update Request to the DUT, where the payload includes a single TLV with single address + # as below: Address Registration TLV: MA2 + # - Pass Criteria: + # - N/A + print("Step 4: MED registers multicast address MA2 with its parent.") + med_reg_pkt = pkts.filter_mle_cmd(consts.MLE_CHILD_UPDATE_REQUEST).\ + filter(lambda p: p.wpan.src64 == MED or p.wpan.src16 == MED_16).\ + filter(lambda p: {consts.ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)).\ + filter(lambda p: has_addr(p, MA2)).\ + must_next() + + # Step 5 + # - Device: Router (DUT) + # - Description: Automatically responds to the registration request. + # - Pass Criteria: + # - The DUT MUST unicast an MLE Child Update Response command to MTD, where the payload contains (amongst other + # fields) this TLV with two addresses inside: + # - Address Registration TLV: ML-EID of MTD (M1g) + # - MA2 + print("Step 5: Router responds to the registration request.") + pkts.filter_mle_cmd(consts.MLE_CHILD_UPDATE_RESPONSE).\ + filter(lambda p: p.wpan.src64 == ROUTER or p.wpan.src16 == ROUTER_16).\ + filter(lambda p: p.wpan.dst64 == MED or p.wpan.dst16 == MED_16).\ + filter(lambda p: {consts.ADDRESS_REGISTRATION_TLV} <= set(p.mle.tlv.type)).\ + filter(lambda p: has_addr(p, MA2) and any(has_addr(p, addr) for addr in MED_ADDRS)).\ + must_next() + + # Step 6 + # - Device: Router (DUT) + # - Description: Automatically requests to register multicast address MA2 at BR_1 + # - Pass Criteria: + # - The DUT MUST unicast an MLR.req CoAP request to BR_1 as follows: coap://[]:MM/n/mr + # - Where the payload contains: + # - IPv6 Addresses TLV: MA2 + print("Step 6: Router requests to register multicast address MA2 at BR_1.") + pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.wpan.src64 == ROUTER or p.wpan.src16 == ROUTER_16).\ + filter(lambda p: p.ipv6.dst in (BR1_RLOC, PBBR_ALOC)).\ + filter(lambda p: MA2 in p.coap.tlv.ipv6_address).\ + must_next() + + # Automatically responds to initial registration + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.wpan.src64 == pv.vars['BR_1'] or p.wpan.src16 == BR1_RLOC16).\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + # Note: Checks for BLMR.ntf and MLDv2 are intentionally skipped. + + # Step 7 + # - Device: Router (DUT) + # - Description: Before MLR timeout, automatically requests to re-register multicast address, MA2, at BR_1. + # - Pass Criteria: + # - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + # - Where the payload contains one TLV with at least one address as follows: + # - IPv6 Addresses TLV: MA2 + # - The CoAP request MUST be sent within (MLR_TIMEOUT_MIN + 30) seconds of step 4 + print("Step 7: Router re-registers multicast address MA2 at BR_1 before timeout.") + re_reg_pkt_1 = pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.wpan.src64 == ROUTER or p.wpan.src16 == ROUTER_16).\ + filter(lambda p: p.ipv6.dst in (BR1_RLOC, PBBR_ALOC)).\ + filter(lambda p: MA2 in p.coap.tlv.ipv6_address).\ + must_next() + + # Timing check for Step 7: within (MLR_TIMEOUT_MIN + 30) seconds of step 4 + delta = re_reg_pkt_1.sniff_timestamp - med_reg_pkt.sniff_timestamp + assert delta <= kMlrTimeoutExtended, f"Step 7 timing failure: {delta} > {kMlrTimeoutExtended}" + + # Step 8 + # - Device: BR_1 + # - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + # - Pass Criteria: + # - N/A + print("Step 8: BR_1 responds to the multicast registration.") + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.wpan.src64 == pv.vars['BR_1'] or p.wpan.src16 == BR1_RLOC16).\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + # Step 9 + # - Device: Router (DUT) + # - Description: Before MLR timeout, automatically re-registers for multicast address MA2. + # - Pass Criteria: + # - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + # - Where the payload contains one TLV with at least one address as follows: + # - IPv6 Addresses TLV: MA2 + # - The request MUST be sent within (MLR_TIMEOUT_MIN + 30) seconds of step 6, + print("Step 9: Router re-registers for multicast address MA2.") + re_reg_pkt_2 = pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.wpan.src64 == ROUTER or p.wpan.src16 == ROUTER_16).\ + filter(lambda p: p.ipv6.dst in (BR1_RLOC, PBBR_ALOC)).\ + filter(lambda p: MA2 in p.coap.tlv.ipv6_address).\ + must_next() + + # Timing check for Step 9: within (MLR_TIMEOUT_MIN + 30) seconds of step 6 + delta = re_reg_pkt_2.sniff_timestamp - re_reg_pkt_1.sniff_timestamp + assert delta <= kMlrTimeoutExtended, f"Step 9 timing failure: {delta} > {kMlrTimeoutExtended}" + + # Step 10 + # - Device: BR_1 + # - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + # - Pass Criteria: + # - N/A + print("Step 10: BR_1 responds to the multicast registration.") + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.wpan.src64 == pv.vars['BR_1'] or p.wpan.src16 == BR1_RLOC16).\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + # Step 11 + # - Device: BR_1 + # - Description: Harness configures the value of the device MLR timeout to be MLR_TIMEOUT_MIN seconds and + # distributes the BBR Dataset. + # - Pass Criteria: + # - N/A + print("Step 11: BR_1 configures the MLR timeout to be MLR_TIMEOUT_MIN seconds.") + # Look for Network Data update from BR1 + step11_pkt = pkts.filter_mle_cmd(consts.MLE_DATA_RESPONSE).\ + filter(lambda p: p.wpan.src64 == pv.vars['BR_1'] or p.wpan.src16 == BR1_RLOC16).\ + must_next() + + # Step 12 + # - Device: Router (DUT) + # - Description: Automatically recognizes the BBR Dataset update and re-registers MA2. + # - Pass Criteria: + # - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + # - Where the payload contains at least MA2: + # - IPv6 Addresses TLV: MA2 + print("Step 12: Router recognizes BBR Dataset update and re-registers MA2.") + re_reg_pkt_3 = pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.wpan.src64 == ROUTER or p.wpan.src16 == ROUTER_16).\ + filter(lambda p: p.ipv6.dst in (BR1_RLOC, PBBR_ALOC)).\ + filter(lambda p: MA2 in p.coap.tlv.ipv6_address).\ + must_next() + + # Step 13 + # - Device: BR_1 + # - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + # - Pass Criteria: + # - N/A + print("Step 13: BR_1 responds to the multicast registration.") + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.wpan.src64 == pv.vars['BR_1'] or p.wpan.src16 == BR1_RLOC16).\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + # Step 14 + # - Device: Router (DUT) + # - Description: Before MLR timeout, automatically re-registers for multicast address MA2. + # - Pass Criteria: + # - The DUT MUST unicast an MLR.req CoAP request to BR_1: coap://[]:MM/n/mr + # - Where the payload contains at least MA2: + # - IPv6 Addresses TLV: MA2 + # - The request MUST be sent within MLR_TIMEOUT_MIN seconds of step 11, + print("Step 14: Router re-registers for multicast address MA2.") + re_reg_pkt_4 = pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.wpan.src64 == ROUTER or p.wpan.src16 == ROUTER_16).\ + filter(lambda p: p.ipv6.dst in (BR1_RLOC, PBBR_ALOC)).\ + filter(lambda p: MA2 in p.coap.tlv.ipv6_address).\ + must_next() + + # Timing check for Step 14: within MLR_TIMEOUT_MIN seconds of step 11 + delta = re_reg_pkt_4.sniff_timestamp - step11_pkt.sniff_timestamp + assert delta <= kMlrTimeoutMin, f"Step 14 timing failure: {delta} > {kMlrTimeoutMin}" + + # Step 15 + # - Device: BR_1 + # - Description: Automatically responds to the multicast registration with MLR.rsp with status ST_MLR_SUCCESS. + # - Pass Criteria: + # - N/A + print("Step 15: BR_1 responds to the multicast registration.") + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.wpan.src64 == pv.vars['BR_1'] or p.wpan.src16 == BR1_RLOC16).\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + +if __name__ == '__main__': + verify_utils.run_main(verify) diff --git a/tests/scripts/thread-cert/pktverify/layer_fields.py b/tests/scripts/thread-cert/pktverify/layer_fields.py index 8ea606a8d..f62ebe8a3 100644 --- a/tests/scripts/thread-cert/pktverify/layer_fields.py +++ b/tests/scripts/thread-cert/pktverify/layer_fields.py @@ -347,6 +347,7 @@ _LAYER_FIELDS = { 'mle.tlv.addr16': _auto, 'mle.tlv.channel': _auto, 'mle.tlv.addr_reg_iid': _list(_auto), + 'mle.tlv.addr_reg_ipv6': _list(_ipv6_addr), 'mle.tlv.link_enh_ack_flags': _auto, 'mle.tlv.link_forward_series': _list(_auto), 'mle.tlv.link_requested_type_id_flags': _list(_hex),