mirror of
https://github.com/espressif/openthread.git
synced 2026-10-02 05:47:40 +00:00
[nexus] add MATN-TC-19 test case for multicast registration by MTD (#12707)
This commit implements the Thread 1.2 test MATN-TC-19: Multicast registration by MTD in the Nexus simulation framework. The test verifies that an MTD can correctly register multicast addresses through a parent Thread Router and receive multicast traffic from the backbone. Key implementation details: - Created test_1_2_MATN_TC_19.cpp to simulate the network topology (BR_1, BR_2, Router, MTD, and Host) and execute the test steps using direct method calls. - Implemented verify_1_2_MATN_TC_19.py for PCAP-based verification of MLE Child Update exchanges, MLR registrations, and multicast ICMPv6 Echo Request/Reply forwarding. - Configured the test to use Note log level and included 1-line log output for each step to match existing Nexus tests. - Integrated the new test into the Nexus build system via CMakeLists.txt and added it to the default test execution list in run_nexus_tests.sh.
This commit is contained in:
@@ -245,6 +245,7 @@ ot_nexus_test(1_2_MATN_TC_10 "cert;nexus")
|
|||||||
ot_nexus_test(1_2_MATN_TC_12 "cert;nexus")
|
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_15 "cert;nexus")
|
||||||
ot_nexus_test(1_2_MATN_TC_16 "cert;nexus")
|
ot_nexus_test(1_2_MATN_TC_16 "cert;nexus")
|
||||||
|
ot_nexus_test(1_2_MATN_TC_19 "cert;nexus")
|
||||||
|
|
||||||
# Misc tests
|
# Misc tests
|
||||||
ot_nexus_test(border_admitter "core;nexus")
|
ot_nexus_test(border_admitter "core;nexus")
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ DEFAULT_TESTS=(
|
|||||||
"1_2_MATN_TC_12"
|
"1_2_MATN_TC_12"
|
||||||
"1_2_MATN_TC_15"
|
"1_2_MATN_TC_15"
|
||||||
"1_2_MATN_TC_16"
|
"1_2_MATN_TC_16"
|
||||||
|
"1_2_MATN_TC_19"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use provided arguments or the default test list
|
# Use provided arguments or the default test list
|
||||||
|
|||||||
@@ -0,0 +1,391 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdio.h>
|
||||||
|
|
||||||
|
#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 a node to join as a child, in milliseconds.
|
||||||
|
*/
|
||||||
|
static constexpr uint32_t kAttachAsChildTime = 10 * 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to advance for ICMPv6 Echo Reply, in milliseconds.
|
||||||
|
*/
|
||||||
|
static constexpr uint32_t kEchoReplyWaitTime = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll period for SED in milliseconds.
|
||||||
|
*/
|
||||||
|
static constexpr uint32_t kSedPollPeriod = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to allow for a Child Update Request, in milliseconds.
|
||||||
|
*/
|
||||||
|
static constexpr uint32_t kChildUpdateRequestTimeMs = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICMPv6 Echo Request payload size.
|
||||||
|
*/
|
||||||
|
static constexpr uint16_t kEchoPayloadSize = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICMPv6 Echo Request identifier.
|
||||||
|
*/
|
||||||
|
static constexpr uint16_t kEchoIdentifier = 0x1234;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multicast address MA2.
|
||||||
|
*/
|
||||||
|
static const char kMA2[] = "ff05::2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multicast address MA3.
|
||||||
|
*/
|
||||||
|
static const char kMA3[] = "ff05::3";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multicast address MA5.
|
||||||
|
*/
|
||||||
|
static const char kMA5[] = "ff05::5";
|
||||||
|
|
||||||
|
void TestMatnTc19(void)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 5.10.15 MATN-TC-19: Multicast registration by MTD
|
||||||
|
*
|
||||||
|
* Purpose:
|
||||||
|
* The purpose of this test case is to verify that an MTD can register a multicast address through a Parent Thread
|
||||||
|
* Router.
|
||||||
|
*
|
||||||
|
* Spec Reference | V1.2 Section
|
||||||
|
* ---------------|-------------
|
||||||
|
* MATN-TC-19 | 5.10.15
|
||||||
|
*/
|
||||||
|
|
||||||
|
Core nexus;
|
||||||
|
Node &br1 = nexus.CreateNode();
|
||||||
|
Node &br2 = nexus.CreateNode();
|
||||||
|
Node &router = nexus.CreateNode();
|
||||||
|
Node &mtd = nexus.CreateNode();
|
||||||
|
Node &host = nexus.CreateNode();
|
||||||
|
Ip6::Address ma2;
|
||||||
|
Ip6::Address ma3;
|
||||||
|
Ip6::Address ma5;
|
||||||
|
const Ip6::Address *hostUla;
|
||||||
|
|
||||||
|
br1.SetName("BR_1");
|
||||||
|
br2.SetName("BR_2");
|
||||||
|
router.SetName("Router");
|
||||||
|
mtd.SetName("MTD");
|
||||||
|
host.SetName("Host");
|
||||||
|
|
||||||
|
SuccessOrQuit(ma2.FromString(kMA2));
|
||||||
|
SuccessOrQuit(ma3.FromString(kMA3));
|
||||||
|
SuccessOrQuit(ma5.FromString(kMA5));
|
||||||
|
|
||||||
|
nexus.AdvanceTime(0);
|
||||||
|
|
||||||
|
Instance::SetLogLevel(kLogLevelNote);
|
||||||
|
|
||||||
|
Log("Step 0: Topology formation – BR_1, BR_2, Router, MTD");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 0
|
||||||
|
* - Device: N/A
|
||||||
|
* - Description: Topology formation – BR_1, BR_2, Router, MTD
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - N/A
|
||||||
|
*/
|
||||||
|
|
||||||
|
br1.AllowList(br2);
|
||||||
|
br1.AllowList(router);
|
||||||
|
br2.AllowList(br1);
|
||||||
|
br2.AllowList(router);
|
||||||
|
router.AllowList(br1);
|
||||||
|
router.AllowList(br2);
|
||||||
|
router.AllowList(mtd);
|
||||||
|
mtd.AllowList(router);
|
||||||
|
|
||||||
|
br1.Form();
|
||||||
|
nexus.AdvanceTime(kFormNetworkTime);
|
||||||
|
VerifyOrQuit(br1.Get<Mle::Mle>().IsLeader());
|
||||||
|
|
||||||
|
br1.Get<BorderRouter::InfraIf>().Init(1, true);
|
||||||
|
br1.Get<BorderRouter::RoutingManager>().Init();
|
||||||
|
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().SetEnabled(true));
|
||||||
|
br1.Get<BackboneRouter::Local>().SetEnabled(true);
|
||||||
|
|
||||||
|
br2.Join(br1, Node::kAsFtd);
|
||||||
|
router.Join(br1, Node::kAsFtd);
|
||||||
|
nexus.AdvanceTime(kAttachToRouterTime);
|
||||||
|
|
||||||
|
VerifyOrQuit(br2.Get<Mle::Mle>().IsRouter());
|
||||||
|
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
|
||||||
|
|
||||||
|
br2.Get<BorderRouter::InfraIf>().Init(1, true);
|
||||||
|
br2.Get<BorderRouter::RoutingManager>().Init();
|
||||||
|
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().SetEnabled(true));
|
||||||
|
br2.Get<BackboneRouter::Local>().SetEnabled(true);
|
||||||
|
|
||||||
|
SuccessOrQuit(mtd.Get<DataPollSender>().SetExternalPollPeriod(kSedPollPeriod));
|
||||||
|
mtd.Join(router, Node::kAsSed);
|
||||||
|
nexus.AdvanceTime(kAttachAsChildTime);
|
||||||
|
VerifyOrQuit(mtd.Get<Mle::Mle>().IsChild());
|
||||||
|
|
||||||
|
nexus.AdvanceTime(kStabilizationTime);
|
||||||
|
|
||||||
|
VerifyOrQuit(br1.Get<BackboneRouter::Local>().IsPrimary());
|
||||||
|
VerifyOrQuit(router.Get<BackboneRouter::Leader>().HasPrimary());
|
||||||
|
|
||||||
|
hostUla = &host.mInfraIf.FindMatchingAddress("fd00::/8");
|
||||||
|
|
||||||
|
nexus.AddTestVar("TEST_MA2", kMA2);
|
||||||
|
nexus.AddTestVar("TEST_MA3", kMA3);
|
||||||
|
nexus.AddTestVar("TEST_MA5", kMA5);
|
||||||
|
nexus.AddTestVar("HOST_BACKBONE_ULA", hostUla->ToString().AsCString());
|
||||||
|
|
||||||
|
Log("Step 1a: MTD Registers addresses MA2 & MA5 with its parent.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 1a
|
||||||
|
* - Device: MTD
|
||||||
|
* - Description: Registers addresses MA2 & MA5 with its parent. (MA5 is optional for MEDs)
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = MTD:
|
||||||
|
* - The DUT MUST unicast an MLE Child Update Request command to Router, where the payload contains (amongst other
|
||||||
|
* fields):
|
||||||
|
* - Address Registration TLV: MA2
|
||||||
|
* - MA5 (Optional for MED)
|
||||||
|
*/
|
||||||
|
SuccessOrQuit(mtd.Get<Ip6::Netif>().SubscribeExternalMulticast(ma2));
|
||||||
|
SuccessOrQuit(mtd.Get<Ip6::Netif>().SubscribeExternalMulticast(ma5));
|
||||||
|
nexus.AdvanceTime(kChildUpdateRequestTimeMs);
|
||||||
|
|
||||||
|
Log("Step 1b: Router Automatically responds to the registration request.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 1b
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Automatically responds to the registration request.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = Router:
|
||||||
|
* - The DUT MUST unicast an MLE Child Update Response command to MTD, where the payload contains (amongst other
|
||||||
|
* fields):
|
||||||
|
* - Address Registration TLV: MA2
|
||||||
|
* - MA5
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 1c: Router Automatically registers multicast address, MA2 at BR_1");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 1c
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Automatically registers multicast address, MA2 at BR_1
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = Router:
|
||||||
|
* - The DUT MUST unicast an MLR.req CoAP request to BR_1 as follows: coap://[<BR_1 RLOC or PBBR ALOC>]:MM/n/mr
|
||||||
|
* - Where the payload contains:
|
||||||
|
* - IPv6 Addresses TLV: MA2
|
||||||
|
*/
|
||||||
|
nexus.AdvanceTime(kMlrRegistrationTime);
|
||||||
|
|
||||||
|
Log("Step 2: MTD Registers addresses MA2 and MA3 with its parent.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 2
|
||||||
|
* - Device: MTD
|
||||||
|
* - Description: Registers addresses MA2 and MA3 with its parent.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = MTD:
|
||||||
|
* - The DUT MUST unicast an MLE Child Update Request command to Router_1, where the payload contains (amongst
|
||||||
|
* other fields):
|
||||||
|
* - Address Registration TLV: MA2 and MA3.
|
||||||
|
*/
|
||||||
|
SuccessOrQuit(mtd.Get<Ip6::Netif>().UnsubscribeExternalMulticast(ma5));
|
||||||
|
SuccessOrQuit(mtd.Get<Ip6::Netif>().SubscribeExternalMulticast(ma3));
|
||||||
|
nexus.AdvanceTime(kChildUpdateRequestTimeMs);
|
||||||
|
|
||||||
|
Log("Step 3: Router Automatically responds to the registration request.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 3
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Automatically responds to the registration request.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = Router:
|
||||||
|
* - The DUT MUST unicast an MLE Child Update Response command to MTD, where the payload contains (amongst other
|
||||||
|
* fields):
|
||||||
|
* - Address Registration TLV: MA2 and MA3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 4: Router Wait at most PARENT_AGGREGATE_DELAY.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 4
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Wait at most PARENT_AGGREGATE_DELAY.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - The next message (below) is sent not later than PARENT_AGGREGATE_DELAY seconds since step 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 5: Router Automatically registers multicast addresses, MA2 and MA3 at BR_1.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 5
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Automatically registers multicast addresses, MA2 and MA3 at BR_1. Unicasts an MLR.req CoAP request
|
||||||
|
* to BR_1 as follows: coap://[<BR_1 RLOC or PBBR ALOC>]:MM/n/mr Where the payload contains: IPv6 Addresses TLV:
|
||||||
|
* MA3, MA2 (optional).
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - N/A
|
||||||
|
*/
|
||||||
|
nexus.AdvanceTime(kMlrRegistrationTime);
|
||||||
|
|
||||||
|
Log("Step 6: BR_1 Automatically responds to the registration request.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 6
|
||||||
|
* - Device: BR_1
|
||||||
|
* - Description: Automatically responds to the registration request. Unicasts an MLR.rsp CoAP response to Router_1
|
||||||
|
* as follows: 2.04 changed Where the payload contains: Status TLV: ST_MLR_SUCCESS.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - N/A
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 7: Host ICMPv6 Echo (ping) Request packet to multicast address, MA3, on the backbone link.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 7
|
||||||
|
* - Device: Host
|
||||||
|
* - Description: Harness instructs the device to send ICMPv6 Echo (ping) Request packet to multicast address, MA3,
|
||||||
|
* on the backbone link.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - N/A
|
||||||
|
*/
|
||||||
|
host.mInfraIf.SendEchoRequest(*hostUla, ma3, kEchoIdentifier, kEchoPayloadSize);
|
||||||
|
nexus.AdvanceTime(0);
|
||||||
|
|
||||||
|
Log("Step 8: BR_1 Automatically forwards the ping request packet to its Thread Network.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 8
|
||||||
|
* - Device: BR_1
|
||||||
|
* - Description: Automatically forwards the ping request packet to its Thread Network encapsulated in an MPL
|
||||||
|
* packet.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - N/A
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 8a: Router Receives the multicast ping request packet.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 8a
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Receives the multicast ping request packet and if MTD is a SED then it automatically pends it for
|
||||||
|
* delivery to MTD. If MTD is MED, it automatically forwards the packet as defined in step 10.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - None.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 9: MTD polls for the pending ping request packet.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 9
|
||||||
|
* - Device: MTD
|
||||||
|
* - Description: If MTD is a SED, it wakes up and/or polls for the pending ping request packet. If MTD is a MED,
|
||||||
|
* this test step is skipped.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = MTD (SED):
|
||||||
|
* - The DUT MUST send a MAC poll request to Router_1.
|
||||||
|
*/
|
||||||
|
nexus.AdvanceTime(kEchoReplyWaitTime);
|
||||||
|
|
||||||
|
Log("Step 10: Router Automatically forwards the ping request packet to MTD.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 10
|
||||||
|
* - Device: Router
|
||||||
|
* - Description: Automatically forwards the ping request packet to MTD.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = Router:
|
||||||
|
* - The DUT MUST forward the ICMPv6 Echo (ping) Request packet with multicast address, MA3, to MTD using
|
||||||
|
* 802.15.4 unicast.
|
||||||
|
* - The ping request packet MUST NOT be encapsulated in an MPL packet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Log("Step 11: MTD sends a unicast ICMPv6 Echo Response packet back to Host.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 11
|
||||||
|
* - Device: MTD
|
||||||
|
* - Description: Receives the multicast ping request packet and sends a unicast ICMPv6 Echo (ping) Response packet
|
||||||
|
* back to Host.
|
||||||
|
* - Pass Criteria:
|
||||||
|
* - For DUT = MTD:
|
||||||
|
* - The DUT MUST unicast a ICMPv6 Echo (ping) Response packet back to Host.
|
||||||
|
*/
|
||||||
|
nexus.AdvanceTime(kEchoReplyWaitTime);
|
||||||
|
|
||||||
|
nexus.SaveTestInfo("test_1_2_MATN_TC_19.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace Nexus */
|
||||||
|
} /* namespace ot */
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
ot::Nexus::TestMatnTc19();
|
||||||
|
printf("All tests passed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
#!/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
|
||||||
|
from pktverify.null_field import nullField
|
||||||
|
|
||||||
|
ST_MLR_SUCCESS = 0
|
||||||
|
PARENT_AGGREGATE_DELAY = 5
|
||||||
|
|
||||||
|
|
||||||
|
def verify(pv):
|
||||||
|
# 5.10.15 MATN-TC-19: Multicast registration by MTD
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# The purpose of this test case is to verify that an MTD can register a multicast address through a Parent Thread
|
||||||
|
# Router.
|
||||||
|
#
|
||||||
|
# Spec Reference | V1.2 Section
|
||||||
|
# ---------------|-------------
|
||||||
|
# MATN-TC-19 | 5.10.15
|
||||||
|
|
||||||
|
pkts = pv.pkts
|
||||||
|
pv.summary.show()
|
||||||
|
|
||||||
|
BR_1 = pv.vars['BR_1']
|
||||||
|
MTD = pv.vars['MTD']
|
||||||
|
|
||||||
|
# Use the test-specific variables from the core
|
||||||
|
v_MA2 = Ipv6Addr(pv.vars['TEST_MA2'])
|
||||||
|
v_MA3 = Ipv6Addr(pv.vars['TEST_MA3'])
|
||||||
|
v_MA5 = Ipv6Addr(pv.vars['TEST_MA5'])
|
||||||
|
HOST_ULA = Ipv6Addr(pv.vars['HOST_BACKBONE_ULA'])
|
||||||
|
|
||||||
|
# Step 0
|
||||||
|
# - Device: N/A
|
||||||
|
# - Description: Topology formation – BR_1, BR_2, Router, MTD
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - N/A
|
||||||
|
print("Step 0: Topology formation – BR_1, BR_2, Router, MTD")
|
||||||
|
|
||||||
|
# Step 1a
|
||||||
|
# - Device: MTD
|
||||||
|
# - Description: Registers addresses MA2 & MA5 with its parent. (MA5 is optional for MEDs)
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = MTD:
|
||||||
|
# - The DUT MUST unicast an MLE Child Update Request command to Router, where the payload contains (amongst other
|
||||||
|
# fields):
|
||||||
|
# - Address Registration TLV: MA2
|
||||||
|
# - MA5 (Optional for MED)
|
||||||
|
print("Step 1a: MTD Registers addresses MA2 & MA5 with its parent.")
|
||||||
|
pkt = pkts.filter_wpan_src64(MTD).\
|
||||||
|
filter_mle_cmd(consts.MLE_CHILD_UPDATE_REQUEST).\
|
||||||
|
filter(lambda p: consts.ADDRESS_REGISTRATION_TLV in p.mle.tlv.type).\
|
||||||
|
filter(lambda p: v_MA2 in p.mle.tlv.addr_reg).\
|
||||||
|
must_next()
|
||||||
|
assert v_MA5 in pkt.mle.tlv.addr_reg
|
||||||
|
|
||||||
|
# Step 1b
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Automatically responds to the registration request.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = Router:
|
||||||
|
# - The DUT MUST unicast an MLE Child Update Response command to MTD, where the payload contains (amongst other
|
||||||
|
# fields):
|
||||||
|
# - Address Registration TLV: MA2
|
||||||
|
# - MA5
|
||||||
|
print("Step 1b: Router Automatically responds to the registration request.")
|
||||||
|
pkt = pkts.filter_wpan_dst64(MTD).\
|
||||||
|
filter_mle_cmd(consts.MLE_CHILD_UPDATE_RESPONSE).\
|
||||||
|
filter(lambda p: consts.ADDRESS_REGISTRATION_TLV in p.mle.tlv.type).\
|
||||||
|
filter(lambda p: v_MA2 in p.mle.tlv.addr_reg).\
|
||||||
|
must_next()
|
||||||
|
assert v_MA5 in pkt.mle.tlv.addr_reg
|
||||||
|
|
||||||
|
# Step 1c
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Automatically registers multicast address, MA2 at BR_1
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = Router:
|
||||||
|
# - The DUT MUST unicast an MLR.req CoAP request to BR_1 as follows: coap://[<BR_1 RLOC or PBBR ALOC>]:MM/n/mr
|
||||||
|
# - Where the payload contains:
|
||||||
|
# - IPv6 Addresses TLV: MA2
|
||||||
|
print("Step 1c: Router Automatically registers multicast address, MA2 at BR_1")
|
||||||
|
target_pkt = pkts.filter_coap_request('/n/mr').\
|
||||||
|
filter(lambda p: v_MA2 in p.coap.tlv.ipv6_address).\
|
||||||
|
must_next()
|
||||||
|
|
||||||
|
# Step 2
|
||||||
|
# - Device: MTD
|
||||||
|
# - Description: Registers addresses MA2 and MA3 with its parent.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = MTD:
|
||||||
|
# - The DUT MUST unicast an MLE Child Update Request command to Router_1, where the payload contains (amongst
|
||||||
|
# other fields):
|
||||||
|
# - Address Registration TLV: MA2 and MA3.
|
||||||
|
print("Step 2: MTD Registers addresses MA2 and MA3 with its parent.")
|
||||||
|
pkt = pkts.filter_wpan_src64(MTD).\
|
||||||
|
filter_mle_cmd(consts.MLE_CHILD_UPDATE_REQUEST).\
|
||||||
|
filter(lambda p: consts.ADDRESS_REGISTRATION_TLV in p.mle.tlv.type).\
|
||||||
|
filter(lambda p: v_MA3 in p.mle.tlv.addr_reg).\
|
||||||
|
must_next()
|
||||||
|
assert v_MA2 in pkt.mle.tlv.addr_reg and v_MA5 not in pkt.mle.tlv.addr_reg
|
||||||
|
|
||||||
|
# Step 3
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Automatically responds to the registration request.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = Router:
|
||||||
|
# - The DUT MUST unicast an MLE Child Update Response command to MTD, where the payload contains (amongst other
|
||||||
|
# fields):
|
||||||
|
# - Address Registration TLV: MA2 and MA3.
|
||||||
|
print("Step 3: Router Automatically responds to the registration request.")
|
||||||
|
step3_pkt = pkts.filter_wpan_dst64(MTD).\
|
||||||
|
filter_mle_cmd(consts.MLE_CHILD_UPDATE_RESPONSE).\
|
||||||
|
filter(lambda p: consts.ADDRESS_REGISTRATION_TLV in p.mle.tlv.type).\
|
||||||
|
filter(lambda p: v_MA3 in p.mle.tlv.addr_reg).\
|
||||||
|
must_next()
|
||||||
|
assert v_MA2 in step3_pkt.mle.tlv.addr_reg
|
||||||
|
|
||||||
|
# Step 4
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Wait at most PARENT_AGGREGATE_DELAY.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - The next message (below) is sent not later than PARENT_AGGREGATE_DELAY seconds since step 3.
|
||||||
|
print("Step 4: Router Wait at most PARENT_AGGREGATE_DELAY.")
|
||||||
|
|
||||||
|
# Step 5
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Automatically registers multicast addresses, MA2 and MA3 at BR_1. Unicasts an MLR.req CoAP request
|
||||||
|
# to BR_1 as follows: coap://[<BR_1 RLOC or PBBR ALOC>]:MM/n/mr Where the payload contains: IPv6 Addresses TLV:
|
||||||
|
# MA3, MA2 (optional).
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - N/A
|
||||||
|
print("Step 5: Router Automatically registers multicast addresses, MA2 and MA3 at BR_1.")
|
||||||
|
step5_pkt = pkts.filter_coap_request('/n/mr').\
|
||||||
|
filter(lambda p: v_MA3 in p.coap.tlv.ipv6_address).\
|
||||||
|
must_next()
|
||||||
|
# MA2 is optional in Step 5 as per spec. OpenThread might only send new registrations.
|
||||||
|
# assert v_MA2 in step5_pkt.coap.tlv.ipv6_address, f"MA2 ({v_MA2}) not in {step5_pkt.coap.tlv.ipv6_address}"
|
||||||
|
|
||||||
|
# Verify Step 4 timing
|
||||||
|
assert step5_pkt.sniff_timestamp - step3_pkt.sniff_timestamp <= PARENT_AGGREGATE_DELAY + 2.0, \
|
||||||
|
f"Step 5 sent too late: {step5_pkt.sniff_timestamp - step3_pkt.sniff_timestamp}s > {PARENT_AGGREGATE_DELAY}s"
|
||||||
|
|
||||||
|
# Step 6
|
||||||
|
# - Device: BR_1
|
||||||
|
# - Description: Automatically responds to the registration request. Unicasts an MLR.rsp CoAP response to Router_1
|
||||||
|
# as follows: 2.04 changed Where the payload contains: Status TLV: ST_MLR_SUCCESS.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - N/A
|
||||||
|
print("Step 6: BR_1 Automatically responds to the registration request.")
|
||||||
|
pkts.filter_coap_ack('/n/mr').\
|
||||||
|
filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\
|
||||||
|
must_next()
|
||||||
|
|
||||||
|
# Step 7
|
||||||
|
# - Device: Host
|
||||||
|
# - Description: Harness instructs the device to send ICMPv6 Echo (ping) Request packet to multicast address, MA3,
|
||||||
|
# on the backbone link.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - N/A
|
||||||
|
print("Step 7: Host ICMPv6 Echo (ping) Request packet to multicast address, MA3, on the backbone link.")
|
||||||
|
|
||||||
|
# Step 8
|
||||||
|
# - Device: BR_1
|
||||||
|
# - Description: Automatically forwards the ping request packet to its Thread Network encapsulated in an MPL packet.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - N/A
|
||||||
|
print("Step 8: BR_1 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').\
|
||||||
|
must_next()
|
||||||
|
|
||||||
|
# Step 8a
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Receives the multicast ping request packet and if MTD is a SED then it automatically pends it for
|
||||||
|
# delivery to MTD. If MTD is MED, it automatically forwards the packet as defined in step 10.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - None.
|
||||||
|
print("Step 8a: Router Receives the multicast ping request packet.")
|
||||||
|
|
||||||
|
# Step 9
|
||||||
|
# - Device: MTD
|
||||||
|
# - Description: If MTD is a SED, it wakes up and/or polls for the pending ping request packet. If MTD is a MED,
|
||||||
|
# this test step is skipped.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = MTD (SED):
|
||||||
|
# - The DUT MUST send a MAC poll request to Router_1.
|
||||||
|
print("Step 9: MTD sends a MAC poll request.")
|
||||||
|
pkts.filter_wpan_src64(MTD).\
|
||||||
|
filter(lambda p: p.wpan.frame_type == consts.MAC_FRAME_TYPE_MAC_CMD and p.wpan.cmd == consts.WPAN_DATA_REQUEST).\
|
||||||
|
must_next()
|
||||||
|
|
||||||
|
# Step 10
|
||||||
|
# - Device: Router
|
||||||
|
# - Description: Automatically forwards the ping request packet to MTD.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = Router:
|
||||||
|
# - The DUT MUST forward the ICMPv6 Echo (ping) Request packet with multicast address, MA3, to MTD using 802.15.4
|
||||||
|
# unicast.
|
||||||
|
# - The ping request packet MUST NOT be encapsulated in an MPL packet.
|
||||||
|
print("Step 10: Router Automatically forwards the ping request packet to MTD.")
|
||||||
|
pkts.filter_ping_request().\
|
||||||
|
filter(lambda p: p.ipv6.dst == v_MA3).\
|
||||||
|
filter(lambda p: p.wpan.dst64 == MTD or (p.wpan.dst16 is not nullField and p.wpan.dst16 != 0xffff)).\
|
||||||
|
filter(lambda p: not p.ipv6inner).\
|
||||||
|
must_next()
|
||||||
|
|
||||||
|
# Step 11
|
||||||
|
# - Device: MTD
|
||||||
|
# - Description: Receives the multicast ping request packet and sends a unicast ICMPv6 Echo (ping) Response packet
|
||||||
|
# back to Host.
|
||||||
|
# - Pass Criteria:
|
||||||
|
# - For DUT = MTD:
|
||||||
|
# - The DUT MUST unicast a ICMPv6 Echo (ping) Response packet back to Host.
|
||||||
|
print("Step 11: MTD sends a unicast ICMPv6 Echo Response packet back to Host.")
|
||||||
|
pkts.filter_wpan_src64(MTD).\
|
||||||
|
filter_ping_reply().\
|
||||||
|
filter(lambda p: p.ipv6.dst == HOST_ULA).\
|
||||||
|
must_next()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
verify_utils.run_main(verify)
|
||||||
@@ -269,6 +269,7 @@ def apply_patches():
|
|||||||
Layer.get_field = patched_get_field
|
Layer.get_field = patched_get_field
|
||||||
|
|
||||||
layer_fields._LAYER_FIELDS['mle.tlv.link_forward_series'] = layer_fields._list(layer_fields._auto)
|
layer_fields._LAYER_FIELDS['mle.tlv.link_forward_series'] = layer_fields._list(layer_fields._auto)
|
||||||
|
layer_fields._LAYER_FIELDS['mle.tlv.addr_reg_ipv6'] = layer_fields._list(layer_fields._ipv6_addr)
|
||||||
layer_fields._LAYER_FIELDS['mle.tlv.link_forward_series_flags'] = layer_fields._auto
|
layer_fields._LAYER_FIELDS['mle.tlv.link_forward_series_flags'] = layer_fields._auto
|
||||||
layer_fields._LAYER_FIELDS['mle.tlv.link_status_sub_tlv'] = layer_fields._auto
|
layer_fields._LAYER_FIELDS['mle.tlv.link_status_sub_tlv'] = layer_fields._auto
|
||||||
layer_fields._LAYER_FIELDS['mle.tlv.query_id'] = layer_fields._auto
|
layer_fields._LAYER_FIELDS['mle.tlv.query_id'] = layer_fields._auto
|
||||||
|
|||||||
@@ -722,6 +722,9 @@ def get_layer_field(packet: RawPacket, field_uri: str) -> Any:
|
|||||||
field_uri = '.'.join([layer_name] + secs[1:])
|
field_uri = '.'.join([layer_name] + secs[1:])
|
||||||
layer_depth = 1
|
layer_depth = 1
|
||||||
|
|
||||||
|
if field_uri == 'mle.tlv.addr_reg':
|
||||||
|
field_uri = 'mle.tlv.addr_reg_ipv6'
|
||||||
|
|
||||||
if is_layer_field(field_uri):
|
if is_layer_field(field_uri):
|
||||||
candidate_layers = _get_candidate_layers(packet, layer_name)
|
candidate_layers = _get_candidate_layers(packet, layer_name)
|
||||||
for layers in candidate_layers:
|
for layers in candidate_layers:
|
||||||
|
|||||||
Reference in New Issue
Block a user