[mle] separate router and leader upgrade thresholds (#13227)

This commit separates the single `ROUTER_UPGRADE_THRESHOLD` into two
distinct concepts: a local threshold for role transitions (used by a
REED deciding when to upgrade) and a leader threshold (used by the
Leader deciding whether to accept a router upgrade request).

This prepares for adding support for a new Router Config feature,
where the local threshold can be changed while the threshold used
when the device acts as a leader must remain the spec-defined value.

To preserve backward compatibility with existing tests and tools,
`otThreadSetRouterUpgradeThreshold` continues to set both thresholds
simultaneously.

Several Nexus test scenarios have been updated to explicitly set
the new leader threshold alongside the local router threshold.
This commit is contained in:
Abtin Keshavarzian
2026-06-22 17:32:35 -07:00
committed by GitHub
parent 6e8570e233
commit 7874555efb
13 changed files with 45 additions and 4 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (606)
#define OPENTHREAD_API_VERSION (607)
/**
* @addtogroup api-instance
+7 -1
View File
@@ -394,11 +394,17 @@ void otThreadSetNetworkIdTimeout(otInstance *aInstance, uint8_t aTimeout);
uint8_t otThreadGetRouterUpgradeThreshold(otInstance *aInstance);
/**
* Set the ROUTER_UPGRADE_THRESHOLD parameter used in the Leader role.
* Sets the ROUTER_UPGRADE_THRESHOLD parameter.
*
* @note This API is reserved for testing and demo purposes only. Changing settings with
* this API will render a production application non-compliant with the Thread Specification.
*
* This API historically set a single threshold value that was used for both local role transitions (deciding when
* the device itself should upgrade to a router) and by the leader (deciding whether to allow other devices to
* upgrade). These behaviors have now been separated into distinct router and leader thresholds. To preserve backward
* compatibility with existing applications and test scripts, this function continues to configure both thresholds
* (both the local router upgrade threshold and the leader upgrade threshold).
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aThreshold The ROUTER_UPGRADE_THRESHOLD value.
*
+1
View File
@@ -152,6 +152,7 @@ uint8_t otThreadGetRouterUpgradeThreshold(otInstance *aInstance)
void otThreadSetRouterUpgradeThreshold(otInstance *aInstance, uint8_t aThreshold)
{
AsCoreType(aInstance).Get<Mle::Mle>().SetRouterUpgradeThreshold(aThreshold);
AsCoreType(aInstance).Get<Mle::Mle>().SetLeaderUpgradeThreshold(aThreshold);
}
uint8_t otThreadGetChildRouterLinks(otInstance *aInstance)
+1
View File
@@ -87,6 +87,7 @@ Mle::Mle(Instance &aInstance)
, mPreviousPartitionIdTimeout(0)
, mChildRouterLinks(kChildRouterLinks)
, mAlternateRloc16Timeout(0)
, mLeaderUpgradeThreshold(kRouterUpgradeThreshold)
, mParentPriority(kParentPriorityUnspecified)
, mPreviousPartitionIdRouter(0)
, mPreviousPartitionId(0)
+24
View File
@@ -987,10 +987,33 @@ public:
/**
* Sets the ROUTER_UPGRADE_THRESHOLD value.
*
* This threshold is used by the device to decide whether to upgrade from a child (REED) to a router role. It is
* not used when the device is acting as the Leader to evaluate upgrade requests from other devices.
*
* @param[in] aThreshold The ROUTER_UPGRADE_THRESHOLD value.
*/
void SetRouterUpgradeThreshold(uint8_t aThreshold) { mRoleTransitioner.SetUpgradeThreshold(aThreshold); }
/**
* Returns the leader upgrade threshold value.
*
* This threshold is used only when the device is operating as the Leader to decide whether to accept or reject
* an Address Solicit request from a router-eligible end device (REED) wishing to upgrade to a router.
*
* @returns The leader upgrade threshold value.
*/
uint8_t GetLeaderUpgradeThreshold(void) const { return mLeaderUpgradeThreshold; }
/**
* Sets the leader upgrade threshold value.
*
* This threshold is used only when the device is operating as the Leader to decide whether to accept or reject
* an Address Solicit request from a router-eligible end device (REED) wishing to upgrade to a router.
*
* @param[in] aThreshold The leader upgrade threshold value.
*/
void SetLeaderUpgradeThreshold(uint8_t aThreshold) { mLeaderUpgradeThreshold = aThreshold; }
/**
* Returns the ROUTER_DOWNGRADE_THRESHOLD value.
*
@@ -2604,6 +2627,7 @@ private:
uint8_t mPreviousPartitionIdTimeout;
uint8_t mChildRouterLinks;
uint8_t mAlternateRloc16Timeout;
uint8_t mLeaderUpgradeThreshold;
int8_t mParentPriority;
uint32_t mPreviousPartitionIdRouter;
uint32_t mPreviousPartitionId;
+2 -2
View File
@@ -3457,7 +3457,7 @@ void Mle::ProcessAddressSolicit(AddrSolicitInfo &aInfo)
switch (aInfo.mReason)
{
case kReasonTooFewRouters:
VerifyOrExit(mRoleTransitioner.IsRouterCountBelowUpgradeThreshold());
VerifyOrExit(Get<RouterTable>().GetActiveRouterCount() < mLeaderUpgradeThreshold);
break;
case kReasonHaveChildIdRequest:
@@ -3465,7 +3465,7 @@ void Mle::ProcessAddressSolicit(AddrSolicitInfo &aInfo)
break;
case kReasonBorderRouterRequest:
if (!mRoleTransitioner.IsRouterCountBelowUpgradeThreshold() &&
if ((Get<RouterTable>().GetActiveRouterCount() >= mLeaderUpgradeThreshold) &&
(Get<NetworkData::Leader>().CountBorderRouters(NetworkData::kRouterRoleOnly) >=
kRouterUpgradeBorderRouterRequestThreshold))
{
+2
View File
@@ -89,7 +89,9 @@ void Test5_2_3(void)
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
leader.Get<Mle::Mle>().SetRouterUpgradeThreshold(kMaxRouters);
leader.Get<Mle::Mle>().SetLeaderUpgradeThreshold(kMaxRouters);
leader.Get<Mle::Mle>().SetRouterDowngradeThreshold(kMaxRouters);
for (uint8_t i = 0; i < kMaxRouters; i++)
{
routers[i]->Get<Mle::Mle>().SetRouterUpgradeThreshold(kMaxRouters);
+1
View File
@@ -109,6 +109,7 @@ void Test_5_2_6(void)
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
leader.Get<Mle::Mle>().SetRouterUpgradeThreshold(kRouterUpgradeThreshold);
leader.Get<Mle::Mle>().SetLeaderUpgradeThreshold(kRouterUpgradeThreshold);
leader.Get<Mle::Mle>().SetRouterDowngradeThreshold(kRouterDowngradeThreshold);
for (uint16_t i = 0; i < kInitialRouterCount - 2; i++)
@@ -76,6 +76,8 @@ void TestBrUpgradeRouterRole(void)
Log("Set the router upgrade threshold to 2 on all nodes.");
leader.Get<Mle::Mle>().SetRouterUpgradeThreshold(2);
leader.Get<Mle::Mle>().SetLeaderUpgradeThreshold(2);
router.Get<Mle::Mle>().SetRouterUpgradeThreshold(2);
br1.Get<Mle::Mle>().SetRouterUpgradeThreshold(2);
br2.Get<Mle::Mle>().SetRouterUpgradeThreshold(2);
+1
View File
@@ -51,6 +51,7 @@ void TestCompactRouteTlv(void)
Log("Form topology - leader + %u routers", kNumRouters);
leader.Get<Mle::Mle>().SetRouterUpgradeThreshold(32);
leader.Get<Mle::Mle>().SetLeaderUpgradeThreshold(32);
leader.Get<Mle::Mle>().SetRouterDowngradeThreshold(33);
leader.Form();
@@ -50,6 +50,7 @@ void TestReedAddressSolicitRejected(void)
// Leader sets RouterUpgradeThreshold to 1 to reject further routers
leader.Get<Mle::Mle>().SetRouterUpgradeThreshold(1);
leader.Get<Mle::Mle>().SetLeaderUpgradeThreshold(1);
Log("Step 1: Form network and join REED");
AllowLinkBetween(leader, reed);
+1
View File
@@ -62,6 +62,7 @@ void TestRouterReattach(void)
// Step 1: Start Leader
Log("Step 1: Starting Leader");
nodes[0]->Get<Mle::Mle>().SetRouterUpgradeThreshold(32);
nodes[0]->Get<Mle::Mle>().SetLeaderUpgradeThreshold(32);
nodes[0]->Get<Mle::Mle>().SetRouterDowngradeThreshold(32);
nodes[0]->Form();
nexus.AdvanceTime(kFormNetworkTime);
@@ -80,6 +80,7 @@ void TestSrpServerRebootPort(void)
Log("0. Start the server & client devices.");
client.Get<Mle::Mle>().SetRouterUpgradeThreshold(2);
client.Get<Mle::Mle>().SetLeaderUpgradeThreshold(2);
server.Get<Mle::Mle>().SetRouterUpgradeThreshold(2);
client.Form();