diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 96ccc4284..6929f8e20 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1272,8 +1272,9 @@ void RoutingManager::OnLinkPrefixManager::Init(void) lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay)); - entry->mPrefix = savedPrefix.GetPrefix(); - entry->mExpireTime = now + Time::SecToMsec(lifetime); + entry->mPrefix = savedPrefix.GetPrefix(); + entry->mDeprecateTime = now; + entry->mExpireTime = now + Time::SecToMsec(lifetime); LogInfo("Restored old prefix %s, lifetime:%lu", entry->mPrefix.ToString().AsCString(), ToUlong(lifetime)); @@ -1658,6 +1659,16 @@ Error RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(RouterAdvert::TxMes continue; } + // If another router on the infrastructure link is actively + // advertising this prefix as a preferred on-link prefix after we + // started deprecating it, we skip including it as a deprecating + // PIO to avoid sending conflicting advertisements. + + if (Get().HasSeenPreferredOnLinkPrefixAfter(oldPrefix.mPrefix, oldPrefix.mDeprecateTime)) + { + continue; + } + validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now); flags = PrefixInfoOption::kOnLinkFlag | PrefixInfoOption::kAutoConfigFlag; @@ -1755,8 +1766,9 @@ void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix & Get().RemoveBrOnLinkPrefix(removedPrefix); } - entry->mPrefix = aPrefix; - entry->mExpireTime = aExpireTime; + entry->mPrefix = aPrefix; + entry->mDeprecateTime = TimerMilli::GetNow(); + entry->mExpireTime = aExpireTime; mTimer.FireAtIfEarlier(aExpireTime); SavePrefix(aPrefix, aExpireTime); diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 1b1988c01..942a82cbd 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -742,6 +742,7 @@ private: bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } Ip6::Prefix mPrefix; + TimeMilli mDeprecateTime; TimeMilli mExpireTime; }; diff --git a/src/core/border_router/rx_ra_tracker.cpp b/src/core/border_router/rx_ra_tracker.cpp index 6a63d7aa5..42cbb59a5 100644 --- a/src/core/border_router/rx_ra_tracker.cpp +++ b/src/core/border_router/rx_ra_tracker.cpp @@ -1183,6 +1183,27 @@ exit: return isOnLink; } +bool RxRaTracker::HasSeenPreferredOnLinkPrefixAfter(const Ip6::Prefix &aPrefix, TimeMilli aTime) const +{ + bool hasSeen = false; + + for (const Router &router : mRouters) + { + for (const OnLinkPrefix &onLinkPrefix : router.mOnLinkPrefixes) + { + if (!onLinkPrefix.IsDeprecated() && onLinkPrefix.Matches(aPrefix) && + (onLinkPrefix.GetLastUpdateTime() >= aTime)) + { + hasSeen = true; + ExitNow(); + } + } + } + +exit: + return hasSeen; +} + bool RxRaTracker::IsPrefixOnLink(const Ip6::Prefix &aPrefix) const { bool isOnLink = false; diff --git a/src/core/border_router/rx_ra_tracker.hpp b/src/core/border_router/rx_ra_tracker.hpp index c641d6548..353a7b31d 100644 --- a/src/core/border_router/rx_ra_tracker.hpp +++ b/src/core/border_router/rx_ra_tracker.hpp @@ -307,6 +307,18 @@ public: */ bool IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const; + /** + * Indicates whether a given prefix is seen as a preferred (non-deprecated) on-link prefix in any tracked RA + * received after a given time. + * + * @param[in] aPrefix The IPv6 prefix to check. + * @param[in] aTime The time threshold. + * + * @retval TRUE The prefix is seen as a preferred on-link prefix after @p aTime. + * @retval FALSE The prefix is not seen as a preferred on-link prefix after @p aTime. + */ + bool HasSeenPreferredOnLinkPrefixAfter(const Ip6::Prefix &aPrefix, TimeMilli aTime) const; + /** * Indicates whether a given prefix is seen as an on-link prefix in any tracked RA. * diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 3664000ad..90b3a1179 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -2780,7 +2780,7 @@ void TestLocalOnLinkPrefixDeprecation(void) void TestUnadvertisedLocalOnLinkPrefix(void) { - static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; Ip6::Prefix localOnLink; Ip6::Prefix oldLocalOnLink; @@ -2931,7 +2931,7 @@ void TestExtPanIdChange(void) { static constexpr uint32_t kMaxRaTxInterval = 196; // In seconds - static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; static const otExtendedPanId kExtPanId2 = {{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99, 0x88}}; static const otExtendedPanId kExtPanId3 = {{0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xcd, 0xef}}; static const otExtendedPanId kExtPanId4 = {{0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00}}; @@ -3437,6 +3437,139 @@ void TestExtPanIdChange(void) FinalizeTest(); } +void TestOldOnLinkPrefixAdvertisedOnLink(void) +{ + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + otOperationalDataset dataset; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestOldOnLinkPrefixAdvertisedOnLink"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + 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()); + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID to deprecate the current local on-link prefix. + + Log("Changing ext PAN ID"); + + oldLocalOnLink = localOnLink; + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + dataset.mActiveTimestamp.mSeconds++; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + VerifyOrQuit(localOnLink != oldLocalOnLink); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that the old local on-link prefix is initially included as a + // deprecating PIO in the emitted RA. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A advertising `oldLocalOnLink` as a preferred on-link prefix. + // Ensure that it is no longer included as a deprecating PIO in emitted RAs. + + Log("Router A advertises old on-link prefix as preferred"); + + SendRouterAdvert(routerAddressA, {Pio(oldLocalOnLink, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.IsEmpty()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A deprecating `oldLocalOnLink` (zero preferred lifetime). + // Ensure that this BR resumes including `oldLocalOnLink` as a deprecating PIO. + + Log("Router A deprecates old on-link prefix"); + + SendRouterAdvert(routerAddressA, {Pio(oldLocalOnLink, kValidLitime, 0)}); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Router A advertises `oldLocalOnLink` as preferred again. + // Ensure that the deprecating PIO is suppressed once more. + + Log("Router A advertises old on-link prefix as preferred again"); + + SendRouterAdvert(routerAddressA, {Pio(oldLocalOnLink, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.IsEmpty()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + SuccessOrQuit(sInstance->Get().SetEnabled(false)); + AdvanceTime(3000); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + Log("End of TestOldOnLinkPrefixAdvertisedOnLink"); + FinalizeTest(); +} + void TestPrefixStaleTime(void) { Ip6::Prefix localOnLink; @@ -3996,7 +4129,7 @@ void TestLearnRaHeader(void) void TestConflictingPrefix(void) { - static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; Ip6::Prefix localOnLink; Ip6::Prefix oldLocalOnLink; @@ -4219,7 +4352,7 @@ void TestConflictingPrefix(void) #if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE void TestSavedOnLinkPrefixes(void) { - static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; Ip6::Prefix localOnLink; Ip6::Prefix oldLocalOnLink; @@ -5611,6 +5744,7 @@ int main(void) ot::TestLocalOnLinkPrefixDeprecation(); ot::TestUnadvertisedLocalOnLinkPrefix(); ot::TestExtPanIdChange(); + ot::TestOldOnLinkPrefixAdvertisedOnLink(); ot::TestConflictingPrefix(); ot::TestPrefixStaleTime(); ot::TestRouterNsProbe();