diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 495d04da6..a1aab1076 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -352,6 +352,7 @@ void RoutingManager::HandleNotifierEvents(Events aEvents) if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged)) { UpdateDiscoveredPrefixTableOnNetDataChange(); + mLocalOnLinkPrefix.HandleNetDataChange(); ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); } @@ -532,7 +533,7 @@ void RoutingManager::EvaluateOnLinkPrefix(void) // and therefore is the same for all BRs on the same Thread // mesh. - mLocalOnLinkPrefix.Advertise(); + mLocalOnLinkPrefix.PublishAndAdvertise(); // We remove the local on-link prefix from discovered prefix // table, in case it was previously discovered and included in @@ -548,7 +549,7 @@ void RoutingManager::EvaluateOnLinkPrefix(void) mFavoredDiscoveredOnLinkPrefix.Clear(); } - else if (mLocalOnLinkPrefix.IsAdvertising()) + else if (mLocalOnLinkPrefix.IsPublishingOrAdvertising()) { // When an application-specific on-link prefix is received and // it is larger than the local prefix, we will not remove the @@ -611,7 +612,7 @@ bool RoutingManager::IsInitalPolicyEvaluationDone(void) const // and published in the Thread Network Data. return mIsRunning && !mFavoredOmrPrefix.IsEmpty() && - (mFavoredDiscoveredOnLinkPrefix.GetLength() != 0 || mLocalOnLinkPrefix.IsAdvertising()); + (mFavoredDiscoveredOnLinkPrefix.GetLength() != 0 || mLocalOnLinkPrefix.IsPublishingOrAdvertising()); } void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode) @@ -994,7 +995,7 @@ bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOpti ExitNow(); } - if (mLocalOnLinkPrefix.IsAdvertising()) + if (mLocalOnLinkPrefix.IsPublishingOrAdvertising()) { VerifyOrExit(aPrefix != mLocalOnLinkPrefix.GetPrefix()); } @@ -1077,18 +1078,36 @@ bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) co { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - bool contain = false; + bool contains = false; while (Get().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone) { if (IsValidOmrPrefix(onMeshPrefixConfig) && onMeshPrefixConfig.GetPrefix() == aPrefix) { - contain = true; + contains = true; break; } } - return contain; + return contains; +} + +bool RoutingManager::NetworkDataContainsExternalRoute(const Ip6::Prefix &aPrefix) const +{ + NetworkData::Iterator iterator = NetworkData::kIteratorInit; + NetworkData::ExternalRouteConfig routeConfig; + bool contains = false; + + while (Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) + { + if (routeConfig.mStable && routeConfig.GetPrefix() == aPrefix) + { + contains = true; + break; + } + } + + return contains; } void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage) @@ -1994,6 +2013,15 @@ void RoutingManager::LocalOnLinkPrefix::Stop(void) Get().UnpublishExternalRoute(mPrefix); + if (mState == kPublishing) + { + // If we are waiting for prefix to be added in Network Data + // and not yet advertised it in any RA, there is no need to + // deprecate it and we can directly go to `kIdle` state. + mState = kIdle; + ExitNow(); + } + mState = kDeprecating; // Start deprecating the local on-link prefix to send a PIO @@ -2004,20 +2032,37 @@ exit: return; } -void RoutingManager::LocalOnLinkPrefix::Advertise(void) +void RoutingManager::LocalOnLinkPrefix::PublishAndAdvertise(void) { - // Start advertising the local on-link prefix if not already. This - // will also publish it in the Network Data as an external route - // entry. + // Start publishing and advertising the local on-link prefix if + // not already. - VerifyOrExit(mState != kAdvertising); + switch (mState) + { + case kIdle: + case kDeprecating: + break; - mState = kAdvertising; - mExpireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); - LogInfo("Start advertising on-link prefix %s", mPrefix.ToString().AsCString()); + case kPublishing: + case kAdvertising: + ExitNow(); + } + + mState = kPublishing; + LogInfo("Publishing local on-link prefix %s in netdata", mPrefix.ToString().AsCString()); Get().EvaluatePublishingPrefix(mPrefix); + // We wait for the prefix to be added in Network Data before + // starting to advertise it in RAs. However, if it is already + // present in Network Data (e.g., added by another BR on the same + // Thread mesh), we can immediately start advertising it. + + if (Get().NetworkDataContainsExternalRoute(mPrefix)) + { + EnterAdvertisingState(); + } + exit: return; } @@ -2031,11 +2076,24 @@ void RoutingManager::LocalOnLinkPrefix::Deprecate(void) // with zero preferred lifetime and the remaining valid lifetime // until the timer expires. - VerifyOrExit(mState == kAdvertising); + switch (mState) + { + case kPublishing: + mState = kIdle; + Get().EvaluatePublishingPrefix(mPrefix); + mState = kIdle; + break; - mState = kDeprecating; - mTimer.FireAtIfEarlier(mExpireTime); - LogInfo("Deprecate local on-link prefix %s", mPrefix.ToString().AsCString()); + case kAdvertising: + mState = kDeprecating; + mTimer.FireAtIfEarlier(mExpireTime); + LogInfo("Deprecate local on-link prefix %s", mPrefix.ToString().AsCString()); + break; + + case kIdle: + case kDeprecating: + ExitNow(); + } exit: return; @@ -2051,6 +2109,7 @@ bool RoutingManager::LocalOnLinkPrefix::ShouldPublish(NetworkData::ExternalRoute { case kIdle: break; + case kPublishing: case kAdvertising: case kDeprecating: shouldPublish = true; @@ -2067,6 +2126,19 @@ bool RoutingManager::LocalOnLinkPrefix::ShouldPublish(NetworkData::ExternalRoute return shouldPublish; } +void RoutingManager::LocalOnLinkPrefix::EnterAdvertisingState(void) +{ + mState = kAdvertising; + mExpireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); + + LogInfo("Start advertising local on-link prefix %s", mPrefix.ToString().AsCString()); +} + +bool RoutingManager::LocalOnLinkPrefix::IsPublishingOrAdvertising(void) const +{ + return (mState == kPublishing) || (mState == kAdvertising); +} + void RoutingManager::LocalOnLinkPrefix::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) { AppendCurPrefix(aRaMessage); @@ -2099,6 +2171,7 @@ void RoutingManager::LocalOnLinkPrefix::AppendCurPrefix(Ip6::Nd::RouterAdvertMes break; case kIdle: + case kPublishing: ExitNow(); } @@ -2127,6 +2200,20 @@ exit: return; } +void RoutingManager::LocalOnLinkPrefix::HandleNetDataChange(void) +{ + VerifyOrExit(mState == kPublishing); + + if (Get().NetworkDataContainsExternalRoute(mPrefix)) + { + EnterAdvertisingState(); + Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); + } + +exit: + return; +} + void RoutingManager::LocalOnLinkPrefix::HandleExtPanIdChange(void) { // If the prefix is advertised or being deprecated we remember it @@ -2141,6 +2228,10 @@ void RoutingManager::LocalOnLinkPrefix::HandleExtPanIdChange(void) case kIdle: break; + case kPublishing: + Get().UnpublishExternalRoute(mPrefix); + break; + case kAdvertising: case kDeprecating: if (mOldPrefix.GetLength() != 0) diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index d56abe834..5e1cd9187 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -613,12 +613,13 @@ private: void Generate(void); void Start(void); void Stop(void); - void Advertise(void); + void PublishAndAdvertise(void); void Deprecate(void); bool ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const; void AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage); const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } - bool IsAdvertising(void) const { return (mState == kAdvertising); } + bool IsPublishingOrAdvertising(void) const; + void HandleNetDataChange(void); void HandleExtPanIdChange(void); void HandleTimer(void); @@ -626,10 +627,12 @@ private: enum State : uint8_t { kIdle, + kPublishing, kAdvertising, kDeprecating, }; + void EnterAdvertisingState(void); void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); void AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); @@ -775,6 +778,7 @@ private: bool ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix); void UpdateDiscoveredPrefixTableOnNetDataChange(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; + bool NetworkDataContainsExternalRoute(const Ip6::Prefix &aPrefix) const; void UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage); bool IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const; void ResetDiscoveredPrefixStaleTimer(void); diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 5f224b67c..a70f3f264 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -300,7 +300,7 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) break; } } - else + else if (sExpectOldOnLinkPio) { VerifyOrQuit(pio.GetPreferredLifetime() == 0, "Old on link prefix is not deprecated"); sOldOnLinkPrefix = prefix; @@ -347,16 +347,23 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) break; case kPioAdvertisingLocalOnLink: case kPioDeprecatingLocalOnLink: - VerifyOrQuit(sawExpectedPio, "Did not see on-link prefix PIO in the RA"); + // First emitted RAs may not yet have the expected PIO + // so we exit and not set `sRaValidated` to allow it + // to be checked for next received RA. + VerifyOrExit(sawExpectedPio); + break; } if (sExpectOldOnLinkPio) { - VerifyOrQuit(sawExpecteOldPio, "Did not see old on-link prefix PIO in the RA"); + VerifyOrExit(sawExpecteOldPio); } sRaValidated = true; } + +exit: + return; } void LogRouterAdvert(const Icmp6Packet &aPacket)