diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index cbaedd5b3..7e23f5cfd 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1728,26 +1728,51 @@ void MleRouter::HandleTimeTick(void) router.SetSelectableAsParent(false); } #endif - - if (router.IsStateValid() && (age >= kMaxNeighborAge)) + if (router.IsStateValid()) { - // 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. + // Neighbor router age and link recovery + // + // If the device is an FTD child and has more than + // `mChildRouterLinks` neighbors, it uses a longer age, + // `kMaxNeighborAgeOnChild`, and removes the neighboring + // router upon expiration without trying to re-establish + // its link with it. + // + // Otherwise, if the device itself is a router, or it is an + // FTD child with `mChildRouterLinks` or fewer neighbors, + // it uses a shorter `kMaxNeighborAge`. Upon expiration, it + // tries to re-establish its link with the neighboring router. - if (!mDelayedSender.HasAnyScheduledLinkRequest(router) && !router.IsWaitingForLinkAccept()) + if (IsChild() && (mRouterTable.GetNeighborCount(kLinkQuality1) > mChildRouterLinks)) { - LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16()); - router.SetLinkRequestAttemptsToMax(); + if (age >= kMaxNeighborAgeOnChild) + { + LogInfo("No Adv from router 0x%04x - removing router", router.GetRloc16()); + mDelayedSender.RemoveScheduledLinkRequest(router); + RemoveNeighbor(router); + continue; + } } - - if (router.HasRemainingLinkRequestAttempts()) + else if (age >= kMaxNeighborAge) { - router.DecrementLinkRequestAttempts(); - mDelayedSender.ScheduleLinkRequest( - router, Random::NonCrypto::GetUint32InRange(0, kMaxLinkRequestDelayOnRouter)); + // We send a Link Request every time tick (second), up to + // max attempts. Each transmission is randomly delayed + // (one-second window). After the last attempt, we wait for + // the "Link Accept" timeout (~3 seconds) before removing the + // neighboring router. + + 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)); + } } } diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 42da00cf4..a9e8ccd37 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -503,7 +503,8 @@ private: #endif static constexpr uint32_t kMaxUnicastAdvertisementDelay = 1000; // Max random delay for unciast Adv tx - static constexpr uint32_t kMaxNeighborAge = 100000; // Max neighbor age (in msec) + static constexpr uint32_t kMaxNeighborAge = 100000; // Max neighbor age on router (in msec) + static constexpr uint32_t kMaxNeighborAgeOnChild = 150000; // Max neighbor age on FTD child (in msec) static constexpr uint32_t kMaxLeaderToRouterTimeout = 90000; // (in msec) static constexpr uint8_t kMinDowngradeNeighbors = 7; static constexpr uint8_t kNetworkIdTimeout = 120; // (in sec)