[mle] track "router role allowed" state in a new variable (#12854)

This commit introduces a new member variable `mRouterRoleAllowed` in
the `Mle` class to cache the evaluation of whether the device is
currently permitted to operate as a router.

Previously, the `IsRouterEligible()` method evaluated several
conditions (e.g., `IsFullThreadDevice()`, `mRouterEligible` config,
and various fields in `SecurityPolicy`) every time it was called.
Since this method is invoked frequently across different `Mle`
operations, re-evaluating these conditions repeatedly was
inefficient.

The new `mRouterRoleAllowed` variable caches the final computed
result. It is updated via the `UpdateRouterRoleAllowed()` method
whenever any underlying input changes, such as:
- `Mle` starting.
- Configuration parameter updates (e.g., `SetRouterEligible()`).
- Security policy changes from the `KeyManager`.

This change centralizes the logic for handling role permission updates
into a single location (`UpdateRouterRoleAllowed()`). By
consolidating the actions taken when the allowed state changes, the
codebase is cleaner and easier to maintain and update.

It also provides a clearer conceptual distinction between the user's
router configuration (`mRouterEligible`) and the effective state
used by the device.
This commit is contained in:
Abtin Keshavarzian
2026-04-14 14:15:22 -05:00
committed by GitHub
parent 582dc1cd67
commit 221a9cbbb0
8 changed files with 145 additions and 89 deletions
+99 -68
View File
@@ -76,9 +76,9 @@ void Mle::HandlePartitionChange(void)
mRouterTable.Clear();
}
bool Mle::IsRouterEligible(void) const
bool Mle::DetermineIfRouterRoleAllowed(void) const
{
bool rval = false;
bool allowed = false;
const SecurityPolicy &secPolicy = Get<KeyManager>().GetSecurityPolicy();
VerifyOrExit(mRouterEligible && IsFullThreadDevice());
@@ -86,6 +86,7 @@ bool Mle::IsRouterEligible(void) const
#if OPENTHREAD_CONFIG_THREAD_VERSION == OT_THREAD_VERSION_1_1
VerifyOrExit(secPolicy.mRoutersEnabled);
#else
if (secPolicy.mCommercialCommissioningEnabled)
{
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
@@ -94,6 +95,7 @@ bool Mle::IsRouterEligible(void) const
VerifyOrExit(secPolicy.mNonCcmRoutersEnabled);
#endif
}
if (!secPolicy.mRoutersEnabled)
{
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
@@ -105,12 +107,73 @@ bool Mle::IsRouterEligible(void) const
kThreadVersion);
#endif
}
#endif
rval = true;
#endif // #if OPENTHREAD_CONFIG_THREAD_VERSION == OT_THREAD_VERSION_1_1
allowed = true;
exit:
return rval;
return allowed;
}
void Mle::UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason)
{
bool allowed = DetermineIfRouterRoleAllowed();
VerifyOrExit(allowed != mRouterRoleAllowed);
mRouterRoleAllowed = allowed;
if (IsAttached())
{
Get<Mac::Mac>().SetBeaconEnabled(mRouterRoleAllowed);
}
// Take action based on the current role, the new `mRouterRoleAllowed`,
// and the reason for the change.
if (IsChild() && mRouterRoleAllowed && (aReason == kReasonConfigParameterChanged))
{
mRouterRoleTransition.StartTimeout();
}
if (IsRouterOrLeader())
{
// If currently acting as router or leader, but the config or
// security policy changes such that the router role is no
// longer allowed, we take action based on the reason. If the
// change is due to a security policy update, we start a jitter
// timeout to downgrade (per Section 5.9.9 in the Thread spec),
// adding an extra delay if acting as leader. If this is
// triggered due to parameter change (`SetRouterEligible(false)`
// was called by the user), we take immediate action and become
// detached.
VerifyOrExit(!mRouterRoleAllowed);
switch (aReason)
{
case kReasonMleInit:
case kReasonDeviceModeChanged:
break;
case kReasonConfigParameterChanged:
IgnoreError(BecomeDetached());
break;
case kReasonSecurityPolicyChanged:
VerifyOrExit(!mRouterRoleTransition.IsPending());
mRouterRoleTransition.StartTimeout();
if (IsLeader())
{
mRouterRoleTransition.IncreaseTimeout(kLeaderDowngradeExtraDelay);
}
break;
}
}
exit:
return;
}
Error Mle::SetRouterEligible(bool aEligible)
@@ -125,57 +188,26 @@ Error Mle::SetRouterEligible(bool aEligible)
VerifyOrExit(aEligible != mRouterEligible);
mRouterEligible = aEligible;
switch (mRole)
{
case kRoleDisabled:
case kRoleDetached:
break;
case kRoleChild:
if (mRouterEligible)
{
mRouterRoleTransition.StartTimeout();
}
Get<Mac::Mac>().SetBeaconEnabled(mRouterEligible);
break;
case kRoleRouter:
case kRoleLeader:
if (!mRouterEligible)
{
IgnoreError(BecomeDetached());
}
break;
}
UpdateRouterRoleAllowed(kReasonConfigParameterChanged);
exit:
return error;
}
void Mle::HandleSecurityPolicyChanged(void)
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
void Mle::SetCcmEnabled(bool aEnabled)
{
// If we are currently router or leader and no longer eligible to
// be a router (due to security policy change), we start jitter
// timeout to downgrade.
VerifyOrExit(IsRouterOrLeader() && !IsRouterEligible());
VerifyOrExit(!mRouterRoleTransition.IsPending());
mRouterRoleTransition.StartTimeout();
if (IsLeader())
{
mRouterRoleTransition.IncreaseTimeout(kLeaderDowngradeExtraDelay);
}
exit:
return;
mCcmEnabled = aEnabled;
UpdateRouterRoleAllowed(kReasonConfigParameterChanged);
}
void Mle::SetThreadVersionCheckEnabled(bool aEnabled)
{
mThreadVersionCheckEnabled = aEnabled;
UpdateRouterRoleAllowed(kReasonConfigParameterChanged);
}
#endif
#if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
void Mle::SetDeviceProperties(const DeviceProperties &aDeviceProperties)
{
@@ -204,7 +236,7 @@ Error Mle::BecomeRouter(RouterUpgradeReason aReason)
ExitNow();
}
VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable);
VerifyOrExit(IsRouterRoleAllowed(), error = kErrorNotCapable);
LogInfo("Attempt to become router, reason:%s", RouterUpgradeReasonToString(aReason));
@@ -230,7 +262,7 @@ Error Mle::BecomeLeader(LeaderWeightCheck aMode)
#endif
VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
VerifyOrExit(!IsLeader(), error = kErrorNone);
VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable);
VerifyOrExit(IsRouterRoleAllowed(), error = kErrorNotCapable);
if ((aMode == kCheckLeaderWeight) && IsAttached())
{
@@ -314,10 +346,7 @@ void Mle::HandleChildStart(void)
StopLeader();
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMle);
if (mRouterEligible)
{
Get<Mac::Mac>().SetBeaconEnabled(true);
}
Get<Mac::Mac>().SetBeaconEnabled(IsRouterRoleAllowed());
Get<ThreadNetif>().SubscribeAllRoutersMulticast();
@@ -451,12 +480,14 @@ void Mle::HandleAdvertiseTrickleTimer(TrickleTimer &aTimer) { aTimer.Get<Mle>().
void Mle::HandleAdvertiseTrickleTimer(void)
{
VerifyOrExit(IsRouterEligible(), mAdvertiseTrickleTimer.Stop());
SendMulticastAdvertisement();
exit:
return;
if (!IsRouterRoleAllowed())
{
mAdvertiseTrickleTimer.Stop();
}
else
{
SendMulticastAdvertisement();
}
}
void Mle::StopAdvertiseTrickleTimer(void) { mAdvertiseTrickleTimer.Stop(); }
@@ -1128,7 +1159,7 @@ bool Mle::IsSingleton(void) const
{
bool isSingleton = true;
VerifyOrExit(IsAttached() && IsRouterEligible());
VerifyOrExit(IsAttached() && IsRouterRoleAllowed());
isSingleton = (mRouterTable.GetActiveRouterCount() <= 1);
exit:
@@ -1421,7 +1452,7 @@ void Mle::HandleParentRequest(RxInfo &aRxInfo)
Log(kMessageReceive, kTypeParentRequest, aRxInfo.mMessageInfo.GetPeerAddr());
VerifyOrExit(IsRouterEligible());
VerifyOrExit(IsRouterRoleAllowed());
VerifyOrExit(!IsDetached() && !IsAttaching());
VerifyOrExit(!mDetacher.IsDetaching());
@@ -1590,9 +1621,9 @@ void Mle::HandleTimeTick(void)
OT_FALL_THROUGH;
case kRoleLeader:
if (!IsRouterEligible())
if (!IsRouterRoleAllowed())
{
LogInfo("No longer router eligible");
LogInfo("Router role no longer allowed");
IgnoreError(BecomeDetached());
}
@@ -1611,7 +1642,7 @@ void Mle::HandleTimeTick(void)
break;
case kRoleChild:
if (!IsRouterEligible())
if (!IsRouterRoleAllowed())
{
break;
}
@@ -2084,7 +2115,7 @@ void Mle::HandleChildIdRequest(RxInfo &aRxInfo)
Log(kMessageReceive, kTypeChildIdRequest, aRxInfo.mMessageInfo.GetPeerAddr());
VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
VerifyOrExit(IsRouterRoleAllowed(), error = kErrorInvalidState);
VerifyOrExit(IsAttached(), error = kErrorInvalidState);
@@ -2732,7 +2763,7 @@ void Mle::HandleDiscoveryRequest(RxInfo &aRxInfo)
Log(kMessageReceive, kTypeDiscoveryRequest, aRxInfo.mMessageInfo.GetPeerAddr());
VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
VerifyOrExit(IsRouterRoleAllowed(), error = kErrorInvalidState);
SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange));
@@ -3473,7 +3504,7 @@ bool Mle::WillBecomeRouterSoon(void) const
bool willBecomeRouter = false;
VerifyOrExit(IsRouterEligible() && IsChild());
VerifyOrExit(IsRouterRoleAllowed() && IsChild());
VerifyOrExit(!mAddressSolicitRejected);
if (!mAddressSolicitPending)