diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index ba55c4bbc..4b36669d0 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -678,13 +678,13 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) // RA message max length is derived to accommodate: // // - The RA header, - // - At most one PIO (for local on-link prefix), + // - At most two PIOs (for current and old local on-link prefixes), // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes. // Factor two is used for RIO to account for entries invalidating // previous prefixes while adding new ones. static constexpr uint16_t kMaxRaLength = - sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) + + sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) * 2 + 2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix)); uint8_t buffer[kMaxRaLength]; @@ -693,9 +693,10 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) NetworkData::OnMeshPrefixConfig prefixConfig; // Append PIO for local on-link prefix if is either being - // advertised or deprecated. + // advertised or deprecated and for old prefix if is being + // deprecated. - mLocalOnLinkPrefix.AppendAsPioTo(raMsg); + mLocalOnLinkPrefix.AppendAsPiosTo(raMsg); // Determine which previously advertised prefixes need to be // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need @@ -2038,6 +2039,7 @@ RoutingManager::LocalOnLinkPrefix::LocalOnLinkPrefix(Instance &aInstance) , mTimer(aInstance, HandleTimer) { mPrefix.Clear(); + mOldPrefix.Clear(); } void RoutingManager::LocalOnLinkPrefix::Generate(void) @@ -2061,9 +2063,18 @@ void RoutingManager::LocalOnLinkPrefix::Start(void) void RoutingManager::LocalOnLinkPrefix::Stop(void) { + mTimer.Stop(); + + if (mOldPrefix.GetLength() != 0) + { + Get().UnpublishExternalRoute(mOldPrefix); + mOldPrefix.Clear(); + } + VerifyOrExit(mState != kIdle); Get().UnpublishExternalRoute(mPrefix); + mState = kDeprecating; // Start deprecating the local on-link prefix to send a PIO @@ -2084,11 +2095,10 @@ Error RoutingManager::LocalOnLinkPrefix::Advertise(void) VerifyOrExit(mState != kAdvertising); - mTimer.Stop(); - SuccessOrExit(error = Get().PublishExternalRoute(mPrefix, NetworkData::kRoutePreferenceMedium)); - mState = kAdvertising; + mState = kAdvertising; + mExpireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); LogInfo("Start advertising on-link prefix %s", mPrefix.ToString().AsCString()); exit: @@ -2107,36 +2117,41 @@ void RoutingManager::LocalOnLinkPrefix::Deprecate(void) VerifyOrExit(mState == kAdvertising); mState = kDeprecating; + mTimer.FireAtIfEarlier(mExpireTime); LogInfo("Deprecate local on-link prefix %s", mPrefix.ToString().AsCString()); exit: return; } -void RoutingManager::LocalOnLinkPrefix::AppendAsPioTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) +void RoutingManager::LocalOnLinkPrefix::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + AppendCurPrefix(aRaMessage); + AppendOldPrefix(aRaMessage); +} + +void RoutingManager::LocalOnLinkPrefix::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage) { // Append the local on-link prefix to the `aRaMessage` as a PIO // only if it is being advertised or deprecated. // - // If in `kAdvertising` state, we restart the lifetime timer. + // If in `kAdvertising` state, we reset the expire time. // If in `kDeprecating` state, we include it as PIO with zero - // preferred lifetime and the remaining valid lifetime which - // is tracked by the timer. + // preferred lifetime and the remaining valid lifetime. uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; - TimeMilli now; + TimeMilli now = TimerMilli::GetNow(); switch (mState) { case kAdvertising: - mTimer.Start(TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime)); + mExpireTime = now + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); break; case kDeprecating: - now = TimerMilli::GetNow(); - VerifyOrExit(mTimer.IsRunning() && (mTimer.GetFireTime() > now)); - validLifetime = TimeMilli::MsecToSec(mTimer.GetFireTime() - now); + VerifyOrExit(mExpireTime > now); + validLifetime = TimeMilli::MsecToSec(mExpireTime - now); preferredLifetime = 0; break; @@ -2153,22 +2168,41 @@ exit: return; } +void RoutingManager::LocalOnLinkPrefix::AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + TimeMilli now = TimerMilli::GetNow(); + uint32_t validLifetime; + + VerifyOrExit((mOldPrefix.GetLength() != 0) && (mOldExpireTime > now)); + + validLifetime = TimeMilli::MsecToSec(mOldExpireTime - now); + SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mOldPrefix, validLifetime, 0)); + + LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=0)", mOldPrefix.ToString().AsCString(), validLifetime); + +exit: + return; +} + void RoutingManager::LocalOnLinkPrefix::HandleExtPanIdChange(void) { + // If the prefix is advertised or being deprecated we remember it + // as `mOldPrefix` and deprecate it. It will be included in + // emitted RAs as PIO with zero preferred lifetime. It will still + // be present in Network Data until its expire time so to allow + // Thread nodes to continue to communicate with `InfraIf` devices + // using addresses based on this prefix. + switch (mState) { case kIdle: break; case kAdvertising: - Get().UnpublishExternalRoute(mPrefix); - - // TODO: consider deprecating/invalidating the existing - // on-link prefix. - break; - case kDeprecating: - mTimer.Stop(); + mOldPrefix = mPrefix; + mOldExpireTime = mExpireTime; + mTimer.FireAtIfEarlier(mOldExpireTime); break; } @@ -2183,19 +2217,41 @@ void RoutingManager::LocalOnLinkPrefix::HandleTimer(Timer &aTimer) void RoutingManager::LocalOnLinkPrefix::HandleTimer(void) { - VerifyOrExit(mState == kDeprecating); + TimeMilli now = TimerMilli::GetNow(); - LogInfo("Local on-link prefix %s expired", mPrefix.ToString().AsCString()); - - if (!Get().mDiscoveredPrefixTable.ContainsOnLinkPrefix(mPrefix)) + if ((mState == kDeprecating) && (now >= mExpireTime)) { - Get().UnpublishExternalRoute(mPrefix); + LogInfo("Local on-link prefix %s expired", mPrefix.ToString().AsCString()); + Unpublish(mPrefix); + mState = kIdle; } - mState = kIdle; + if ((mOldPrefix.GetLength() != 0) && (now >= mOldExpireTime)) + { + LogInfo("Old local on-link prefix %s expired", mOldPrefix.ToString().AsCString()); + Unpublish(mOldPrefix); + mOldPrefix.Clear(); + } -exit: - return; + // Re-schedule the timer + + if (mState == kDeprecating) + { + mTimer.FireAt(mExpireTime); + } + + if (mOldPrefix.GetLength() != 0) + { + mTimer.FireAtIfEarlier(mOldExpireTime); + } +} + +void RoutingManager::LocalOnLinkPrefix::Unpublish(const Ip6::Prefix &aPrefix) +{ + if (!Get().mDiscoveredPrefixTable.ContainsOnLinkPrefix(aPrefix)) + { + Get().UnpublishExternalRoute(aPrefix); + } } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 41e480366..3bdfd5482 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -590,7 +590,7 @@ private: void Stop(void); Error Advertise(void); void Deprecate(void); - void AppendAsPioTo(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage); const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } bool IsAdvertising(void) const { return (mState == kAdvertising); } void HandleExtPanIdChange(void); @@ -603,11 +603,18 @@ private: kDeprecating, }; + void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void Unpublish(const Ip6::Prefix &aPrefix); + static void HandleTimer(Timer &aTimer); void HandleTimer(void); Ip6::Prefix mPrefix; State mState; + TimeMilli mExpireTime; + Ip6::Prefix mOldPrefix; + TimeMilli mOldExpireTime; TimerMilli mTimer; }; diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 6de01a1bf..b5e9f0046 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -78,10 +78,13 @@ enum ExpectedPio static Ip6::Address sInfraIfAddress; -bool sRsEmitted; // Indicates if an RS message was emitted by BR. -bool sRaValidated; // Indicates if an RA was emitted by BR and successfully validated. -ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`). -uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA. +bool sRsEmitted; // Indicates if an RS message was emitted by BR. +bool sRaValidated; // Indicates if an RA was emitted by BR and successfully validated. +ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`). +bool sExpectOldOnLinkPio; // Expect to see old local prefix PIO +uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA. +uint32_t sOldOnLinkLifetime; // Valid lifetime of the old local prefix PIO (when `sExpectOldOnLinkPio`). +Ip6::Prefix sOldOnLinkPrefix; // The old on-link PIO prefix in last processed RA (when `sExpectOldOnLinkPio`) static constexpr uint16_t kMaxRioPrefixes = 10; @@ -208,7 +211,6 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, Log(" Router Advertisement message"); LogRouterAdvert(packet); ValidateRouterAdvert(packet); - sRaValidated = true; break; default: @@ -257,6 +259,8 @@ void AdvanceTime(uint32_t aDuration) void ValidateRouterAdvert(const Icmp6Packet &aPacket) { Ip6::Nd::RouterAdvertMessage raMsg(aPacket); + bool sawExpectedPio = false; + bool sawExpecteOldPio = false; VerifyOrQuit(raMsg.IsValid()); @@ -273,22 +277,39 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(pio.IsValid()); pio.GetPrefix(prefix); - VerifyOrQuit(sExpectedPio != kNoPio, "Received RA contain an unexpected PIO"); - SuccessOrQuit(otBorderRoutingGetOnLinkPrefix(sInstance, &localOnLink)); - VerifyOrQuit(prefix == localOnLink); - if (sExpectedPio == kPioAdvertisingLocalOnLink) + if (prefix == localOnLink) { - VerifyOrQuit(pio.GetPreferredLifetime() > 0, "On link prefix is deprecated unexpectedly"); + switch (sExpectedPio) + { + case kNoPio: + break; + + case kPioAdvertisingLocalOnLink: + VerifyOrQuit(pio.GetPreferredLifetime() > 0, "On link prefix is deprecated unexpectedly"); + sOnLinkLifetime = pio.GetValidLifetime(); + sawExpectedPio = true; + break; + + case kPioDeprecatingLocalOnLink: + VerifyOrQuit(pio.GetPreferredLifetime() == 0, "On link prefix is not deprecated"); + sOnLinkLifetime = pio.GetValidLifetime(); + sawExpectedPio = true; + break; + } } else { - VerifyOrQuit(pio.GetPreferredLifetime() == 0, "On link prefix is not deprecated"); + VerifyOrQuit(pio.GetPreferredLifetime() == 0, "Old on link prefix is not deprecated"); + sOldOnLinkPrefix = prefix; + sOldOnLinkLifetime = pio.GetValidLifetime(); + + if (sExpectOldOnLinkPio) + { + sawExpecteOldPio = true; + } } - - sOnLinkLifetime = pio.GetValidLifetime(); - break; } @@ -316,6 +337,25 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(false, "Unexpected option type in RA msg"); } } + + if (!sRaValidated) + { + switch (sExpectedPio) + { + case kNoPio: + break; + case kPioAdvertisingLocalOnLink: + case kPioDeprecatingLocalOnLink: + VerifyOrQuit(sawExpectedPio, "Did not see on-link prefix PIO in the RA"); + } + + if (sExpectOldOnLinkPio) + { + VerifyOrQuit(sawExpecteOldPio, "Did not see old on-link prefix PIO in the RA"); + } + + sRaValidated = true; + } } void LogRouterAdvert(const Icmp6Packet &aPacket) @@ -702,6 +742,12 @@ void InitTest(void) AdvanceTime(10000); VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); + + // Reset all test flags + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kNoPio; + sExpectedRios.Clear(); } //--------------------------------------------------------------------------------------------------------------------- @@ -1435,6 +1481,246 @@ void TestDomainPrefixAsOmr(void) } #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE +void TestExtPanIdChange(void) +{ + static constexpr uint32_t kMaxRaTxInterval = 601; // In seconds + + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId2 = {{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99, 0x88}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + uint32_t oldPrefixLifetime; + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestExtPanIdChange"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + 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()); + Log("Local on-link prefix is being advertised, lifetime: %d", sOnLinkLifetime); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is + // being advertised. + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + Log("Changing ext PAN ID"); + + oldLocalOnLink = localOnLink; + oldPrefixLifetime = sOnLinkLifetime; + + sRaValidated = false; + sExpectOldOnLinkPio = true; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the received RA message and that it contains the + // old on-link prefix being deprecated. + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sOldOnLinkPrefix == oldLocalOnLink); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data to contain both the current and old + // local on-link prefixes. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for old local on-link prefix to expire. + + while (oldPrefixLifetime > kMaxRaTxInterval) + { + // Ensure Network Data entries remain as before. Mainly we still + // see the deprecating local on-link prefix. + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium)}); + + // Keep checking the emitted RAs and make sure the prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is deprecating, remaining lifetime:%d", sOldOnLinkLifetime); + VerifyOrQuit(sOldOnLinkLifetime < oldPrefixLifetime); + oldPrefixLifetime = sOldOnLinkLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The local on-link prefix must be expired now and should no + // longer be seen in the emitted RA message. + + sRaValidated = false; + sExpectOldOnLinkPio = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data now only contains the current local + // on-link prefix. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is being + // deprecated. + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with a new on-link (PIO) which is preferred over + // the local on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that the local on-link prefix is deprecated. + + sRaValidated = false; + sExpectOldOnLinkPio = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + oldLocalOnLink = localOnLink; + oldPrefixLifetime = sOnLinkLifetime; + + dataset.mExtendedPanId = kExtPanId2; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + 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 still being included + // as PIO in the emitted RA. + + sRaValidated = false; + sExpectOldOnLinkPio = true; + sExpectedPio = kNoPio; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that Network Data contains the old local on-link + // prefix along with entry from router A. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for old local on-link prefix to expire. + + while (oldPrefixLifetime > kMaxRaTxInterval) + { + // Send same RA from router A to keep its on-link prefix alive. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + // Ensure Network Data entries remain as before. Mainly we still + // see the deprecating old local on-link prefix. + + VerifyExternalRoutesInNetData({ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + // Keep checking the emitted RAs and make sure the prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is deprecating, remaining lifetime:%d", sOldOnLinkLifetime); + VerifyOrQuit(sOldOnLinkLifetime < oldPrefixLifetime); + oldPrefixLifetime = sOldOnLinkLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The old on-link prefix must be expired now and should no + // longer be seen in the emitted RA message. + + sRaValidated = false; + sExpectOldOnLinkPio = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data to now only contains entry from router A + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestExtPanIdChange"); + + testFreeInstance(sInstance); +} + #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE int main(void) @@ -1447,6 +1733,7 @@ int main(void) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE TestDomainPrefixAsOmr(); #endif + TestExtPanIdChange(); printf("All tests passed\n"); #else printf("BORDER_ROUTING feature is not enabled\n");