From 725b101150c754241b671f1e64a6245e608de15b Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 16 Mar 2026 18:57:51 -0500 Subject: [PATCH] [nexus] add MATN-TC-05 test case for multicast re-registration (#12693) Implement Thread 1.2 test MATN-TC-05: Re-registration to same Multicast Group. This test verifies that a Primary Backbone Router (BBR) correctly manages multicast address re-registration and handles UDP multicast traffic between the backbone and Thread network. Key additions: - Added SendUdp to Nexus InfraIf to support simulated UDP multicast traffic from backbone hosts. - Implemented test_1_2_MATN_TC_5.cpp to simulate the network topology (DUT, BR_2, Router, and Host) and the test steps. - Implemented verify_1_2_MATN_TC_5.py for pcap-based verification of multicast forwarding and BBR timeout behavior. - Integrated the new test into the Nexus build system and the default test execution script. --- tests/nexus/CMakeLists.txt | 1 + tests/nexus/platform/nexus_infra_if.cpp | 43 ++- tests/nexus/platform/nexus_infra_if.hpp | 5 + tests/nexus/run_nexus_tests.sh | 1 + tests/nexus/test_1_2_MATN_TC_5.cpp | 343 ++++++++++++++++++++++++ tests/nexus/verify_1_2_MATN_TC_5.py | 235 ++++++++++++++++ 6 files changed, 624 insertions(+), 4 deletions(-) create mode 100644 tests/nexus/test_1_2_MATN_TC_5.cpp create mode 100644 tests/nexus/verify_1_2_MATN_TC_5.py diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 451432510..e0145a72d 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -238,6 +238,7 @@ ot_nexus_test(1_2_MATN_TC_1 "cert;nexus") ot_nexus_test(1_2_MATN_TC_2 "cert;nexus") ot_nexus_test(1_2_MATN_TC_3 "cert;nexus") ot_nexus_test(1_2_MATN_TC_4 "cert;nexus") +ot_nexus_test(1_2_MATN_TC_5 "cert;nexus") # Misc tests ot_nexus_test(border_admitter "core;nexus") diff --git a/tests/nexus/platform/nexus_infra_if.cpp b/tests/nexus/platform/nexus_infra_if.cpp index 92fc40ccc..0c1b65721 100644 --- a/tests/nexus/platform/nexus_infra_if.cpp +++ b/tests/nexus/platform/nexus_infra_if.cpp @@ -255,7 +255,7 @@ void InfraIf::SendEchoRequest(const Ip6::Address &aSrcAddress, Ip6::Header ip6Header; Ip6::Icmp::Header icmpHeader; - message = GetNode().Get().Allocate(Message::kTypeIp6); + message = GetNode().Get().NewMessage(); VerifyOrQuit(message != nullptr); ip6Header.Clear(); @@ -272,9 +272,8 @@ void InfraIf::SendEchoRequest(const Ip6::Address &aSrcAddress, icmpHeader.SetId(aIdentifier); icmpHeader.SetSequence(0); - SuccessOrQuit(message->SetLength(aPayloadSize)); - SuccessOrQuit(message->Prepend(icmpHeader)); - message->SetOffset(0); + SuccessOrQuit(message->Append(icmpHeader)); + SuccessOrQuit(message->IncreaseLength(aPayloadSize)); Checksum::UpdateMessageChecksum(*message, aSrcAddress, aDestAddress, Ip6::kProtoIcmp6); @@ -283,6 +282,42 @@ void InfraIf::SendEchoRequest(const Ip6::Address &aSrcAddress, mPendingTxQueue.Enqueue(*message); } +void InfraIf::SendUdp(const Ip6::Address &aSrcAddress, + const Ip6::Address &aDestAddress, + uint16_t aSourcePort, + uint16_t aDestPort, + uint16_t aPayloadSize) +{ + Message *message; + Ip6::Header ip6Header; + Ip6::Udp::Header udpHeader; + + message = GetNode().Get().NewMessage(); + VerifyOrQuit(message != nullptr); + + ip6Header.Clear(); + ip6Header.InitVersionTrafficClassFlow(); + ip6Header.SetPayloadLength(sizeof(Ip6::Udp::Header) + aPayloadSize); + ip6Header.SetNextHeader(Ip6::kProtoUdp); + ip6Header.SetHopLimit(64); + ip6Header.SetSource(aSrcAddress); + ip6Header.SetDestination(aDestAddress); + + udpHeader.SetSourcePort(aSourcePort); + udpHeader.SetDestinationPort(aDestPort); + udpHeader.SetLength(sizeof(Ip6::Udp::Header) + aPayloadSize); + udpHeader.SetChecksum(0); + + SuccessOrQuit(message->Append(udpHeader)); + SuccessOrQuit(message->IncreaseLength(aPayloadSize)); + + Checksum::UpdateMessageChecksum(*message, aSrcAddress, aDestAddress, Ip6::kProtoUdp); + + SuccessOrQuit(message->Prepend(ip6Header)); + + mPendingTxQueue.Enqueue(*message); +} + void InfraIf::Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMessage) { Node &node = GetNode(); diff --git a/tests/nexus/platform/nexus_infra_if.hpp b/tests/nexus/platform/nexus_infra_if.hpp index 55c1d72d8..62d63eb5a 100644 --- a/tests/nexus/platform/nexus_infra_if.hpp +++ b/tests/nexus/platform/nexus_infra_if.hpp @@ -64,6 +64,11 @@ public: const Ip6::Address &aDestAddress, uint16_t aIdentifier, uint16_t aPayloadSize); + void SendUdp(const Ip6::Address &aSrcAddress, + const Ip6::Address &aDestAddress, + uint16_t aSourcePort, + uint16_t aDestPort, + uint16_t aPayloadSize); void Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMessage); void GetLinkLayerAddress(otPlatInfraIfLinkLayerAddress &aLinkLayerAddress) const; diff --git a/tests/nexus/run_nexus_tests.sh b/tests/nexus/run_nexus_tests.sh index b065dbd34..cad71571d 100755 --- a/tests/nexus/run_nexus_tests.sh +++ b/tests/nexus/run_nexus_tests.sh @@ -173,6 +173,7 @@ DEFAULT_TESTS=( "1_2_MATN_TC_2" "1_2_MATN_TC_3" "1_2_MATN_TC_4" + "1_2_MATN_TC_5" ) # Use provided arguments or the default test list diff --git a/tests/nexus/test_1_2_MATN_TC_5.cpp b/tests/nexus/test_1_2_MATN_TC_5.cpp new file mode 100644 index 000000000..002947237 --- /dev/null +++ b/tests/nexus/test_1_2_MATN_TC_5.cpp @@ -0,0 +1,343 @@ +/* + * 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; + +/** + * Time to advance for MLR registration, in milliseconds. + */ +static constexpr uint32_t kMlrRegistrationTime = 5 * 1000; + +/** + * MLR Timeout Minimum value in seconds. + */ +static constexpr uint32_t kMlrTimeoutMin = 300; + +/** + * ICMPv6 Echo Request identifier. + */ +static constexpr uint16_t kEchoIdentifier = 0x1234; + +/** + * ICMPv6 Echo Request payload size. + */ +static constexpr uint16_t kEchoPayloadSize = 10; + +/** + * UDP payload size. + */ +static constexpr uint16_t kUdpPayloadSize = 20; + +/** + * CoAP port. + */ +static constexpr uint16_t kCoapPort = 5683; + +/** + * Multicast address MA1. + */ +static const char kMA1[] = "ff04::1234:777a:1"; + +/** + * ULA prefix for host address lookup. + */ +static const char kUlaPrefix[] = "fd00::/8"; + +void TestMatnTc5(void) +{ + /** + * 5.10.5 MATN-TC-05: Re-registration to same Multicast Group + * + * 5.10.5.1 Topology + * - BR_1 (DUT) + * - BR_2 + * - Router + * - Host + * + * 5.10.5.2 Purpose & Description + * Verify that a Primary BBR (DUT) can manage a re-registration of a device on its network to remain receiving + * multicasts. The test also verifies the usage of UDP multicast packets across backbone and internal Thread + * network. + * + * Spec Reference | V1.2 Section | V1.3.0 Section + * -----------------|--------------|--------------- + * Multicast | 5.10.5 | 5.10.5 + */ + + Core nexus; + Node &br1 = nexus.CreateNode(); + Node &br2 = nexus.CreateNode(); + Node &router = nexus.CreateNode(); + Node &host = nexus.CreateNode(); + Ip6::Address ma1; + const Ip6::Address *hostUla; + + br1.SetName("BR_1"); + br2.SetName("BR_2"); + router.SetName("Router"); + host.SetName("Host"); + + SuccessOrQuit(ma1.FromString(kMA1)); + + nexus.AdvanceTime(0); + + Instance::SetLogLevel(kLogLevelNote); + + /** + * Step 0 + * - Device: BR_1 (DUT) + * - Description: Topology formation – BR_1 (DUT). Boot DUT. Configure the MLR timeout value of the DUT to be + * MLR_TIMEOUT_MIN seconds. + * - Pass Criteria: + * - N/A + */ + Log("Step 0: Topology formation – BR_1 (DUT)"); + + br1.AllowList(br2); + br1.AllowList(router); + br2.AllowList(br1); + br2.AllowList(router); + router.AllowList(br1); + router.AllowList(br2); + + 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); + + { + BackboneRouter::Config config; + br1.Get().GetConfig(config); + config.mMlrTimeout = kMlrTimeoutMin; + SuccessOrQuit(br1.Get().SetConfig(config)); + } + + /** + * Step 0 (cont.) + * - Device: N/A + * - Description: Topology formation – BR_2, Router + * - Pass Criteria: + * - N/A + */ + Log("Step 0 (cont.): Topology formation – BR_2, Router"); + + router.Join(br1, Node::kAsFtd); + br2.Join(br1, Node::kAsFtd); + nexus.AdvanceTime(kAttachToRouterTime); + + VerifyOrQuit(router.Get().IsRouter()); + VerifyOrQuit(br2.Get().IsRouter()); + + br2.Get().Init(1, true); + br2.Get().Init(); + SuccessOrQuit(br2.Get().SetEnabled(true)); + br2.Get().SetEnabled(true); + + host.mInfraIf.Init(host); + host.mInfraIf.AddAddress(host.mInfraIf.GetLinkLocalAddress()); + + nexus.AdvanceTime(kStabilizationTime * 2); + + VerifyOrQuit(br1.Get().IsPrimary()); + + hostUla = &host.mInfraIf.FindMatchingAddress(kUlaPrefix); + + // Add multicast variables to test info manually to ensure verify script sees them. + nexus.AddTestVar("MA1", kMA1); + nexus.AddTestVar("HOST_ULA", hostUla->ToString().AsCString()); + { + String<16> timeoutString; + timeoutString.Append("%lu", ToUlong(kMlrTimeoutMin)); + nexus.AddTestVar("MLR_TIMEOUT_MIN", timeoutString.AsCString()); + } + + /** + * Step 1 + * - Device: Router + * - Description: Harness instructs the device to register multicast address, MA1 + * - Pass Criteria: + * - N/A + */ + Log("Step 1: Router registers multicast address, MA1"); + SuccessOrQuit(router.Get().SubscribeExternalMulticast(ma1)); + nexus.AdvanceTime(kMlrRegistrationTime); + + /** + * Step 2 + * - Device: Host + * - Description: Harness instructs the device to send an ICMPv6 Echo (ping) Request packet to the multicast + * address, MA1. + * - Pass Criteria: + * - N/A + */ + Log("Step 2: Host sends an ICMPv6 Echo (ping) Request packet to the multicast address, MA1."); + host.mInfraIf.SendEchoRequest(*hostUla, ma1, kEchoIdentifier, kEchoPayloadSize); + nexus.AdvanceTime(0); + + /** + * Step 3 + * - Device: BR_1 (DUT) + * - Description: Automatically forwards the ping request packet to its Thread Network. + * - Pass Criteria: + * - The DUT MUST forward the ICMPv6 Echo (ping) Request packet with multicast address, MA1, to its Thread + * Network encapsulated in an MPL packet. + */ + Log("Step 3: BR_1 (DUT) automatically forwards the ping request packet to its Thread Network."); + nexus.AdvanceTime(kStabilizationTime); + + /** + * Step 4 + * - Device: Router + * - Description: Receives the MPL packet containing an encapsulated ping request packet to MA1, sent by Host, and + * automatically unicasts an ICMPv6 (ping) Reply packet back to Host. + * - Pass Criteria: + * - N/A + */ + Log("Step 4: Router automatically unicasts an ICMPv6 (ping) Reply packet back to Host."); + nexus.AdvanceTime(kStabilizationTime); + + /** + * Step 4a + * - Device: Router + * - Description: Within MLR_TIMEOUT_MIN seconds of initial registration, Harness instructs the device to + * re-register for multicast address, MA1, at BR_1 by performing test case MATN-TC-02 (see 5.10.2) using Router + * as TD with the respective pass criteria. + * - Pass Criteria: + * - N/A + */ + Log("Step 4a: Router re-registers for multicast address, MA1, at BR_1."); + IgnoreError(router.Get().UnsubscribeExternalMulticast(ma1)); + SuccessOrQuit(router.Get().SubscribeExternalMulticast(ma1)); + nexus.AdvanceTime(kMlrRegistrationTime); + + /** + * Step 5 + * - Device: Host + * - Description: Within MLR_TIMEOUT_MIN seconds, Harness instructs the device to send a UDP packet to the + * multicast address, MA1. The destination port 5683 is used for the UDP Multicast packet transmission. + * - Pass Criteria: + * - N/A + */ + Log("Step 5: Host sends a UDP packet to the multicast address, MA1, port 5683."); + host.mInfraIf.SendUdp(*hostUla, ma1, kCoapPort, kCoapPort, kUdpPayloadSize); + nexus.AdvanceTime(0); + + /** + * Step 6 + * - Device: BR_1 (DUT) + * - Description: Automatically forwards the UDP ping request packet to its Thread Network. + * - Pass Criteria: + * - The DUT MUST forward the UDP packet with multicast address, MA1, to its Thread Network + * encapsulated in an MPL packet. + */ + Log("Step 6: BR_1 (DUT) automatically forwards the UDP packet to its Thread Network."); + nexus.AdvanceTime(kStabilizationTime); + + /** + * Step 7 + * - Device: Router + * - Description: Receives the ping request packet. Automatically uses the port 5683 (CoAP port) to verify that the + * UDP Multicast packet is received. + * - Pass Criteria: + * - N/A + */ + Log("Step 7: Router receives the UDP multicast packet."); + nexus.AdvanceTime(kStabilizationTime); + + /** + * Step 7a + * - Device: Router + * - Description: Harness instructs the device to stop listening to the multicast address, MA1. + * - Pass Criteria: + * - N/A + */ + Log("Step 7a: Router stops listening to the multicast address, MA1."); + SuccessOrQuit(router.Get().UnsubscribeExternalMulticast(ma1)); + nexus.AdvanceTime(kStabilizationTime); + + /** + * Step 8 + * - Device: Host + * - Description: After (MLR_TIMEOUT_MIN+2) seconds, harness instructs the device to multicast an ICMPv6 Echo (ping) + * Request packet to multicast address, MA1, on the backbone link. + * - Pass Criteria: + * - N/A + */ + Log("Step 8: After (MLR_TIMEOUT_MIN+2) seconds, Host multicasts an ICMPv6 Echo Request to MA1."); + nexus.AdvanceTime((kMlrTimeoutMin + 2) * 1000); + host.mInfraIf.SendEchoRequest(*hostUla, ma1, kEchoIdentifier, kEchoPayloadSize); + nexus.AdvanceTime(0); + + /** + * Step 9 + * - Device: BR_1 (DUT) + * - Description: Does not forward the ping request packet to its Thread Network. + * - Pass Criteria: + * - The DUT MUST NOT forward the ICMPv6 Echo (ping) Request packet with multicast address, MA1, to its Thread + * Network. + */ + Log("Step 9: BR_1 (DUT) does not forward the ping request packet to its Thread Network."); + nexus.AdvanceTime(kStabilizationTime); + + nexus.SaveTestInfo("test_1_2_MATN_TC_5.json"); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestMatnTc5(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/verify_1_2_MATN_TC_5.py b/tests/nexus/verify_1_2_MATN_TC_5.py new file mode 100644 index 000000000..feb058258 --- /dev/null +++ b/tests/nexus/verify_1_2_MATN_TC_5.py @@ -0,0 +1,235 @@ +#!/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 +import struct + +# 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.addrs import Ipv6Addr +from pktverify.bytes import Bytes + +# CoAP port. +COAP_PORT = 5683 + +# Status SUCCESS +ST_MLR_SUCCESS = 0 + + +def verify(pv): + # 5.10.5 MATN-TC-05: Re-registration to same Multicast Group + # + # 5.10.5.1 Topology + # - BR_1 (DUT) + # - BR_2 + # - Router + # - Host + # + # 5.10.5.2 Purpose & Description + # Verify that a Primary BBR (DUT) can manage a re-registration of a device on its network to remain receiving + # multicasts. The test also verifies the usage of UDP multicast packets across backbone and internal Thread + # network. + # + # Spec Reference | V1.2 Section | V1.3.0 Section + # -----------------|--------------|--------------- + # Multicast | 5.10.5 | 5.10.5 + + pkts = pv.pkts + pv.summary.show() + + BR_1 = pv.vars['BR_1'] + MA1 = pv.vars['MA1'] + HOST_ULA = pv.vars['HOST_ULA'] + BR_1_RLOC = pv.vars['BR_1_RLOC'] + BR_1_RLOC16 = pv.vars['BR_1_RLOC16'] + + def check_mpl(p): + if p.ipv6.src == Ipv6Addr(BR_1_RLOC): + return p.ipv6.opt.mpl.flag.s == 0 + else: + return p.ipv6.opt.mpl.flag.s == 1 and \ + p.ipv6.opt.mpl.seed_id == Bytes(struct.pack('>H', BR_1_RLOC16)) + + # Step 0 + # - Device: BR_1 (DUT) + # - Description: Topology formation – BR_1 (DUT). Boot DUT. Configure the MLR timeout value of the DUT to be + # MLR_TIMEOUT_MIN seconds. + # - Pass Criteria: + # - N/A + print("Step 0: Topology formation – BR_1 (DUT)") + + # Step 0 (cont.) + # - Device: N/A + # - Description: Topology formation – BR_2, Router + # - Pass Criteria: + # - N/A + print("Step 0 (cont.): Topology formation – BR_2, Router") + + # Step 1 + # - Device: Router + # - Description: Harness instructs the device to register multicast address, MA1 + # - Pass Criteria: + # - N/A + print("Step 1: Router registers multicast address, MA1") + pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.ipv6.dst == Ipv6Addr(BR_1_RLOC) or \ + Ipv6Addr(p.ipv6.dst).endswith(Bytes('000000fffe00fc00'))).\ + filter(lambda p: MA1 in p.coap.tlv.ipv6_address).\ + must_next() + + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + # Step 2 + # - Device: Host + # - Description: Harness instructs the device to send an ICMPv6 Echo (ping) Request packet to the multicast + # address, MA1. + # - Pass Criteria: + # - N/A + print("Step 2: Host sends an ICMPv6 Echo (ping) Request packet to the multicast address, MA1.") + pkts.filter_ping_request().\ + filter(lambda p: p.ipv6.src == Ipv6Addr(HOST_ULA)).\ + filter(lambda p: p.ipv6.dst == Ipv6Addr(MA1)).\ + must_next() + + # Step 3 + # - Device: BR_1 (DUT) + # - Description: Automatically forwards the ping request packet to its Thread Network. + # - Pass Criteria: + # - The DUT MUST forward the ICMPv6 Echo (ping) Request packet with multicast address, MA1, to its Thread + # Network encapsulated in an MPL packet. + print("Step 3: BR_1 (DUT) automatically forwards the ping request packet to its Thread Network.") + pkts.filter_wpan_src64(BR_1).\ + filter_ping_request().\ + filter(lambda p: p.ipv6.dst == 'ff03::fc' or \ + p.ipv6.dst == Ipv6Addr(MA1)).\ + filter(check_mpl).\ + must_next() + + # Step 4 + # - Device: Router + # - Description: Receives the MPL packet containing an encapsulated ping request packet to MA1, sent by Host, and + # automatically unicasts an ICMPv6 (ping) Reply packet back to Host. + # - Pass Criteria: + # - N/A + print("Step 4: Router automatically unicasts an ICMPv6 (ping) Reply packet back to Host.") + + # Step 4a + # - Device: Router + # - Description: Within MLR_TIMEOUT_MIN seconds of initial registration, Harness instructs the device to + # re-register for multicast address, MA1, at BR_1 by performing test case MATN-TC-02 (see 5.10.2) using Router + # as TD with the respective pass criteria. + # - Pass Criteria: + # - N/A + # - Note: MLDv2 and BLMR.ntf checks are intentionally skipped because they are part of MATN-TC-02. + print("Step 4a: Router re-registers for multicast address, MA1, at BR_1.") + pkts.filter_coap_request('/n/mr').\ + filter(lambda p: p.ipv6.dst == Ipv6Addr(BR_1_RLOC) or \ + Ipv6Addr(p.ipv6.dst).endswith(Bytes('000000fffe00fc00'))).\ + filter(lambda p: MA1 in p.coap.tlv.ipv6_address).\ + must_next() + + pkts.filter_coap_ack('/n/mr').\ + filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\ + must_next() + + # Step 5 + # - Device: Host + # - Description: Within MLR_TIMEOUT_MIN seconds, Harness instructs the device to send an ICMPv6 Echo (ping) Request + # packet to the multicast address, MA1. The destination port 5683 is used for the UDP Multicast packet + # transmission. + # - Pass Criteria: + # - N/A + print("Step 5: Host sends an ICMPv6 Echo (ping) Request packet (UDP) to the multicast address, MA1, port 5683.") + pkts.filter(lambda p: p.ipv6.src == Ipv6Addr(HOST_ULA)).\ + filter(lambda p: p.ipv6.dst == Ipv6Addr(MA1)).\ + filter(lambda p: p.udp.dstport == COAP_PORT).\ + must_next() + + # Step 6 + # - Device: BR_1 (DUT) + # - Description: Automatically forwards the UDP ping request packet to its Thread Network. + # - Pass Criteria: + # - The DUT MUST forward the UDP Echo (ping) Request packet with multicast address, MA1, to its Thread Network + # encapsulated in an MPL packet. + print("Step 6: BR_1 (DUT) automatically forwards the UDP ping request packet to its Thread Network.") + pkts.filter_wpan_src64(BR_1).\ + filter(lambda p: p.ipv6.dst == 'ff03::fc' or \ + p.ipv6.dst == Ipv6Addr(MA1)).\ + filter(lambda p: p.udp.dstport == COAP_PORT).\ + filter(check_mpl).\ + must_next() + + # Step 7 + # - Device: Router + # - Description: Receives the ping request packet. Automatically uses the port 5683 (CoAP port) to verify that the + # UDP Multicast packet is received. + # - Pass Criteria: + # - N/A + print("Step 7: Router receives the UDP multicast packet.") + + # Step 7a + # - Device: Router + # - Description: Harness instructs the device to stop listening to the multicast address, MA1. + # - Pass Criteria: + # - N/A + print("Step 7a: Router stops listening to the multicast address, MA1.") + + # Step 8 + # - Device: Host + # - Description: After (MLR_TIMEOUT_MIN+2) seconds, harness instructs the device to multicast an ICMPv6 Echo (ping) + # Request packet to multicast address, MA1, on the backbone link. + # - Pass Criteria: + # - N/A + print("Step 8: After (MLR_TIMEOUT_MIN+2) seconds, Host multicasts an ICMPv6 Echo Request to MA1.") + pkts.filter_ping_request().\ + filter(lambda p: p.ipv6.src == Ipv6Addr(HOST_ULA)).\ + filter(lambda p: p.ipv6.dst == Ipv6Addr(MA1)).\ + must_next() + + # Step 9 + # - Device: BR_1 (DUT) + # - Description: Does not forward the ping request packet to its Thread Network. + # - Pass Criteria: + # - The DUT MUST NOT forward the ICMPv6 Echo (ping) Request packet with multicast address, MA1, to its Thread + # Network. + print("Step 9: BR_1 (DUT) does not forward the ping request packet to its Thread Network.") + pkts.filter_wpan_src64(BR_1).\ + filter_ping_request().\ + filter(lambda p: p.ipv6.dst == 'ff03::fc' or p.ipv6.dst == Ipv6Addr(MA1)).\ + must_not_next() + + +if __name__ == '__main__': + verify_utils.run_main(verify)