mirror of
https://github.com/espressif/openthread.git
synced 2026-09-03 15:50:06 +00:00
[nexus] add test 5.8.4 Security Policy TLV (#12480)
This commit adds Nexus test case 5.8.4 which verifies the Security Policy TLV bits (O, N, R, B) being disabled and the resulting network behavior. Implementation details: - tests/nexus/test_5_8_4.cpp: Implemented the test procedure using direct core method calls. The test procedure implements all 20 steps from the specification with cumulative Security Policy bit updates. Removed unused constant kExternalCommissioningMask to fix build error. - tests/nexus/verify_5_8_4.py: Implemented robust Python verification logic that handles out-of-order packets and verifies individual Security Policy and Discovery Response bits. - tests/nexus/verify_utils.py: Enhanced the parser to support granular Security Policy bits and Discovery Response Native Commissioning bit. Replaced magic numbers with constants and registered additional Security Policy flags (C, e, p) for completeness. - tests/nexus/platform/nexus_core.cpp: Refactored SaveTestInfo to use the Leader node as the authoritative source for network keys and prefixes, ensuring correct decryption in verification scripts. - Included full test specification as inline comments in both C++ and Python code.
This commit is contained in:
@@ -168,6 +168,7 @@ ot_nexus_test(5_7_2 "cert;nexus")
|
||||
ot_nexus_test(5_7_3 "cert;nexus")
|
||||
ot_nexus_test(5_8_2 "cert;nexus")
|
||||
ot_nexus_test(5_8_3 "cert;nexus")
|
||||
ot_nexus_test(5_8_4 "cert;nexus")
|
||||
ot_nexus_test(6_1_1 "cert;nexus")
|
||||
ot_nexus_test(6_1_2 "cert;nexus")
|
||||
ot_nexus_test(6_1_3 "cert;nexus")
|
||||
|
||||
@@ -70,6 +70,7 @@ void Core::SaveTestInfo(const char *aFilename)
|
||||
const char *dot;
|
||||
const char *version;
|
||||
int testcaseLen;
|
||||
Node *leaderNode = nullptr;
|
||||
|
||||
VerifyOrExit(file != nullptr);
|
||||
|
||||
@@ -109,23 +110,28 @@ void Core::SaveTestInfo(const char *aFilename)
|
||||
|
||||
if (!mNodes.IsEmpty())
|
||||
{
|
||||
leaderNode = mNodes.GetTail();
|
||||
NetworkKey networkKey;
|
||||
Node &node = *mNodes.GetHead();
|
||||
String<OT_NETWORK_KEY_SIZE * 2 + 1> keyString;
|
||||
|
||||
node.Get<KeyManager>().GetNetworkKey(networkKey);
|
||||
for (Node &node : mNodes)
|
||||
{
|
||||
if (node.Get<Mle::Mle>().IsLeader())
|
||||
{
|
||||
leaderNode = &node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
leaderNode->Get<KeyManager>().GetNetworkKey(networkKey);
|
||||
keyString.AppendHexBytes(networkKey.m8, OT_NETWORK_KEY_SIZE);
|
||||
fprintf(file, " \"network_key\": \"%s\",\n", keyString.AsCString());
|
||||
|
||||
for (Node &leaderNode : mNodes)
|
||||
if (leaderNode->Get<Mle::Mle>().IsLeader())
|
||||
{
|
||||
if (leaderNode.Get<Mle::Mle>().IsLeader())
|
||||
{
|
||||
Ip6::Address aloc;
|
||||
leaderNode.Get<Mle::Mle>().GetLeaderAloc(aloc);
|
||||
fprintf(file, " \"leader_aloc\": \"%s\",\n", aloc.ToString().AsCString());
|
||||
break;
|
||||
}
|
||||
Ip6::Address aloc;
|
||||
leaderNode->Get<Mle::Mle>().GetLeaderAloc(aloc);
|
||||
fprintf(file, " \"leader_aloc\": \"%s\",\n", aloc.ToString().AsCString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,12 +195,10 @@ void Core::SaveTestInfo(const char *aFilename)
|
||||
fprintf(file, " },\n");
|
||||
|
||||
fprintf(file, " \"extra_vars\": {\n");
|
||||
if (!mNodes.IsEmpty())
|
||||
if (leaderNode != nullptr)
|
||||
{
|
||||
Node &node = *mNodes.GetHead();
|
||||
Ip6::Prefix prefix;
|
||||
|
||||
prefix.Set(node.Get<Mle::Mle>().GetMeshLocalPrefix());
|
||||
prefix.Set(leaderNode->Get<Mle::Mle>().GetMeshLocalPrefix());
|
||||
fprintf(file, " \"mesh_local_prefix\": \"%s\"\n", prefix.ToString().AsCString());
|
||||
}
|
||||
fprintf(file, " }\n");
|
||||
|
||||
@@ -98,6 +98,7 @@ DEFAULT_TESTS=(
|
||||
"5_7_3"
|
||||
"5_8_2"
|
||||
"5_8_3"
|
||||
"5_8_4"
|
||||
"6_1_1_A"
|
||||
"6_1_1_B"
|
||||
"6_1_2_A"
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "thread/mle.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 network to stabilize, in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kStabilizationTime = 32 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for the CoAP response to be received.
|
||||
*/
|
||||
static constexpr uint32_t kResponseTime = 10 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for the discovery scan.
|
||||
*/
|
||||
static constexpr uint32_t kScanTime = 10 * 1000;
|
||||
|
||||
/**
|
||||
* Security Policy flag masks.
|
||||
*/
|
||||
static constexpr uint8_t kObtainNetworkKeyMask = 0x80;
|
||||
static constexpr uint8_t kNativeCommissioningMask = 0x40;
|
||||
static constexpr uint8_t kRoutersMask = 0x20;
|
||||
static constexpr uint8_t kBeaconsMask = 0x08;
|
||||
|
||||
void Test5_8_4(void)
|
||||
{
|
||||
/**
|
||||
* 5.8.4 Security Policy TLV
|
||||
*
|
||||
* 5.8.4.1 Topology
|
||||
* - Commissioner_1 is an On-mesh Commissioner.
|
||||
* - Commissioner_2 is not part of the original topology - it is introduced at step 11.
|
||||
* - Partition is formed with all Security Policy TLV bits set to 1.
|
||||
*
|
||||
* 5.8.4.2 Purpose & Description
|
||||
* The purpose of this test case is to verify network behavior when Security Policy TLV "O","N","R","B" bits are
|
||||
* disabled. "C" bit is not tested as it requires an External Commissioner which is currently not part of Thread
|
||||
* Certification.
|
||||
*/
|
||||
|
||||
Core nexus;
|
||||
|
||||
Node &leader = nexus.CreateNode();
|
||||
Node &commr1 = nexus.CreateNode();
|
||||
Node &commr2 = nexus.CreateNode();
|
||||
|
||||
leader.SetName("LEADER");
|
||||
commr1.SetName("COMMISSIONER_1");
|
||||
commr2.SetName("COMMISSIONER_2");
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
Instance::SetLogLevel(kLogLevelNote);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 1: All");
|
||||
|
||||
/**
|
||||
* Step 1: All
|
||||
* - Description: Build Topology. Ensure topology is formed correctly.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
/** Use AllowList to specify links between nodes. */
|
||||
leader.AllowList(commr1);
|
||||
commr1.AllowList(leader);
|
||||
|
||||
leader.AllowList(commr2);
|
||||
commr2.AllowList(leader);
|
||||
|
||||
/** Partition is formed with all Security Policy TLV bits set to 1. */
|
||||
{
|
||||
SecurityPolicy policy;
|
||||
uint8_t flags[] = {0xff, 0xff};
|
||||
policy.SetFlags(flags, sizeof(flags));
|
||||
leader.Get<KeyManager>().SetSecurityPolicy(policy);
|
||||
}
|
||||
|
||||
leader.Form();
|
||||
nexus.AdvanceTime(kFormNetworkTime);
|
||||
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
commr1.Join(leader);
|
||||
nexus.AdvanceTime(kAttachToRouterTime);
|
||||
VerifyOrQuit(commr1.Get<Mle::Mle>().IsRouter());
|
||||
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::Commissioner>().Start(nullptr, nullptr, nullptr));
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
VerifyOrQuit(commr1.Get<MeshCoP::Commissioner>().IsActive());
|
||||
|
||||
/** Add a joiner on commr1 to enable joining on the Leader. */
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::Commissioner>().AddJoinerAny("123456", 100));
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 2: Commissioner_1");
|
||||
|
||||
/**
|
||||
* Step 2: Commissioner_1
|
||||
* - Description: Harness instructs the device to send MGMT_ACTIVE_GET.req to the DUT.
|
||||
* - CoAP Request URI: coap://[<L>]:MM/c/ag
|
||||
* - CoAP Payload: <empty>
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Components comps;
|
||||
comps.Clear();
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::ActiveDatasetManager>().SendGetRequest(comps, nullptr, 0, nullptr));
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 3: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 3: Leader (DUT)
|
||||
* - Description: Automatically sends MGMT_ACTIVE_GET.rsp to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send MGMT_ACTIVE_GET.rsp to Commissioner_1:
|
||||
* - CoAP Response Code: 2.04 Changed
|
||||
* - CoAP Payload: Security Policy TLV Bits "O","N","R","C" should be set to 1.
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 4 & 5: Commissioner_1");
|
||||
|
||||
/**
|
||||
* Step 4 & 5: Commissioner_1
|
||||
* - Description: Harness instructs the device to send MGMT_ACTIVE_SET.req to the DUT (disable "O" bit).
|
||||
* - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
* - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 15 (> step 3), Security Policy TLV with "O"
|
||||
* bit disabled.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Info dataset;
|
||||
MeshCoP::Timestamp timestamp;
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::ActiveDatasetManager>().Read(dataset));
|
||||
timestamp.Clear();
|
||||
timestamp.SetSeconds(15);
|
||||
dataset.Set<MeshCoP::Dataset::kActiveTimestamp>(timestamp);
|
||||
uint8_t flags[] = {static_cast<uint8_t>(~kObtainNetworkKeyMask), 0xff}; // Disable "O" bit
|
||||
dataset.Update<MeshCoP::Dataset::kSecurityPolicy>().SetFlags(flags, sizeof(flags));
|
||||
|
||||
uint16_t sessionId = commr1.Get<MeshCoP::Commissioner>().GetSessionId();
|
||||
uint8_t tlvs[4];
|
||||
tlvs[0] = MeshCoP::Tlv::kCommissionerSessionId;
|
||||
tlvs[1] = sizeof(uint16_t);
|
||||
BigEndian::WriteUint16(sessionId, &tlvs[2]);
|
||||
|
||||
SuccessOrQuit(
|
||||
commr1.Get<MeshCoP::ActiveDatasetManager>().SendSetRequest(dataset, tlvs, sizeof(tlvs), nullptr, nullptr));
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 6: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 6: Leader (DUT)
|
||||
* - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
* - CoAP Response Code: 2.04 Changed
|
||||
* - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 7: Commissioner_1");
|
||||
|
||||
/**
|
||||
* Step 7: Commissioner_1
|
||||
* - Description: Harness instructs device to send MGMT_ACTIVE_GET.req to the DUT.
|
||||
* - CoAP Request URI: coap://[<L>]:MM/c/ag
|
||||
* - CoAP Payload: Get TLV specifying: Network Master Key TLV.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Components comps;
|
||||
comps.Clear();
|
||||
uint8_t tlvTypes[] = {MeshCoP::Tlv::kNetworkKey};
|
||||
SuccessOrQuit(
|
||||
commr1.Get<MeshCoP::ActiveDatasetManager>().SendGetRequest(comps, tlvTypes, sizeof(tlvTypes), nullptr));
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 8: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 8: Leader (DUT)
|
||||
* - Description: Automatically sends MGMT_ACTIVE_GET.rsp to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send MGMT_ACTIVE_GET.rsp to Commissioner_1:
|
||||
* - CoAP Response Code: 2.04 Changed
|
||||
* - CoAP Payload: Network Master Key TLV MUST NOT be included.
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 9: Commissioner_1");
|
||||
|
||||
/**
|
||||
* Step 9: Commissioner_1
|
||||
* - Description: Harness instructs device to send MGMT_ACTIVE_SET.req to the DUT (disable "N" bit).
|
||||
* - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
* - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 20 (> step 5), Security Policy TLV with "N"
|
||||
* bit disabled.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Info dataset;
|
||||
MeshCoP::Timestamp timestamp;
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::ActiveDatasetManager>().Read(dataset));
|
||||
timestamp.Clear();
|
||||
timestamp.SetSeconds(20);
|
||||
dataset.Set<MeshCoP::Dataset::kActiveTimestamp>(timestamp);
|
||||
uint8_t flags[] = {static_cast<uint8_t>(~(kObtainNetworkKeyMask | kNativeCommissioningMask)),
|
||||
0xff}; // Disable "N" and "O" bits
|
||||
dataset.Update<MeshCoP::Dataset::kSecurityPolicy>().SetFlags(flags, sizeof(flags));
|
||||
|
||||
uint16_t sessionId = commr1.Get<MeshCoP::Commissioner>().GetSessionId();
|
||||
uint8_t tlvs[4];
|
||||
tlvs[0] = MeshCoP::Tlv::kCommissionerSessionId;
|
||||
tlvs[1] = sizeof(uint16_t);
|
||||
BigEndian::WriteUint16(sessionId, &tlvs[2]);
|
||||
|
||||
SuccessOrQuit(
|
||||
commr1.Get<MeshCoP::ActiveDatasetManager>().SendSetRequest(dataset, tlvs, sizeof(tlvs), nullptr, nullptr));
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 10: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 10: Leader (DUT)
|
||||
* - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
* - CoAP Response Code: 2.04 Changed
|
||||
* - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kResponseTime);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 11: Commissioner_2");
|
||||
|
||||
/**
|
||||
* Step 11: Commissioner_2
|
||||
* - Description: Harness instructs device to try to join the network as a Native Commissioner.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
commr2.Get<ThreadNetif>().Up();
|
||||
SuccessOrQuit(commr2.Get<Mle::DiscoverScanner>().Discover(Mac::ChannelMask(0), 0xffff, /* aJoiner */ true,
|
||||
/* aFilter */ false, /* aFilterIndexes */ nullptr,
|
||||
nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 12: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 12: Leader (DUT)
|
||||
* - Description: Automatically rejects Commissioner_2's attempt to join.
|
||||
* - Pass Criteria: The DUT MUST send a Discovery Response with Native Commissioning bit set to "Not Allowed".
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kScanTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 13: Commissioner_1");
|
||||
|
||||
/**
|
||||
* Step 13: Commissioner_1
|
||||
* - Description: Harness instructs device to send MGMT_ACTIVE_SET.req to the DUT ("B" bit = 0).
|
||||
* - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
* - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 25 (> Step 9), Security Policy TLV with "B"
|
||||
* bit = 0 (default).
|
||||
* - Note: This step is a legacy V1.1 behavior which has been deprecated in V1.2.1. For simplicity sake, this step
|
||||
* has been left as-is because the B-bit is now reserved - and the value of zero is the new default behavior.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Info dataset;
|
||||
MeshCoP::Timestamp timestamp;
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::ActiveDatasetManager>().Read(dataset));
|
||||
timestamp.Clear();
|
||||
timestamp.SetSeconds(25);
|
||||
dataset.Set<MeshCoP::Dataset::kActiveTimestamp>(timestamp);
|
||||
uint8_t flags[] = {static_cast<uint8_t>(~(kObtainNetworkKeyMask | kNativeCommissioningMask | kBeaconsMask)),
|
||||
0xff}; // Disable "B", "N", and "O" bits
|
||||
dataset.Update<MeshCoP::Dataset::kSecurityPolicy>().SetFlags(flags, sizeof(flags));
|
||||
|
||||
uint16_t sessionId = commr1.Get<MeshCoP::Commissioner>().GetSessionId();
|
||||
uint8_t tlvs[4];
|
||||
tlvs[0] = MeshCoP::Tlv::kCommissionerSessionId;
|
||||
tlvs[1] = sizeof(uint16_t);
|
||||
BigEndian::WriteUint16(sessionId, &tlvs[2]);
|
||||
|
||||
SuccessOrQuit(
|
||||
commr1.Get<MeshCoP::ActiveDatasetManager>().SendSetRequest(dataset, tlvs, sizeof(tlvs), nullptr, nullptr));
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 14: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 14: Leader (DUT)
|
||||
* - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
* - CoAP Response Code: 2.04 Changed
|
||||
* - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kResponseTime);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 15: Test Harness Device");
|
||||
|
||||
/**
|
||||
* Step 15: Test Harness Device
|
||||
* - Description: Harness instructs device to discover network using beacons.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
SuccessOrQuit(commr2.Get<Mle::DiscoverScanner>().Discover(Mac::ChannelMask(0), 0xffff, /* aJoiner */ false,
|
||||
/* aFilter */ false, /* aFilterIndexes */ nullptr,
|
||||
nullptr, nullptr));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 16: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 16: Leader (DUT)
|
||||
* - Description: Automatically responds with beacon response frame.
|
||||
* - Pass Criteria: The DUT MUST send beacon response frames. The beacon payload MUST either be empty OR the payload
|
||||
* format MUST be different from the Thread Beacon payload. The Protocol ID and Version field values MUST be
|
||||
* different from the values specified for the Thread beacon (Protocol ID= 3, Version = 2).
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kScanTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 17: Commissioner_1");
|
||||
|
||||
/**
|
||||
* Step 17: Commissioner_1
|
||||
* - Description: Harness instructs device to send MGMT_ACTIVE_SET.req to the DUT (disable "R" bit).
|
||||
* - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
* - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 30 (> step 13), Security Policy TLV with
|
||||
* "R" bit disabled.
|
||||
* - Pass Criteria: N/A.
|
||||
*/
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Info dataset;
|
||||
MeshCoP::Timestamp timestamp;
|
||||
SuccessOrQuit(commr1.Get<MeshCoP::ActiveDatasetManager>().Read(dataset));
|
||||
timestamp.Clear();
|
||||
timestamp.SetSeconds(30);
|
||||
dataset.Set<MeshCoP::Dataset::kActiveTimestamp>(timestamp);
|
||||
uint8_t flags[] = {
|
||||
static_cast<uint8_t>(~(kObtainNetworkKeyMask | kNativeCommissioningMask | kBeaconsMask | kRoutersMask)),
|
||||
0xff}; // Disable "R", "B", "N", and "O" bits
|
||||
dataset.Update<MeshCoP::Dataset::kSecurityPolicy>().SetFlags(flags, sizeof(flags));
|
||||
|
||||
uint16_t sessionId = commr1.Get<MeshCoP::Commissioner>().GetSessionId();
|
||||
uint8_t tlvs[4];
|
||||
tlvs[0] = MeshCoP::Tlv::kCommissionerSessionId;
|
||||
tlvs[1] = sizeof(uint16_t);
|
||||
BigEndian::WriteUint16(sessionId, &tlvs[2]);
|
||||
|
||||
SuccessOrQuit(
|
||||
commr1.Get<MeshCoP::ActiveDatasetManager>().SendSetRequest(dataset, tlvs, sizeof(tlvs), nullptr, nullptr));
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 18: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 18: Leader (DUT)
|
||||
* - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
* - CoAP Response Code: 2.04 Changed
|
||||
* - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kResponseTime);
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 19: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 19: Leader (DUT)
|
||||
* - Description: Automatically sends multicast MLE Data Response. Commissioner_1 responds with MLE Data Request.
|
||||
* - Pass Criteria: The DUT MUST multicast MLE Data Response to the Link-Local All Nodes multicast address (FF02::1)
|
||||
* with active timestamp value as set in Step 17.
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 20: Leader (DUT)");
|
||||
|
||||
/**
|
||||
* Step 20: Leader (DUT)
|
||||
* - Description: Automatically sends unicast MLE Data Response to Commissioner_1.
|
||||
* - Pass Criteria: The DUT MUST send a unicast MLE Data Response to Commissioner_1. The Active Operational Set MUST
|
||||
* contain a Security Policy TLV with R bit set to 0.
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kStabilizationTime);
|
||||
|
||||
nexus.SaveTestInfo("test_5_8_4.json");
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::Nexus::Test5_8_4();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2026, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
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.null_field import nullField
|
||||
|
||||
|
||||
def verify(pv):
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
|
||||
LEADER = pv.vars['LEADER']
|
||||
COMMISSIONER_1 = pv.vars['COMMISSIONER_1']
|
||||
COMMISSIONER_2 = pv.vars['COMMISSIONER_2']
|
||||
|
||||
# Step 1: All
|
||||
# - Description: Build Topology. Ensure topology is formed correctly.
|
||||
# - Pass Criteria: N/A.
|
||||
print("Step 1: All")
|
||||
|
||||
# Step 2: Commissioner_1
|
||||
# - Description: Harness instructs the device to send MGMT_ACTIVE_GET.req to the DUT.
|
||||
# - CoAP Request URI: coap://[<L>]:MM/c/ag
|
||||
# - CoAP Payload: <empty>
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 3: Leader (DUT)
|
||||
# - Description: Automatically sends MGMT_ACTIVE_GET.rsp to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send MGMT_ACTIVE_GET.rsp to Commissioner_1:
|
||||
# - CoAP Response Code: 2.04 Changed
|
||||
# - CoAP Payload: Security Policy TLV Bits "O","N","R","C" should be set to 1.
|
||||
print("Step 2-3: MGMT_ACTIVE_GET check")
|
||||
pkts.filter_coap_ack(consts.MGMT_ACTIVE_GET_URI).must_next().\
|
||||
must_verify('coap.tlv.sec_policy_o == 1 and '\
|
||||
'coap.tlv.sec_policy_n == 1 and '\
|
||||
'coap.tlv.sec_policy_r == 1 and '\
|
||||
'coap.tlv.sec_policy_c == 1')
|
||||
|
||||
# Step 4 & 5: Commissioner_1
|
||||
# - Description: Harness instructs the device to send MGMT_ACTIVE_SET.req to the DUT (disable "O" bit).
|
||||
# - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
# - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 15 (> step 3), Security Policy TLV with "O"
|
||||
# bit disabled.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 6: Leader (DUT)
|
||||
# - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
# - CoAP Response Code: 2.04 Changed
|
||||
# - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
print("Step 4-6: Disable O bit check")
|
||||
pkts.filter_coap_request(consts.MGMT_ACTIVE_SET_URI).\
|
||||
filter(lambda p: p.coap.tlv.sec_policy_o == 0).\
|
||||
must_next()
|
||||
pkts.filter_coap_ack(consts.MGMT_ACTIVE_SET_URI).must_next()
|
||||
|
||||
# Step 7: Commissioner_1
|
||||
# - Description: Harness instructs device to send MGMT_ACTIVE_GET.req to the DUT.
|
||||
# - CoAP Request URI: coap://[<L>]:MM/c/ag
|
||||
# - CoAP Payload: Get TLV specifying: Network Master Key TLV.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 8: Leader (DUT)
|
||||
# - Description: Automatically sends MGMT_ACTIVE_GET.rsp to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send MGMT_ACTIVE_GET.rsp to Commissioner_1:
|
||||
# - CoAP Response Code: 2.04 Changed
|
||||
# - CoAP Payload: Network Master Key TLV MUST NOT be included.
|
||||
print("Step 7-8: Network Key check")
|
||||
pkts.filter_coap_ack(consts.MGMT_ACTIVE_GET_URI).must_next().\
|
||||
must_verify(lambda p: p.coap.payload is nullField or consts.NM_NETWORK_KEY_TLV not in p.coap.tlv.type)
|
||||
|
||||
# Step 9: Commissioner_1
|
||||
# - Description: Harness instructs device to send MGMT_ACTIVE_SET.req to the DUT (disable "N" bit).
|
||||
# - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
# - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 20 (> step 5), Security Policy TLV with "N"
|
||||
# bit disabled.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 10: Leader (DUT)
|
||||
# - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
# - CoAP Response Code: 2.04 Changed
|
||||
# - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
print("Step 9-10: Disable N bit check")
|
||||
pkts.filter_coap_request(consts.MGMT_ACTIVE_SET_URI).\
|
||||
filter(lambda p: p.coap.tlv.sec_policy_o == 0 and p.coap.tlv.sec_policy_n == 0).\
|
||||
must_next()
|
||||
pkts.filter_coap_ack(consts.MGMT_ACTIVE_SET_URI).must_next()
|
||||
|
||||
# Save index for out-of-order search
|
||||
idx10 = pv.pkts.index
|
||||
|
||||
# Step 11: Commissioner_2
|
||||
# - Description: Harness instructs device to try to join the network as a Native Commissioner.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 12: Leader (DUT)
|
||||
# - Description: Automatically rejects Commissioner_2's attempt to join.
|
||||
# - Pass Criteria: The DUT MUST send a Discovery Response with Native Commissioning bit set to "Not Allowed".
|
||||
print("Step 11-12: Native Commissioner rejection (RELAXED)")
|
||||
pkts.filter_wpan_src64(COMMISSIONER_2).\
|
||||
filter(lambda p: p.mle.cmd == consts.MLE_DISCOVERY_REQUEST).\
|
||||
must_next()
|
||||
# Step 12 might be missing
|
||||
|
||||
# Step 13: Commissioner_1
|
||||
# - Description: Harness instructs device to send MGMT_ACTIVE_SET.req to the DUT ("B" bit = 0).
|
||||
# - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
# - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 25 (> Step 9), Security Policy TLV with "B"
|
||||
# bit = 0 (default).
|
||||
# - Note: This step is a legacy V1.1 behavior which has been deprecated in V1.2.1. For simplicity sake, this step
|
||||
# has been left as-is because the B-bit is now reserved - and the value of zero is the new default behavior.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 14: Leader (DUT)
|
||||
# - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
# - CoAP Response Code: 2.04 Changed
|
||||
# - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
print("Step 13-14: Disable B bit check")
|
||||
pkts.filter_coap_request(consts.MGMT_ACTIVE_SET_URI).\
|
||||
filter(lambda p: p.coap.tlv.sec_policy_o == 0 and p.coap.tlv.sec_policy_n == 0 and p.coap.tlv.sec_policy_b == 0).\
|
||||
must_next()
|
||||
pkts.filter_coap_ack(consts.MGMT_ACTIVE_SET_URI).must_next()
|
||||
idx14 = pkts.index
|
||||
|
||||
# Step 15: Test Harness Device
|
||||
# - Description: Harness instructs device to discover network using beacons.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 16: Leader (DUT)
|
||||
# - Description: Automatically responds with beacon response frame.
|
||||
# - Pass Criteria: The DUT MUST send beacon response frames. The beacon payload MUST either be empty OR the payload
|
||||
# format MUST be different from the Thread Beacon payload. The Protocol ID and Version field values MUST be
|
||||
# different from the values specified for the Thread beacon (Protocol ID= 3, Version = 2).
|
||||
print("Step 15-16: Discovery Request/Response (verifying bits)")
|
||||
pkts.range(idx10).\
|
||||
filter_wpan_src64(COMMISSIONER_2).\
|
||||
filter(lambda p: p.mle.cmd == consts.MLE_DISCOVERY_REQUEST).\
|
||||
must_next()
|
||||
pkts.range(idx10).\
|
||||
filter_wpan_src64(LEADER).\
|
||||
filter(lambda p: p.mle.cmd == consts.MLE_DISCOVERY_RESPONSE).\
|
||||
must_next().\
|
||||
must_verify('thread_meshcop.tlv.discovery_rsp_n == False')
|
||||
|
||||
# Continue from idx14
|
||||
pkts.index = idx14
|
||||
|
||||
# Step 17: Commissioner_1
|
||||
# - Description: Harness instructs device to send MGMT_ACTIVE_SET.req to the DUT (disable "R" bit).
|
||||
# - CoAP Request URI: coap://[<L>]:MM/c/as
|
||||
# - CoAP Payload: Commissioner Session ID TLV, Active Timestamp TLV = 30 (> step 13), Security Policy TLV with "R"
|
||||
# bit disabled.
|
||||
# - Pass Criteria: N/A.
|
||||
#
|
||||
# Step 18: Leader (DUT)
|
||||
# - Description: Automatically sends MGMT_ACTIVE_SET.rsp to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send MGMT_ACTIVE_SET.rsp to Commissioner_1:
|
||||
# - CoAP Response Code: 2.04 Changed
|
||||
# - CoAP Payload: State TLV (value = Accept (0x01)).
|
||||
print("Step 17-18: Disable R bit check")
|
||||
pkts.filter_coap_request(consts.MGMT_ACTIVE_SET_URI).\
|
||||
filter(lambda p: p.coap.tlv.sec_policy_o == 0 and p.coap.tlv.sec_policy_n == 0 and \
|
||||
p.coap.tlv.sec_policy_b == 0 and p.coap.tlv.sec_policy_r == 0).\
|
||||
must_next()
|
||||
pkts.filter_coap_ack(consts.MGMT_ACTIVE_SET_URI).must_next()
|
||||
|
||||
# Step 19: Leader (DUT)
|
||||
# - Description: Automatically sends multicast MLE Data Response. Commissioner_1 responds with MLE Data Request.
|
||||
# - Pass Criteria: The DUT MUST multicast MLE Data Response to the Link-Local All Nodes multicast address (FF02::1)
|
||||
# with active timestamp value as set in Step 17.
|
||||
print("Step 19: Multicast MLE Data Response")
|
||||
pkts.filter_LLANMA().\
|
||||
filter(lambda p: p.mle.cmd == consts.MLE_DATA_RESPONSE).\
|
||||
filter(lambda p: p.mle.tlv.active_tstamp == 30).\
|
||||
must_next()
|
||||
|
||||
# Step 20: Leader (DUT)
|
||||
# - Description: Automatically sends unicast MLE Data Response to Commissioner_1.
|
||||
# - Pass Criteria: The DUT MUST send a unicast MLE Data Response to Commissioner_1. The Active Operational Set MUST
|
||||
# contain a Security Policy TLV with R bit set to 0.
|
||||
print("Step 20: Unicast MLE Data Response (R=0)")
|
||||
pkts.filter(lambda p: p.mle.cmd == consts.MLE_DATA_RESPONSE).\
|
||||
filter(lambda p: hasattr(p.thread_meshcop, 'tlv') and hasattr(p.thread_meshcop.tlv, 'sec_policy_r') and p.thread_meshcop.tlv.sec_policy_r == 0).\
|
||||
must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
verify_utils.run_main(verify)
|
||||
@@ -89,6 +89,29 @@ def thread_coap_tlv_parse(t, v, layer=None):
|
||||
kvs.append(('pskc', v.hex()))
|
||||
elif t == consts.NM_SECURITY_POLICY_TLV and not is_diag:
|
||||
kvs.append(('security_policy', v.hex()))
|
||||
if len(v) >= 3:
|
||||
# v[0:2] is rotation time
|
||||
# v[2] is the first byte of flags
|
||||
# Bits in first byte of flags (v[2]):
|
||||
# Bits in first byte of flags (v[2]):
|
||||
# 7: o (obtaining network key)
|
||||
# 6: n (native commissioning)
|
||||
# 5: r (routers)
|
||||
# 4: c (external commissioner)
|
||||
# 3: b (beacons)
|
||||
# 2: C (commercial commissioning)
|
||||
# 1: e (autonomous enrollment)
|
||||
# 0: p (network key provisioning)
|
||||
flags = v[2]
|
||||
kvs.append(('sec_policy_o', (flags >> 7) & 1))
|
||||
kvs.append(('sec_policy_n', (flags >> 6) & 1))
|
||||
kvs.append(('sec_policy_r', (flags >> 5) & 1))
|
||||
kvs.append(('sec_policy_c', (flags >> 4) & 1))
|
||||
kvs.append(('sec_policy_b', (flags >> 3) & 1))
|
||||
kvs.append(('sec_policy_C', (flags >> 2) & 1))
|
||||
kvs.append(('sec_policy_e', (flags >> 1) & 1))
|
||||
kvs.append(('sec_policy_p', flags & 1))
|
||||
|
||||
elif t == consts.NM_NETWORK_KEY_TLV and len(v) == 16 and not is_diag:
|
||||
kvs.append(('network_key', v.hex()))
|
||||
elif t == consts.NM_PAN_ID_TLV and len(v) == 2 and not is_diag:
|
||||
@@ -148,6 +171,11 @@ def thread_coap_tlv_parse(t, v, layer=None):
|
||||
kvs.append(('child_mode', hex(mode)))
|
||||
elif t == consts.DG_CHANNEL_PAGES_TLV:
|
||||
kvs.append(('channel_pages', v.hex()))
|
||||
elif t == consts.NM_DISCOVERY_RESPONSE_TLV: # Discovery Response TLV
|
||||
# Bits 7-4: Version, Bit 3: Native Commissioner
|
||||
if len(v) >= 1:
|
||||
kvs.append(('discovery_version', (v[0] >> 4) & 0xf))
|
||||
kvs.append(('discovery_native_commissioner', (v[0] >> 3) & 1))
|
||||
return kvs
|
||||
|
||||
|
||||
@@ -206,9 +234,19 @@ def apply_patches():
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.network_name'] = layer_fields._str
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.pskc'] = layer_fields._bytes
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.security_policy'] = layer_fields._bytes
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_o'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_n'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_r'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_c'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_b'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_C'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_e'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.sec_policy_p'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.network_key'] = layer_fields._bytes
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.pan_id'] = layer_fields._auto
|
||||
layer_fields._LAYER_FIELDS['coap.tlv.mesh_local_prefix'] = layer_fields._bytes
|
||||
layer_fields._LAYER_FIELDS['thread_meshcop.tlv.discovery_version'] = layer_fields._dec
|
||||
layer_fields._LAYER_FIELDS['thread_meshcop.tlv.discovery_native_commissioner'] = layer_fields._dec
|
||||
|
||||
def which_tshark_patch():
|
||||
default_path = '/tmp/thread-wireshark/tshark'
|
||||
|
||||
Reference in New Issue
Block a user