diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 642b15f54..ba55c4bbc 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -74,9 +74,8 @@ RoutingManager::RoutingManager(Instance &aInstance) #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE , mInfraIfNat64PrefixStaleTimer(aInstance, HandleInfraIfNat64PrefixStaleTimer) #endif + , mRsSender(aInstance) , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer) - , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer) - , mRouterSolicitCount(0) , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer) { mFavoredDiscoveredOnLinkPrefix.Clear(); @@ -296,7 +295,7 @@ void RoutingManager::Start(void) mIsRunning = true; UpdateDiscoveredPrefixTableOnNetDataChange(); mLocalOnLinkPrefix.Start(); - StartRouterSolicitationDelay(); + mRsSender.Start(); #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE mInfraIfNat64PrefixStaleTimer.Start(0); #endif @@ -332,8 +331,7 @@ void RoutingManager::Stop(void) mRaInfo.mTxCount = 0; - mRouterSolicitTimer.Stop(); - mRouterSolicitCount = 0; + mRsSender.Stop(); mRoutingPolicyTimer.Stop(); @@ -521,7 +519,7 @@ exit: void RoutingManager::EvaluateOnLinkPrefix(void) { - VerifyOrExit(!IsRouterSolicitationInProgress()); + VerifyOrExit(!mRsSender.IsInProgress()); mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredOnLinkPrefix); @@ -675,46 +673,6 @@ void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode) mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime); } -// starts sending Router Solicitations in random delay -// between 0 and kMaxRtrSolicitationDelay. -void RoutingManager::StartRouterSolicitationDelay(void) -{ - uint32_t randomDelay; - - VerifyOrExit(!IsRouterSolicitationInProgress()); - - OT_ASSERT(mRouterSolicitCount == 0); - - static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay"); - randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay)); - - LogInfo("Start Router Solicitation, scheduled in %u milliseconds", randomDelay); - mTimeRouterSolicitStart = TimerMilli::GetNow(); - mRouterSolicitTimer.Start(randomDelay); - -exit: - return; -} - -bool RoutingManager::IsRouterSolicitationInProgress(void) const -{ - return mRouterSolicitTimer.IsRunning() || mRouterSolicitCount > 0; -} - -Error RoutingManager::SendRouterSolicitation(void) -{ - Ip6::Address destAddress; - Ip6::Nd::RouterSolicitMessage routerSolicit; - InfraIf::Icmp6Packet packet; - - OT_ASSERT(IsInitialized()); - - packet.InitFrom(routerSolicit); - destAddress.SetToLinkLocalAllRoutersMulticast(); - - return mInfraIf.Send(packet, destAddress); -} - void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) { // RA message max length is derived to accommodate: @@ -973,61 +931,25 @@ bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix) !aOnLinkPrefix.IsMulticast(); } -void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer) +void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime) { - aTimer.Get().HandleRouterSolicitTimer(); -} + // This is a callback from `RsSender` and is invoked when it + // finishes a cycle of sending Router Solicitations. `aStartTime` + // specifies the start time of the RS transmission cycle. + // + // We remove or deprecate old entries in discovered table that are + // not refreshed during Router Solicitation. We also invalidate + // the learned RA header if it is not refreshed during Router + // Solicitation. -void RoutingManager::HandleRouterSolicitTimer(void) -{ - LogInfo("Router solicitation times out"); + mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(aStartTime); - if (mRouterSolicitCount < kMaxRtrSolicitations) + if (mRaInfo.mHeaderUpdateTime <= aStartTime) { - uint32_t nextSolicitationDelay; - Error error; - - error = SendRouterSolicitation(); - - if (error == kErrorNone) - { - LogDebg("Successfully sent %uth Router Solicitation", mRouterSolicitCount); - ++mRouterSolicitCount; - nextSolicitationDelay = - (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval; - } - else - { - LogCrit("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error)); - - // It's unexpected that RS will fail and we will retry sending RS messages in 60 seconds. - // Notice that `mRouterSolicitCount` is not incremented for failed RS and thus we will - // not start configuring on-link prefixes before `kMaxRtrSolicitations` successful RS - // messages have been sent. - nextSolicitationDelay = kRtrSolicitationRetryDelay; - mRouterSolicitCount = 0; - } - - LogDebg("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay); - mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay)); + UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); } - else - { - // Remove route prefixes and deprecate on-link prefixes that - // are not refreshed during Router Solicitation. - mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(mTimeRouterSolicitStart); - // Invalidate the learned RA message if it is not refreshed during Router Solicitation. - if (mRaInfo.mHeaderUpdateTime <= mTimeRouterSolicitStart) - { - UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); - } - - mRouterSolicitCount = 0; - - // Re-evaluate our routing policy and send Router Advertisement if necessary. - ScheduleRoutingPolicyEvaluation(kImmediately); - } + ScheduleRoutingPolicyEvaluation(kImmediately); } void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer) @@ -1038,7 +960,7 @@ void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer) void RoutingManager::HandleDiscoveredPrefixStaleTimer(void) { LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected"); - StartRouterSolicitationDelay(); + mRsSender.Start(); } void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer) @@ -2312,6 +2234,89 @@ void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefi } } +//--------------------------------------------------------------------------------------------------------------------- +// RsSender + +RoutingManager::RsSender::RsSender(Instance &aInstance) + : InstanceLocator(aInstance) + , mTxCount(0) + , mTimer(aInstance, HandleTimer) +{ +} + +void RoutingManager::RsSender::Start(void) +{ + uint32_t delay; + + VerifyOrExit(!IsInProgress()); + + delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay); + LogInfo("Scheduled Router Solicitation in %u milliseconds", delay); + + mTxCount = 0; + mStartTime = TimerMilli::GetNow(); + mTimer.Start(delay); + +exit: + return; +} + +void RoutingManager::RsSender::Stop(void) +{ + mTimer.Stop(); +} + +Error RoutingManager::RsSender::SendRs(void) +{ + Ip6::Address destAddress; + Ip6::Nd::RouterSolicitMessage routerSolicit; + InfraIf::Icmp6Packet packet; + + packet.InitFrom(routerSolicit); + destAddress.SetToLinkLocalAllRoutersMulticast(); + + return Get().mInfraIf.Send(packet, destAddress); +} + +void RoutingManager::RsSender::HandleTimer(Timer &aTimer) +{ + aTimer.Get().mRsSender.HandleTimer(); +} + +void RoutingManager::RsSender::HandleTimer(void) +{ + Error error; + uint32_t delay; + + if (mTxCount >= kMaxTxCount) + { + Get().HandleRsSenderFinished(mStartTime); + ExitNow(); + } + + error = SendRs(); + + if (error == kErrorNone) + { + mTxCount++; + LogInfo("Successfully sent RS %d/%d", mTxCount, kMaxTxCount); + delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval; + } + else + { + LogCrit("Failed to send RS %d, error:%s", mTxCount + 1, ErrorToString(error)); + + // Note that `mTxCount` is intentionally not incremented + // if the tx fails. + delay = kRetryDelay; + } + + mTimer.Start(delay); + +exit: + return; +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index e3a0087cd..41e480366 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -322,9 +322,6 @@ private: // The maximum number of initial Router Advertisements. static constexpr uint32_t kMaxInitRtrAdvertisements = 3; - // The maximum number of Router Solicitations before sending Router Advertisements. - static constexpr uint32_t kMaxRtrSolicitations = 3; - static constexpr uint32_t kDefaultOmrPrefixLifetime = 1800; // The default OMR prefix valid lifetime. In sec. static constexpr uint32_t kDefaultOnLinkPrefixLifetime = 1800; // The default on-link prefix valid lifetime. In sec. static constexpr uint32_t kDefaultNat64PrefixLifetime = 300; // The default NAT64 prefix valid lifetime. In sec. @@ -332,13 +329,9 @@ private: static constexpr uint32_t kMinRtrAdvInterval = kMaxRtrAdvInterval / 3; // Min RA Interval. In sec. static constexpr uint32_t kMaxInitRtrAdvInterval = 16; // Max Initial RA Interval. In sec. static constexpr uint32_t kRaReplyJitter = 500; // Jitter for sending RA after rx RS. In msec. - static constexpr uint32_t kRtrSolicitationInterval = 4; // Interval between RSs. In sec. - static constexpr uint32_t kMaxRtrSolicitationDelay = 1; // Max delay for initial solicitation. In sec. static constexpr uint32_t kPolicyEvaluationMinDelay = 2000; // Min delay for policy evaluation. In msec. static constexpr uint32_t kPolicyEvaluationMaxDelay = 4000; // Max delay for policy evaluation. In msec. - static constexpr uint32_t kRtrSolicitationRetryDelay = - kRtrSolicitationInterval; // The delay before retrying failed RS tx. In Sec. - static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. + static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. // The STALE_RA_TIME in seconds. The Routing Manager will consider the prefixes // and learned RA parameters STALE when they are not refreshed in STALE_RA_TIME @@ -651,6 +644,39 @@ private: TimeMilli mLastTxTime; }; + class RsSender : public InstanceLocator + { + public: + // This class implements tx of Router Solicitation (RS) + // messages to discover other routers. `Start()` schedules + // a cycle of RS transmissions of `kMaxTxCount` separated + // by `kTxInterval`. At the end of cycle the callback + // `HandleRsSenderFinished()` is invoked to inform end of + // the cycle to `RoutingManager`. + + explicit RsSender(Instance &aInstance); + + bool IsInProgress(void) const { return mTimer.IsRunning(); } + void Start(void); + void Stop(void); + + private: + // All time intervals are in msec. + static constexpr uint32_t kMaxStartDelay = 1000; // Max random delay to send the first RS. + static constexpr uint32_t kTxInterval = 4000; // Interval between RS tx. + static constexpr uint32_t kRetryDelay = kTxInterval; // Interval to wait to retry a failed RS tx. + static constexpr uint32_t kWaitOnLastAttempt = 1000; // Wait interval after last RS tx. + static constexpr uint8_t kMaxTxCount = 3; // Number of RS tx in one cycle. + + Error SendRs(void); + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + + uint8_t mTxCount; + TimerMilli mTimer; + TimeMilli mStartTime; + }; + void EvaluateState(void); void Start(void); void Stop(void); @@ -672,13 +698,9 @@ private: void EvaluateOmrPrefix(void); Error PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); void UnpublishExternalRoute(const Ip6::Prefix &aPrefix); - void StartRouterSolicitationDelay(void); - Error SendRouterSolicitation(void); + void HandleRsSenderFinished(TimeMilli aStartTime); void SendRouterAdvertisement(RouterAdvTxMode aRaTxMode); - bool IsRouterSolicitationInProgress(void) const; - static void HandleRouterSolicitTimer(Timer &aTimer); - void HandleRouterSolicitTimer(void); static void HandleDiscoveredPrefixInvalidTimer(Timer &aTimer); void HandleDiscoveredPrefixInvalidTimer(void); static void HandleDiscoveredPrefixStaleTimer(Timer &aTimer); @@ -748,14 +770,10 @@ private: TimerMilli mInfraIfNat64PrefixStaleTimer; #endif - RaInfo mRaInfo; + RaInfo mRaInfo; + RsSender mRsSender; TimerMilli mDiscoveredPrefixStaleTimer; - - TimerMilli mRouterSolicitTimer; - TimeMilli mTimeRouterSolicitStart; - uint8_t mRouterSolicitCount; - TimerMilli mRoutingPolicyTimer; };