mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[nexus] add 1_3_GEN_TC_1 to verify Thread Version TLV (#12810)
This commit adds Nexus test case 1_3_GEN_TC_1, which verifies that the Thread Version TLV uses the value '4' or higher, as required by the Thread 1.3.x and 1.4.x specifications. The implementation includes: - tests/nexus/test_1_3_GEN_TC_1.cpp: C++ test logic that sets up a topology with a Border Router, a Router, and an End Device. It triggers MLE attachment procedures and discovery scans to generate MLE packets containing the Version TLV. For 1.4 devices, it also sends TMF Get Diagnostic Requests to verify the Version TLV in DIAG_GET.rsp. - tests/nexus/verify_1_3_GEN_TC_1.py: Python script that verifies the MLE Version TLV in Parent Request/Response, Child ID Request, and Discovery Response packets. It also verifies the Version TLV in Network Diagnostic responses for 1.4 devices. - Integration into tests/nexus/CMakeLists.txt and tests/nexus/run_nexus_tests.sh for automated building and execution.
This commit is contained in:
@@ -281,6 +281,7 @@ ot_nexus_test(1_3_SRPC_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_3_SRPC_TC_4 "cert;nexus")
|
||||
ot_nexus_test(1_3_SRPC_TC_5 "cert;nexus")
|
||||
ot_nexus_test(1_3_SRPC_TC_7 "cert;nexus")
|
||||
ot_nexus_test(1_3_GEN_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_3_DIAG_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_3_DIAG_TC_2 "cert;nexus")
|
||||
ot_nexus_test(1_4_TREL_TC_1 "cert;nexus")
|
||||
|
||||
@@ -216,6 +216,7 @@ DEFAULT_TESTS=(
|
||||
"1_3_SRPC_TC_4"
|
||||
"1_3_SRPC_TC_5"
|
||||
"1_3_SRPC_TC_7"
|
||||
"1_3_GEN_TC_1"
|
||||
"1_3_DIAG_TC_1"
|
||||
"1_3_DIAG_TC_2"
|
||||
"1_4_TREL_TC_1"
|
||||
@@ -238,7 +239,7 @@ run_test()
|
||||
# Strip 'nexus_' prefix if present
|
||||
test_full="${test_full#nexus_}"
|
||||
|
||||
local test_base="${test_full%_[AB]}"
|
||||
local test_base="${test_full%_[ABC]}"
|
||||
local topology=""
|
||||
if [[ $test_full != "$test_base" ]]; then
|
||||
topology="${test_full##*_}"
|
||||
@@ -313,6 +314,9 @@ for t in "${TESTS_TO_RUN[@]}"; do
|
||||
1_1_6_1_1 | 1_1_6_1_2 | 1_1_6_1_3 | 1_1_6_1_6 | 1_1_6_2_1 | 1_1_6_2_2 | 1_1_6_3_1 | 1_1_6_3_2 | 1_1_6_4_1 | 1_1_6_4_2 | 1_1_6_5_1 | 1_1_6_5_2 | 1_1_6_5_3 | 1_1_6_6_1 | 1_1_6_6_2 | 1_1_9_2_1 | 1_1_9_2_3 | 1_1_9_2_4 | 1_1_9_2_19)
|
||||
expanded_tests+=("${t}_A" "${t}_B")
|
||||
;;
|
||||
1_3_GEN_TC_1)
|
||||
expanded_tests+=("${t}_A" "${t}_B" "${t}_C")
|
||||
;;
|
||||
*)
|
||||
expanded_tests+=("$t")
|
||||
;;
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* 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 <string.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 = 13 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for a node to join as a child and upgrade to a router, in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kAttachToRouterTime = 200 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for the discovery scan to complete.
|
||||
*/
|
||||
static constexpr uint32_t kDiscoveryTime = 5 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for the diagnostic response to be received.
|
||||
*/
|
||||
static constexpr uint32_t kDiagResponseTime = 5 * 1000;
|
||||
|
||||
/**
|
||||
* Data poll period for SED, in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kPollPeriod = 500;
|
||||
|
||||
enum Topology
|
||||
{
|
||||
kTopologyA, // FED
|
||||
kTopologyB, // MED
|
||||
kTopologyC, // SED
|
||||
};
|
||||
|
||||
void Test_1_3_GEN_TC_1(Topology aTopology, const char *aJsonFileName)
|
||||
{
|
||||
/**
|
||||
* 9.1. [1.3] [CERT] Thread Version is '4' or higher
|
||||
*
|
||||
* 9.1.1. Purpose
|
||||
* Verify that the Thread Version TLV uses the value '4' or higher. '4' is required for Thread 1.3.x devices. '5'
|
||||
* is required for Thread 1.4.x devices.
|
||||
*
|
||||
* 9.1.2. Topology
|
||||
* - BR_1: Thread Border Router DUT
|
||||
* - Router_1: Thread Router DUT
|
||||
* - ED_1: Thread End Device DUT, including FEDs, MEDs and SEDs
|
||||
*
|
||||
* Spec Reference | V1.1 Section | V1.3.0 Section
|
||||
* -----------------|--------------|---------------
|
||||
* Thread Version | 5.18.2.1 | 5.18.2.1
|
||||
*/
|
||||
|
||||
Core nexus;
|
||||
|
||||
Node &br1 = nexus.CreateNode();
|
||||
Node &router1 = nexus.CreateNode();
|
||||
Node &ed1 = nexus.CreateNode();
|
||||
|
||||
br1.SetName("BR_1");
|
||||
router1.SetName("Router_1");
|
||||
ed1.SetName("ED_1");
|
||||
|
||||
/**
|
||||
* In the cpp, use AllowList to specify links between nodes. There is a link between the following node pairs:
|
||||
* - BR_1 and Router_1
|
||||
* - Router_1 and ED_1
|
||||
*/
|
||||
br1.AllowList(router1);
|
||||
router1.AllowList(br1);
|
||||
router1.AllowList(ed1);
|
||||
ed1.AllowList(router1);
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
|
||||
|
||||
switch (aTopology)
|
||||
{
|
||||
case kTopologyA:
|
||||
Log("Topology A: ED_1 as FED");
|
||||
break;
|
||||
case kTopologyB:
|
||||
Log("Topology B: ED_1 as MED");
|
||||
break;
|
||||
case kTopologyC:
|
||||
Log("Topology C: ED_1 as SED");
|
||||
break;
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 1: BR_1 Enable");
|
||||
|
||||
/**
|
||||
* - Step 1
|
||||
* - Device: BR_1
|
||||
* - Description (GEN-9.1): Enable
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = BR_1: The DUT MUST become Leader of the Thread Network.
|
||||
*/
|
||||
br1.Form();
|
||||
nexus.AdvanceTime(kFormNetworkTime);
|
||||
VerifyOrQuit(br1.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 2: Router_1 Enable");
|
||||
|
||||
/**
|
||||
* - Step 2
|
||||
* - Device: Router_1
|
||||
* - Description (GEN-9.1): Enable
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - N/A
|
||||
*/
|
||||
router1.Join(br1);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 3: Router_1 Automatically sends MLE Parent Request multicast.");
|
||||
|
||||
/**
|
||||
* - Step 3
|
||||
* - Device: Router_1
|
||||
* - Description (GEN-9.1): Automatically sends MLE Parent Request multicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = Router: The DUT MUST sends MLE Parent Request multicast, with MLE Version TLV with value >= 4.
|
||||
* - Step 4
|
||||
* - Device: BR_1
|
||||
* - Description (GEN-9.1): Automatically sends MLE Parent Response unicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = BR_1: The DUT MUST sends MLE Parent Response unicast, with MLE Version TLV with value >= 4.
|
||||
* - Step 5
|
||||
* - Device: Router_1
|
||||
* - Description (GEN-9.1): Automatically completes attachment procedure and joins the network.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = Router: The DUT MUST send MLE Child ID Request message with MLE Version TLV with value >= 4.
|
||||
*/
|
||||
nexus.AdvanceTime(kAttachToRouterTime);
|
||||
VerifyOrQuit(router1.Get<Mle::Mle>().IsRouter());
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 6: ED_1 Enable, sends MLE Parent Request multicast.");
|
||||
|
||||
/**
|
||||
* - Step 6
|
||||
* - Device: ED_1
|
||||
* - Description (GEN-9.1): Automatically sends MLE Parent Request multicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = ED_1: The DUT MUST sends MLE Parent Request multicast with MLE Version TLV with value >= 4.
|
||||
*/
|
||||
switch (aTopology)
|
||||
{
|
||||
case kTopologyA:
|
||||
ed1.Join(router1, Node::kAsFed);
|
||||
break;
|
||||
case kTopologyB:
|
||||
ed1.Join(router1, Node::kAsMed);
|
||||
break;
|
||||
case kTopologyC:
|
||||
ed1.Join(router1, Node::kAsSed);
|
||||
SuccessOrQuit(ed1.Get<DataPollSender>().SetExternalPollPeriod(kPollPeriod));
|
||||
break;
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 7: Router_1 Automatically sends MLE Parent Response unicast.");
|
||||
|
||||
/**
|
||||
* - Step 7
|
||||
* - Device: Router_1
|
||||
* - Description (GEN-9.1): Automatically sends MLE Parent Response unicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = Router_1: The DUT MUST sends MLE Parent Response unicast with MLE Version TLV with value >= 4.
|
||||
* - Step 8
|
||||
* - Device: ED_1
|
||||
* - Description (GEN-9.1): Automatically completes attachment procedure and joins the network.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = ED_1: The DUT MUST send MLE Child ID Request message with MLE Version TLV with value >= 4.
|
||||
*/
|
||||
nexus.AdvanceTime(kAttachToRouterTime);
|
||||
VerifyOrQuit(ed1.Get<Mle::Mle>().IsChild());
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 9: Router_1 Harness instructs device to send MLE Discovery Request multicast.");
|
||||
|
||||
/**
|
||||
* - Step 9
|
||||
* - Device: Router_1
|
||||
* - Description (GEN-9.1): Note: only performed if BR_1 is DUT. Harness instructs device to send MLE Discovery
|
||||
* Request multicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - N/A
|
||||
*/
|
||||
SuccessOrQuit(router1.Get<Mle::DiscoverScanner>().Discover(
|
||||
Mac::ChannelMask(1 << br1.Get<Mac::Mac>().GetPanChannel()), 0xffff, /* aJoiner */ false, /* aFilter */ false,
|
||||
/* aFilterIndexes */ nullptr, nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 10: BR_1 Automatically responds with MLE Discovery Response unicast.");
|
||||
|
||||
/**
|
||||
* - Step 10
|
||||
* - Device: BR_1
|
||||
* - Description (GEN-9.1): Note: only performed if BR_1 is DUT. Automatically responds with MLE Discovery
|
||||
* Response unicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = BR_1: The DUT MUST send MLE Discovery Response with MLE Discovery Response TLV with a value >= 4
|
||||
* of the 'Ver' field bits.
|
||||
*/
|
||||
nexus.AdvanceTime(kDiscoveryTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 11: ED_1 Harness instructs device to send MLE Discovery Request multicast.");
|
||||
|
||||
/**
|
||||
* - Step 11
|
||||
* - Device: ED_1
|
||||
* - Description (GEN-9.1): Note: only performed if Router_1 is DUT. Harness instructs device to send MLE
|
||||
* Discovery Request multicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - N/A
|
||||
*/
|
||||
SuccessOrQuit(ed1.Get<Mle::DiscoverScanner>().Discover(
|
||||
Mac::ChannelMask(1 << router1.Get<Mac::Mac>().GetPanChannel()), 0xffff, /* aJoiner */ false,
|
||||
/* aFilter */ false, /* aFilterIndexes */ nullptr, nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 12: Router_1 Automatically responds with MLE Discovery Response unicast.");
|
||||
|
||||
/**
|
||||
* - Step 12
|
||||
* - Device: Router_1
|
||||
* - Description (GEN-9.1): Note: only performed if Router_1 is DUT. Automatically responds with MLE Discovery
|
||||
* Response unicast.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = Router_1: The DUT MUST send MLE Discovery Response with MLE Discovery Response TLV with a value
|
||||
* >= 4 of the 'Ver' field bits.
|
||||
*/
|
||||
nexus.AdvanceTime(kDiscoveryTime);
|
||||
|
||||
// Diagnostics for v1.4 devices
|
||||
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 13: Router_1 Harness instructs device to send TMF Get Diagnostic Request unicast to the DUT.");
|
||||
|
||||
/**
|
||||
* - Step 13
|
||||
* - Device: Router_1 [1.4]
|
||||
* - Description (GEN-9.1): Note: only performed if BR_1 is DUT and >= v1.4 Device. Harness instructs device to
|
||||
* send TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT, requesting Diagnostic Version TLV
|
||||
* (Type 24)
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - N/A
|
||||
*/
|
||||
Ip6::Address br1Rloc;
|
||||
br1Rloc.SetToRoutingLocator(router1.Get<Mle::Mle>().GetMeshLocalPrefix(), br1.Get<Mle::Mle>().GetRloc16());
|
||||
uint8_t tlvTypes[] = {NetworkDiagnostic::Tlv::kVersion};
|
||||
SuccessOrQuit(router1.Get<NetworkDiagnostic::Client>().SendDiagnosticGet(br1Rloc, tlvTypes, sizeof(tlvTypes),
|
||||
nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 14: BR_1 Automatically responds with Get Diagnostic Response unicast to Router_1.");
|
||||
|
||||
/**
|
||||
* - Step 14
|
||||
* - Device: BR_1 [1.4]
|
||||
* - Description (GEN-9.1): Note: only performed if BR_1 is DUT and >= v1.4 Device. Automatically responds with
|
||||
* Get Diagnostic Response (DIAG_GET.rsp) unicast to Router_1.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = BR_1 and >= v1.4 Device: The DUT MUST send DIAG_GET.RSP with Version TLV (Type 24)
|
||||
* with value >= 5.
|
||||
*/
|
||||
nexus.AdvanceTime(kDiagResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 15: BR_1 Harness instructs device to send TMF Get Diagnostic Request unicast to the DUT.");
|
||||
|
||||
/**
|
||||
* - Step 15
|
||||
* - Device: BR_1 [1.4]
|
||||
* - Description (GEN-9.1): Note: only performed if Router_1 is DUT and >= v1.4 Device. Harness instructs device
|
||||
* to send TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT, requesting Diagnostic Version TLV (Type
|
||||
* 24)
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - N/A
|
||||
*/
|
||||
Ip6::Address router1Rloc;
|
||||
router1Rloc.SetToRoutingLocator(br1.Get<Mle::Mle>().GetMeshLocalPrefix(), router1.Get<Mle::Mle>().GetRloc16());
|
||||
SuccessOrQuit(br1.Get<NetworkDiagnostic::Client>().SendDiagnosticGet(router1Rloc, tlvTypes, sizeof(tlvTypes),
|
||||
nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 16: Router_1 Automatically responds with Get Diagnostic Response unicast to the DUT.");
|
||||
|
||||
/**
|
||||
* - Step 16
|
||||
* - Device: Router_1 [1.4]
|
||||
* - Description (GEN-9.1): Note: only performed if Router_1 is DUT and >= v1.4 Device. Automatically responds
|
||||
* with Get Diagnostic Response (DIAG_GET.rsp) unicast to the DUT.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = Router_1 and >= v1.4 device: The DUT MUST send DIAG_GET.RSP with Version TLV (Type 24) with value
|
||||
* >= 5.
|
||||
*/
|
||||
nexus.AdvanceTime(kDiagResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 17: Router_1 Harness instructs device to send TMF Get Diagnostic Request unicast to the DUT.");
|
||||
|
||||
/**
|
||||
* - Step 17
|
||||
* - Device: Router_1 [1.4]
|
||||
* - Description (GEN-9.1): Note: only performed if ED_1 is DUT and >= v1.4 Device. Harness instructs device to
|
||||
* send TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT, requesting Diagnostic Version TLV
|
||||
* (Type 24)
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - N/A
|
||||
*/
|
||||
Ip6::Address ed1Rloc;
|
||||
ed1Rloc.SetToRoutingLocator(router1.Get<Mle::Mle>().GetMeshLocalPrefix(), ed1.Get<Mle::Mle>().GetRloc16());
|
||||
SuccessOrQuit(router1.Get<NetworkDiagnostic::Client>().SendDiagnosticGet(ed1Rloc, tlvTypes, sizeof(tlvTypes),
|
||||
nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 18: ED_1 Optionally responds with Get Diagnostic Response unicast to Router_1.");
|
||||
|
||||
/**
|
||||
* - Step 18
|
||||
* - Device: ED_1 [1.4]
|
||||
* - Description (GEN-9.1): Note: only performed if ED_1 is DUT and >= v1.4 Device. Optionally responds with Get
|
||||
* Diagnostic Response (DIAG_GET.rsp) unicast to Router_1.
|
||||
* - Pass Criteria (only applies if Device == DUT):
|
||||
* - For DUT = ED_1 and >= v1.4 device: The DUT MUST respond in either of below two ways:
|
||||
* - 1. DIAG_GET.rsp Response is successfully received, and MUST contain the Version TLV (Type 24)
|
||||
* with value >= 5.
|
||||
* - 2. CoAP error response is received with code 4.04 Not Found.
|
||||
* - Note: in case 2, the DUT does not support the optional diagnostics request.
|
||||
*/
|
||||
nexus.AdvanceTime(kDiagResponseTime);
|
||||
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4
|
||||
|
||||
nexus.SaveTestInfo(aJsonFileName);
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
// If no argument, run all topologies.
|
||||
ot::Nexus::Test_1_3_GEN_TC_1(ot::Nexus::kTopologyA, "test_1_3_GEN_TC_1_A.json");
|
||||
ot::Nexus::Test_1_3_GEN_TC_1(ot::Nexus::kTopologyB, "test_1_3_GEN_TC_1_B.json");
|
||||
ot::Nexus::Test_1_3_GEN_TC_1(ot::Nexus::kTopologyC, "test_1_3_GEN_TC_1_C.json");
|
||||
}
|
||||
else if (strcmp(argv[1], "A") == 0)
|
||||
{
|
||||
ot::Nexus::Test_1_3_GEN_TC_1(ot::Nexus::kTopologyA, (argc > 2) ? argv[2] : "test_1_3_GEN_TC_1_A.json");
|
||||
}
|
||||
else if (strcmp(argv[1], "B") == 0)
|
||||
{
|
||||
ot::Nexus::Test_1_3_GEN_TC_1(ot::Nexus::kTopologyB, (argc > 2) ? argv[2] : "test_1_3_GEN_TC_1_B.json");
|
||||
}
|
||||
else if (strcmp(argv[1], "C") == 0)
|
||||
{
|
||||
ot::Nexus::Test_1_3_GEN_TC_1(ot::Nexus::kTopologyC, (argc > 2) ? argv[2] : "test_1_3_GEN_TC_1_C.json");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: Invalid topology '%s'. Must be 'A', 'B', or 'C'.\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
#!/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 import consts, coap
|
||||
from pktverify.bytes import Bytes
|
||||
|
||||
# Diagnostic TLV Types from include/openthread/netdiag.h
|
||||
DG_TYPE_LIST_TLV = 18
|
||||
DG_VERSION_TLV = 24
|
||||
|
||||
# CoAP Codes
|
||||
COAP_CODE_CHANGED = 68
|
||||
COAP_CODE_NOT_FOUND = 132
|
||||
|
||||
|
||||
def get_diag_tlvs(p):
|
||||
"""
|
||||
Parses Thread Diagnostic TLVs from CoAP payload.
|
||||
Returns a list of (type, value) tuples.
|
||||
"""
|
||||
try:
|
||||
payload = p.coap.payload
|
||||
except AttributeError:
|
||||
return []
|
||||
tlvs = []
|
||||
offset = 0
|
||||
while offset < len(payload):
|
||||
t = payload[offset]
|
||||
l = payload[offset + 1]
|
||||
v = Bytes(payload[offset + 2:offset + 2 + l])
|
||||
tlvs.append((t, v))
|
||||
offset += 2 + l
|
||||
return tlvs
|
||||
|
||||
|
||||
def get_tlv_value(tlvs, tlv_type):
|
||||
"""
|
||||
Returns the value of the first TLV of a given type.
|
||||
"""
|
||||
for t, v in tlvs:
|
||||
if t == tlv_type:
|
||||
return v
|
||||
return None
|
||||
|
||||
|
||||
def get_diag_tlv_type_list(p):
|
||||
"""
|
||||
Parses Thread Diagnostic Type List TLV from CoAP payload.
|
||||
Returns a list of TLV types.
|
||||
"""
|
||||
try:
|
||||
payload = p.coap.payload
|
||||
except AttributeError:
|
||||
return []
|
||||
offset = 0
|
||||
while offset < len(payload):
|
||||
t = payload[offset]
|
||||
l = payload[offset + 1]
|
||||
if t == DG_TYPE_LIST_TLV:
|
||||
return list(payload[offset + 2:offset + 2 + l])
|
||||
offset += 2 + l
|
||||
return []
|
||||
|
||||
|
||||
def verify(pv):
|
||||
# 9.1. [1.3] [CERT] Thread Version is '4' or higher
|
||||
#
|
||||
# 9.1.1. Purpose
|
||||
# Verify that the Thread Version TLV uses the value '4' or higher. '4' is required for Thread 1.3.x devices. '5'
|
||||
# is required for Thread 1.4.x devices.
|
||||
#
|
||||
# 9.1.2. Topology
|
||||
# - BR_1: Thread Border Router DUT
|
||||
# - Router_1: Thread Router DUT
|
||||
# - ED_1: Thread End Device DUT, including FEDs, MEDs and SEDs
|
||||
#
|
||||
# Spec Reference | V1.1 Section | V1.3.0 Section
|
||||
# -----------------|--------------|---------------
|
||||
# Thread Version | 5.18.2.1 | 5.18.2.1
|
||||
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
|
||||
BR_1 = pv.vars['BR_1']
|
||||
ROUTER_1 = pv.vars['Router_1']
|
||||
ED_1 = pv.vars['ED_1']
|
||||
|
||||
BR_1_RLOC16 = pv.vars['BR_1_RLOC16']
|
||||
ROUTER_1_RLOC16 = pv.vars['Router_1_RLOC16']
|
||||
ED_1_RLOC16 = pv.vars['ED_1_RLOC16']
|
||||
|
||||
# Step 1: BR_1 Enable
|
||||
# - Description (GEN-9.1): Enable
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = BR_1: The DUT MUST become Leader of the Thread Network.
|
||||
print("Step 1: BR_1 becomes Leader of the Thread Network.")
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_mle_cmd(consts.MLE_ADVERTISEMENT).\
|
||||
filter(lambda p: {
|
||||
consts.LEADER_DATA_TLV,
|
||||
consts.SOURCE_ADDRESS_TLV
|
||||
} <= set(p.mle.tlv.type)).\
|
||||
must_next()
|
||||
|
||||
# Step 2: Router_1 Enable
|
||||
# - Description (GEN-9.1): Enable
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - N/A
|
||||
print("Step 2: Router_1 is enabled.")
|
||||
|
||||
# Step 3: Router_1 Automatically sends MLE Parent Request multicast.
|
||||
# - Description (GEN-9.1): Automatically sends MLE Parent Request multicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = Router: The DUT MUST sends MLE Parent Request multicast, with MLE Version TLV with value >= 4.
|
||||
print("Step 3: Router_1 sends MLE Parent Request multicast with MLE Version TLV >= 4.")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_LLARMA().\
|
||||
filter_mle_cmd(consts.MLE_PARENT_REQUEST).\
|
||||
filter(lambda p: p.mle.tlv.version >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 4: BR_1 Automatically sends MLE Parent Response unicast.
|
||||
# - Description (GEN-9.1): Automatically sends MLE Parent Response unicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = BR_1: The DUT MUST sends MLE Parent Response unicast, with MLE Version TLV with value >= 4.
|
||||
print("Step 4: BR_1 sends MLE Parent Response unicast with MLE Version TLV >= 4.")
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_wpan_dst64(ROUTER_1).\
|
||||
filter_mle_cmd(consts.MLE_PARENT_RESPONSE).\
|
||||
filter(lambda p: p.mle.tlv.version >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 5: Router_1 Automatically completes attachment procedure and joins the network.
|
||||
# - Description (GEN-9.1): Automatically completes attachment procedure and joins the network.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = Router: The DUT MUST send MLE Child ID Request message with MLE Version TLV with value >= 4.
|
||||
print("Step 5: Router_1 sends MLE Child ID Request with MLE Version TLV >= 4.")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_mle_cmd(consts.MLE_CHILD_ID_REQUEST).\
|
||||
filter(lambda p: p.mle.tlv.version >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 6: ED_1 Automatically sends MLE Parent Request multicast.
|
||||
# - Description (GEN-9.1): Automatically sends MLE Parent Request multicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = ED_1: The DUT MUST sends MLE Parent Request multicast with MLE Version TLV with value >= 4.
|
||||
print("Step 6: ED_1 sends MLE Parent Request multicast with MLE Version TLV >= 4.")
|
||||
pkts.filter_wpan_src64(ED_1).\
|
||||
filter_LLARMA().\
|
||||
filter_mle_cmd(consts.MLE_PARENT_REQUEST).\
|
||||
filter(lambda p: p.mle.tlv.version >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 7: Router_1 Automatically sends MLE Parent Response unicast.
|
||||
# - Description (GEN-9.1): Automatically sends MLE Parent Response unicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = Router_1: The DUT MUST sends MLE Parent Response unicast with MLE Version TLV with value >= 4.
|
||||
print("Step 7: Router_1 sends MLE Parent Response unicast with MLE Version TLV >= 4.")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_wpan_dst64(ED_1).\
|
||||
filter_mle_cmd(consts.MLE_PARENT_RESPONSE).\
|
||||
filter(lambda p: p.mle.tlv.version >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 8: ED_1 Automatically completes attachment procedure and joins the network.
|
||||
# - Description (GEN-9.1): Automatically completes attachment procedure and joins the network.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = ED_1: The DUT MUST send MLE Child ID Request message with MLE Version TLV with value >= 4.
|
||||
print("Step 8: ED_1 sends MLE Child ID Request with MLE Version TLV >= 4.")
|
||||
pkts.filter_wpan_src64(ED_1).\
|
||||
filter_mle_cmd(consts.MLE_CHILD_ID_REQUEST).\
|
||||
filter(lambda p: p.mle.tlv.version >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 9: Router_1 Harness instructs device to send MLE Discovery Request multicast.
|
||||
# - Description (GEN-9.1): Note: only performed if BR_1 is DUT. Harness instructs device to send MLE Discovery
|
||||
# Request multicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - N/A
|
||||
print("Step 9: Router_1 sends MLE Discovery Request multicast.")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_LLARMA().\
|
||||
filter_mle_cmd(consts.MLE_DISCOVERY_REQUEST).\
|
||||
must_next()
|
||||
|
||||
# Step 10: BR_1 Automatically responds with MLE Discovery Response unicast.
|
||||
# - Description (GEN-9.1): Note: only performed if BR_1 is DUT. Automatically responds with MLE Discovery
|
||||
# Response unicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = BR_1: The DUT MUST send MLE Discovery Response with MLE Discovery Response TLV with a value >= 4
|
||||
# of the 'Ver' field bits.
|
||||
print("Step 10: BR_1 sends MLE Discovery Response with Discovery Version >= 4.")
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_wpan_dst64(ROUTER_1).\
|
||||
filter_mle_cmd(consts.MLE_DISCOVERY_RESPONSE).\
|
||||
filter(lambda p: p.thread_meshcop.tlv.discovery_rsp_ver >= 4).\
|
||||
must_next()
|
||||
|
||||
# Step 11: ED_1 Harness instructs device to send MLE Discovery Request multicast.
|
||||
# - Description (GEN-9.1): Note: only performed if Router_1 is DUT. Harness instructs device to send MLE Discovery
|
||||
# Request multicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - N/A
|
||||
print("Step 11: ED_1 sends MLE Discovery Request multicast.")
|
||||
pkts.filter_wpan_src64(ED_1).\
|
||||
filter_LLARMA().\
|
||||
filter_mle_cmd(consts.MLE_DISCOVERY_REQUEST).\
|
||||
must_next()
|
||||
|
||||
# Step 12: Router_1 Automatically responds with MLE Discovery Response unicast.
|
||||
# - Description (GEN-9.1): Note: only performed if Router_1 is DUT. Automatically responds with MLE Discovery
|
||||
# Response unicast.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = Router_1: The DUT MUST send MLE Discovery Response with MLE Discovery Response TLV with a value >= 4
|
||||
# of the 'Ver' field bits.
|
||||
print("Step 12: Router_1 sends MLE Discovery Response with Discovery Version >= 4.")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_wpan_dst64(ED_1).\
|
||||
filter_mle_cmd(consts.MLE_DISCOVERY_RESPONSE).\
|
||||
filter(lambda p: p.thread_meshcop.tlv.discovery_rsp_ver >= 4).\
|
||||
must_next()
|
||||
|
||||
# Steps 13-18 are for v1.4 devices
|
||||
# Check if we have diagnostic packets to decide if we should verify them
|
||||
if pkts.filter_coap_request('/d/dg'):
|
||||
# Step 13: Router_1 sends TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT.
|
||||
# - Description (GEN-9.1): Note: only performed if BR_1 is DUT and >= v1.4 Device. Harness instructs device to
|
||||
# send TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT, requesting Diagnostic Version TLV
|
||||
# (Type 24)
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - N/A
|
||||
print("Step 13: Router_1 sends DIAG_GET.req to BR_1.")
|
||||
pkts.filter_wpan_src16(ROUTER_1_RLOC16).\
|
||||
filter_wpan_dst16(BR_1_RLOC16).\
|
||||
filter_coap_request('/d/dg').\
|
||||
filter(lambda p: DG_VERSION_TLV in get_diag_tlv_type_list(p)).\
|
||||
must_next()
|
||||
|
||||
# Step 14: BR_1 Automatically responds with Get Diagnostic Response unicast to Router_1.
|
||||
# - Description (GEN-9.1): Note: only performed if BR_1 is DUT and >= v1.4 Device. Automatically responds with
|
||||
# Get Diagnostic Response (DIAG_GET.rsp) unicast to Router_1.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = BR_1 and >= v1.4 Device: The DUT MUST send DIAG_GET.RSP with Version TLV (Type 24)
|
||||
# with value >= 5.
|
||||
print("Step 14: BR_1 sends DIAG_GET.rsp with Version TLV >= 5.")
|
||||
pkts.filter_wpan_src16(BR_1_RLOC16).\
|
||||
filter_wpan_dst16(ROUTER_1_RLOC16).\
|
||||
filter_coap_ack('/d/dg').\
|
||||
filter(lambda p: struct.unpack('>H', get_tlv_value(get_diag_tlvs(p), DG_VERSION_TLV))[0] >= 5).\
|
||||
must_next()
|
||||
|
||||
# Step 15: BR_1 Harness instructs device to send TMF Get Diagnostic Request unicast to the DUT.
|
||||
# - Description (GEN-9.1): Note: only performed if Router_1 is DUT and >= v1.4 Device. Harness instructs device
|
||||
# to send TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT, requesting Diagnostic Version TLV
|
||||
# (Type 24)
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - N/A
|
||||
print("Step 15: BR_1 sends DIAG_GET.req to Router_1.")
|
||||
pkts.filter_wpan_src16(BR_1_RLOC16).\
|
||||
filter_wpan_dst16(ROUTER_1_RLOC16).\
|
||||
filter_coap_request('/d/dg').\
|
||||
filter(lambda p: DG_VERSION_TLV in get_diag_tlv_type_list(p)).\
|
||||
must_next()
|
||||
|
||||
# Step 16: Router_1 Automatically responds with Get Diagnostic Response unicast to the DUT.
|
||||
# - Description (GEN-9.1): Note: only performed if Router_1 is DUT and >= v1.4 Device. Automatically responds
|
||||
# with Get Diagnostic Response (DIAG_GET.rsp) unicast to the DUT.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = Router_1 and >= v1.4 device: The DUT MUST send DIAG_GET.RSP with Version TLV (Type 24)
|
||||
# with value >= 5.
|
||||
print("Step 16: Router_1 sends DIAG_GET.rsp with Version TLV >= 5.")
|
||||
pkts.filter_wpan_src16(ROUTER_1_RLOC16).\
|
||||
filter_wpan_dst16(BR_1_RLOC16).\
|
||||
filter_coap_ack('/d/dg').\
|
||||
filter(lambda p: struct.unpack('>H', get_tlv_value(get_diag_tlvs(p), DG_VERSION_TLV))[0] >= 5).\
|
||||
must_next()
|
||||
|
||||
# Step 17: Router_1 Harness instructs device to send TMF Get Diagnostic Request unicast to the DUT.
|
||||
# - Description (GEN-9.1): Note: only performed if ED_1 is DUT and >= v1.4 Device. Harness instructs device to
|
||||
# send TMF Get Diagnostic Request (DIAG_GET.req) unicast to the DUT, requesting Diagnostic Version TLV
|
||||
# (Type 24)
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - N/A
|
||||
print("Step 17: Router_1 sends DIAG_GET.req to ED_1.")
|
||||
pkts.filter_wpan_src16(ROUTER_1_RLOC16).\
|
||||
filter_wpan_dst16(ED_1_RLOC16).\
|
||||
filter_coap_request('/d/dg').\
|
||||
filter(lambda p: DG_VERSION_TLV in get_diag_tlv_type_list(p)).\
|
||||
must_next()
|
||||
|
||||
# Step 18: ED_1 Optionally responds with Get Diagnostic Response unicast to Router_1.
|
||||
# - Description (GEN-9.1): Note: only performed if ED_1 is DUT and >= v1.4 Device. Optionally responds with Get
|
||||
# Diagnostic Response (DIAG_GET.rsp) unicast to Router_1.
|
||||
# - Pass Criteria (only applies if Device == DUT):
|
||||
# - For DUT = ED_1 and >= v1.4 device: The DUT MUST respond in either of below two ways:
|
||||
# - 1. DIAG_GET.rsp Response is successfully received, and MUST contain the Version TLV (Type 24)
|
||||
# with value >= 5.
|
||||
# - 2. CoAP error response is received with code 4.04 Not Found.
|
||||
# - Note: in case 2, the DUT does not support the optional diagnostics request.
|
||||
print("Step 18: ED_1 optionally responds with Get Diagnostic Response or 4.04 Not Found.")
|
||||
pkts.filter_wpan_src16(ED_1_RLOC16).\
|
||||
filter_wpan_dst16(ROUTER_1_RLOC16).\
|
||||
filter_coap_ack('/d/dg').\
|
||||
filter(lambda p: (p.coap.code == COAP_CODE_CHANGED and\
|
||||
struct.unpack('>H', get_tlv_value(get_diag_tlvs(p), DG_VERSION_TLV))[0] >= 5) or\
|
||||
(p.coap.code == COAP_CODE_NOT_FOUND)).\
|
||||
must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
verify_utils.run_main(verify)
|
||||
Reference in New Issue
Block a user