diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 7c6a2b97b..986efbfb9 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -399,6 +399,7 @@ ot_nexus_test(form_join "core;nexus") ot_nexus_test(ipv6_fragmentation "core;nexus") ot_nexus_test(ipv6_source_selection "core;nexus") ot_nexus_test(key_rotation_guard_time "core;nexus") +ot_nexus_test(leader_reboot_multiple_link_request "core;nexus") ot_nexus_test(log_override "core;nexus") ot_nexus_test(mac_scan "core;nexus") ot_nexus_test(mle_blocking_downgrade "core;nexus") @@ -408,6 +409,7 @@ ot_nexus_test(netdata_publisher "core;nexus") ot_nexus_test(reed_address_solicit_rejected "core;nexus") ot_nexus_test(router_downgrade_on_sec_policy_change "core;nexus") ot_nexus_test(router_reattach "core;nexus") +ot_nexus_test(router_reboot_multiple_link_request "core;nexus") ot_nexus_test(srp_auto_start "core;nexus") ot_nexus_test(srp_client_change_lease "core;nexus") ot_nexus_test(srp_client_remove_host "core;nexus") diff --git a/tests/nexus/run_nexus_tests.sh b/tests/nexus/run_nexus_tests.sh index 5561416c3..b10b67f34 100755 --- a/tests/nexus/run_nexus_tests.sh +++ b/tests/nexus/run_nexus_tests.sh @@ -235,6 +235,8 @@ DEFAULT_TESTS=( "1_4_CS_TC_3" "srp_client_change_lease" "inform_previous_parent_on_reattach" + "leader_reboot_multiple_link_request" + "router_reboot_multiple_link_request" ) # Use provided arguments or the default test list diff --git a/tests/nexus/test_leader_reboot_multiple_link_request.cpp b/tests/nexus/test_leader_reboot_multiple_link_request.cpp new file mode 100644 index 000000000..b0b4deccc --- /dev/null +++ b/tests/nexus/test_leader_reboot_multiple_link_request.cpp @@ -0,0 +1,120 @@ +/* + * 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 { + +/** + * 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 = 10 * 1000; + +/** + * Time to advance after Leader reset to allow it to send all link requests. + */ +static constexpr uint32_t kResetRestoringTime = 10 * 1000; + +void TestLeaderRebootMultipleLinkRequest(void) +{ + /** + * Test Leader Reboot Multiple Link Request + * + * Topology + * - Leader + * - Router + * + * Purpose & Description + * The purpose of this test case is to show that when the Leader is rebooted, it sends + * MLE_MAX_RESTORING_TRANSMISSION_COUNT MLE link request packets if no response is received. + */ + + Core nexus; + + Node &leader = nexus.CreateNode(); + Node &router = nexus.CreateNode(); + + leader.SetName("LEADER"); + router.SetName("ROUTER"); + + SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo)); + + Log("Step 1: Form topology"); + + AllowLinkBetween(leader, router); + + leader.Form(); + nexus.AdvanceTime(kFormNetworkTime); + VerifyOrQuit(leader.Get().IsLeader()); + + router.Join(leader); + nexus.AdvanceTime(kAttachToRouterTime); + VerifyOrQuit(router.Get().IsRouter()); + + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 2: Reset Leader and isolate it from Router"); + + leader.Reset(); + + // Isolate Leader from Router so it doesn't receive Link Accept + UnallowLinkBetween(leader, router); + + Log("Step 3: Start Leader and wait for it to send multiple Link Requests"); + + leader.Get().Up(); + SuccessOrQuit(leader.Get().Start()); + + nexus.AdvanceTime(kResetRestoringTime); + + nexus.SaveTestInfo("test_leader_reboot_multiple_link_request.json", &leader); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestLeaderRebootMultipleLinkRequest(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/test_router_reboot_multiple_link_request.cpp b/tests/nexus/test_router_reboot_multiple_link_request.cpp new file mode 100644 index 000000000..c8e32236c --- /dev/null +++ b/tests/nexus/test_router_reboot_multiple_link_request.cpp @@ -0,0 +1,136 @@ +/* + * 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 { + +/** + * 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 a node to join as a child, in milliseconds. + */ +static constexpr uint32_t kAttachAsChildTime = 10 * 1000; + +/** + * Time to advance for the network to stabilize, in milliseconds. + */ +static constexpr uint32_t kStabilizationTime = 10 * 1000; + +/** + * Time to advance after Router reset to allow it to send all link requests. + * + * Router transitions must account for a potential jitter of up to 2 minutes (120 seconds). + */ +static constexpr uint32_t kResetRestoringTime = 120 * 1000; + +void TestRouterRebootMultipleLinkRequest(void) +{ + /** + * Test Router Reboot Multiple Link Request + * + * Topology + * - Leader + * - Router + * - MED (attached to Router) + * + * Purpose & Description + * The purpose of this test case is to show that when a router is rebooted, it sends + * MLE_MAX_RESTORING_TRANSMISSION_COUNT MLE link request packets if no response is received. + */ + + Core nexus; + + Node &leader = nexus.CreateNode(); + Node &router = nexus.CreateNode(); + Node &med = nexus.CreateNode(); + + leader.SetName("LEADER"); + router.SetName("ROUTER"); + med.SetName("MED"); + + SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo)); + + Log("Step 1: Form topology"); + + AllowLinkBetween(leader, router); + AllowLinkBetween(router, med); + + leader.Form(); + nexus.AdvanceTime(kFormNetworkTime); + VerifyOrQuit(leader.Get().IsLeader()); + + router.Join(leader); + nexus.AdvanceTime(kAttachToRouterTime); + VerifyOrQuit(router.Get().IsRouter()); + + med.Join(router, Node::kAsMed); + nexus.AdvanceTime(kAttachAsChildTime); + VerifyOrQuit(med.Get().IsChild()); + VerifyOrQuit(med.Get().GetParent().GetExtAddress() == router.Get().GetExtAddress()); + + nexus.AdvanceTime(kStabilizationTime); + + Log("Step 2: Reset Router and isolate it from Leader"); + + router.Reset(); + + // Isolate Router from Leader so it doesn't receive Link Accept + UnallowLinkBetween(leader, router); + + Log("Step 3: Start Router and wait for it to send multiple Link Requests"); + + router.Get().Up(); + SuccessOrQuit(router.Get().Start()); + + nexus.AdvanceTime(kResetRestoringTime); + + nexus.SaveTestInfo("test_router_reboot_multiple_link_request.json", &leader); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestRouterRebootMultipleLinkRequest(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/verify_leader_reboot_multiple_link_request.py b/tests/nexus/verify_leader_reboot_multiple_link_request.py new file mode 100644 index 000000000..d8c3cee95 --- /dev/null +++ b/tests/nexus/verify_leader_reboot_multiple_link_request.py @@ -0,0 +1,87 @@ +#!/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 + +# 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): + # Test Leader Reboot Multiple Link Request + # + # Topology + # - Leader + # - Router + # + # Purpose & Description + # The purpose of this test case is to show that when the Leader is rebooted, it sends + # MLE_MAX_RESTORING_TRANSMISSION_COUNT MLE link request packets if no response is received. + + pkts = pv.pkts + pv.summary.show() + + LEADER = pv.vars['LEADER'] + ROUTER = pv.vars['ROUTER'] + + # Step 1: Form topology + # - Verify topology is formed correctly. + pv.verify_attached('ROUTER', 'LEADER') + + # Step 2: Reset Leader and isolate it from Router + # - The Leader MUST send MLE_MAX_RESTORING_TRANSMISSION_COUNT multicast Link Request + # since it doesn't receive Link Accept from Router. + + # The value of MLE_MAX_RESTORING_TRANSMISSION_COUNT is 4 by default. + MLE_MAX_RESTORING_TRANSMISSION_COUNT = 4 + + for i in range(MLE_MAX_RESTORING_TRANSMISSION_COUNT): + pkts.filter_wpan_src64(LEADER).\ + filter_LLARMA().\ + filter_mle_cmd(consts.MLE_LINK_REQUEST).\ + filter(lambda p: { + consts.CHALLENGE_TLV, + consts.VERSION_TLV, + consts.TLV_REQUEST_TLV, + consts.ADDRESS16_TLV, + consts.ROUTE64_TLV + } <= set(p.mle.tlv.type) and\ + p.mle.tlv.addr16 is nullField and\ + p.mle.tlv.route64.id_mask is nullField).\ + must_next() + + +if __name__ == '__main__': + verify_utils.run_main(verify) diff --git a/tests/nexus/verify_router_reboot_multiple_link_request.py b/tests/nexus/verify_router_reboot_multiple_link_request.py new file mode 100644 index 000000000..3c0ccd94c --- /dev/null +++ b/tests/nexus/verify_router_reboot_multiple_link_request.py @@ -0,0 +1,90 @@ +#!/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 + +# 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): + # Test Router Reboot Multiple Link Request + # + # Topology + # - Leader + # - Router + # - MED (attached to Router) + # + # Purpose & Description + # The purpose of this test case is to show that when a router is rebooted, it sends + # MLE_MAX_RESTORING_TRANSMISSION_COUNT MLE link request packets if no response is received. + + pkts = pv.pkts + pv.summary.show() + + LEADER = pv.vars['LEADER'] + ROUTER = pv.vars['ROUTER'] + MED = pv.vars['MED'] + + # Step 1: Form topology + # - Verify topology is formed correctly. + pv.verify_attached('ROUTER', 'LEADER') + pv.verify_attached('MED', 'ROUTER', 'MTD') + + # Step 2: Reset Router and isolate it from Leader + # - The router MUST send MLE_MAX_RESTORING_TRANSMISSION_COUNT multicast Link Request + # since it doesn't receive Link Accept from Leader. + + # The value of MLE_MAX_RESTORING_TRANSMISSION_COUNT is 4 by default. + MLE_MAX_RESTORING_TRANSMISSION_COUNT = 4 + + for i in range(MLE_MAX_RESTORING_TRANSMISSION_COUNT): + pkts.filter_wpan_src64(ROUTER).\ + filter_LLARMA().\ + filter_mle_cmd(consts.MLE_LINK_REQUEST).\ + filter(lambda p: { + consts.CHALLENGE_TLV, + consts.VERSION_TLV, + consts.TLV_REQUEST_TLV, + consts.ADDRESS16_TLV, + consts.ROUTE64_TLV + } <= set(p.mle.tlv.type) and\ + p.mle.tlv.addr16 is nullField and\ + p.mle.tlv.route64.id_mask is nullField).\ + must_next() + + +if __name__ == '__main__': + verify_utils.run_main(verify) diff --git a/tests/scripts/thread-cert/test_leader_reboot_multiple_link_request.py b/tests/scripts/thread-cert/test_leader_reboot_multiple_link_request.py deleted file mode 100755 index 7cb4524d7..000000000 --- a/tests/scripts/thread-cert/test_leader_reboot_multiple_link_request.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2023, 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 copy -import unittest - -import command -import config -import mle -import thread_cert -from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV -from pktverify.packet_verifier import PacketVerifier -from pktverify.null_field import nullField - -DUT_LEADER = 1 -DUT_ROUTER1 = 2 - -# Test Purpose and Description: -# ----------------------------- -# The purpose of this test case is to show that when the Leader is rebooted, it sends MLE_MAX_RESTORING_TRANSMISSION_COUNT MLE link request packets if no response is received. -# -# Test Topology: -# ------------- -# Leader -# | -# Router -# -# DUT Types: -# ---------- -# Leader -# Router - - -class Test_LeaderRebootMultipleLinkRequest(thread_cert.TestCase): - #USE_MESSAGE_FACTORY = False - - TOPOLOGY = { - DUT_LEADER: { - 'name': 'LEADER', - 'mode': 'rdn', - 'allowlist': [DUT_ROUTER1] - }, - DUT_ROUTER1: { - 'name': 'ROUTER', - 'mode': 'rdn', - 'allowlist': [DUT_LEADER] - }, - } - - def _setUpLeader(self): - self.nodes[DUT_LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64()) - self.nodes[DUT_LEADER].enable_allowlist() - - def test(self): - self.nodes[DUT_LEADER].start() - self.simulator.go(config.LEADER_STARTUP_DELAY) - self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader') - - self.nodes[DUT_ROUTER1].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) - self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router') - - leader_rloc = self.nodes[DUT_LEADER].get_ip6_address(config.ADDRESS_TYPE.RLOC) - - leader_rloc16 = self.nodes[DUT_LEADER].get_addr16() - self.nodes[DUT_LEADER].reset() - self.assertFalse(self.nodes[DUT_ROUTER1].ping(leader_rloc)) - self._setUpLeader() - - # Router1 will not reply to leader's link request - self.nodes[DUT_ROUTER1].clear_allowlist() - - self.nodes[DUT_LEADER].start() - - self.simulator.go(config.LEADER_RESET_DELAY) - - def verify(self, pv): - pkts = pv.pkts - pv.summary.show() - - LEADER = pv.vars['LEADER'] - ROUTER = pv.vars['ROUTER'] - - # Verify topology is formed correctly. - pv.verify_attached('ROUTER', 'LEADER') - - # The DUT MUST send properly formatted MLE Advertisements with - # an IP Hop Limit of 255 to the Link-Local All Nodes multicast - # address (FF02::1). - # The following TLVs MUST be present in the MLE Advertisements: - # - Leader Data TLV - # - Route64 TLV - # - Source Address TLV - with pkts.save_index(): - pkts.filter_wpan_src64(LEADER).\ - filter_mle_advertisement('Leader').\ - must_next() - pkts.filter_wpan_src64(ROUTER).\ - filter_mle_advertisement('Router').\ - must_next() - - pkts.filter_ping_request().\ - filter_wpan_src64(ROUTER).\ - must_next() - - # The Leader MUST send MLE_MAX_RESTORING_TRANSMISSION_COUNT multicast Link Request - # The following TLVs MUST be present in the Link Request: - # - Challenge TLV - # - Version TLV - # - TLV Request TLV: Address16 TLV, Route64 TLV - for i in range(0, config.MLE_MAX_RESTORING_TRANSMISSION_COUNT): - pkts.filter_wpan_src64(LEADER).\ - filter_LLARMA().\ - filter_mle_cmd(MLE_LINK_REQUEST).\ - filter(lambda p: { - CHALLENGE_TLV, - VERSION_TLV, - TLV_REQUEST_TLV, - ADDRESS16_TLV, - ROUTE64_TLV - } <= set(p.mle.tlv.type) and\ - p.mle.tlv.addr16 is nullField and\ - p.mle.tlv.route64.id_mask is nullField - ).\ - must_next() - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/scripts/thread-cert/test_router_reboot_multiple_link_request.py b/tests/scripts/thread-cert/test_router_reboot_multiple_link_request.py deleted file mode 100755 index cf4d32ea4..000000000 --- a/tests/scripts/thread-cert/test_router_reboot_multiple_link_request.py +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2023, 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 copy -import unittest - -import command -import config -import mle -import thread_cert -from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV -from pktverify.packet_verifier import PacketVerifier -from pktverify.null_field import nullField - -LEADER = 1 -DUT_ROUTER = 2 -MED1 = 3 -MED2 = 4 -MED3 = 5 -MED4 = 6 -MED5 = 7 -MED6 = 8 - -# Test Purpose and Description: -# ----------------------------- -# The purpose of this test case is to show that when a router is rebooted, it sends MLE_MAX_RESTORING_TRANSMISSION_COUNT MLE link request packets if no response is received. -# -# Test Topology: -# ------------- -# Leader -# | -# Router ------------------------+ -# | | | | | | -# MED1 MED2 MED3 MED4 MED5 MED6 -# -# DUT Types: -# ---------- -# Router - - -class Test_LeaderRebootMultipleLinkRequest(thread_cert.TestCase): - #USE_MESSAGE_FACTORY = False - - TOPOLOGY = { - LEADER: { - 'name': 'LEADER', - 'mode': 'rdn', - 'allowlist': [DUT_ROUTER] - }, - DUT_ROUTER: { - 'name': 'ROUTER', - 'mode': 'rdn', - 'allowlist': [LEADER, MED1, MED2, MED3, MED4, MED5, MED6] - }, - MED1: { - 'name': 'MED1', - 'mode': 'rn', - 'allowlist': [DUT_ROUTER] - }, - MED2: { - 'name': 'MED2', - 'mode': 'rn', - 'allowlist': [DUT_ROUTER] - }, - MED3: { - 'name': 'MED3', - 'mode': 'rn', - 'allowlist': [DUT_ROUTER] - }, - MED4: { - 'name': 'MED4', - 'mode': 'rn', - 'allowlist': [DUT_ROUTER] - }, - MED5: { - 'name': 'MED5', - 'mode': 'rn', - 'allowlist': [DUT_ROUTER] - }, - MED6: { - 'name': 'MED6', - 'mode': 'rn', - 'allowlist': [DUT_ROUTER] - }, - } - - def test(self): - self.nodes[LEADER].start() - self.simulator.go(config.LEADER_STARTUP_DELAY) - self.assertEqual(self.nodes[LEADER].get_state(), 'leader') - - self.nodes[DUT_ROUTER].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) - self.assertEqual(self.nodes[DUT_ROUTER].get_state(), 'router') - - for medid in range(MED1, MED6 + 1): - self.nodes[medid].start() - self.simulator.go(config.ED_STARTUP_DELAY) - self.assertEqual(self.nodes[medid].get_state(), 'child') - - self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) - - self.nodes[DUT_ROUTER].reset() - # Leader will not reply to router's link request - self.nodes[LEADER].clear_allowlist() - - self.nodes[DUT_ROUTER].start() - - self.simulator.go(config.LEADER_RESET_DELAY) - - def verify(self, pv): - pkts = pv.pkts - pv.summary.show() - - LEADER = pv.vars['LEADER'] - ROUTER = pv.vars['ROUTER'] - - # Verify topology is formed correctly. - pv.verify_attached('ROUTER', 'LEADER') - for i in range(1, 7): - pv.verify_attached('MED%d' % i, 'ROUTER', 'MTD') - - pkts.filter_wpan_src64(ROUTER).\ - filter_mle_advertisement('Router').\ - must_next() - - # The router MUST send MLE_MAX_RESTORING_TRANSMISSION_COUNT multicast Link Request - # The following TLVs MUST be present in the Link Request: - # - Challenge TLV - # - Version TLV - # - TLV Request TLV: Address16 TLV, Route64 TLV - for i in range(0, config.MLE_MAX_RESTORING_TRANSMISSION_COUNT): - pkts.filter_wpan_src64(ROUTER).\ - filter_LLARMA().\ - filter_mle_cmd(MLE_LINK_REQUEST).\ - filter(lambda p: { - CHALLENGE_TLV, - VERSION_TLV, - TLV_REQUEST_TLV, - ADDRESS16_TLV, - ROUTE64_TLV - } <= set(p.mle.tlv.type) and\ - p.mle.tlv.addr16 is nullField and\ - p.mle.tlv.route64.id_mask is nullField - ).\ - must_next() - - -if __name__ == '__main__': - unittest.main()