diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index a2d670567..ee2c46a2e 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -4574,6 +4574,15 @@ void Mle::DelayedSender::ScheduleParentResponse(const ParentResponseInfo &aInfo, AddSchedule(kTypeParentResponse, destination, aDelay, &aInfo, sizeof(aInfo)); } +void Mle::DelayedSender::ScheduleAdvertisement(const Ip6::Address &aDestination, uint16_t aDelay) +{ + VerifyOrExit(!HasMatchingSchedule(kTypeAdvertisement, aDestination)); + AddSchedule(kTypeAdvertisement, aDestination, aDelay, nullptr, 0); + +exit: + return; +} + void Mle::DelayedSender::ScheduleMulticastDataResponse(uint16_t aDelay) { Ip6::Address destination; @@ -4717,6 +4726,10 @@ void Mle::DelayedSender::Execute(const Schedule &aSchedule) break; } + case kTypeAdvertisement: + Get().SendAdvertisement(header.mDestination); + break; + case kTypeDataResponse: Get().SendMulticastDataResponse(); break; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 8b5ec5241..4371f29e4 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1139,6 +1139,7 @@ private: void ScheduleChildUpdateRequestToParent(uint16_t aDelay); #if OPENTHREAD_FTD void ScheduleParentResponse(const ParentResponseInfo &aInfo, uint16_t aDelay); + void ScheduleAdvertisement(const Ip6::Address &aDestination, uint16_t aDelay); void ScheduleMulticastDataResponse(uint16_t aDelay); void ScheduleLinkRequest(const Router &aRouter, uint16_t aDelay); void ScheduleLinkAccept(const LinkAcceptInfo &aInfo, uint16_t aDelay); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index ca4ad041b..60fece0b1 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -486,7 +486,7 @@ void MleRouter::HandleAdvertiseTrickleTimer(void) { VerifyOrExit(IsRouterEligible(), mAdvertiseTrickleTimer.Stop()); - SendAdvertisement(); + SendMulticastAdvertisement(); exit: return; @@ -534,11 +534,27 @@ exit: return; } -void MleRouter::SendAdvertisement(void) +void MleRouter::SendMulticastAdvertisement(void) { - Error error = kErrorNone; Ip6::Address destination; - TxMessage *message = nullptr; + + destination.SetToLinkLocalAllNodesMulticast(); + SendAdvertisement(destination); +} + +void MleRouter::ScheduleUnicastAdvertisementTo(const Router &aRouter) +{ + Ip6::Address destination; + + destination.SetToLinkLocalAddress(aRouter.GetExtAddress()); + mDelayedSender.ScheduleAdvertisement(destination, + Random::NonCrypto::GetUint32InRange(0, kMaxUnicastAdvertisementDelay)); +} + +void MleRouter::SendAdvertisement(const Ip6::Address &aDestination) +{ + Error error = kErrorNone; + TxMessage *message = nullptr; // Suppress MLE Advertisements when trying to attach to a better // partition. Without this, a candidate parent might incorrectly @@ -574,10 +590,9 @@ void MleRouter::SendAdvertisement(void) OT_ASSERT(false); } - destination.SetToLinkLocalAllNodesMulticast(); - SuccessOrExit(error = message->SendTo(destination)); + SuccessOrExit(error = message->SendTo(aDestination)); - Log(kMessageSend, kTypeAdvertisement, destination); + Log(kMessageSend, kTypeAdvertisement, aDestination); exit: FreeMessageOnError(message, error); @@ -1604,7 +1619,7 @@ void MleRouter::HandleTimeTick(void) if (!mAdvertiseTrickleTimer.IsRunning()) { - SendAdvertisement(); + SendMulticastAdvertisement(); mAdvertiseTrickleTimer.Start(TrickleTimer::kModePlainTimer, kReedAdvIntervalMin, kReedAdvIntervalMax); } @@ -3464,7 +3479,7 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage, // up the dissemination of the new Router ID to other routers. // This can also help with quicker link establishment with our // former parent and other routers. - SendAdvertisement(); + SendMulticastAdvertisement(); for (Child &child : Get().Iterate(Child::kInStateChildIdRequest)) { diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index f7db4cf78..42da00cf4 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -362,6 +362,14 @@ public: */ void FillConnectivityTlv(ConnectivityTlv &aTlv); + /** + * Schedule tx of MLE Advertisement message (unicast) to the given neighboring router after a random delay. + * + * @param[in] aRouter The router to send the Advertisement to. + * + */ + void ScheduleUnicastAdvertisementTo(const Router &aRouter); + #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE /** * Sets steering data out of band @@ -494,6 +502,7 @@ private: static constexpr uint32_t kAdvIntervalMaxLogRoutes = 5000; #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 kMaxLeaderToRouterTimeout = 90000; // (in msec) static constexpr uint8_t kMinDowngradeNeighbors = 7; @@ -616,7 +625,8 @@ private: const Router *aRouter, const Ip6::MessageInfo &aMessageInfo); void SendAddressRelease(void); - void SendAdvertisement(void); + void SendMulticastAdvertisement(void); + void SendAdvertisement(const Ip6::Address &aDestination); void SendLinkRequest(Router *aRouter); Error SendLinkAccept(const LinkAcceptInfo &aInfo); void SendParentResponse(const ParentResponseInfo &aInfo); diff --git a/src/core/thread/router_table.cpp b/src/core/thread/router_table.cpp index 9f8b7677f..ce0246371 100644 --- a/src/core/thread/router_table.cpp +++ b/src/core/thread/router_table.cpp @@ -596,6 +596,25 @@ void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighbor neighbor->SetLinkQualityOut(linkQuality); SignalTableChanged(); } + + // If the `aRouteTlv` indicates that the neighboring + // router claims to have no link to us (by setting its + // `GetLinkQualityOut()` towards us as `kLinkQuality0`), + // and we have previously established a link with it, and + // our two-way link quality to this router is at least + // `kLinkQuality2`, we schedule a unicast Advertisement to + // be sent to this neighbor. This helps expedite recovery + // from any temporary router link quality mismatch. + // Otherwise, the neighboring router will continue to + // advertise that it has no link to us until our next + // trickle timer-triggered Advertisement transmission + // (which can be up to 32 seconds later). + + if (neighbor->IsStateValid() && (aRouteTlv.GetLinkQualityOut(index) == kLinkQuality0) && + (neighbor->GetTwoWayLinkQuality() >= kLinkQuality2)) + { + Get().ScheduleUnicastAdvertisementTo(*neighbor); + } } break;