Skip to content

Commit 91e7c33

Browse files
[nexus] stricter tests related to MLE role transitions (openthread#13068)
Key changes: * Added `mle_router_role_allowed` nexus test, which includes a test of the correct type of advertisement used by each type of node. * Updated the `router_downgrade_on_sec_policy_change` nexus test to also test changes of the Router role allowed/disallowed when multiple factors are changed * Updated checks in `verify_1_1_5_3_6.py` to verify that only Router advertisments are sent during the test, to verify that REED advertisements are not sent unless the unit is no longer attempting to upgrade
1 parent ef6fabd commit 91e7c33

5 files changed

Lines changed: 553 additions & 34 deletions

tests/nexus/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ ot_nexus_test(key_rotation_guard_time "core;nexus")
411411
ot_nexus_test(leader_reboot_multiple_link_request "core;nexus")
412412
ot_nexus_test(log_override "core;nexus")
413413
ot_nexus_test(mac_scan "core;nexus")
414+
ot_nexus_test(mle_router_role_allowed "core;nexus")
414415
ot_nexus_test(mle_blocking_downgrade "core;nexus")
415416
ot_nexus_test(mle_msg_key_seq_jump "core;nexus")
416417
ot_nexus_test(nat64_translator "core;nexus")
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* Copyright (c) 2026, The OpenThread Authors.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* 3. Neither the name of the copyright holder nor the
13+
* names of its contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
29+
#include <stdio.h>
30+
31+
#include "platform/nexus_core.hpp"
32+
#include "platform/nexus_node.hpp"
33+
34+
namespace ot {
35+
namespace Nexus {
36+
37+
/**
38+
* Time to advance for a node to form a network and become leader, in milliseconds.
39+
*/
40+
static constexpr uint32_t kFormNetworkTime = 13 * 1000;
41+
42+
/**
43+
* Time to advance for a node to join as a child and upgrade to a router, in milliseconds.
44+
*/
45+
static constexpr uint32_t kAttachToRouterTime = 200 * 1000;
46+
47+
/**
48+
* Maximum interval between Router advertisements.
49+
*/
50+
static constexpr uint32_t kMaximumRouterAdvertisementInterval = 32 * 1000;
51+
52+
/**
53+
* Device mode to configure as a Full Thread Device (FTD).
54+
*/
55+
static const Mle::DeviceMode kFtdDeviceMode(Mle::DeviceMode::kModeRxOnWhenIdle |
56+
Mle::DeviceMode::kModeFullThreadDevice |
57+
Mle::DeviceMode::kModeFullNetworkData);
58+
59+
/**
60+
* Device mode to configure as a Minimal End Device (MED).
61+
*/
62+
static const Mle::DeviceMode kMedDeviceMode(Mle::DeviceMode::kModeRxOnWhenIdle | Mle::DeviceMode::kModeFullNetworkData);
63+
64+
void TestMleRouterRoleAllowed(void)
65+
{
66+
// This test validates that correctly-formatted transmissions occur based on whether the Router
67+
// role is allowed. This includes Advertisements (only from REEDs and Active Routers),
68+
// which should not occur for devices with Router role disallowed.
69+
// This test verifies Router role disallowed on FED and MED devices, with a duplicate
70+
// pair used to demonstrate that these transmissions begin in the correct format when
71+
// re-configured so that the Router role becomes allowed.
72+
73+
Core nexus;
74+
75+
Node &leader = nexus.CreateNode();
76+
Node &router = nexus.CreateNode();
77+
Node &reed = nexus.CreateNode();
78+
Node &fedOnly = nexus.CreateNode();
79+
Node &medOnly = nexus.CreateNode();
80+
Node &fedAndRouter = nexus.CreateNode();
81+
Node &medAndRouter = nexus.CreateNode();
82+
83+
leader.SetName("Leader");
84+
router.SetName("Router");
85+
reed.SetName("REED");
86+
fedOnly.SetName("FED_ONLY");
87+
medOnly.SetName("MED_ONLY");
88+
fedAndRouter.SetName("FED_Router");
89+
medAndRouter.SetName("MED_Router");
90+
91+
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo));
92+
93+
Log("---------------------------------------------------------------------------------------");
94+
Log("Step 1: Form topology");
95+
// Child links are only set up with the leader and router,
96+
// so that the REED will not become a router due to a parent request.
97+
98+
AllowLinkBetween(leader, router);
99+
AllowLinkBetween(leader, reed);
100+
AllowLinkBetween(leader, fedOnly);
101+
AllowLinkBetween(leader, medOnly);
102+
AllowLinkBetween(leader, fedAndRouter);
103+
AllowLinkBetween(leader, medAndRouter);
104+
105+
AllowLinkBetween(router, reed);
106+
AllowLinkBetween(router, fedOnly);
107+
AllowLinkBetween(router, medOnly);
108+
AllowLinkBetween(router, fedAndRouter);
109+
AllowLinkBetween(router, medAndRouter);
110+
111+
Log("---------------------------------------------------------------------------------------");
112+
Log("Step 1.1: Leader");
113+
// All Leader advertisements are expected to have a router source address
114+
115+
leader.Form();
116+
nexus.AdvanceTime(kFormNetworkTime);
117+
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
118+
VerifyOrQuit(leader.Get<Mle::Mle>().IsRouterRoleAllowed());
119+
120+
Log("---------------------------------------------------------------------------------------");
121+
Log("Step 1.2: Join Router");
122+
// All Router advertisements are expected to have a router source address
123+
124+
router.Join(leader);
125+
nexus.AdvanceTime(kAttachToRouterTime);
126+
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
127+
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
128+
VerifyOrQuit(router.Get<Mle::Mle>().IsRouterRoleAllowed());
129+
130+
Log("---------------------------------------------------------------------------------------");
131+
Log("Step 1.3: Join end devices (only) - reed, fed, & med");
132+
133+
// All REED advertisements should have a child source address
134+
reed.Get<Mle::Mle>().SetRouterUpgradeThreshold(1);
135+
reed.Join(leader);
136+
// Router role is allowed for the REED, but should not upgrade due to the updated threshold
137+
VerifyOrQuit(reed.Get<Mle::Mle>().IsRouterRoleAllowed());
138+
139+
fedOnly.Join(leader);
140+
// Attach as an FTD, but not configured as router-eligible
141+
SuccessOrQuit(fedOnly.Get<Mle::Mle>().SetRouterEligible(false));
142+
VerifyOrQuit(!fedOnly.Get<Mle::Mle>().IsRouterRoleAllowed());
143+
144+
medOnly.Join(leader, Node::kAsMed);
145+
VerifyOrQuit(!medOnly.Get<Mle::Mle>().IsRouterRoleAllowed());
146+
147+
nexus.AdvanceTime(kAttachToRouterTime);
148+
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
149+
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
150+
VerifyOrQuit(reed.Get<Mle::Mle>().IsChild());
151+
VerifyOrQuit(fedOnly.Get<Mle::Mle>().IsChild());
152+
VerifyOrQuit(medOnly.Get<Mle::Mle>().IsChild());
153+
154+
Log("---------------------------------------------------------------------------------------");
155+
Log("Step 1.4: Join end devices that will have router role allowed - fed & med");
156+
157+
fedAndRouter.Join(leader);
158+
// Attach as an FTD, but not configured as router-eligible
159+
SuccessOrQuit(fedAndRouter.Get<Mle::Mle>().SetRouterEligible(false));
160+
VerifyOrQuit(fedAndRouter.Get<Mle::Mle>().IsFullThreadDevice());
161+
VerifyOrQuit(!fedAndRouter.Get<Mle::Mle>().IsRouterRoleAllowed());
162+
163+
medAndRouter.Join(leader, Node::kAsMed);
164+
VerifyOrQuit(!medAndRouter.Get<Mle::Mle>().IsFullThreadDevice());
165+
VerifyOrQuit(!medAndRouter.Get<Mle::Mle>().IsRouterRoleAllowed());
166+
167+
Log("---------------------------------------------------------------------------------------");
168+
Log("Step 1.5: Child attachment");
169+
// FED and MED nodes with router role disallowed should not advertise during this time
170+
171+
nexus.AdvanceTime(kAttachToRouterTime);
172+
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
173+
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
174+
VerifyOrQuit(reed.Get<Mle::Mle>().IsChild());
175+
VerifyOrQuit(fedOnly.Get<Mle::Mle>().IsChild());
176+
VerifyOrQuit(medOnly.Get<Mle::Mle>().IsChild());
177+
VerifyOrQuit(fedAndRouter.Get<Mle::Mle>().IsChild());
178+
VerifyOrQuit(medAndRouter.Get<Mle::Mle>().IsChild());
179+
180+
Log("---------------------------------------------------------------------------------------");
181+
Log("Step 2: Set FED & MED Router Role allowed");
182+
183+
// Now router eligible so that the former FED is allowed to take the Router role
184+
SuccessOrQuit(fedAndRouter.Get<Mle::Mle>().SetRouterEligible(true));
185+
VerifyOrQuit(fedAndRouter.Get<Mle::Mle>().IsRouterRoleAllowed());
186+
187+
// Enabling FTD mode so that the former MED is allowed to take the Router role
188+
SuccessOrQuit(medAndRouter.Get<Mle::Mle>().SetDeviceMode(kFtdDeviceMode));
189+
VerifyOrQuit(medAndRouter.Get<Mle::Mle>().IsFullThreadDevice());
190+
VerifyOrQuit(medAndRouter.Get<Mle::Mle>().IsRouterRoleAllowed());
191+
192+
// FED and MED nodes should advertise as Routers at least once during this time
193+
nexus.AdvanceTime(kAttachToRouterTime);
194+
nexus.AdvanceTime(kMaximumRouterAdvertisementInterval);
195+
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
196+
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
197+
VerifyOrQuit(reed.Get<Mle::Mle>().IsChild());
198+
VerifyOrQuit(fedOnly.Get<Mle::Mle>().IsChild());
199+
VerifyOrQuit(medOnly.Get<Mle::Mle>().IsChild());
200+
VerifyOrQuit(fedAndRouter.Get<Mle::Mle>().IsRouter());
201+
VerifyOrQuit(medAndRouter.Get<Mle::Mle>().IsRouter());
202+
203+
Log("---------------------------------------------------------------------------------------");
204+
Log("Step 3: Set FED & MED back to their original configuration");
205+
206+
// Setting the router role by configuration is expected to immediately detach
207+
SuccessOrQuit(fedAndRouter.Get<Mle::Mle>().SetRouterEligible(false));
208+
VerifyOrQuit(!fedAndRouter.Get<Mle::Mle>().IsRouterRoleAllowed());
209+
210+
// Setting the mode by configuration is expected to immediately detach
211+
SuccessOrQuit(medAndRouter.Get<Mle::Mle>().SetDeviceMode(kMedDeviceMode));
212+
VerifyOrQuit(!medAndRouter.Get<Mle::Mle>().IsFullThreadDevice());
213+
VerifyOrQuit(!medAndRouter.Get<Mle::Mle>().IsRouterRoleAllowed());
214+
215+
// FED and MED nodes should attach as children and send no further advertisements
216+
nexus.AdvanceTime(kAttachToRouterTime);
217+
nexus.AdvanceTime(kMaximumRouterAdvertisementInterval);
218+
219+
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
220+
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
221+
VerifyOrQuit(reed.Get<Mle::Mle>().IsChild());
222+
VerifyOrQuit(fedOnly.Get<Mle::Mle>().IsChild());
223+
VerifyOrQuit(medOnly.Get<Mle::Mle>().IsChild());
224+
VerifyOrQuit(fedAndRouter.Get<Mle::Mle>().IsChild());
225+
VerifyOrQuit(medAndRouter.Get<Mle::Mle>().IsChild());
226+
227+
nexus.SaveTestInfo("test_mle_router_role_allowed.json", &leader);
228+
}
229+
230+
} // namespace Nexus
231+
} // namespace ot
232+
233+
int main(void)
234+
{
235+
ot::Nexus::TestMleRouterRoleAllowed();
236+
printf("All tests passed\n");
237+
return 0;
238+
}

0 commit comments

Comments
 (0)