diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 20eeae539..3d627784c 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -4614,6 +4614,15 @@ void Mle::DelayedSender::RemoveScheduledLinkRequest(const Router &aRouter) RemoveMatchingSchedules(kTypeLinkRequest, destination); } +bool Mle::DelayedSender::HasAnyScheduledLinkRequest(const Router &aRouter) const +{ + Ip6::Address destination; + + destination.SetToLinkLocalAddress(aRouter.GetExtAddress()); + + return HasMatchingSchedule(kTypeLinkRequest, destination); +} + void Mle::DelayedSender::ScheduleLinkAccept(const LinkAcceptInfo &aInfo, uint16_t aDelay) { Ip6::Address destination; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 4371f29e4..d0e643730 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1142,11 +1142,12 @@ private: void ScheduleAdvertisement(const Ip6::Address &aDestination, uint16_t aDelay); void ScheduleMulticastDataResponse(uint16_t aDelay); void ScheduleLinkRequest(const Router &aRouter, uint16_t aDelay); + void RemoveScheduledLinkRequest(const Router &aRouter); + bool HasAnyScheduledLinkRequest(const Router &aRouter) const; void ScheduleLinkAccept(const LinkAcceptInfo &aInfo, uint16_t aDelay); void ScheduleDiscoveryResponse(const Ip6::Address &aDestination, const DiscoveryResponseInfo &aInfo, uint16_t aDelay); - void RemoveScheduledLinkRequest(const Router &aRouter); #endif void RemoveScheduledChildUpdateRequestToParent(void); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 60fece0b1..cbaedd5b3 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1720,32 +1720,6 @@ void MleRouter::HandleTimeTick(void) age = TimerMilli::GetNow() - router.GetLastHeard(); - if (router.IsStateValid() && (age >= kMaxNeighborAge)) - { - bool sendLinkRequest = true; - - // Once router age expires, we send Link Request every - // time tick (second), up to `kMaxTxCount`. Each rx is - // randomly delayed (one second window). After the last - // attempt, we wait for the "Link Accept" timeout - // (~3 seconds), before the router is removed. - - if (!router.IsWaitingForLinkAccept()) - { - LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16()); - } - else - { - sendLinkRequest = (age < kMaxNeighborAge + kMaxTxCount * TimeMilli::kOneSecondInMsec); - } - - if (sendLinkRequest) - { - mDelayedSender.ScheduleLinkRequest( - router, Random::NonCrypto::GetUint32InRange(0, kMaxLinkRequestDelayOnRouter)); - } - } - #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE router.DecrementParentReselectTimeout(); @@ -1755,6 +1729,28 @@ void MleRouter::HandleTimeTick(void) } #endif + if (router.IsStateValid() && (age >= kMaxNeighborAge)) + { + // Once router age expires, we send Link Request every + // time tick (second), up to max attempts. Each rx is + // randomly delayed (one second window). After the last + // attempt, we wait for the "Link Accept" timeout + // (~3 seconds), before the router is removed. + + if (!mDelayedSender.HasAnyScheduledLinkRequest(router) && !router.IsWaitingForLinkAccept()) + { + LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16()); + router.SetLinkRequestAttemptsToMax(); + } + + if (router.HasRemainingLinkRequestAttempts()) + { + router.DecrementLinkRequestAttempts(); + mDelayedSender.ScheduleLinkRequest( + router, Random::NonCrypto::GetUint32InRange(0, kMaxLinkRequestDelayOnRouter)); + } + } + if (router.IsWaitingForLinkAccept() && (router.DecrementLinkAcceptTimeout() == 0)) { LogInfo("Router 0x%04x - Link Accept timeout expired", router.GetRloc16()); diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index 95f52a7c6..c679c1e8f 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -91,7 +91,9 @@ constexpr uint8_t kMaxRouteCost = 16; ///< Maximum path cost #endif constexpr uint8_t kMeshLocalPrefixContextId = 0; ///< Reserved 6lowpan context ID for Mesh Local Prefix -constexpr uint8_t kLinkAcceptTimeout = 3; ///< Timeout in seconds to rx Link Accept after Link Request tx. + +constexpr uint8_t kLinkRequestAttempts = 3; ///< Number of Link Request attempts when re-establishing link. +constexpr uint8_t kLinkAcceptTimeout = 3; ///< Timeout in seconds to rx Link Accept after Link Request tx. /** * Specifies parent reselect timeout duration in seconds used on FTD child devices. diff --git a/src/core/thread/router.hpp b/src/core/thread/router.hpp index 10152d690..86120e56a 100644 --- a/src/core/thread/router.hpp +++ b/src/core/thread/router.hpp @@ -118,6 +118,27 @@ public: */ uint8_t DecrementLinkAcceptTimeout(void) { return --mLinkAcceptTimeout; } + /** + * Sets the counter tracking the number of Link Request attempts during link re-establishment to its maximum value + * `Mle::kLinkRequestAttempts`. + */ + void SetLinkRequestAttemptsToMax(void) { mLinkRequestAttempts = Mle::kLinkRequestAttempts; } + + /** + * Indicates whether there are remaining Link Request attempts (during link re-establishment). + * + * @retval TRUE There are remaining Link Request attempts. + * @retval FALSE There are no more Link Request attempts (the counter is zero). + */ + bool HasRemainingLinkRequestAttempts(void) const { return mLinkRequestAttempts > 0; } + + /** + * Decrements the counter tracking the number of remaining Link Request attempts during link re-establishment. + * + * Caller MUST ensure the current counter is non-zero by checking `HasRemainingLinkRequestAttempts()`. + */ + void DecrementLinkRequestAttempts(void) { mLinkRequestAttempts--; } + /** * Gets the router ID of the next hop to this router. * @@ -208,14 +229,16 @@ public: private: static_assert(Mle::kLinkAcceptTimeout < 4, "kLinkAcceptTimeout won't fit in mLinkAcceptTimeout (2-bit field)"); + static_assert(Mle::kLinkRequestAttempts < 4, "kLinkRequestAttempts won't fit in mLinkRequestAttempts (2-bit field"); #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE static_assert(Mle::kParentReselectTimeout <= (1U << 15) - 1, "kParentReselectTimeout won't fit in mParentReselectTimeout (15-bit filed)"); #endif - uint8_t mNextHop; // The next hop towards this router - uint8_t mLinkAcceptTimeout : 2; // Timeout (in seconds) after sending Link Request waiting for Link Accept + uint8_t mNextHop; // The next hop towards this router + uint8_t mLinkRequestAttempts : 2; // Number of Link Request attempts + uint8_t mLinkAcceptTimeout : 2; // Timeout (in seconds) after sending Link Request waiting for Link Accept #if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE uint8_t mCost : 4; // The cost to this router via neighbor router #else