diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 1648506eb..37117a5b1 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -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") diff --git a/tests/nexus/run_nexus_tests.sh b/tests/nexus/run_nexus_tests.sh index aecf5d2f6..e8284072c 100755 --- a/tests/nexus/run_nexus_tests.sh +++ b/tests/nexus/run_nexus_tests.sh @@ -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") ;; diff --git a/tests/nexus/test_1_3_GEN_TC_1.cpp b/tests/nexus/test_1_3_GEN_TC_1.cpp new file mode 100644 index 000000000..3e35bfc66 --- /dev/null +++ b/tests/nexus/test_1_3_GEN_TC_1.cpp @@ -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 +#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 = 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().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().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().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().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().Discover( + Mac::ChannelMask(1 << br1.Get().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().Discover( + Mac::ChannelMask(1 << router1.Get().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().GetMeshLocalPrefix(), br1.Get().GetRloc16()); + uint8_t tlvTypes[] = {NetworkDiagnostic::Tlv::kVersion}; + SuccessOrQuit(router1.Get().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().GetMeshLocalPrefix(), router1.Get().GetRloc16()); + SuccessOrQuit(br1.Get().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().GetMeshLocalPrefix(), ed1.Get().GetRloc16()); + SuccessOrQuit(router1.Get().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; +} diff --git a/tests/nexus/verify_1_3_GEN_TC_1.py b/tests/nexus/verify_1_3_GEN_TC_1.py new file mode 100644 index 000000000..bf20fff83 --- /dev/null +++ b/tests/nexus/verify_1_3_GEN_TC_1.py @@ -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)