diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 65a2326ba..4e96a9c83 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (255) +#define OPENTHREAD_API_VERSION (256) /** * @addtogroup api-instance diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h index 2a4892baa..0b2f03576 100644 --- a/include/openthread/platform/settings.h +++ b/include/openthread/platform/settings.h @@ -72,6 +72,7 @@ enum OT_SETTINGS_KEY_SRP_CLIENT_INFO = 0x000c, ///< The SRP client info (selected SRP server address). OT_SETTINGS_KEY_SRP_SERVER_INFO = 0x000d, ///< The SRP server info (UDP port). OT_SETTINGS_KEY_BR_ULA_PREFIX = 0x000f, ///< BR ULA prefix. + OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES = 0x0010, ///< BR local on-link prefixes. // Deprecated and reserved key values: // diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 4b7a4978a..2babb4f45 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -92,7 +92,7 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix); #endif - mOnLinkPrefixManager.GenerateLocalPrefix(); + mOnLinkPrefixManager.Init(); error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning); @@ -2054,6 +2054,66 @@ RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance) mOldLocalPrefixes.Clear(); } +void RoutingManager::OnLinkPrefixManager::Init(void) +{ + TimeMilli now = TimerMilli::GetNow(); + Settings::BrOnLinkPrefix savedPrefix; + bool refreshStoredPrefixes = false; + + // Restore old prefixes from `Settings` + + for (int index = 0; Get().ReadBrOnLinkPrefix(index, savedPrefix) == kErrorNone; index++) + { + uint32_t lifetime; + OldPrefix *entry; + + if (mOldLocalPrefixes.ContainsMatching(savedPrefix.GetPrefix())) + { + // We should not see duplicate entries in `Settings` + // but if we do we refresh the stored prefixes to make + // it consistent. + refreshStoredPrefixes = true; + continue; + } + + entry = mOldLocalPrefixes.PushBack(); + + if (entry == nullptr) + { + // If there are more stored prefixes, we refresh the + // prefixes in `Settings` to remove the ones we cannot + // handle. + + refreshStoredPrefixes = true; + break; + } + + lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay)); + + entry->mPrefix = savedPrefix.GetPrefix(); + entry->mExpireTime = now + Time::SecToMsec(lifetime); + + LogInfo("Restored old prefix %s, lifetime:%u", entry->mPrefix.ToString().AsCString(), lifetime); + + mTimer.FireAtIfEarlier(entry->mExpireTime); + } + + if (refreshStoredPrefixes) + { + // We clear the entries in `Settings` and re-write the entries + // from `mOldLocalPrefixes` array. + + IgnoreError(Get().DeleteAllBrOnLinkPrefixes()); + + for (OldPrefix &oldPrefix : mOldLocalPrefixes) + { + SavePrefix(oldPrefix.mPrefix, oldPrefix.mExpireTime); + } + } + + GenerateLocalPrefix(); +} + void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void) { const MeshCoP::ExtendedPanId &extPanId = Get().GetExtPanId(); @@ -2291,6 +2351,7 @@ void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow) { mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); mTimer.FireAtIfEarlier(mExpireTime); + SavePrefix(mLocalPrefix, mExpireTime); } void RoutingManager::OnLinkPrefixManager::EnterAdvertisingState(void) @@ -2427,6 +2488,8 @@ void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix & VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix)); + LogInfo("Deprecating old on-link prefix %s", aPrefix.ToString().AsCString()); + if (!mOldLocalPrefixes.IsFull()) { entry = mOldLocalPrefixes.PushBack(); @@ -2447,12 +2510,16 @@ void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix & } removedPrefix = entry->mPrefix; + + IgnoreError(Get().RemoveBrOnLinkPrefix(removedPrefix)); } entry->mPrefix = aPrefix; entry->mExpireTime = aExpireTime; mTimer.FireAtIfEarlier(aExpireTime); + SavePrefix(aPrefix, aExpireTime); + Get().EvaluatePublishingPrefix(aPrefix); if (removedPrefix.GetLength() != 0) @@ -2464,6 +2531,15 @@ exit: return; } +void RoutingManager::OnLinkPrefixManager::SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime) +{ + Settings::BrOnLinkPrefix savedPrefix; + + savedPrefix.SetPrefix(aPrefix); + savedPrefix.SetLifetime(TimeMilli::MsecToSec(aExpireTime - TimerMilli::GetNow())); + IgnoreError(Get().AddOrUpdateBrOnLinkPrefix(savedPrefix)); +} + void RoutingManager::OnLinkPrefixManager::HandleTimer(void) { TimeMilli now = TimerMilli::GetNow(); @@ -2480,6 +2556,7 @@ void RoutingManager::OnLinkPrefixManager::HandleTimer(void) if (now >= mExpireTime) { LogInfo("Local on-link prefix %s expired", mLocalPrefix.ToString().AsCString()); + IgnoreError(Get().RemoveBrOnLinkPrefix(mLocalPrefix)); mState = kIdle; Get().EvaluatePublishingPrefix(mLocalPrefix); } @@ -2505,7 +2582,9 @@ void RoutingManager::OnLinkPrefixManager::HandleTimer(void) for (const Ip6::Prefix &prefix : expiredPrefixes) { LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString()); + IgnoreError(Get().RemoveBrOnLinkPrefix(prefix)); mOldLocalPrefixes.RemoveMatching(prefix); + Get().EvaluatePublishingPrefix(prefix); } diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index b1a455b8c..37f5d79dc 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -666,7 +666,7 @@ private: // Max number of old on-link prefixes to retain to deprecate. static constexpr uint16_t kMaxOldPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES; - void GenerateLocalPrefix(void); + void Init(void); void Start(void); void Stop(void); void Evaluate(void); @@ -697,6 +697,7 @@ private: TimeMilli mExpireTime; }; + void GenerateLocalPrefix(void); void PublishAndAdvertise(void); void Deprecate(void); void ResetExpireTime(TimeMilli aNow); @@ -704,6 +705,7 @@ private: void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); void AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage); void DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime); + void SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime); using ExpireTimer = TimerMilliIn; diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp index e88e19550..eae22ef92 100644 --- a/src/core/common/settings.cpp +++ b/src/core/common/settings.cpp @@ -156,6 +156,7 @@ const char *SettingsBase::KeyToString(Key aKey) "SrpServerInfo", // (13) kKeySrpServerInfo "", // (14) Removed (previously NAT64 prefix) "BrUlaPrefix", // (15) kKeyBrUlaPrefix + "BrOnLinkPrefixes" // (16) kKeyBrOnLinkPrefixes }; static_assert(1 == kKeyActiveDataset, "kKeyActiveDataset value is incorrect"); @@ -169,8 +170,9 @@ const char *SettingsBase::KeyToString(Key aKey) static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect"); static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect"); static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect"); + static_assert(16 == kKeyBrOnLinkPrefixes, "kKeyBrOnLinkPrefixes is incorrect"); - static_assert(kLastKey == kKeyBrUlaPrefix, "kLastKey is not valid"); + static_assert(kLastKey == kKeyBrOnLinkPrefixes, "kLastKey is not valid"); OT_ASSERT(aKey <= kLastKey); @@ -308,6 +310,83 @@ exit: } #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +Error Settings::AddOrUpdateBrOnLinkPrefix(const BrOnLinkPrefix &aBrOnLinkPrefix) +{ + Error error = kErrorNone; + int index = 0; + BrOnLinkPrefix brPrefix; + bool didUpdate = false; + + while (ReadBrOnLinkPrefix(index, brPrefix) == kErrorNone) + { + if (brPrefix.GetPrefix() == aBrOnLinkPrefix.GetPrefix()) + { + if (brPrefix.GetLifetime() == aBrOnLinkPrefix.GetLifetime()) + { + // Existing entry fully matches `aBrOnLinkPrefix`. + // No need to make any changes. + ExitNow(); + } + + SuccessOrExit(error = Get().Delete(kKeyBrOnLinkPrefixes, index)); + didUpdate = true; + break; + } + + index++; + } + + SuccessOrExit(error = Get().Add(kKeyBrOnLinkPrefixes, &aBrOnLinkPrefix, sizeof(BrOnLinkPrefix))); + brPrefix.Log(didUpdate ? "Updated" : "Added"); + +exit: + return error; +} + +Error Settings::RemoveBrOnLinkPrefix(const Ip6::Prefix &aPrefix) +{ + Error error = kErrorNotFound; + BrOnLinkPrefix brPrefix; + + for (int index = 0; ReadBrOnLinkPrefix(index, brPrefix) == kErrorNone; index++) + { + if (brPrefix.GetPrefix() == aPrefix) + { + SuccessOrExit(error = Get().Delete(kKeyBrOnLinkPrefixes, index)); + brPrefix.Log("Removed"); + break; + } + } + +exit: + return error; +} + +Error Settings::DeleteAllBrOnLinkPrefixes(void) +{ + return Get().Delete(kKeyBrOnLinkPrefixes); +} + +Error Settings::ReadBrOnLinkPrefix(int aIndex, BrOnLinkPrefix &aBrOnLinkPrefix) +{ + uint16_t length = sizeof(BrOnLinkPrefix); + + aBrOnLinkPrefix.Init(); + + return Get().Get(kKeyBrOnLinkPrefixes, aIndex, &aBrOnLinkPrefix, &length); +} + +void Settings::BrOnLinkPrefix::Log(const char *aActionText) const +{ + OT_UNUSED_VARIABLE(aActionText); + + LogInfo("%s %s entry {prefix:%s,lifetime:%u}", aActionText, KeyToString(kKeyBrOnLinkPrefixes), + GetPrefix().ToString().AsCString(), GetLifetime()); +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + Error Settings::ReadEntry(Key aKey, void *aValue, uint16_t aMaxLength) const { Error error; diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp index 7f57fed5e..0c20f5910 100644 --- a/src/core/common/settings.hpp +++ b/src/core/common/settings.hpp @@ -119,9 +119,11 @@ public: kKeySrpClientInfo = OT_SETTINGS_KEY_SRP_CLIENT_INFO, kKeySrpServerInfo = OT_SETTINGS_KEY_SRP_SERVER_INFO, kKeyBrUlaPrefix = OT_SETTINGS_KEY_BR_ULA_PREFIX, + kKeyBrOnLinkPrefixes = OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES, }; - static constexpr Key kLastKey = kKeyBrUlaPrefix; ///< The last (numerically) enumerator value in `Key`. + static constexpr Key kLastKey = kKeyBrOnLinkPrefixes; ///< The last (numerically) enumerator value in `Key`. + static_assert(static_cast(kLastKey) < static_cast(OT_SETTINGS_KEY_VENDOR_RESERVED_MIN), "Core settings keys overlap with vendor reserved keys"); @@ -583,7 +585,65 @@ public: private: BrUlaPrefix(void) = default; }; -#endif + + /** + * This class represents a BR on-link prefix entry for settings storage. + * + */ + OT_TOOL_PACKED_BEGIN + class BrOnLinkPrefix : public Clearable + { + friend class Settings; + + public: + static constexpr Key kKey = kKeyBrOnLinkPrefixes; ///< The associated key. + + /** + * This method initializes the `BrOnLinkPrefix` object. + * + */ + void Init(void) { Clear(); } + + /** + * This method gets the prefix. + * + * @returns The prefix. + * + */ + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + + /** + * This method set the prefix. + * + * @param[in] aPrefix The prefix. + * + */ + void SetPrefix(const Ip6::Prefix &aPrefix) { mPrefix = aPrefix; } + + /** + * This method gets the remaining prefix lifetime in seconds. + * + * @returns The prefix lifetime in seconds. + * + */ + uint32_t GetLifetime(void) const { return mLifetime; } + + /** + * This method sets the the prefix lifetime. + * + * @param[in] aLifetime The prefix lifetime in seconds. + * + */ + void SetLifetime(uint32_t aLifetime) { mLifetime = aLifetime; } + + private: + void Log(const char *aActionText) const; + + Ip6::Prefix mPrefix; + uint32_t mLifetime; + } OT_TOOL_PACKED_END; + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE /** @@ -1068,6 +1128,56 @@ public: }; #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + /** + * This method adds or updates an on-link prefix. + * + * If there is no matching entry (matching the same `GetPrefix()`) saved in `Settings`, the new entry will be added. + * If there is matching entry, it will be updated to the new @p aPrefix. + * + * @param[in] aBrOnLinkPrefix The on-link prefix to save (add or updated). + * + * @retval kErrorNone Successfully added or updated the entry in settings. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error AddOrUpdateBrOnLinkPrefix(const BrOnLinkPrefix &aBrOnLinkPrefix); + + /** + * This method removes an on-link prefix entry matching a given prefix. + * + * @param[in] aPrefix The prefix to remove + * + * @retval kErrorNone Successfully removed the matching entry in settings. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error RemoveBrOnLinkPrefix(const Ip6::Prefix &aPrefix); + + /** + * This method deletes all on-link prefix entries from the settings. + * + * @retval kErrorNone Successfully deleted the entries. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error DeleteAllBrOnLinkPrefixes(void); + + /** + * This method retrieves an entry from on-link prefixes list at a given index. + * + * @param[in] aIndex The index to read. + * @param[out] aBrOnLinkPrefix A reference to `BrOnLinkPrefix` to output the read value. + * + * @retval kErrorNone Successfully read the value. + * @retval kErrorNotFound No corresponding value in the setting store. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error ReadBrOnLinkPrefix(int aIndex, BrOnLinkPrefix &aBrOnLinkPrefix); + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + private: #if OPENTHREAD_FTD class ChildInfoIteratorBuilder : public InstanceLocator diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 5f26338dd..6c7392dde 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -37,8 +37,6 @@ enum FLASH_SWAP_NUM = 2, }; -static uint8_t sFlash[FLASH_SWAP_SIZE * FLASH_SWAP_NUM]; - ot::Instance *testInitInstance(void) { otInstance *instance = nullptr; @@ -362,9 +360,22 @@ OT_TOOL_WEAK void otPlatSettingsWipe(otInstance *) { } +uint8_t *GetFlash(void) +{ + static uint8_t sFlash[FLASH_SWAP_SIZE * FLASH_SWAP_NUM]; + static bool sInitialized; + + if (!sInitialized) + { + memset(sFlash, 0xff, sizeof(sFlash)); + sInitialized = true; + } + + return sFlash; +} + OT_TOOL_WEAK void otPlatFlashInit(otInstance *) { - memset(sFlash, 0xff, sizeof(sFlash)); } OT_TOOL_WEAK uint32_t otPlatFlashGetSwapSize(otInstance *) @@ -380,7 +391,7 @@ OT_TOOL_WEAK void otPlatFlashErase(otInstance *, uint8_t aSwapIndex) address = aSwapIndex ? FLASH_SWAP_SIZE : 0; - memset(sFlash + address, 0xff, FLASH_SWAP_SIZE); + memset(GetFlash() + address, 0xff, FLASH_SWAP_SIZE); } OT_TOOL_WEAK void otPlatFlashRead(otInstance *, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize) @@ -393,7 +404,7 @@ OT_TOOL_WEAK void otPlatFlashRead(otInstance *, uint8_t aSwapIndex, uint32_t aOf address = aSwapIndex ? FLASH_SWAP_SIZE : 0; - memcpy(aData, sFlash + address + aOffset, aSize); + memcpy(aData, GetFlash() + address + aOffset, aSize); } OT_TOOL_WEAK void otPlatFlashWrite(otInstance *, @@ -412,7 +423,7 @@ OT_TOOL_WEAK void otPlatFlashWrite(otInstance *, for (uint32_t index = 0; index < aSize; index++) { - sFlash[address + aOffset + index] &= ((uint8_t *)aData)[index]; + GetFlash()[address + aOffset + index] &= ((uint8_t *)aData)[index]; } } diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 838802a9b..a55b26f71 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -856,6 +856,14 @@ void InitTest(void) sRespondToNs = true; } +void FinalizeTest(void) +{ + SuccessOrQuit(otIp6SetEnabled(sInstance, false)); + SuccessOrQuit(otThreadSetEnabled(sInstance, false)); + SuccessOrQuit(otInstanceErasePersistentInfo(sInstance)); + testFreeInstance(sInstance); +} + //--------------------------------------------------------------------------------------------------------------------- void TestSamePrefixesFromMultipleRouters(void) @@ -1000,7 +1008,7 @@ void TestSamePrefixesFromMultipleRouters(void) Log("End of TestSamePrefixesFromMultipleRouters"); - testFreeInstance(sInstance); + FinalizeTest(); } void TestOmrSelection(void) @@ -1116,7 +1124,7 @@ void TestOmrSelection(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Log("End of TestOmrSelection"); - testFreeInstance(sInstance); + FinalizeTest(); } void TestDefaultRoute(void) @@ -1300,7 +1308,7 @@ void TestDefaultRoute(void) Log("End of TestDefaultRoute"); - testFreeInstance(sInstance); + FinalizeTest(); } void TestLocalOnLinkPrefixDeprecation(void) @@ -1437,7 +1445,7 @@ void TestLocalOnLinkPrefixDeprecation(void) Log("End of TestLocalOnLinkPrefixDeprecation"); - testFreeInstance(sInstance); + FinalizeTest(); } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE @@ -1583,7 +1591,7 @@ void TestDomainPrefixAsOmr(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Log("End of TestDomainPrefixAsOmr"); - testFreeInstance(sInstance); + FinalizeTest(); } #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE @@ -2104,7 +2112,7 @@ void TestExtPanIdChange(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Log("End of TestExtPanIdChange"); - testFreeInstance(sInstance); + FinalizeTest(); } void TestRouterNsProbe(void) @@ -2252,7 +2260,7 @@ void TestRouterNsProbe(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Log("End of TestRouterNsProbe"); - testFreeInstance(sInstance); + FinalizeTest(); } void TestConflictingPrefix(void) @@ -2478,9 +2486,168 @@ void TestConflictingPrefix(void) Log("End of TestConflictingPrefix"); - testFreeInstance(sInstance); + FinalizeTest(); } +#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE +void TestSavedOnLinkPrefixes(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"); + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSavedOnLinkPrefixes"); + + 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()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and on-link prefix. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Changing ext PAN ID"); + + oldLocalOnLink = localOnLink; + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + dataset.mActiveTimestamp.mSeconds++; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(30000); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable the instance and re-enable it. + + Log("Disabling and re-enabling OT Instance"); + + testFreeInstance(sInstance); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + AdvanceTime(100); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising an on-link prefix. + // This ensures the local on-link prefix is not advertised, but + // it must be deprecated since it was advertised last time and + // saved in `Settings`. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the saved prefixes are in netdata and being + // deprecated. + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for more than 1800 seconds to let the deprecating + // prefixes expire (keep sending RA from router A). + + for (uint16_t index = 0; index < 185; index++) + { + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + AdvanceTime(10 * 1000); + } + + VerifyExternalRoutesInNetData({ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable the instance and re-enable it and restart Routing Manager. + + Log("Disabling and re-enabling OT Instance again"); + + testFreeInstance(sInstance); + InitTest(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + AdvanceTime(100); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising an on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kNoPio; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 0); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that previously saved on-link prefixes are no longer + // seen in Network Data (indicating that they were removed from + // `Settings`). + + VerifyExternalRoutesInNetData({ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestSavedOnLinkPrefixes"); + FinalizeTest(); +} +#endif // OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE void TestAutoEnableOfSrpServer(void) { @@ -2684,7 +2851,7 @@ void TestAutoEnableOfSrpServer(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Log("End of TestAutoEnableOfSrpServer"); - testFreeInstance(sInstance); + FinalizeTest(); } #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE @@ -2703,6 +2870,9 @@ int main(void) TestExtPanIdChange(); TestConflictingPrefix(); TestRouterNsProbe(); +#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + TestSavedOnLinkPrefixes(); +#endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE TestAutoEnableOfSrpServer(); #endif