diff --git a/src/core/config/mle.h b/src/core/config/mle.h index dc40a51fd..506c5289a 100644 --- a/src/core/config/mle.h +++ b/src/core/config/mle.h @@ -226,11 +226,37 @@ * @def OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS * * Specifies the desired number of router links that a REED / FED attempts to maintain. + * + * This is default value used on an FTD child. This parameter can also be changed at run-time using the public APIs + * `otThreadGetChildRouterLinks/otThreadSetChildRouterLinks()`. */ #ifndef OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS #define OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS 3 #endif +/** + * @def OPENTHREAD_CONFIG_MLE_EXTRA_CHILD_ROUTER_LINKS_GRADUAL + * + * Specifies the extra router links in addition to those specified by `otThreadSetChildRouterLinks()` that an FTD child + * can try to establish gradually over a longer span of time. + * + * A child device communicates through its parent but can establish links with other neighboring routers so that it can + * receive multicast messages (forwarded MPL) from other routers, thus improving multicast reliability. + * + * An FTD child tries to establish links with the first `CHILD_ROUTER_LINK` routers as quickly as possible + * (sending MLE Link Request to initiate link upon receiving advertisements from a neighboring router). + * + * After that, the FTD child uses the "gradual router link establishment" mechanism to establish router links with + * `OPENTHREAD_CONFIG_MLE_EXTRA_CHILD_ROUTER_LINKS_GRADUAL` additional routers. This is done slowly and over a longer + * span of time. + * + * This parameter can be set to zero to disable "gradual router link establishment" fully. Alternatively, it can be set + * to the maximum router count of 32 to allow the FTD child to establish links with all neighboring routers. + */ +#ifndef OPENTHREAD_CONFIG_MLE_EXTRA_CHILD_ROUTER_LINKS_GRADUAL +#define OPENTHREAD_CONFIG_MLE_EXTRA_CHILD_ROUTER_LINKS_GRADUAL 32 +#endif + /** * @def OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE * diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 9095b4bee..ca4ad041b 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1298,15 +1298,7 @@ Error MleRouter::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddre router = mRouterTable.FindRouterById(routerId); VerifyOrExit(router != nullptr); - if (!router->IsStateValid() && !router->IsStateLinkRequest() && - (mRouterTable.GetNeighborCount(kLinkQuality1) < mChildRouterLinks)) - { - InitNeighbor(*router, aRxInfo); - router->SetState(Neighbor::kStateLinkRequest); - delay = Random::NonCrypto::GetUint32InRange(kMinLinkRequestDelayOnChild, kMaxLinkRequestDelayOnChild); - mDelayedSender.ScheduleLinkRequest(*router, delay); - ExitNow(error = kErrorNoRoute); - } + EstablishRouterLinkOnFtdChild(*router, aRxInfo, linkMargin); } #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE @@ -1372,6 +1364,68 @@ exit: return error; } +void MleRouter::EstablishRouterLinkOnFtdChild(Router &aRouter, RxInfo &aRxInfo, uint8_t aLinkMargin) +{ + // Decide on an FTD child whether to establish a link with a + // router upon receiving an advertisement from it. + + uint8_t neighborCount; + uint32_t minDelay; + uint32_t maxDelay; + + VerifyOrExit(!aRouter.IsStateValid() && !aRouter.IsStateLinkRequest()); + + // The first `mChildRouterLinks` are established quickly. After that, + // the "gradual router link establishment" mechanism is used, which + // allows `kExtraChildRouterLinks` additional router links to be + // established, but it is done slowly and over a longer span of time. + // + // Gradual router link establishment conditions: + // - The maximum `neighborCount` limit is not yet reached. + // - We see Link Quality 2 or better. + // - Always skipped in the first 5 minutes + // (`kWaitDurationAfterAttach`) after the device attaches. + // - The child randomly decides whether to perform/skip this (with a 5% + // probability, `kProbabilityPercentage`). + // - If the child decides to send Link Request, a longer random delay + // window is used, [1.5-10] seconds. + // + // Even in a dense network, if the advertisement is received by 500 FTD + // children with a 5% selection probability, on average, 25 nodes will + // try to send a Link Request, which will be randomly spread over a + // [1.5-10] second window. + // + // With a 5% probability, on average, it takes 20 trials (20 advertisement + // receptions for an FTD child to send a Link Request). Advertisements + // are, on average, ~32 seconds apart, so, on average, a child will try + // to establish a link in `20 * 32 = 640` seconds (~10 minutes). + + neighborCount = mRouterTable.GetNeighborCount(kLinkQuality1); + + if (neighborCount < mChildRouterLinks) + { + minDelay = kMinLinkRequestDelayOnChild; + maxDelay = kMaxLinkRequestDelayOnChild; + } + else + { + VerifyOrExit(neighborCount < mChildRouterLinks + GradualChildRouterLink::kExtraChildRouterLinks); + VerifyOrExit(LinkQualityForLinkMargin(aLinkMargin) >= kLinkQuality2); + VerifyOrExit(GetCurrentAttachDuration() > GradualChildRouterLink::kWaitDurationAfterAttach); + VerifyOrExit(Random::NonCrypto::GetUint8InRange(0, 100) < GradualChildRouterLink::kProbabilityPercentage); + + minDelay = GradualChildRouterLink::kMinLinkRequestDelay; + maxDelay = GradualChildRouterLink::kMaxLinkRequestDelay; + } + + InitNeighbor(aRouter, aRxInfo); + aRouter.SetState(Neighbor::kStateLinkRequest); + mDelayedSender.ScheduleLinkRequest(aRouter, Random::NonCrypto::GetUint32InRange(minDelay, maxDelay)); + +exit: + return; +} + void MleRouter::HandleParentRequest(RxInfo &aRxInfo) { Error error = kErrorNone; diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index b5bd491c6..f7db4cf78 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -517,6 +517,16 @@ private: static constexpr uint8_t kChildRouterLinks = OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS; static constexpr uint8_t kMaxChildIpAddresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD; + // Constants for gradual router link establishment (on FTD child) + struct GradualChildRouterLink + { + static constexpr uint8_t kExtraChildRouterLinks = OPENTHREAD_CONFIG_MLE_EXTRA_CHILD_ROUTER_LINKS_GRADUAL; + static constexpr uint32_t kWaitDurationAfterAttach = 300; // in seconds (5 minutes) + static constexpr uint32_t kMinLinkRequestDelay = 1500; // in msec + static constexpr uint32_t kMaxLinkRequestDelay = 10000; // in msec + static constexpr uint32_t kProbabilityPercentage = 5; // in percent + }; + static constexpr uint8_t kMinCriticalChildrenCount = 6; static constexpr uint16_t kChildSupervisionDefaultIntervalForOlderVersion = @@ -595,6 +605,7 @@ private: void HandleDataRequest(RxInfo &aRxInfo); void HandleNetworkDataUpdateRouter(void); void HandleDiscoveryRequest(RxInfo &aRxInfo); + void EstablishRouterLinkOnFtdChild(Router &aRouter, RxInfo &aRxInfo, uint8_t aLinkMargin); Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo); Error ReadAndProcessRouteTlvOnFtdChild(RxInfo &aRxInfo, uint8_t aParentId); void StopAdvertiseTrickleTimer(void);