/* * Copyright (c) 2026, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "platform/nexus_core.hpp" #include "platform/nexus_node.hpp" #include "meshcop/commissioner.hpp" #include "meshcop/dataset.hpp" #include "meshcop/dataset_manager.hpp" #include "meshcop/meshcop_leader.hpp" #include "meshcop/meshcop_tlvs.hpp" #include "meshcop/network_name.hpp" #include "thread/key_manager.hpp" #include "thread/mle.hpp" namespace ot { namespace Nexus { using MeshCoP::ExtendedPanId; using MeshCoP::NetworkName; /** * 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 after nodes have attached. */ static constexpr uint32_t kStabilizationTime = 10 * 1000; /** * Delay Timer value in milliseconds (300 seconds). */ static constexpr uint32_t kDelayTimer300s = 300 * 1000; /** * Active Timestamp value 20000. */ static constexpr uint64_t kActiveTimestamp20000 = 20000; /** * Active Timestamp value 20. */ static constexpr uint64_t kActiveTimestamp20 = 20; /** * Pending Timestamp value 20. */ static constexpr uint64_t kPendingTimestamp20 = 20; /** * Network Master Key 1. */ static const uint8_t kNetworkKey1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; /** * Network Master Key 2. */ static const uint8_t kNetworkKey2[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; /** * PSKc 1. */ static const uint8_t kPskc1[] = {0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x6a, 0x70, 0x61, 0x6b, 0x65, 0x74, 0x65, 0x73, 0x74, 0x04}; /** * PSKc 2. */ static const uint8_t kPskc2[] = {0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x6a, 0x70, 0x61, 0x6b, 0x65, 0x74, 0x65, 0x73, 0x74, 0x05}; void Test9_2_18(void) { /** * 9.2.18 Rolling Back the Active Timestamp with Pending Operational Dataset * * 9.2.18.1 Topology * - Commissioner * - Leader * - Router 1 * - MED 1 * - SED 1 * * 9.2.18.2 Purpose & Description * The purpose of this test case is to ensure that the DUT can roll back the Active Timestamp value by scheduling * an update through the Pending Operational Dataset only with the inclusion of a new Network Master Key. * * Spec Reference | V1.1 Section | V1.3.0 Section * ------------------------|--------------|--------------- * Delay Timer Management | 8.4.3.4 | 8.4.3.4 */ Core nexus; Node &commissioner = nexus.CreateNode(); Node &leader = nexus.CreateNode(); Node &router1 = nexus.CreateNode(); Node &med1 = nexus.CreateNode(); Node &sed1 = nexus.CreateNode(); commissioner.SetName("COMMISSIONER"); leader.SetName("LEADER"); router1.SetName("ROUTER_1"); med1.SetName("MED_1"); sed1.SetName("SED_1"); nexus.AdvanceTime(0); SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote)); Log("---------------------------------------------------------------------------------------"); Log("Step 1: All"); /** * Step 1: All * - Description: Ensure topology is formed correctly. * - Pass Criteria: N/A */ AllowLinkBetween(commissioner, leader); AllowLinkBetween(router1, leader); AllowLinkBetween(router1, med1); AllowLinkBetween(router1, sed1); MeshCoP::Dataset::Info datasetInfo; SuccessOrQuit(datasetInfo.GenerateRandom(leader.GetInstance())); { MeshCoP::Timestamp timestamp; timestamp.Clear(); timestamp.SetSeconds(1); datasetInfo.Set(timestamp); } memcpy(datasetInfo.Update().m8, kNetworkKey1, sizeof(kNetworkKey1)); datasetInfo.Set(11); datasetInfo.Set(0x1234); { uint8_t extPanIdBytes[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; memcpy(datasetInfo.Update().m8, extPanIdBytes, sizeof(extPanIdBytes)); } SuccessOrQuit(datasetInfo.Update().Set("Initial")); memcpy(datasetInfo.Update().m8, kPskc1, sizeof(kPskc1)); leader.Get().SaveLocal(datasetInfo); leader.Get().Up(); SuccessOrQuit(leader.Get().Start()); nexus.AdvanceTime(kFormNetworkTime); VerifyOrQuit(leader.Get().IsLeader()); commissioner.Join(leader); router1.Join(leader); nexus.AdvanceTime(kAttachToRouterTime); VerifyOrQuit(commissioner.Get().IsFullThreadDevice()); VerifyOrQuit(router1.Get().IsRouter()); { NetworkKey key; memcpy(key.m8, kNetworkKey1, sizeof(key.m8)); nexus.AddNetworkKey(key); memcpy(key.m8, kNetworkKey2, sizeof(key.m8)); nexus.AddNetworkKey(key); } med1.Join(router1, Node::kAsFed); sed1.Join(router1, Node::kAsSed); nexus.AdvanceTime(kStabilizationTime); VerifyOrQuit(med1.Get().IsChild()); VerifyOrQuit(sed1.Get().IsChild()); /** Start Commissioner and wait for it to become active. */ SuccessOrQuit(commissioner.Get().Start(nullptr, nullptr, nullptr)); nexus.AdvanceTime(kStabilizationTime); VerifyOrQuit(commissioner.Get().IsActive()); Log("---------------------------------------------------------------------------------------"); Log("Step 2: Commissioner"); /** * Step 2: Commissioner * - Description: Harness instructs Commissioner to send MGMT_ACTIVE_SET.req to the Leader Routing or Anycast * Locator: * - CoAP Request URI: coap://[]:MM/c/as * - CoAP Payload: * - Commissioner Session ID TLV (valid) * - Active Timestamp TLV <20000> * - Network Name TLV: "GRL" * - PSKc TLV: 74:68:72:65:61:64:6a:70:61:6b:65:74:65:73:74:04 * - The Leader Anycast Locator uses the Mesh local prefix with an IID of 0000:00FF:FE00:FC00 * - Pass Criteria: N/A */ MeshCoP::Dataset::Info activeDatasetInfo; activeDatasetInfo.Clear(); { MeshCoP::Timestamp timestamp; timestamp.Clear(); timestamp.SetSeconds(kActiveTimestamp20000); activeDatasetInfo.Set(timestamp); } SuccessOrQuit(activeDatasetInfo.Update().Set("GRL")); memcpy(activeDatasetInfo.Update().m8, kPskc1, sizeof(kPskc1)); /** * Step 3: Leader * - Description: Automatically responds with a MGMT_ACTIVE_SET.rsp to the Commissioner. * - Pass Criteria: For DUT = Leader: The DUT MUST send MGMT_ACTIVE_SET.rsp to the Commissioner: * - CoAP Response Code: 2.04 Changed * - CoAP Payload: State TLV */ { Tmf::Agent &agent = commissioner.Get(); Coap::Message *message = agent.AllocateAndInitPriorityConfirmablePostMessage(kUriActiveSet); VerifyOrQuit(message != nullptr); SuccessOrQuit(Tlv::Append( *message, commissioner.Get().GetSessionId())); { MeshCoP::Dataset dataset; dataset.SetFrom(activeDatasetInfo); SuccessOrQuit(message->AppendBytes(dataset.GetBytes(), dataset.GetLength())); } SuccessOrQuit(agent.SendMessageToLeaderAloc(*message)); } nexus.AdvanceTime(kStabilizationTime); VerifyOrQuit(leader.Get().GetTimestamp().GetSeconds() == kActiveTimestamp20000); Log("---------------------------------------------------------------------------------------"); Log("Step 4: Commissioner"); /** * Step 4: Commissioner * - Description: Harness instructs the Commissioner to send MGMT_PENDING_SET.req to the Leader Routing or Anycast * Locator: * - CoAP Request URI: coap://[]:MM/c/ps * - CoAP Payload: * - Commissioner Session ID TLV (valid) * - Pending Timestamp TLV <20s> * - Active Timestamp TLV <20s> * - Delay Timer TLV <20s> * - Network Name TLV : "Should not be" * - Pass Criteria: N/A */ MeshCoP::Dataset::Info pendingDatasetInfo1; pendingDatasetInfo1.Clear(); { MeshCoP::Timestamp timestamp; timestamp.Clear(); timestamp.SetSeconds(kPendingTimestamp20); pendingDatasetInfo1.Set(timestamp); timestamp.SetSeconds(kActiveTimestamp20); pendingDatasetInfo1.Set(timestamp); } pendingDatasetInfo1.Set(20000); // 20s SuccessOrQuit(pendingDatasetInfo1.Update().Set("Should not be")); /** * Step 5: Leader * - Description: Automatically sends MGMT_PENDING_SET.rsp to the Commissioner. * - Pass Criteria: For DUT = Leader: The DUT MUST send MGMT_PENDING_SET.rsp to the Commissioner: * - CoAP Response Code: 2.04 Changed * - CoAP Payload: State TLV */ { Tmf::Agent &agent = commissioner.Get(); Coap::Message *message = agent.AllocateAndInitPriorityConfirmablePostMessage(kUriPendingSet); VerifyOrQuit(message != nullptr); SuccessOrQuit(Tlv::Append( *message, commissioner.Get().GetSessionId())); { MeshCoP::Dataset dataset; dataset.SetFrom(pendingDatasetInfo1); SuccessOrQuit(message->AppendBytes(dataset.GetBytes(), dataset.GetLength())); } SuccessOrQuit(agent.SendMessageToLeaderAloc(*message)); } nexus.AdvanceTime(kStabilizationTime); VerifyOrQuit(!leader.Get().GetTimestamp().IsValid()); Log("---------------------------------------------------------------------------------------"); Log("Step 6: Commissioner"); /** * Step 6: Commissioner * - Description: Harness instructs Commissioner to send MGMT_PENDING_SET.req to the Leader Routing or Anycast * Locator: * - CoAP Request URI: coap://[]:MM/c/ps * - CoAP Payload: * - Commissioner Session ID TLV (valid) * - Pending Timestamp TLV <20s> * - Active Timestamp TLV <20s> * - Delay Timer TLV <300s> * - Network Name TLV : "My House" * - PSKc TLV: 74:68:72:65:61:64:6a:70:61:6b:65:74:65:73:74:05 * - Network Master Key TLV: (00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ee) * - The Leader Anycast Locator uses the Mesh local prefix with an IID of 0000:00FF:FE00:FC00. * - Pass Criteria: N/A */ MeshCoP::Dataset::Info pendingDatasetInfo2; pendingDatasetInfo2.Clear(); { MeshCoP::Timestamp timestamp; timestamp.Clear(); timestamp.SetSeconds(kPendingTimestamp20); pendingDatasetInfo2.Set(timestamp); timestamp.SetSeconds(kActiveTimestamp20); pendingDatasetInfo2.Set(timestamp); } pendingDatasetInfo2.Set(kDelayTimer300s); SuccessOrQuit(pendingDatasetInfo2.Update().Set("My House")); memcpy(pendingDatasetInfo2.Update().m8, kPskc2, sizeof(kPskc2)); memcpy(pendingDatasetInfo2.Update().m8, kNetworkKey2, sizeof(kNetworkKey2)); /** * Step 7: Leader * - Description: Automatically sends MGMT_PENDING_SET.rsp to the Commissioner. * - Pass Criteria: For DUT = Leader: The DUT MUST send MGMT_PENDING_SET.rsp to Commissioner: * - CoAP Response Code: 2.04 Changed * - CoAP Payload: State TLV */ { Tmf::Agent &agent = commissioner.Get(); Coap::Message *message = agent.AllocateAndInitPriorityConfirmablePostMessage(kUriPendingSet); VerifyOrQuit(message != nullptr); SuccessOrQuit(Tlv::Append( *message, commissioner.Get().GetSessionId())); { MeshCoP::Dataset dataset; dataset.SetFrom(pendingDatasetInfo2); SuccessOrQuit(message->AppendBytes(dataset.GetBytes(), dataset.GetLength())); } SuccessOrQuit(agent.SendMessageToLeaderAloc(*message)); } nexus.AdvanceTime(kStabilizationTime); VerifyOrQuit(leader.Get().GetTimestamp().GetSeconds() == kPendingTimestamp20); /** * Step 8: Leader * - Description: Automatically sends new network data to neighbors and rx-on-when-idle Children. * - Pass Criteria: For DUT = Leader: The DUT MUST multicast a MLE Data Response to the Link-Local All Nodes * multicast address (FF02::1) with the new information, which includes the following TLVs: * - Source Address TLV * - Leader Data TLV (Data Version field , Stable Data Version field ) * - Network Data TLV: Commissioning Data TLV: (Stable flag , Border Agent Locator TLV, Commissioner * Session ID TLV) * - Active Timestamp TLV * - Pending Timestamp TLV */ Log("---------------------------------------------------------------------------------------"); Log("Step 8: Leader"); /** * Step 9: Router_1 * - Description: Automatically sends a unicast MLE Data Request to the Leader. * - Pass Criteria: For DUT = Router: The DUT MUST send a unicast MLE Data Request to the Leader including the * following TLVs: * - TLV Request TLV: Network Data TLV * - Active Timestamp TLV */ Log("---------------------------------------------------------------------------------------"); Log("Step 9: Router_1"); /** * Step 10: Leader * - Description: Automatically sends a unicast MLE Data Response to Router_1. * - Pass Criteria: For DUT = Leader: The DUT MUST send a unicast MLE Data Response to Router_1, which includes the * following TLVs: * - Source Address TLV * - Leader Data TLV * - Network Data TLV: Commissioning Data TLV: (Stable flag , Border Agent Locator TLV, Commissioner * Session ID TLV) * - Active Timestamp TLV * - Pending Timestamp TLV * - Pending Operational Dataset TLV: (Active Timestamp TLV, Network Master Key TLV, Network Name TLV) */ Log("---------------------------------------------------------------------------------------"); Log("Step 10: Leader"); /** * Step 11: Router_1 * - Description: Automatically sends the new network data to neighbors and rx-on-while-idle Children (MED_1). * - Pass Criteria: For DUT = Router: The DUT MUST multicast a MLE Data Response with the new information, which the * following TLVs: * - Source Address TLV * - Leader Data TLV (Data Version field MUST have the same value that the Leader set in Step 8, Stable Data * Version field MUST have the same value that the Leader set in Step 8) * - Network Data TLV: (Stable flag , Commissioning Data TLV: Border Agent Locator TLV, Commissioner * Session ID TLV) * - Active Timestamp TLV * - Pending Timestamp TLV */ Log("---------------------------------------------------------------------------------------"); Log("Step 11: Router_1"); /** * Step 12A: Router_1 * - Description: Automatically sends notification of new network data to SED_1 via a unicast MLE Child Update * Request. * - Pass Criteria: For DUT = Router: The DUT MUST send MLE Child Update Request to SED_1, including the following * TLVs: * - Source Address TLV * - Leader Data TLV (Data version numbers MUST be the same as the ones sent in the multicast data response in * step 8.) * - Network Data TLV * - Active Timestamp TLV <20000s> * - Pending Timestamp TLV <20s> * - Goto step 13 */ Log("---------------------------------------------------------------------------------------"); Log("Step 12A: Router_1"); /** * Step 13: SED_1 * - Description: Automatically requests the full network data from Router_1 via a unicast MLE Data Request. * - Pass Criteria: For DUT = SED: The DUT MUST send a unicast MLE Data Request to Router_1, including the following * TLVs: * - TLV Request TLV: Network Data TLV * - Active Timestamp TLV */ Log("---------------------------------------------------------------------------------------"); Log("Step 13: SED_1"); /** * Step 14: Router_1 * - Description: Automatically sends the requested full network data to SED_1. * - Pass Criteria: For DUT = Router: The DUT MUST send a unicast MLE Data Response to SED_1, including the * following TLVs: * - Source Address TLV * - Network Data TLV * - Pending Operational Dataset TLV: (Channel TLV, Active Timestamp TLV, Channel Mask TLV, Extended PAN ID TLV, * Network Mesh-Local Prefix TLV, Network Master Key TLV, Network Name TLV, PAN ID TLV, PSKc TLV, Security * Policy TLV, Delay Timer TLV) * - Active Timestamp TLV <20000s> * - Pending Timestamp TLV <20s> */ Log("---------------------------------------------------------------------------------------"); Log("Step 14: Router_1"); Log("---------------------------------------------------------------------------------------"); Log("Step 16: Harness"); /** * Step 16: Harness * - Description: Wait for data to distribute and for Pending set Delay time to expire ~300s. * - Pass Criteria: N/A */ nexus.AdvanceTime(kDelayTimer300s); Log("---------------------------------------------------------------------------------------"); Log("Step 17: Commissioner"); /** * Step 17: Commissioner * - Description: Harness verifies connectivity by instructing Commissioner to send an ICMPv6 Echo Request to the * DUT mesh local address. * - Pass Criteria: The DUT MUST respond with an ICMPv6 Echo Reply. ** Note that Wireshark will not be able to * decode the ICMPv6 Echo Request packet. */ VerifyOrQuit(leader.Get().GetTimestamp().GetSeconds() == kActiveTimestamp20); nexus.AdvanceTime(kStabilizationTime); // Verify connectivity by pinging from Commissioner to Leader commissioner.SendEchoRequest(leader.Get().GetMeshLocalEid(), 0); nexus.AdvanceTime(kStabilizationTime); nexus.SaveTestInfo("test_1_1_9_2_18.json"); } } // namespace Nexus } // namespace ot int main(void) { ot::Nexus::Test9_2_18(); printf("All tests passed\n"); return 0; }