From ca8799a4b86a38a89ee339b399d24e0c095fd0d8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 27 Oct 2022 15:12:59 -0700 Subject: [PATCH] [routing-manager] persist deprecating old prefixes in `Settings` (#8330) This commit adds mechanism to save deprecating old on-link prefixes in non-volatile `Settings`. With this, if the BR is restarted, it will remember the old prefixes and keep deprecating them (advertising them in emitted RA and publish them in Thread Network Data). This commit also adds `TestSavedOnLinkPrefixes()` test case in `test_routing_manager` to cover the behavior of the newly added mechanism. --- include/openthread/instance.h | 2 +- include/openthread/platform/settings.h | 1 + src/core/border_router/routing_manager.cpp | 81 ++++++++- src/core/border_router/routing_manager.hpp | 4 +- src/core/common/settings.cpp | 81 ++++++++- src/core/common/settings.hpp | 114 ++++++++++++- tests/unit/test_platform.cpp | 23 ++- tests/unit/test_routing_manager.cpp | 188 ++++++++++++++++++++- 8 files changed, 473 insertions(+), 21 deletions(-) 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