From 16c927ea1c3b7c3c83c68dc6d0d0541d569ab60d Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 12 Oct 2022 15:13:16 -0700 Subject: [PATCH] [routing-manager] enhance mechanism to publish/unpublish route prefixes (#8187) This commit updates and enhances the mechanism in `RoutingManager` to publish/unpublish external route prefixes in Thread Network Data. Instead of each sub-component (e.g., `DiscoveredPrefixTable`, `LocalOnLinkPrefix`, `Nat64PrefixManager`, ...) directly trying to publish/unpublish its entries which can lead to conflicting requests that then needs to be checked and handled by each component, this commit adds a new method `EvaluatePublishingPrefix()`. This method makes a collective decision by checking with different sub-components to see whether or not each wants this prefix published and if so at what preference level and flags. This change help simplify the code and handling of conflicting situations and edge cases (e.g., while deprecating current/old local on-link prefix, other routers adding/removing same prefix as PIO or RIO in their emitted RAs). --- src/core/border_router/routing_manager.cpp | 224 +++++++++++--------- src/core/border_router/routing_manager.hpp | 54 +++-- tests/unit/test_routing_manager.cpp | 228 +++++++++++++++++++++ 3 files changed, 384 insertions(+), 122 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 727e79609..495d04da6 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -386,8 +386,7 @@ void RoutingManager::UpdateDiscoveredPrefixTableOnNetDataChange(void) continue; } - mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix(), - DiscoveredPrefixTable::kUnpublishFromNetData); + mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix()); if (prefixConfig.mDefaultRoute) { @@ -458,20 +457,53 @@ exit: return; } -void RoutingManager::PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64) +void RoutingManager::EvaluatePublishingPrefix(const Ip6::Prefix &aPrefix) { - NetworkData::ExternalRouteConfig routeConfig; + // This method evaluates whether to publish or unpublish a given + // `aPrefix` as an external route in the Network Data. It makes a + // collective decision by checking with different sub-components to + // see whether or not each wants this prefix published and if so + // at what preference level and flags. + // + // Before calling this method, the sub-components need to make sure + // to update their internal state such that their `ShouldPublish()` + // provides the correct info. - OT_ASSERT(mIsRunning); + bool shouldPublish = false; + NetworkData::ExternalRouteConfig routeConfig; routeConfig.Clear(); routeConfig.SetPrefix(aPrefix); + routeConfig.mPreference = NetworkData::kRoutePreferenceLow; routeConfig.mStable = true; - routeConfig.mNat64 = aNat64; - routeConfig.mPreference = aRoutePreference; - SuccessOrAssert( - Get().PublishExternalRoute(routeConfig, NetworkData::Publisher::kFromRoutingManager)); + // The order of checks is important. The Discovered Prefix Table is + // first followed by Local On Link Prefix manager and finally NAT64 + // prefix manager. + + shouldPublish = mDiscoveredPrefixTable.ShouldPublish(routeConfig); + + if (mLocalOnLinkPrefix.ShouldPublish(routeConfig)) + { + shouldPublish = true; + } + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + if (mNat64PrefixManager.ShouldPublish(routeConfig)) + { + shouldPublish = true; + } +#endif + + if (shouldPublish) + { + SuccessOrAssert(Get().PublishExternalRoute( + routeConfig, NetworkData::Publisher::kFromRoutingManager)); + } + else + { + UnpublishExternalRoute(aPrefix); + } } void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix) @@ -512,8 +544,7 @@ void RoutingManager::EvaluateOnLinkPrefix(void) // not allow the local on-link prefix to be added in the prefix // table while we are advertising it. - mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalOnLinkPrefix.GetPrefix(), - DiscoveredPrefixTable::kKeepInNetData); + mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalOnLinkPrefix.GetPrefix()); mFavoredDiscoveredOnLinkPrefix.Clear(); } @@ -1236,8 +1267,9 @@ void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::R entry->SetFrom(aRaHeader); } - UpdateNetworkDataOnChangeTo(*entry); mTimer.FireAtIfEarlier(entry->GetExpireTime()); + Get().EvaluatePublishingPrefix(entry->GetPrefix()); + SignalTableChanged(); exit: @@ -1282,8 +1314,9 @@ void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::N entry->AdoptValidAndPreferredLiftimesFrom(newEntry); } - UpdateNetworkDataOnChangeTo(*entry); mTimer.FireAtIfEarlier(entry->GetExpireTime()); + Get().EvaluatePublishingPrefix(entry->GetPrefix()); + SignalTableChanged(); exit: @@ -1325,8 +1358,9 @@ void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd entry->SetFrom(aRio); } - UpdateNetworkDataOnChangeTo(*entry); mTimer.FireAtIfEarlier(entry->GetExpireTime()); + Get().EvaluatePublishingPrefix(entry->GetPrefix()); + SignalTableChanged(); exit: @@ -1335,7 +1369,6 @@ exit: void RoutingManager::DiscoveredPrefixTable::SetAllowDefaultRouteInNetData(bool aAllow) { - Entry * favoredEntry; Ip6::Prefix prefix; VerifyOrExit(aAllow != mAllowDefaultRouteInNetData); @@ -1345,17 +1378,7 @@ void RoutingManager::DiscoveredPrefixTable::SetAllowDefaultRouteInNetData(bool a mAllowDefaultRouteInNetData = aAllow; prefix.Clear(); - favoredEntry = FindFavoredEntryToPublish(prefix); - VerifyOrExit(favoredEntry != nullptr); - - if (mAllowDefaultRouteInNetData) - { - PublishEntry(*favoredEntry); - } - else - { - UnpublishEntry(*favoredEntry); - } + Get().EvaluatePublishingPrefix(prefix); exit: return; @@ -1412,22 +1435,19 @@ bool RoutingManager::DiscoveredPrefixTable::ContainsPrefix(const Entry::Matcher return contains; } -void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode) +void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix) { - RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink), aNetDataMode); + RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink)); } -void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode) +void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix) { - RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute), aNetDataMode); + RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute)); } -void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher, NetDataMode aNetDataMode) +void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher) { // Removes all entries matching a given prefix from the table. - // `aNetDataMode` specifies behavior when a match is found and - // removed. It indicates whether or not to unpublish it from - // Network Data. LinkedList removedEntries; @@ -1438,14 +1458,11 @@ void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &a VerifyOrExit(!removedEntries.IsEmpty()); - if (aNetDataMode == kUnpublishFromNetData) - { - UnpublishEntry(*removedEntries.GetHead()); - } - FreeEntries(removedEntries); RemoveRoutersWithNoEntries(); + Get().EvaluatePublishingPrefix(aMatcher.mPrefix); + SignalTableChanged(); exit: @@ -1463,7 +1480,7 @@ void RoutingManager::DiscoveredPrefixTable::RemoveAllEntries(void) while ((entry = router.mEntries.Pop()) != nullptr) { - UnpublishEntry(*entry); + Get().UnpublishExternalRoute(entry->GetPrefix()); FreeEntry(*entry); SignalTableChanged(); } @@ -1551,8 +1568,8 @@ void RoutingManager::DiscoveredPrefixTable::FreeEntries(LinkedList &aEntr } } -RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish( - const Ip6::Prefix &aPrefix) +const RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish( + const Ip6::Prefix &aPrefix) const { // Finds the favored entry matching a given `aPrefix` in the table // to publish in the Network Data. We can have multiple entries @@ -1561,11 +1578,11 @@ RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTa // select the one with the highest preference as the favored // entry to publish. - Entry *favoredEntry = nullptr; + const Entry *favoredEntry = nullptr; - for (Router &router : mRouters) + for (const Router &router : mRouters) { - for (Entry &entry : router.mEntries) + for (const Entry &entry : router.mEntries) { if (entry.GetPrefix() != aPrefix) { @@ -1582,15 +1599,12 @@ RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTa return favoredEntry; } -void RoutingManager::DiscoveredPrefixTable::UpdateNetworkDataOnChangeTo(Entry &aEntry) +bool RoutingManager::DiscoveredPrefixTable::ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const { - // Updates Network Data when there is a change to `aEntry` which - // can be a newly added entry or an existing entry that is - // modified due to processing of a received RA message. + bool shouldPublish = false; + const Entry *favoredEntry; - Entry *favoredEntry; - - if (aEntry.GetPrefix().GetLength() == 0) + if (aRouteConfig.GetPrefix().GetLength() == 0) { // If the change is to default route ::/0 prefix, make sure we // are allowed to publish default route in Network Data. @@ -1598,23 +1612,14 @@ void RoutingManager::DiscoveredPrefixTable::UpdateNetworkDataOnChangeTo(Entry &a VerifyOrExit(mAllowDefaultRouteInNetData); } - favoredEntry = FindFavoredEntryToPublish(aEntry.GetPrefix()); + favoredEntry = FindFavoredEntryToPublish(aRouteConfig.GetPrefix()); + VerifyOrExit(favoredEntry != nullptr); - OT_ASSERT(favoredEntry != nullptr); - PublishEntry(*favoredEntry); + shouldPublish = true; + aRouteConfig.mPreference = favoredEntry->GetPreference(); exit: - return; -} - -void RoutingManager::DiscoveredPrefixTable::PublishEntry(const Entry &aEntry) -{ - Get().PublishExternalRoute(aEntry.GetPrefix(), aEntry.GetPreference()); -} - -void RoutingManager::DiscoveredPrefixTable::UnpublishEntry(const Entry &aEntry) -{ - Get().UnpublishExternalRoute(aEntry.GetPrefix()); + return shouldPublish; } void RoutingManager::DiscoveredPrefixTable::HandleTimer(void) @@ -1640,16 +1645,7 @@ void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void) for (const Entry &expiredEntry : expiredEntries) { - Entry *favoredEntry = FindFavoredEntryToPublish(expiredEntry.GetPrefix()); - - if (favoredEntry == nullptr) - { - UnpublishEntry(expiredEntry); - } - else - { - PublishEntry(*favoredEntry); - } + Get().EvaluatePublishingPrefix(expiredEntry.GetPrefix()); } if (!expiredEntries.IsEmpty()) @@ -2016,12 +2012,12 @@ void RoutingManager::LocalOnLinkPrefix::Advertise(void) VerifyOrExit(mState != kAdvertising); - Get().PublishExternalRoute(mPrefix, NetworkData::kRoutePreferenceMedium); - mState = kAdvertising; mExpireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); LogInfo("Start advertising on-link prefix %s", mPrefix.ToString().AsCString()); + Get().EvaluatePublishingPrefix(mPrefix); + exit: return; } @@ -2045,6 +2041,32 @@ exit: return; } +bool RoutingManager::LocalOnLinkPrefix::ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const +{ + bool shouldPublish = false; + + if (aRouteConfig.GetPrefix() == mPrefix) + { + switch (mState) + { + case kIdle: + break; + case kAdvertising: + case kDeprecating: + shouldPublish = true; + aRouteConfig.mPreference = NetworkData::kRoutePreferenceMedium; + break; + } + } + else if ((mOldPrefix.GetLength() != 0) && (aRouteConfig.GetPrefix() == mOldPrefix)) + { + shouldPublish = true; + aRouteConfig.mPreference = NetworkData::kRoutePreferenceMedium; + } + + return shouldPublish; +} + void RoutingManager::LocalOnLinkPrefix::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) { AppendCurPrefix(aRaMessage); @@ -2123,7 +2145,10 @@ void RoutingManager::LocalOnLinkPrefix::HandleExtPanIdChange(void) case kDeprecating: if (mOldPrefix.GetLength() != 0) { - Unpublish(mOldPrefix); + Ip6::Prefix prevPrefix = mOldPrefix; + + mOldPrefix.Clear(); + Get().EvaluatePublishingPrefix(prevPrefix); } mOldPrefix = mPrefix; @@ -2143,15 +2168,17 @@ void RoutingManager::LocalOnLinkPrefix::HandleTimer(void) if ((mState == kDeprecating) && (now >= mExpireTime)) { LogInfo("Local on-link prefix %s expired", mPrefix.ToString().AsCString()); - Unpublish(mPrefix); mState = kIdle; + Get().EvaluatePublishingPrefix(mPrefix); } if ((mOldPrefix.GetLength() != 0) && (now >= mOldExpireTime)) { + Ip6::Prefix oldPrefix = mOldPrefix; + LogInfo("Old local on-link prefix %s expired", mOldPrefix.ToString().AsCString()); - Unpublish(mOldPrefix); mOldPrefix.Clear(); + Get().EvaluatePublishingPrefix(oldPrefix); } // Re-schedule the timer @@ -2167,14 +2194,6 @@ void RoutingManager::LocalOnLinkPrefix::HandleTimer(void) } } -void RoutingManager::LocalOnLinkPrefix::Unpublish(const Ip6::Prefix &aPrefix) -{ - if (!Get().mDiscoveredPrefixTable.ContainsOnLinkPrefix(aPrefix)) - { - Get().UnpublishExternalRoute(aPrefix); - } -} - //--------------------------------------------------------------------------------------------------------------------- // OnMeshPrefixArray @@ -2303,14 +2322,18 @@ void RoutingManager::Nat64PrefixManager::Evaluate(void) if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix))) { - Get().UnpublishExternalRoute(mPublishedPrefix); + Ip6::Prefix prevPrefix; + + prevPrefix = mPublishedPrefix; mPublishedPrefix.Clear(); + Get().EvaluatePublishingPrefix(prevPrefix); } - if (shouldPublish && (prefix != mPublishedPrefix)) + if (shouldPublish && ((prefix != mPublishedPrefix) || (preference != mPublishedPreference))) { - Get().PublishExternalRoute(prefix, preference, /* aNat64 */ true); - mPublishedPrefix = prefix; + mPublishedPrefix = prefix; + mPublishedPreference = preference; + Get().EvaluatePublishingPrefix(mPublishedPrefix); } #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE @@ -2318,6 +2341,21 @@ void RoutingManager::Nat64PrefixManager::Evaluate(void) #endif } +bool RoutingManager::Nat64PrefixManager::ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const +{ + bool shouldPublish = false; + + VerifyOrExit(mPublishedPrefix.IsValidNat64()); + VerifyOrExit(mPublishedPrefix == aRouteConfig.GetPrefix()); + + shouldPublish = true; + aRouteConfig.mPreference = mPublishedPreference; + aRouteConfig.mNat64 = true; + +exit: + return shouldPublish; +} + void RoutingManager::Nat64PrefixManager::HandleTimer(void) { Discover(); diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index c548e4129..d56abe834 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -413,12 +413,6 @@ private: // invoked after all the changes are processed. public: - enum NetDataMode : uint8_t // Used in `Remove{}` methods - { - kUnpublishFromNetData, // Unpublish the entry from Network Data if previously published. - kKeepInNetData, // Keep entry in Network Data if previously published. - }; - explicit DiscoveredPrefixTable(Instance &aInstance); void ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage, @@ -428,10 +422,12 @@ private: void FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const; bool ContainsOnLinkPrefix(const Ip6::Prefix &aPrefix) const; - void RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode); + void RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix); bool ContainsRoutePrefix(const Ip6::Prefix &aPrefix) const; - void RemoveRoutePrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode); + void RemoveRoutePrefix(const Ip6::Prefix &aPrefix); + + bool ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const; void RemoveAllEntries(void); void RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold); @@ -546,21 +542,19 @@ private: void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } }; - void ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter); - void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter); - void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter); - bool ContainsPrefix(const Entry::Matcher &aMatcher) const; - void RemovePrefix(const Entry::Matcher &aMatcher, NetDataMode aNetDataMode); - void RemoveRoutersWithNoEntries(void); - Entry *AllocateEntry(void) { return mEntryPool.Allocate(); } - void FreeEntry(Entry &aEntry) { mEntryPool.Free(aEntry); } - void FreeEntries(LinkedList &aEntries); - void UpdateNetworkDataOnChangeTo(Entry &aEntry); - Entry *FindFavoredEntryToPublish(const Ip6::Prefix &aPrefix); - void PublishEntry(const Entry &aEntry); - void UnpublishEntry(const Entry &aEntry); - void RemoveExpiredEntries(void); - void SignalTableChanged(void); + void ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter); + void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter); + void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter); + bool ContainsPrefix(const Entry::Matcher &aMatcher) const; + void RemovePrefix(const Entry::Matcher &aMatcher); + void RemoveRoutersWithNoEntries(void); + Entry * AllocateEntry(void) { return mEntryPool.Allocate(); } + void FreeEntry(Entry &aEntry) { mEntryPool.Free(aEntry); } + void FreeEntries(LinkedList &aEntries); + void UpdateNetworkDataOnChangeTo(Entry &aEntry); + const Entry *FindFavoredEntryToPublish(const Ip6::Prefix &aPrefix) const; + void RemoveExpiredEntries(void); + void SignalTableChanged(void); using SignalTask = TaskletIn; using TableTimer = TimerMilliIn; @@ -621,6 +615,7 @@ private: void Stop(void); void Advertise(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); } @@ -637,7 +632,6 @@ private: void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); void AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); - void Unpublish(const Ip6::Prefix &aPrefix); using ExpireTimer = TimerMilliIn; @@ -678,6 +672,7 @@ private: const Ip6::Prefix &GetLocalPrefix(void) const { return mLocalPrefix; } const Ip6::Prefix &GetFavoredPrefix(RoutePreference &aPreference) const; void Evaluate(void); + bool ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const; void HandleDiscoverDone(const Ip6::Prefix &aPrefix); void HandleTimer(void); @@ -686,10 +681,11 @@ private: using Nat64Timer = TimerMilliIn; - Ip6::Prefix mInfraIfPrefix; // The latest NAT64 prefix discovered on the infrastructure interface. - Ip6::Prefix mLocalPrefix; // The local prefix (from BR ULA prefix). - Ip6::Prefix mPublishedPrefix; // The prefix published in Network Data (may be empty or local or from infra-if). - Nat64Timer mTimer; + Ip6::Prefix mInfraIfPrefix; // The latest NAT64 prefix discovered on the infrastructure interface. + Ip6::Prefix mLocalPrefix; // The local prefix (from BR ULA prefix). + Ip6::Prefix mPublishedPrefix; // The prefix to publish in Net Data (empty or local or from infra-if). + RoutePreference mPublishedPreference; // The published prefix preference. + Nat64Timer mTimer; }; #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE @@ -766,7 +762,7 @@ private: bool IsInitalPolicyEvaluationDone(void) const; void ScheduleRoutingPolicyEvaluation(ScheduleMode aMode); void EvaluateOmrPrefix(void); - void PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); + void EvaluatePublishingPrefix(const Ip6::Prefix &aPrefix); void UnpublishExternalRoute(const Ip6::Prefix &aPrefix); void HandleRsSenderFinished(TimeMilli aStartTime); void SendRouterAdvertisement(RouterAdvTxMode aRaTxMode); diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index cdbd852d9..5f224b67c 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -1760,6 +1760,232 @@ void TestExtPanIdChange(void) testFreeInstance(sInstance); } +void TestConflictingPrefix(void) +{ + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Address routerAddressB = AddressFromString("fd00::bbbb"); + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestConflictingPrefix"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and on-link prefix. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with our local on-link prefix as RIO. + + Log("Send RA from router A with local on-link as RIO"); + SendRouterAdvert(routerAddressA, {Rio(localOnLink, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is still being advertised. + + sRaValidated = false; + AdvanceTime(610000); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to still include the local OMR and on-link prefix. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A removing local on-link prefix as RIO. + + SendRouterAdvert(routerAddressA, {Rio(localOnLink, 0, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Verify that on-link prefix is still included in Network Data and + // the change by router A did not cause it to be unpublished. + + AdvanceTime(10000); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is still being advertised. + + sRaValidated = false; + AdvanceTime(610000); + VerifyOrQuit(sRaValidated); + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router B advertising an on-link prefix. This + // should cause local on-link prefix to be deprecated. + + SendRouterAdvert(routerAddressB, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is now deprecating. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(10000); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("On-link prefix is deprecating, remaining lifetime:%d", sOnLinkLifetime); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the new on-link prefix from router B and + // the deprecating local on-link prefix. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A again adding local on-link prefix as RIO. + + Log("Send RA from router A with local on-link as RIO"); + SendRouterAdvert(routerAddressA, {Rio(localOnLink, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is still being deprecated. + + sRaValidated = false; + AdvanceTime(610000); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A removing the previous RIO. + + SendRouterAdvert(routerAddressA, {Rio(localOnLink, 0, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged and still contains + // the deprecating local on-link prefix. + + AdvanceTime(60000); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router B removing its on-link prefix. + + SendRouterAdvert(routerAddressB, {Pio(onLinkPrefix, kValidLitime, 0)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is once again being advertised. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(10000); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data still contains both prefixes. + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + Log("Changing ext PAN ID"); + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + AdvanceTime(10000); + + oldLocalOnLink = localOnLink; + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + + Log("Local on-link prefix is changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data contains old and new local on-link prefix + // and deprecating prefix from router B. + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A again adding the old local on-link prefix + // as RIO. + + SendRouterAdvert(routerAddressA, {Rio(oldLocalOnLink, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + AdvanceTime(10000); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A removing the previous RIO. + + SendRouterAdvert(routerAddressA, {Rio(localOnLink, 0, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + AdvanceTime(10000); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestConflictingPrefix"); + + testFreeInstance(sInstance); +} + #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE void TestAutoEnableOfSrpServer(void) { @@ -1980,9 +2206,11 @@ int main(void) TestDomainPrefixAsOmr(); #endif TestExtPanIdChange(); + TestConflictingPrefix(); #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE TestAutoEnableOfSrpServer(); #endif + printf("All tests passed\n"); #else printf("BORDER_ROUTING feature is not enabled\n");