From 4384c66e7bcac04200bb4ea04609919682b6c260 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 5 May 2026 07:42:08 -0700 Subject: [PATCH] [mle] consolidate role transition management in `RoleTransitioner` (#12983) This commit introduces the `RoleTransitioner` class (renamed from `RouterRoleTransition`) to centralize the management of router role eligibility, thresholds, and transitions. The following state and logic are moved from the `Mle` class into the `RoleTransitioner`: - Router role eligibility and allowance state (`mRouterEligible`, `mRouterRoleAllowed`). - Upgrade and downgrade thresholds. - Downgrade blocking state (`mDowngradeBlocked`). - Transition decision logic (`DecideWhetherToUpgrade()`, `DecideWhetherToDowngrade()`). - The transition jitter timer and its management. By consolidating these responsibilities, the complexity of the main `Mle` class is reduced, and the role transition process is more explicitly managed within its own sub-component. --- src/core/thread/mle.cpp | 26 ++-- src/core/thread/mle.hpp | 110 ++++++++------- src/core/thread/mle_ftd.cpp | 257 ++++++++++++++++++++---------------- 3 files changed, 215 insertions(+), 178 deletions(-) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index c1a43be07..344645b68 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -80,18 +80,9 @@ Mle::Mle(Instance &aInstance) , mWedAttachTimer(aInstance) #endif #if OPENTHREAD_FTD - , mRouterEligible(true) - , mRouterRoleAllowed(true) - , mBlockDowngrade(false) , mAddressSolicitPending(false) , mAddressSolicitRejected(false) -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - , mCcmEnabled(false) - , mThreadVersionCheckEnabled(true) -#endif , mNetworkIdTimeout(kNetworkIdTimeout) - , mRouterUpgradeThreshold(kRouterUpgradeThreshold) - , mRouterDowngradeThreshold(kRouterDowngradeThreshold) , mPreviousPartitionRouterIdSequence(0) , mPreviousPartitionIdTimeout(0) , mChildRouterLinks(kChildRouterLinks) @@ -108,6 +99,7 @@ Mle::Mle(Instance &aInstance) , mAdvertiseTrickleTimer(aInstance, Mle::HandleAdvertiseTrickleTimer) , mChildTable(aInstance) , mRouterTable(aInstance) + , mRoleTransitioner(aInstance) #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_P2P_ENABLE , mP2p(aInstance) @@ -455,7 +447,7 @@ void Mle::Restore(void) mHasRestored = true; #if OPENTHREAD_FTD - UpdateRouterRoleAllowed(kReasonMleInit); + mRoleTransitioner.UpdateRouterRoleAllowed(RoleTransitioner::kReasonMleInit); #endif exit: @@ -606,7 +598,7 @@ void Mle::SetStateDetached(void) Get().SetRxOnWhenIdle(true); Get().SetBeaconEnabled(false); #if OPENTHREAD_FTD - mBlockDowngrade = false; + mRoleTransitioner.SetDowngradeBlocked(false); ClearAlternateRloc16(); HandleDetachStart(); #endif @@ -727,7 +719,7 @@ Error Mle::SetDeviceMode(DeviceMode aDeviceMode) ClearAlternateRloc16(); } - UpdateRouterRoleAllowed(kReasonDeviceModeChanged); + mRoleTransitioner.UpdateRouterRoleAllowed(RoleTransitioner::kReasonDeviceModeChanged); #endif if (IsAttached()) @@ -1052,21 +1044,23 @@ void Mle::HandleNotifierEvents(Events aEvents) #if OPENTHREAD_FTD if (aEvents.Contains(kEventSecurityPolicyChanged)) { - UpdateRouterRoleAllowed(kReasonSecurityPolicyChanged); + mRoleTransitioner.UpdateRouterRoleAllowed(RoleTransitioner::kReasonSecurityPolicyChanged); } - if (mBlockDowngrade && aEvents.Contains(kEventThreadChildRemoved)) + if (mRoleTransitioner.IsDowngradeBlocked() && aEvents.Contains(kEventThreadChildRemoved)) { - mBlockDowngrade = false; + bool shouldBlock = false; for (const Child &child : Get().Iterate(Child::kInStateValid)) { if (child.IsBlockingParentDowngrade()) { - mBlockDowngrade = true; + shouldBlock = true; break; } } + + mRoleTransitioner.SetDowngradeBlocked(shouldBlock); } #endif diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 765d2f8fc..0340ba3fb 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -771,7 +771,7 @@ public: * @retval kErrorNone Successfully set the router-eligible configuration. * @retval kErrorNotCapable The device is not capable of becoming a router. */ - Error SetRouterEligible(bool aEligible); + Error SetRouterEligible(bool aEligible) { return mRoleTransitioner.SetRouterEligible(aEligible); } /** * Indicates whether the router role is currently allowed. @@ -782,7 +782,7 @@ public: * @retval TRUE If the router role is allowed. * @retval FALSE If the router role is not allowed. */ - bool IsRouterRoleAllowed(void) const { return mRouterRoleAllowed; } + bool IsRouterRoleAllowed(void) const { return mRoleTransitioner.IsRouterRoleAllowed(); } /** * Indicates whether a node is the only router on the network. @@ -928,14 +928,14 @@ public: * * @returns The ROUTER_SELECTION_JITTER value in seconds. */ - uint8_t GetRouterSelectionJitter(void) const { return mRouterRoleTransition.GetJitter(); } + uint8_t GetRouterSelectionJitter(void) const { return mRoleTransitioner.GetJitter(); } /** * Sets the ROUTER_SELECTION_JITTER value. * * @param[in] aRouterJitter The router selection jitter value (in seconds). */ - void SetRouterSelectionJitter(uint8_t aRouterJitter) { mRouterRoleTransition.SetJitter(aRouterJitter); } + void SetRouterSelectionJitter(uint8_t aRouterJitter) { mRoleTransitioner.SetJitter(aRouterJitter); } /** * Indicates whether or not router role transition (upgrade from REED or downgrade to REED) is pending. @@ -943,7 +943,7 @@ public: * @retval TRUE Router role transition is pending. * @retval FALSE Router role transition is not pending */ - bool IsRouterRoleTransitionPending(void) const { return mRouterRoleTransition.IsPending(); } + bool IsRouterRoleTransitionPending(void) const { return mRoleTransitioner.IsTransitionPending(); } /** * Returns the current timeout delay in seconds till router role transition (upgrade from REED or downgrade to @@ -951,35 +951,35 @@ public: * * @returns The timeout in seconds till router role transition, or zero if not pending role transition. */ - uint8_t GetRouterRoleTransitionTimeout(void) const { return mRouterRoleTransition.GetTimeout(); } + uint8_t GetRouterRoleTransitionTimeout(void) const { return mRoleTransitioner.GetTimeout(); } /** * Returns the ROUTER_UPGRADE_THRESHOLD value. * * @returns The ROUTER_UPGRADE_THRESHOLD value. */ - uint8_t GetRouterUpgradeThreshold(void) const { return mRouterUpgradeThreshold; } + uint8_t GetRouterUpgradeThreshold(void) const { return mRoleTransitioner.GetUpgradeThreshold(); } /** * Sets the ROUTER_UPGRADE_THRESHOLD value. * * @param[in] aThreshold The ROUTER_UPGRADE_THRESHOLD value. */ - void SetRouterUpgradeThreshold(uint8_t aThreshold) { mRouterUpgradeThreshold = aThreshold; } + void SetRouterUpgradeThreshold(uint8_t aThreshold) { mRoleTransitioner.SetUpgradeThreshold(aThreshold); } /** * Returns the ROUTER_DOWNGRADE_THRESHOLD value. * * @returns The ROUTER_DOWNGRADE_THRESHOLD value. */ - uint8_t GetRouterDowngradeThreshold(void) const { return mRouterDowngradeThreshold; } + uint8_t GetRouterDowngradeThreshold(void) const { return mRoleTransitioner.GetDowngradeThreshold(); } /** * Sets the ROUTER_DOWNGRADE_THRESHOLD value. * * @param[in] aThreshold The ROUTER_DOWNGRADE_THRESHOLD value. */ - void SetRouterDowngradeThreshold(uint8_t aThreshold) { mRouterDowngradeThreshold = aThreshold; } + void SetRouterDowngradeThreshold(uint8_t aThreshold) { mRoleTransitioner.SetDowngradeThreshold(aThreshold); } /** * Indicates whether or not downgrading from router role to REED is blocked. @@ -997,7 +997,7 @@ public: * @retval TRUE The device is blocked from downgrading. * @retval FALSE The device is not blocked from downgrading. */ - bool IsDowngradeBlocked(void) const { return mBlockDowngrade; } + bool IsDowngradeBlocked(void) const { return mRoleTransitioner.IsDowngradeBlocked(); } /** * Returns the MLE_CHILD_ROUTER_LINKS value. @@ -1022,7 +1022,7 @@ public: * @retval TRUE If the REED is going to become a Router soon. * @retval FALSE If the REED is not going to become a Router soon. */ - bool WillBecomeRouterSoon(void) const; + bool WillBecomeRouterSoon(void) const { return mRoleTransitioner.WillBecomeRouterSoon(); } /** * Removes a link to a neighbor. @@ -1179,14 +1179,14 @@ public: * * @param[in] aEnabled TRUE if the device was commissioned using CCM, FALSE otherwise. */ - void SetCcmEnabled(bool aEnabled); + void SetCcmEnabled(bool aEnabled) { mRoleTransitioner.SetCcmEnabled(aEnabled); } /** * Sets whether the Security Policy TLV version-threshold for routing (VR field) is enabled. * * @param[in] aEnabled TRUE to enable Security Policy TLV version-threshold for routing, FALSE otherwise. */ - void SetThreadVersionCheckEnabled(bool aEnabled); + void SetThreadVersionCheckEnabled(bool aEnabled) { mRoleTransitioner.SetThreadVersionCheckEnabled(aEnabled); } /** * Gets the current Interval Max value used by Advertisement trickle timer. @@ -1473,16 +1473,6 @@ private: kAddrSolicitUnrecognizedReason = 6, }; -#if OPENTHREAD_FTD - enum UpdateRouterRoleAllowedReason : uint8_t // Used in `UpdateRouterRoleAllowed()` - { - kReasonMleInit, - kReasonDeviceModeChanged, - kReasonConfigParameterChanged, - kReasonSecurityPolicyChanged, - }; -#endif - enum MessageAction : uint8_t { kMessageSend, @@ -2216,26 +2206,65 @@ private: #if OPENTHREAD_FTD - class RouterRoleTransition + class RoleTransitioner : public InstanceLocator { - public: - RouterRoleTransition(void); + // Manages the router role upgrade/downgrade transitions - bool IsPending(void) const { return (mTimeout != 0); } + public: + enum UpdateRouterRoleAllowedReason : uint8_t // Used in `UpdateRouterRoleAllowed()` + { + kReasonMleInit, + kReasonDeviceModeChanged, + kReasonConfigParameterChanged, + kReasonSecurityPolicyChanged, + }; + + explicit RoleTransitioner(Instance &aInstance); + + bool IsRouterRoleAllowed(void) const { return mRouterRoleAllowed; } + void UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason); + Error SetRouterEligible(bool aEligible); + uint8_t GetJitter(void) const { return mJitter; } + void SetJitter(uint8_t aJitter) { mJitter = aJitter; } + uint8_t GetUpgradeThreshold(void) const { return mUpgradeThreshold; } + void SetUpgradeThreshold(uint8_t aThreshold) { mUpgradeThreshold = aThreshold; } + uint8_t GetDowngradeThreshold(void) const { return mDowngradeThreshold; } + void SetDowngradeThreshold(uint8_t aThreshold) { mDowngradeThreshold = aThreshold; } + bool IsDowngradeBlocked(void) const { return mDowngradeBlocked; } + void SetDowngradeBlocked(bool aBlocked) { mDowngradeBlocked = aBlocked; } + bool IsTransitionPending(void) const { return (mTimeout != 0); } + bool WillBecomeRouterSoon(void) const; void StartTimeout(void); void StopTimeout(void) { mTimeout = 0; } void IncreaseTimeout(uint8_t aIncrement) { mTimeout += aIncrement; } uint8_t GetTimeout(void) const { return mTimeout; } - bool HandleTimeTick(void); - uint8_t GetJitter(void) const { return mJitter; } - void SetJitter(uint8_t aJitter) { mJitter = aJitter; } + bool IsRouterCountBelowUpgradeThreshold(void) const; + void HandleTimeTick(void); + void DecideWhetherToUpgrade(void); + void DecideWhetherToDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + void SetCcmEnabled(bool aEnabled); + void SetThreadVersionCheckEnabled(bool aEnabled); +#endif private: + bool DetermineIfRouterRoleAllowed(void) const; + bool NeighborHasComparableConnectivity(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const; + + bool mRouterEligible : 1; + bool mRouterRoleAllowed : 1; + bool mDowngradeBlocked : 1; +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + bool mCcmEnabled : 1; + bool mThreadVersionCheckEnabled : 1; +#endif uint8_t mTimeout; uint8_t mJitter; + uint8_t mUpgradeThreshold; + uint8_t mDowngradeThreshold; }; -#endif +#endif // OPENTHREAD_FTD //------------------------------------------------------------------------------------------------------------------ #if OPENTHREAD_CONFIG_P2P_ENABLE @@ -2419,8 +2448,6 @@ private: void ClearAlternateRloc16(void); uint8_t SelectLeaderId(void) const; uint32_t SelectPartitionId(void) const; - bool DetermineIfRouterRoleAllowed(void) const; - void UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason); void DetermineConnectivity(Connectivity &aConnectivity) const; void HandleDetachStart(void); void HandleChildStart(void); @@ -2468,8 +2495,6 @@ private: void SetChildStateToValid(Child &aChild); bool HasChildren(void); void RemoveChildren(void); - bool ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const; - bool NeighborHasComparableConnectivity(const RouteTlv &aRouteTlv, uint8_t aNeighborId) const; void HandleAdvertiseTrickleTimer(void); void HandleTimeTick(void); void HandleRouterTableEvent(RouterTable::Events aEvents); @@ -2548,15 +2573,8 @@ private: #if OPENTHREAD_FTD - bool mRouterEligible : 1; - bool mRouterRoleAllowed : 1; - bool mBlockDowngrade : 1; - bool mAddressSolicitPending : 1; - bool mAddressSolicitRejected : 1; -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - bool mCcmEnabled : 1; - bool mThreadVersionCheckEnabled : 1; -#endif + bool mAddressSolicitPending : 1; + bool mAddressSolicitRejected : 1; uint8_t mRouterId; uint8_t mPreviousRouterId; uint8_t mNetworkIdTimeout; @@ -2579,7 +2597,7 @@ private: TrickleTimer mAdvertiseTrickleTimer; ChildTable mChildTable; RouterTable mRouterTable; - RouterRoleTransition mRouterRoleTransition; + RoleTransitioner mRoleTransitioner; Ip6::Netif::UnicastAddress mLeaderAloc; #if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE DeviceProperties mDeviceProperties; diff --git a/src/core/thread/mle_ftd.cpp b/src/core/thread/mle_ftd.cpp index e3c5a3fe3..567eaf4a4 100644 --- a/src/core/thread/mle_ftd.cpp +++ b/src/core/thread/mle_ftd.cpp @@ -76,12 +76,12 @@ void Mle::HandlePartitionChange(void) mRouterTable.Clear(); } -bool Mle::DetermineIfRouterRoleAllowed(void) const +bool Mle::RoleTransitioner::DetermineIfRouterRoleAllowed(void) const { bool allowed = false; const SecurityPolicy &secPolicy = Get().GetSecurityPolicy(); - VerifyOrExit(mRouterEligible && IsFullThreadDevice()); + VerifyOrExit(mRouterEligible && Get().IsFullThreadDevice()); #if OPENTHREAD_CONFIG_THREAD_VERSION == OT_THREAD_VERSION_1_1 VerifyOrExit(secPolicy.mRoutersEnabled); @@ -116,14 +116,14 @@ exit: return allowed; } -void Mle::UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason) +void Mle::RoleTransitioner::UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason) { bool allowed = DetermineIfRouterRoleAllowed(); VerifyOrExit(allowed != mRouterRoleAllowed); mRouterRoleAllowed = allowed; - if (IsAttached()) + if (Get().IsAttached()) { Get().SetBeaconEnabled(mRouterRoleAllowed); } @@ -131,12 +131,12 @@ void Mle::UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason) // Take action based on the current role, the new `mRouterRoleAllowed`, // and the reason for the change. - if (IsChild() && mRouterRoleAllowed && (aReason == kReasonConfigParameterChanged)) + if (Get().IsChild() && mRouterRoleAllowed && (aReason == kReasonConfigParameterChanged)) { - mRouterRoleTransition.StartTimeout(); + StartTimeout(); } - if (IsRouterOrLeader()) + if (Get().IsRouterOrLeader()) { // If currently acting as router or leader, but the config or // security policy changes such that the router role is no @@ -157,16 +157,16 @@ void Mle::UpdateRouterRoleAllowed(UpdateRouterRoleAllowedReason aReason) break; case kReasonConfigParameterChanged: - IgnoreError(BecomeDetached()); + IgnoreError(Get().BecomeDetached()); break; case kReasonSecurityPolicyChanged: - VerifyOrExit(!mRouterRoleTransition.IsPending()); - mRouterRoleTransition.StartTimeout(); + VerifyOrExit(!IsTransitionPending()); + StartTimeout(); - if (IsLeader()) + if (Get().IsLeader()) { - mRouterRoleTransition.IncreaseTimeout(kLeaderDowngradeExtraDelay); + IncreaseTimeout(kLeaderDowngradeExtraDelay); } break; } @@ -176,11 +176,11 @@ exit: return; } -Error Mle::SetRouterEligible(bool aEligible) +Error Mle::RoleTransitioner::SetRouterEligible(bool aEligible) { Error error = kErrorNone; - if (!IsFullThreadDevice()) + if (!Get().IsFullThreadDevice()) { VerifyOrExit(!aEligible, error = kErrorNotCapable); } @@ -195,13 +195,13 @@ exit: } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE -void Mle::SetCcmEnabled(bool aEnabled) +void Mle::RoleTransitioner::SetCcmEnabled(bool aEnabled) { mCcmEnabled = aEnabled; UpdateRouterRoleAllowed(kReasonConfigParameterChanged); } -void Mle::SetThreadVersionCheckEnabled(bool aEnabled) +void Mle::RoleTransitioner::SetThreadVersionCheckEnabled(bool aEnabled) { mThreadVersionCheckEnabled = aEnabled; UpdateRouterRoleAllowed(kReasonConfigParameterChanged); @@ -241,7 +241,7 @@ Error Mle::BecomeRouter(RouterUpgradeReason aReason) LogInfo("Attempt to become router, reason:%s", RouterUpgradeReasonToString(aReason)); Get().SetRxOnWhenIdle(true); - mRouterRoleTransition.StopTimeout(); + mRoleTransitioner.StopTimeout(); error = SendAddressSolicit(aReason); @@ -341,7 +341,7 @@ void Mle::HandleChildStart(void) { mAddressSolicitRejected = false; - mRouterRoleTransition.StartTimeout(); + mRoleTransitioner.StartTimeout(); StopLeader(); Get().RegisterReceiver(TimeTicker::kMle); @@ -409,7 +409,7 @@ void Mle::HandleChildStart(void) exit: - if (mRouterTable.GetActiveRouterCount() >= mRouterUpgradeThreshold && + if (!mRoleTransitioner.IsRouterCountBelowUpgradeThreshold() && (!IsRouterIdValid(mPreviousRouterId) || !HasChildren())) { SetRouterId(kInvalidRouterId); @@ -515,7 +515,7 @@ void Mle::HandleRouterTableEvent(RouterTable::Events aEvents) if (aEvents & RouterTable::kEventRouterAdded) { - mBlockDowngrade = false; + mRoleTransitioner.SetDowngradeBlocked(false); } if (IsRouterOrLeader() && mAdvertiseTrickleTimer.IsRunning()) @@ -1305,12 +1305,9 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co ExitNow(error = kErrorDetached); } - if (!mRouterRoleTransition.IsPending() && (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold)) - { - mRouterRoleTransition.StartTimeout(); - } - mRouterTable.UpdateRouterOnFtdChild(routeTlv, routerId); + + mRoleTransitioner.DecideWhetherToUpgrade(); } else { @@ -1331,12 +1328,12 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Update routers as a router or leader. + // Inform `RoleTransitioner` to decide whether we need to downgrade - if (IsRouter() && ShouldDowngrade(routerId, routeTlv)) - { - mRouterRoleTransition.StartTimeout(); - } + mRoleTransitioner.DecideWhetherToDowngrade(routerId, routeTlv); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Update routers as a router or leader. router = mRouterTable.FindRouterById(routerId); VerifyOrExit(router != nullptr); @@ -1589,55 +1586,7 @@ void Mle::HandleTimeTick(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Role transitions - if (mRouterRoleTransition.HandleTimeTick()) - { - // `mRouterRoleTransition.HandleTimeTick()` returns `true` - // if role transition timeout expires. - - switch (mRole) - { - case kRoleDisabled: - case kRoleDetached: - break; - - case kRoleChild: - if (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold && HasNeighborWithGoodLinkQuality()) - { - IgnoreError(BecomeRouter(kReasonTooFewRouters)); - } - else - { - mAnnounceHandler.HandleRouterRoleTransitionAttemptDone(); - } - - if (!mAdvertiseTrickleTimer.IsRunning()) - { - SendMulticastAdvertisement(); - - mAdvertiseTrickleTimer.Start(TrickleTimer::kModePlainTimer, kReedAdvIntervalMin, kReedAdvIntervalMax); - } - - break; - - case kRoleRouter: - if (mRouterTable.GetActiveRouterCount() > mRouterDowngradeThreshold) - { - LogNote("Downgrade to REED"); - mAttacher.Attach(kDowngradeToReed); - } - - OT_FALL_THROUGH; - - case kRoleLeader: - if (!IsRouterRoleAllowed()) - { - LogInfo("Router role no longer allowed"); - IgnoreError(BecomeDetached()); - } - - break; - } - } + mRoleTransitioner.HandleTimeTick(); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Leader's age @@ -3485,13 +3434,13 @@ void Mle::HandleAddressSolicitResponse(Coap::Msg *aMsg, Error aResult) // router from downgrading back to a REED to ensure this // child remains connected. // - // The `mBlockDowngrade` is cleared in various situations: + // The `DowngradeBlocked` is cleared in various situations: // - From `SetStateDetached()` (e.g. partition change). // - If a new router is added (new possible parent). // - If all children blocking downgrade are disconnected. child.SetBlockParentDowngrade(true); - mBlockDowngrade = true; + mRoleTransitioner.SetDowngradeBlocked(true); } exit: @@ -3508,18 +3457,18 @@ exit: return error; } -bool Mle::WillBecomeRouterSoon(void) const +bool Mle::RoleTransitioner::WillBecomeRouterSoon(void) const { static constexpr uint8_t kMaxDelay = 10; bool willBecomeRouter = false; - VerifyOrExit(IsRouterRoleAllowed() && IsChild()); - VerifyOrExit(!mAddressSolicitRejected); + VerifyOrExit(IsRouterRoleAllowed() && Get().IsChild()); + VerifyOrExit(!Get().mAddressSolicitRejected); - if (!mAddressSolicitPending) + if (!Get().mAddressSolicitPending) { - VerifyOrExit(mRouterRoleTransition.IsPending() && mRouterRoleTransition.GetTimeout() <= kMaxDelay); + VerifyOrExit(IsTransitionPending() && GetTimeout() <= kMaxDelay); } willBecomeRouter = true; @@ -3597,7 +3546,7 @@ void Mle::ProcessAddressSolicit(AddrSolicitInfo &aInfo) switch (aInfo.mReason) { case kReasonTooFewRouters: - VerifyOrExit(mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold); + VerifyOrExit(mRoleTransitioner.IsRouterCountBelowUpgradeThreshold()); break; case kReasonHaveChildIdRequest: @@ -3605,7 +3554,7 @@ void Mle::ProcessAddressSolicit(AddrSolicitInfo &aInfo) break; case kReasonBorderRouterRequest: - if ((mRouterTable.GetActiveRouterCount() >= mRouterUpgradeThreshold) && + if (!mRoleTransitioner.IsRouterCountBelowUpgradeThreshold() && (Get().CountBorderRouters(NetworkData::kRouterRoleOnly) >= kRouterUpgradeBorderRouterRequestThreshold)) { @@ -3781,30 +3730,29 @@ void Mle::FillConnectivityTlvValue(ConnectivityTlvValue &aTlvValue) const aTlvValue.InitFrom(connectivity); } -bool Mle::ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const +void Mle::RoleTransitioner::DecideWhetherToDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) { // Determine whether all conditions are satisfied for the router // to downgrade after receiving info for a neighboring router // with Router ID `aNeighborId` along with its `aRouteTlv`. - bool shouldDowngrade = false; - uint8_t activeRouterCount = mRouterTable.GetActiveRouterCount(); + uint8_t activeRouterCount = Get().GetActiveRouterCount(); uint8_t count; - VerifyOrExit(IsRouter()); - VerifyOrExit(mRouterTable.IsAllocated(aNeighborId)); - VerifyOrExit(!mBlockDowngrade); + VerifyOrExit(Get().IsRouter()); + VerifyOrExit(Get().IsAllocated(aNeighborId)); + VerifyOrExit(!mDowngradeBlocked); - VerifyOrExit(!mRouterRoleTransition.IsPending()); + VerifyOrExit(!IsTransitionPending()); - VerifyOrExit(activeRouterCount > mRouterDowngradeThreshold); + VerifyOrExit(activeRouterCount > mDowngradeThreshold); // Check that we have at least `kMinDowngradeNeighbors` // neighboring routers with two-way link quality of 2 or better. count = 0; - for (const Router &router : mRouterTable) + for (const Router &router : Get()) { if (!router.IsStateValid() || (router.GetTwoWayLinkQuality() < kLinkQuality2)) { @@ -3823,28 +3771,29 @@ bool Mle::ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const // Check that we have fewer children than three times the number // of excess routers (defined as the difference between number of - // active routers and `mRouterDowngradeThreshold`). + // active routers and `mDowngradeThreshold`). - count = activeRouterCount - mRouterDowngradeThreshold; - VerifyOrExit(mChildTable.GetNumChildren(Child::kInStateValid) < count * 3); + count = activeRouterCount - mDowngradeThreshold; + VerifyOrExit(Get().GetNumChildren(Child::kInStateValid) < count * 3); // Check that the neighbor has as good or better-quality links to // same routers. - VerifyOrExit(NeighborHasComparableConnectivity(aRouteTlv, aNeighborId)); + VerifyOrExit(NeighborHasComparableConnectivity(aNeighborId, aRouteTlv)); #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE // Check if we are eligible to be router due to being a BR. VerifyOrExit(!Get().IsEligibleForRouterRoleUpgradeAsBorderRouter()); #endif - shouldDowngrade = true; + // All conditions are satisfied to start downgrade. + StartTimeout(); exit: - return shouldDowngrade; + return; } -bool Mle::NeighborHasComparableConnectivity(const RouteTlv &aRouteTlv, uint8_t aNeighborId) const +bool Mle::RoleTransitioner::NeighborHasComparableConnectivity(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const { // Check whether the neighboring router with Router ID `aNeighborId` // (along with its `aRouteTlv`) has as good or better-quality links @@ -3860,12 +3809,12 @@ bool Mle::NeighborHasComparableConnectivity(const RouteTlv &aRouteTlv, uint8_t a LinkQuality localLinkQuality; LinkQuality peerLinkQuality; - if ((routerId == mRouterId) || (routerId == aNeighborId)) + if ((routerId == Get().mRouterId) || (routerId == aNeighborId)) { continue; } - router = mRouterTable.FindRouterById(routerId); + router = Get().FindRouterById(routerId); if ((router == nullptr) || !router->IsStateValid()) { @@ -4017,26 +3966,102 @@ const char *Mle::RouterUpgradeReasonToString(uint8_t aReason) #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) //---------------------------------------------------------------------------------------------------------------------- -// RouterRoleTransition +// RoleTransitioner -Mle::RouterRoleTransition::RouterRoleTransition(void) - : mTimeout(0) +Mle::RoleTransitioner::RoleTransitioner(Instance &aInstance) + : InstanceLocator(aInstance) + , mRouterEligible(true) + , mRouterRoleAllowed(true) + , mDowngradeBlocked(false) +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + , mCcmEnabled(false) + , mThreadVersionCheckEnabled(true) +#endif + , mTimeout(0) , mJitter(kRouterSelectionJitter) + , mUpgradeThreshold(kRouterUpgradeThreshold) + , mDowngradeThreshold(kRouterDowngradeThreshold) + { } -void Mle::RouterRoleTransition::StartTimeout(void) { mTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mJitter); } +void Mle::RoleTransitioner::StartTimeout(void) { mTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mJitter); } -bool Mle::RouterRoleTransition::HandleTimeTick(void) +bool Mle::RoleTransitioner::IsRouterCountBelowUpgradeThreshold(void) const { - bool expired = false; + return Get().GetActiveRouterCount() < mUpgradeThreshold; +} - VerifyOrExit(mTimeout > 0); - mTimeout--; - expired = (mTimeout == 0); +void Mle::RoleTransitioner::DecideWhetherToUpgrade(void) +{ + VerifyOrExit(Get().IsChild()); + + VerifyOrExit(IsRouterRoleAllowed()); + + VerifyOrExit(!IsTransitionPending()); + VerifyOrExit(IsRouterCountBelowUpgradeThreshold()); + + StartTimeout(); exit: - return expired; + return; +} + +void Mle::RoleTransitioner::HandleTimeTick(void) +{ + VerifyOrExit(mTimeout > 0); + mTimeout--; + + VerifyOrExit(mTimeout == 0); + + // Timeout expired + + switch (Get().mRole) + { + case kRoleDisabled: + case kRoleDetached: + break; + + case kRoleChild: + if (IsRouterCountBelowUpgradeThreshold() && Get().HasNeighborWithGoodLinkQuality()) + { + IgnoreError(Get().BecomeRouter(kReasonTooFewRouters)); + } + else + { + Get().mAnnounceHandler.HandleRouterRoleTransitionAttemptDone(); + } + + if (!Get().mAdvertiseTrickleTimer.IsRunning()) + { + Get().SendMulticastAdvertisement(); + Get().mAdvertiseTrickleTimer.Start(TrickleTimer::kModePlainTimer, kReedAdvIntervalMin, + kReedAdvIntervalMax); + } + + break; + + case kRoleRouter: + if (Get().GetActiveRouterCount() > mDowngradeThreshold) + { + LogNote("Downgrade to REED"); + Get().mAttacher.Attach(kDowngradeToReed); + } + + OT_FALL_THROUGH; + + case kRoleLeader: + if (!IsRouterRoleAllowed()) + { + LogInfo("Router role no longer allowed"); + IgnoreError(Get().BecomeDetached()); + } + + break; + } + +exit: + return; } } // namespace Mle