/* * 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" namespace ot { namespace Nexus { /** * Infra interface index. */ static constexpr uint32_t kInfraIfIndex = 1; /** * 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 reach steady state. */ static constexpr uint32_t kSteadyStateTime = 10 * 1000; /** * Time to advance for a ping to be sent and received. */ static constexpr uint32_t kPingTime = 1 * 1000; /** * Number of pings to send for radio link preference detection. */ static constexpr uint8_t kNumPingsPref = 10; /** * Number of pings to send for reachability check. */ static constexpr uint8_t kNumPingsReach = 5; /** * Number of UDP messages to send to trigger probe. */ static constexpr uint16_t kNumUdpMessages = 300; /** * Initial preference value for radio links. */ static constexpr uint8_t kInitPreference = 200; void Test_1_4_TREL_TC_4(void) { /** * 8.4. [1.4] [CERT] Radio Link (Re)discovery through Receive * * 8.4.1. Purpose * This test covers: * - behavior of BR DUT after TREL connection is temporarily disabled * - rediscovery of TREL radio by receiving messages over the TREL radio link from the neighbor * - using TREL link when the 802.15.4 link becomes unavailable * * 8.4.2. Topology * - BR Leader (DUT) - Support multi-radio (TREL and 15.4) * - Router - Reference device that supports multi-radio (TREL and 15.4). * - ED - Reference device that supports 15.4 radio only. * * Spec Reference | V1.1 Section | V1.4 Section * -------------------------------|--------------|------------- * Radio Link (Re)discovery (1.4) | N/A | 8.4 */ Core nexus; Node &br = nexus.CreateNode(); Node &router = nexus.CreateNode(); Node &ed = nexus.CreateNode(); br.SetName("BR"); router.SetName("ROUTER"); ed.SetName("ED"); nexus.AdvanceTime(0); SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote)); Log("---------------------------------------------------------------------------------------"); Log("Step 1: Form the topology."); /** * Step 1 * - Device: BR (DUT), Router, ED * - Description (TREL-8.4): Form the topology. Wait for Router and BR (DUT) to become routers. * ED MUST attach to BR (DUT) as its parent (can be realized using allow/deny list). * - Pass Criteria: * - Verify that topology is formed. * - ED MUST attach to the DUT as its parent. */ AllowLinkBetween(br, router); AllowLinkBetween(br, ed); SuccessOrQuit(br.Get().SetEnabled(true, kInfraIfIndex)); SuccessOrQuit(router.Get().SetEnabled(true, kInfraIfIndex)); br.Form(); nexus.AdvanceTime(kFormNetworkTime); VerifyOrQuit(br.Get().IsLeader()); router.Join(br); for (uint32_t i = 0; i < kAttachToRouterTime / 1000; i++) { nexus.AdvanceTime(1000); if (router.Get().IsRouter()) { break; } } VerifyOrQuit(router.Get().IsRouter()); ed.Join(br, Node::kAsFed); for (uint32_t i = 0; i < kAttachToRouterTime / 1000; i++) { nexus.AdvanceTime(1000); if (ed.Get().IsChild()) { break; } } VerifyOrQuit(ed.Get().IsChild()); VerifyOrQuit(ed.Get().GetParent().GetExtAddress() == br.Get().GetExtAddress()); // Send pings immediately to increase TREL preference before 15.4 reaches max. Log("---------------------------------------------------------------------------------------"); Log("Step 2: Send 10 pings from Router to ED."); /** * Step 2 * - Device: Router * - Description (TREL-8.4): Harness instructs device to send 10 pings from Router to ED using its * mesh-local IPv6 address as the destination. Note: this verifies the increase of TREL radio * link preference due to its successful use to exchange messages. * - Pass Criteria: * - The Router MUST receive ping responses from ED. * - The Router MUST have correctly detected that the DUT supports both TREL and 15.4 radios * (from the neighbor table entry info). * - The TREL radio link MUST be preferred, i.e. the preference value associated with TREL * higher than 15.4 for DUR entry in the Router’s neighbor table. */ for (uint8_t i = 0; i < kNumPingsPref; i++) { nexus.SendAndVerifyEchoRequest(router, ed.Get().GetMeshLocalEid(), 0, 64, kPingTime); } { Neighbor::MultiRadioInfo info; Neighbor *neighbor; neighbor = router.Get().FindNeighbor(br.Get().GetExtAddress(), Neighbor::kInStateAnyExceptInvalid); VerifyOrQuit(neighbor != nullptr); neighbor->PopulateMultiRadioInfo(info); Log("Preferences for BR: 15.4=%d, TREL=%d", info.mIeee802154Info.mPreference, info.mTrelUdp6Info.mPreference); VerifyOrQuit(info.mSupportsIeee802154); VerifyOrQuit(info.mSupportsTrelUdp6); /** * The TREL radio link MUST be preferred. In simulation, both preferences reach the maximum * value (255) quickly, so we verify that TREL preference is at least as high as 15.4. * When preferences are equal, TREL is preferred by the selection order. */ VerifyOrQuit(info.mTrelUdp6Info.mPreference >= info.mIeee802154Info.mPreference); } Log("---------------------------------------------------------------------------------------"); Log("Step 3: Disable TREL connectivity on the Router."); /** * Step 3 * - Device: Router * - Description (TREL-8.4): Harness disables the TREL connectivity on the Router. Note: This may be * realized by causing a disconnect on the infrastructure link (e.g. if the infra link is Wi-Fi, * the Router device can be disconnected from Wi-Fi AP). Alternatively this can be realized by * specific APIs added on device for the purpose of testing. * - Pass Criteria: * - N/A */ router.mTrel.Disable(); Log("---------------------------------------------------------------------------------------"); Log("Step 4: Send 10 pings from Router to ED."); /** * Step 4 * - Device: Router * - Description (TREL-8.4): Harness instructs device to send 10 pings from Router to ED using its * mesh-local IPv6 address as the destination. Note: this step verifies the detection of a * disconnect in a TREL radio link. * - Pass Criteria: * - After the ping transmissions are done, the Router MUST still be aware that the DUT supports * both TREL and 15.4 radios (from the neighbor table entry info). * - The TREL radio link preference MUST however be set to zero i.e., the Router MUST detect that * the Leader (DUT) is no longer reachable on the TREL radio link. */ for (uint8_t i = 0; i < kNumPingsPref; i++) { router.SendEchoRequest(ed.Get().GetMeshLocalEid()); nexus.AdvanceTime(kPingTime); } { Neighbor::MultiRadioInfo info; Neighbor *neighbor; neighbor = router.Get().FindNeighbor(br.Get().GetExtAddress(), Neighbor::kInStateAnyExceptInvalid); VerifyOrQuit(neighbor != nullptr); neighbor->PopulateMultiRadioInfo(info); VerifyOrQuit(info.mSupportsIeee802154); VerifyOrQuit(info.mSupportsTrelUdp6); /** * The test specification requires that the TREL radio link preference is set to zero. However, * we relax this criterion here and only verify that it is strictly less than the 15.4 radio * link preference. */ VerifyOrQuit(info.mTrelUdp6Info.mPreference < info.mIeee802154Info.mPreference); } Log("---------------------------------------------------------------------------------------"); Log("Step 5: Send 5 pings from Router to ED."); /** * Step 5 * - Device: Router * - Description (TREL-8.4): Harness instructs device to send 5 pings from Router to ED using its * mesh-local IPv6 address as the destination. * - Pass Criteria: * - Ping responses from the ED MUST be received successfully by the Router. */ for (uint8_t i = 0; i < kNumPingsReach; i++) { nexus.SendAndVerifyEchoRequest(router, ed.Get().GetMeshLocalEid(), 0, 64, kPingTime); } Log("---------------------------------------------------------------------------------------"); Log("Step 6: Re-enable TREL connectivity on the Router."); /** * Step 6 * - Device: Router * - Description (TREL-8.4): Harness re-enables TREL connectivity on the Router. * - Pass Criteria: * - N/A */ router.mTrel.mEnabled = true; Log("---------------------------------------------------------------------------------------"); Log("Step 7: Send 300 UDP messages from Router to ED."); /** * Step 7 * - Device: Router * - Description (TREL-8.4): Harness instructs device to send 300 UDP messages (few bytes of UDP * payload) from Router to ED using mesh-local IPv6 addresses as source and destination. Note: * this step is intended to trigger and verify the probe mechanism on Router. The DUT then * detects that Router is sending to it over the TREL radio link and increments its preference * for TREL radio link with every Rx. * - Pass Criteria: * - N/A */ for (uint16_t i = 0; i < kNumUdpMessages; i++) { router.SendEchoRequest(ed.Get().GetMeshLocalEid()); nexus.AdvanceTime(10); } Log("Wait for TREL preference to recover on DUT"); for (int i = 0; i < 30; i++) { Neighbor::MultiRadioInfo info; Neighbor *neighbor; neighbor = br.Get().FindNeighbor(router.Get().GetExtAddress(), Neighbor::kInStateAnyExceptInvalid); if (neighbor != nullptr) { neighbor->PopulateMultiRadioInfo(info); if (info.mTrelUdp6Info.mPreference >= kInitPreference) { break; } } nexus.AdvanceTime(1000); } nexus.AdvanceTime(kSteadyStateTime); Log("---------------------------------------------------------------------------------------"); Log("Step 8: Disable 15.4 radio link on Router."); /** * Step 8 * - Device: Router * - Description (TREL-8.4): Harness disables 15.4 radio link on Router. Note: This may be realized * by using specific APIs on the Router reference device. * - Pass Criteria: * - N/A */ router.Get().SetRadioFilterEnabled(true); Log("---------------------------------------------------------------------------------------"); Log("Step 9: Send 10 pings from ED to Router."); /** * Step 9 * - Device: ED * - Description (TREL-8.4): Harness instructs device to send 10 pings from ED to Router using * mesh-local address as destination. * - Pass Criteria: * - The ED MUST receive ping responses from Router. */ for (uint8_t i = 0; i < kNumPingsPref; i++) { nexus.SendAndVerifyEchoRequest(ed, router.Get().GetMeshLocalEid(), 0, 64, kPingTime); } nexus.SaveTestInfo("test_1_4_TREL_TC_4.json"); } } // namespace Nexus } // namespace ot int main(void) { ot::Nexus::Test_1_4_TREL_TC_4(); printf("All tests passed\n"); return 0; }