[routing-manager] new mechanism for deprecating old on-link prefixes (#8315)

This commit updates how `OnLinkPrefixManager` retains and deprecates
old local on-link prefixes. Instead of remembering a single old
prefix, a list of old prefixes are remembered and the number of
entries in the list can be configured at build-time using an OT
config.

If the BR is stopped, the deprecating prefixes are still remembered
and the the expire timer keeps running. They are removed from the
list when they expire. If the BR is started again the deprecating old
prefixes are again included as PIO in the emitted RA and also
published in Network Data.

The changes in this commit also ensure we can correctly handle the
situation where the extended PAN ID is changed to a previous value
before the corresponding on-link prefix expires, i.e. when a
currently deprecating prefix becomes the current local on-link prefix
again.

This commit also updates the unit test case validating the behavior of
newly added mechanism under different situations.
This commit is contained in:
Abtin Keshavarzian
2022-10-21 21:27:35 -07:00
committed by GitHub
parent 8c73c9747b
commit ed43da9110
4 changed files with 582 additions and 197 deletions
+174 -99
View File
@@ -361,11 +361,6 @@ void RoutingManager::HandleNotifierEvents(Events aEvents)
if (aEvents.Contains(kEventThreadExtPanIdChanged))
{
mOnLinkPrefixManager.HandleExtPanIdChange();
if (mIsRunning)
{
ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
}
}
exit:
@@ -480,6 +475,8 @@ void RoutingManager::EvaluatePublishingPrefix(const Ip6::Prefix &aPrefix)
routeConfig.mPreference = NetworkData::kRoutePreferenceLow;
routeConfig.mStable = true;
VerifyOrExit(mIsRunning);
// The order of checks is important. The Discovered Prefix Table is
// first followed by Local On Link Prefix manager and finally NAT64
// prefix manager.
@@ -507,6 +504,9 @@ void RoutingManager::EvaluatePublishingPrefix(const Ip6::Prefix &aPrefix)
{
UnpublishExternalRoute(aPrefix);
}
exit:
return;
}
void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix)
@@ -604,14 +604,16 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
{
// RA message max length is derived to accommodate:
//
// - The RA header,
// - At most two PIOs (for current and old local on-link prefixes),
// - The RA header.
// - One PIO for current local on-link prefix.
// - At most `kMaxOldPrefixes` for old deprecating 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) * 2 +
sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) +
sizeof(Ip6::Nd::PrefixInfoOption) * OnLinkPrefixManager::kMaxOldPrefixes +
2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix));
uint8_t buffer[kMaxRaLength];
@@ -1915,60 +1917,73 @@ RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance)
{
mLocalPrefix.Clear();
mFavoredDiscoveredPrefix.Clear();
mOldLocalPrefix.Clear();
mOldLocalPrefixes.Clear();
}
void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void)
{
MeshCoP::ExtendedPanId extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
const MeshCoP::ExtendedPanId &extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
OldPrefix * entry;
// Global ID: 40 most significant bits of Extended PAN ID
// Subnet ID: 16 least significant bits of Extended PAN ID
mLocalPrefix.mPrefix.mFields.m8[0] = 0xfd;
// Global ID: 40 most significant bits of Extended PAN ID
memcpy(mLocalPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5);
// Subnet ID: 16 least significant bits of Extended PAN ID
memcpy(mLocalPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2);
mLocalPrefix.SetLength(kOnLinkPrefixLength);
LogNote("Local on-link prefix: %s", mLocalPrefix.ToString().AsCString());
// Check if the new local prefix happens to be in `mOldLocalPrefixes` array.
// If so, we remove it from the array and set `mState` accordingly.
entry = mOldLocalPrefixes.FindMatching(mLocalPrefix);
if (entry != nullptr)
{
mState = kDeprecating;
mExpireTime = entry->mExpireTime;
mOldLocalPrefixes.Remove(*entry);
}
else
{
mState = kIdle;
}
}
void RoutingManager::OnLinkPrefixManager::Start(void)
{
mState = kIdle;
Get<RoutingManager>().EvaluatePublishingPrefix(mLocalPrefix);
for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
{
Get<RoutingManager>().EvaluatePublishingPrefix(oldPrefix.mPrefix);
}
}
void RoutingManager::OnLinkPrefixManager::Stop(void)
{
mTimer.Stop();
mFavoredDiscoveredPrefix.Clear();
if (mOldLocalPrefix.GetLength() != 0)
for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
{
Get<RoutingManager>().UnpublishExternalRoute(mOldLocalPrefix);
mOldLocalPrefix.Clear();
Get<RoutingManager>().UnpublishExternalRoute(oldPrefix.mPrefix);
}
VerifyOrExit(mState != kIdle);
Get<RoutingManager>().UnpublishExternalRoute(mLocalPrefix);
if (mState == kPublishing)
switch (mState)
{
// If we are waiting for prefix to be added in Network Data
// and not yet advertised it in any RA, there is no need to
// deprecate it and we can directly go to `kIdle` state.
mState = kIdle;
ExitNow();
case kIdle:
break;
case kPublishing:
case kAdvertising:
case kDeprecating:
Get<RoutingManager>().UnpublishExternalRoute(mLocalPrefix);
mState = kDeprecating;
break;
}
mState = kDeprecating;
// Start deprecating the local on-link prefix to send a PIO
// with zero preferred lifetime in the next call to
// `SendRouterAdvertisement()`.
exit:
return;
}
void RoutingManager::OnLinkPrefixManager::Evaluate(void)
@@ -2069,6 +2084,7 @@ void RoutingManager::OnLinkPrefixManager::PublishAndAdvertise(void)
}
mState = kPublishing;
ResetExpireTime(TimerMilli::GetNow());
LogInfo("Publishing local on-link prefix %s in netdata", mLocalPrefix.ToString().AsCString());
Get<RoutingManager>().EvaluatePublishingPrefix(mLocalPrefix);
@@ -2099,24 +2115,15 @@ void RoutingManager::OnLinkPrefixManager::Deprecate(void)
switch (mState)
{
case kPublishing:
mState = kIdle;
Get<RoutingManager>().EvaluatePublishingPrefix(mLocalPrefix);
mState = kIdle;
break;
case kAdvertising:
mState = kDeprecating;
mTimer.FireAtIfEarlier(mExpireTime);
LogInfo("Deprecate local on-link prefix %s", mLocalPrefix.ToString().AsCString());
break;
case kIdle:
case kDeprecating:
ExitNow();
break;
}
exit:
return;
}
bool RoutingManager::OnLinkPrefixManager::ShouldPublish(NetworkData::ExternalRouteConfig &aRouteConfig) const
@@ -2137,7 +2144,7 @@ bool RoutingManager::OnLinkPrefixManager::ShouldPublish(NetworkData::ExternalRou
break;
}
}
else if ((mOldLocalPrefix.GetLength() != 0) && (aRouteConfig.GetPrefix() == mOldLocalPrefix))
else if (mOldLocalPrefixes.ContainsMatching(aRouteConfig.GetPrefix()))
{
shouldPublish = true;
aRouteConfig.mPreference = NetworkData::kRoutePreferenceMedium;
@@ -2146,11 +2153,15 @@ bool RoutingManager::OnLinkPrefixManager::ShouldPublish(NetworkData::ExternalRou
return shouldPublish;
}
void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow)
{
mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
mTimer.FireAtIfEarlier(mExpireTime);
}
void RoutingManager::OnLinkPrefixManager::EnterAdvertisingState(void)
{
mState = kAdvertising;
mExpireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
mState = kAdvertising;
LogInfo("Start advertising local on-link prefix %s", mLocalPrefix.ToString().AsCString());
}
@@ -2162,7 +2173,7 @@ bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const
void RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage)
{
AppendCurPrefix(aRaMessage);
AppendOldPrefix(aRaMessage);
AppendOldPrefixes(aRaMessage);
}
void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage)
@@ -2181,7 +2192,7 @@ void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertM
switch (mState)
{
case kAdvertising:
mExpireTime = now + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
ResetExpireTime(now);
break;
case kDeprecating:
@@ -2204,21 +2215,24 @@ exit:
return;
}
void RoutingManager::OnLinkPrefixManager::AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage)
void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage)
{
TimeMilli now = TimerMilli::GetNow();
uint32_t validLifetime;
VerifyOrExit((mOldLocalPrefix.GetLength() != 0) && (mOldExpireTime > now));
for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
{
if (oldPrefix.mExpireTime < now)
{
continue;
}
validLifetime = TimeMilli::MsecToSec(mOldExpireTime - now);
SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mOldLocalPrefix, validLifetime, 0));
validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=0)", mOldLocalPrefix.ToString().AsCString(),
validLifetime);
exit:
return;
LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=0)", oldPrefix.mPrefix.ToString().AsCString(),
validLifetime);
}
}
void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
@@ -2237,72 +2251,133 @@ exit:
void RoutingManager::OnLinkPrefixManager::HandleExtPanIdChange(void)
{
// If the prefix is advertised or being deprecated we remember it
// as `mOldLocalPrefix` 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.
// If the current local prefix is being advertised or deprecated,
// we save it in `mOldLocalPrefixes` and keep deprecating 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`
// device using addresses based on this prefix.
switch (mState)
uint16_t oldState = mState;
Ip6::Prefix oldPrefix = mLocalPrefix;
GenerateLocalPrefix();
switch (oldState)
{
case kIdle:
break;
case kPublishing:
Get<RoutingManager>().UnpublishExternalRoute(mLocalPrefix);
Get<RoutingManager>().EvaluatePublishingPrefix(oldPrefix);
break;
case kAdvertising:
case kDeprecating:
if (mOldLocalPrefix.GetLength() != 0)
{
Ip6::Prefix prevPrefix = mOldLocalPrefix;
mOldLocalPrefix.Clear();
Get<RoutingManager>().EvaluatePublishingPrefix(prevPrefix);
}
mOldLocalPrefix = mLocalPrefix;
mOldExpireTime = mExpireTime;
mTimer.FireAtIfEarlier(mOldExpireTime);
DeprecateOldPrefix(oldPrefix, mExpireTime);
break;
}
mState = kIdle;
GenerateLocalPrefix();
if (Get<RoutingManager>().mIsRunning)
{
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
}
}
void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
{
OldPrefix * entry = nullptr;
Ip6::Prefix removedPrefix;
removedPrefix.Clear();
VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix));
if (!mOldLocalPrefixes.IsFull())
{
entry = mOldLocalPrefixes.PushBack();
}
else
{
// If there is no more room in `mOldLocalPrefixes` array
// we evict the entry with the earliest expiration time.
entry = &mOldLocalPrefixes[0];
for (OldPrefix &oldPrefix : mOldLocalPrefixes)
{
if ((oldPrefix.mExpireTime < entry->mExpireTime))
{
entry = &oldPrefix;
}
}
removedPrefix = entry->mPrefix;
}
entry->mPrefix = aPrefix;
entry->mExpireTime = aExpireTime;
mTimer.FireAtIfEarlier(aExpireTime);
Get<RoutingManager>().EvaluatePublishingPrefix(aPrefix);
if (removedPrefix.GetLength() != 0)
{
Get<RoutingManager>().EvaluatePublishingPrefix(removedPrefix);
}
exit:
return;
}
void RoutingManager::OnLinkPrefixManager::HandleTimer(void)
{
TimeMilli now = TimerMilli::GetNow();
TimeMilli now = TimerMilli::GetNow();
TimeMilli nextExpireTime = now.GetDistantFuture();
Array<Ip6::Prefix, kMaxOldPrefixes> expiredPrefixes;
if ((mState == kDeprecating) && (now >= mExpireTime))
switch (mState)
{
LogInfo("Local on-link prefix %s expired", mLocalPrefix.ToString().AsCString());
mState = kIdle;
Get<RoutingManager>().EvaluatePublishingPrefix(mLocalPrefix);
case kIdle:
break;
case kPublishing:
case kAdvertising:
case kDeprecating:
if (now >= mExpireTime)
{
LogInfo("Local on-link prefix %s expired", mLocalPrefix.ToString().AsCString());
mState = kIdle;
Get<RoutingManager>().EvaluatePublishingPrefix(mLocalPrefix);
}
else
{
nextExpireTime = mExpireTime;
}
break;
}
if ((mOldLocalPrefix.GetLength() != 0) && (now >= mOldExpireTime))
for (OldPrefix &entry : mOldLocalPrefixes)
{
Ip6::Prefix oldPrefix = mOldLocalPrefix;
LogInfo("Old local on-link prefix %s expired", mOldLocalPrefix.ToString().AsCString());
mOldLocalPrefix.Clear();
Get<RoutingManager>().EvaluatePublishingPrefix(oldPrefix);
if (now >= entry.mExpireTime)
{
SuccessOrAssert(expiredPrefixes.PushBack(entry.mPrefix));
}
else
{
nextExpireTime = Min(nextExpireTime, entry.mExpireTime);
}
}
// Re-schedule the timer
if (mState == kDeprecating)
for (const Ip6::Prefix &prefix : expiredPrefixes)
{
mTimer.FireAt(mExpireTime);
LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString());
mOldLocalPrefixes.RemoveMatching(prefix);
Get<RoutingManager>().EvaluatePublishingPrefix(prefix);
}
if (mOldLocalPrefix.GetLength() != 0)
if (nextExpireTime != now.GetDistantFuture())
{
mTimer.FireAtIfEarlier(mOldExpireTime);
mTimer.FireAtIfEarlier(nextExpireTime);
}
}
+25 -12
View File
@@ -93,12 +93,13 @@ public:
*
* The number of published entries accounts for:
* - Max number of discovered prefix entries,
* - Two entries for local on-link prefixes (current prefix and old one deprecating on extended PAN ID change),
* - One entry for local on-link prefixes,
* - Max number of old (deprecating) local on-link prefixes,
* - One entry for NAT64 published prefix.
*
*/
static constexpr uint16_t kMaxPublishedPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 3;
static constexpr uint16_t kMaxPublishedPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 1 +
OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES + 1;
/**
* This constructor initializes the routing manager.
*
@@ -640,6 +641,9 @@ private:
public:
explicit OnLinkPrefixManager(Instance &aInstance);
// 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 Start(void);
void Stop(void);
@@ -655,7 +659,7 @@ private:
void HandleTimer(void);
private:
enum State : uint8_t
enum State : uint8_t // State of `mLocalPrefix`
{
kIdle,
kPublishing,
@@ -663,21 +667,30 @@ private:
kDeprecating,
};
struct OldPrefix
{
bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; }
Ip6::Prefix mPrefix;
TimeMilli mExpireTime;
};
void PublishAndAdvertise(void);
void Deprecate(void);
void ResetExpireTime(TimeMilli aNow);
void EnterAdvertisingState(void);
void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage);
void AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage);
void AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage);
void DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime);
using ExpireTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleOnLinkPrefixManagerTimer>;
Ip6::Prefix mLocalPrefix;
Ip6::Prefix mFavoredDiscoveredPrefix;
State mState;
TimeMilli mExpireTime;
Ip6::Prefix mOldLocalPrefix;
TimeMilli mOldExpireTime;
ExpireTimer mTimer;
Ip6::Prefix mLocalPrefix;
State mState;
TimeMilli mExpireTime;
Ip6::Prefix mFavoredDiscoveredPrefix;
Array<OldPrefix, kMaxOldPrefixes> mOldLocalPrefixes;
ExpireTimer mTimer;
};
typedef Ip6::Prefix OnMeshPrefix;
+10 -1
View File
@@ -68,7 +68,7 @@
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES
*
* Specified maximum number of on-mesh prefixes (discovered from Thread Network Data) that are included as Route Info
* Specifies maximum number of on-mesh prefixes (discovered from Thread Network Data) that are included as Route Info
* Option in emitted Router Advertisement messages.
*
*/
@@ -76,4 +76,13 @@
#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES 16
#endif
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES
*
* Specifies maximum number of old local on-link prefixes (being deprecated) maintained by routing manager.
*
*/
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES
#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES 3
#endif
#endif // CONFIG_BORDER_ROUTING_H_
+373 -85
View File
@@ -46,8 +46,8 @@
using namespace ot;
// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
#define Log(...) \
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \
#define Log(...) \
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 3600000), (sNow / 60000) % 60, \
(sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
static constexpr uint32_t kInfraIfIndex = 1;
@@ -56,7 +56,8 @@ static const char kInfraIfAddress[] = "fe80::1";
static constexpr uint32_t kValidLitime = 2000;
static constexpr uint32_t kPreferredLifetime = 1800;
static constexpr uint16_t kMaxRaSize = 800;
static constexpr uint16_t kMaxRaSize = 800;
static constexpr uint16_t kMaxDeprecatingPrefixes = 16;
static ot::Instance *sInstance;
@@ -77,15 +78,31 @@ enum ExpectedPio
kPioDeprecatingLocalOnLink, // Expect to see local on-link prefix deprecated (zero preferred lifetime).
};
struct DeprecatingPrefix
{
DeprecatingPrefix(void) = default;
DeprecatingPrefix(const Ip6::Prefix &aPrefix, uint32_t aLifetime)
: mPrefix(aPrefix)
, mLifetime(aLifetime)
{
}
bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; }
Ip6::Prefix mPrefix; // Old on-link prefix being deprecated.
uint32_t mLifetime; // Valid lifetime of prefix from PIO.
};
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`).
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`)
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.
// Array containing deprecating prefixes from PIOs in the last processed RA.
Array<DeprecatingPrefix, kMaxDeprecatingPrefixes> sDeprecatingPrefixes;
static constexpr uint16_t kMaxRioPrefixes = 10;
@@ -139,6 +156,22 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket);
const char *PreferenceToString(int8_t aPreference);
void SendRouterAdvert(const Ip6::Address &aAddress, const Icmp6Packet &aPacket);
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
va_list args;
printf(" ");
va_start(args, aFormat);
vprintf(aFormat, args);
va_end(args);
printf("\n");
}
#endif
//----------------------------------------------------------------------------------------------------------------------
// `otPlatRadio
@@ -260,11 +293,12 @@ void AdvanceTime(uint32_t aDuration)
void ValidateRouterAdvert(const Icmp6Packet &aPacket)
{
Ip6::Nd::RouterAdvertMessage raMsg(aPacket);
bool sawExpectedPio = false;
bool sawExpecteOldPio = false;
bool sawExpectedPio = false;
VerifyOrQuit(raMsg.IsValid());
sDeprecatingPrefixes.Clear();
for (const Ip6::Nd::Option &option : raMsg)
{
switch (option.GetType())
@@ -288,28 +322,26 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket)
break;
case kPioAdvertisingLocalOnLink:
VerifyOrQuit(pio.GetPreferredLifetime() > 0, "On link prefix is deprecated unexpectedly");
sOnLinkLifetime = pio.GetValidLifetime();
sawExpectedPio = true;
if (pio.GetPreferredLifetime() > 0)
{
sOnLinkLifetime = pio.GetValidLifetime();
sawExpectedPio = true;
}
break;
case kPioDeprecatingLocalOnLink:
VerifyOrQuit(pio.GetPreferredLifetime() == 0, "On link prefix is not deprecated");
sOnLinkLifetime = pio.GetValidLifetime();
sawExpectedPio = true;
if (pio.GetPreferredLifetime() == 0)
{
sOnLinkLifetime = pio.GetValidLifetime();
sawExpectedPio = true;
}
break;
}
}
else if (sExpectOldOnLinkPio)
else
{
VerifyOrQuit(pio.GetPreferredLifetime() == 0, "Old on link prefix is not deprecated");
sOldOnLinkPrefix = prefix;
sOldOnLinkLifetime = pio.GetValidLifetime();
if (sExpectOldOnLinkPio)
{
sawExpecteOldPio = true;
}
SuccessOrQuit(sDeprecatingPrefixes.PushBack(DeprecatingPrefix(prefix, pio.GetValidLifetime())));
}
break;
}
@@ -354,11 +386,6 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket)
break;
}
if (sExpectOldOnLinkPio)
{
VerifyOrExit(sawExpecteOldPio);
}
sRaValidated = true;
}
@@ -477,6 +504,15 @@ void VerifyOmrPrefixInNetData(const Ip6::Prefix &aOmrPrefix, bool aDefaultRoute
VerifyOrQuit(otNetDataGetNextOnMeshPrefix(sInstance, &iterator, &prefixConfig) == kErrorNotFound);
}
void VerifyNoOmrPrefixInNetData(void)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
NetworkData::OnMeshPrefixConfig prefixConfig;
Log("VerifyNoOmrPrefixInNetData()");
VerifyOrQuit(otNetDataGetNextOnMeshPrefix(sInstance, &iterator, &prefixConfig) != kErrorNone);
}
using NetworkData::RoutePreference;
struct ExternalRoute
@@ -526,6 +562,15 @@ template <uint16_t kLength> void VerifyExternalRoutesInNetData(const ExternalRou
VerifyOrQuit(counter == kLength);
}
void VerifyNoExternalRouteInNetData(void)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
NetworkData::ExternalRouteConfig routeConfig;
Log("VerifyNoExternalRouteInNetData()");
VerifyOrQuit(otNetDataGetNextRoute(sInstance, &iterator, &routeConfig) != kErrorNone);
}
struct Pio
{
Pio(const Ip6::Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime)
@@ -1495,7 +1540,9 @@ void TestExtPanIdChange(void)
static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}};
static const otExtendedPanId kExtPanId2 = {{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99, 0x88}};
static const otExtendedPanId kExtPanId3 = {{0x12, 0x34, 056, 0x78, 0x9a, 0xab, 0xcd, 0xef}};
static const otExtendedPanId kExtPanId3 = {{0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xcd, 0xef}};
static const otExtendedPanId kExtPanId4 = {{0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00}};
static const otExtendedPanId kExtPanId5 = {{0x77, 0x88, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55}};
Ip6::Prefix localOnLink;
Ip6::Prefix oldLocalOnLink;
@@ -1503,6 +1550,7 @@ void TestExtPanIdChange(void)
Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64);
Ip6::Address routerAddressA = AddressFromString("fd00::aaaa");
uint32_t oldPrefixLifetime;
Ip6::Prefix oldPrefixes[4];
otOperationalDataset dataset;
Log("--------------------------------------------------------------------------------------------");
@@ -1546,9 +1594,8 @@ void TestExtPanIdChange(void)
oldLocalOnLink = localOnLink;
oldPrefixLifetime = sOnLinkLifetime;
sRaValidated = false;
sExpectOldOnLinkPio = true;
sExpectedPio = kPioAdvertisingLocalOnLink;
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
SuccessOrQuit(otDatasetGetActive(sInstance, &dataset));
@@ -1569,7 +1616,9 @@ void TestExtPanIdChange(void)
AdvanceTime(30000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sOldOnLinkPrefix == oldLocalOnLink);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink);
oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate the Network Data to contain both the current and old
@@ -1580,46 +1629,47 @@ void TestExtPanIdChange(void)
ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Change the extended PAN ID again.
// Stop BR and validate that a final RA is emitted deprecating
// both current local on-link prefix and old prefix.
Log("Changing ext PAN ID again");
sRaValidated = false;
sExpectedPio = kPioDeprecatingLocalOnLink;
oldLocalOnLink = localOnLink;
oldPrefixLifetime = sOnLinkLifetime;
sRaValidated = false;
sExpectOldOnLinkPio = true;
sExpectedPio = kPioAdvertisingLocalOnLink;
dataset.mExtendedPanId = kExtPanId3;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
AdvanceTime(500);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().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);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(false));
AdvanceTime(100);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sOldOnLinkPrefix == oldLocalOnLink);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink);
oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime;
sRaValidated = false;
AdvanceTime(350000);
VerifyOrQuit(!sRaValidated);
VerifyNoOmrPrefixInNetData();
VerifyNoExternalRouteInNetData();
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate the Network Data to contain both the current and old
// local on-link prefixes and the previous old prefix is now removed.
// Start BR again and validate old prefix will continue to
// be deprecated.
VerifyOmrPrefixInNetData(localOmr);
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium)});
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(true));
AdvanceTime(300000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink);
VerifyOrQuit(oldPrefixLifetime > sDeprecatingPrefixes[0].mLifetime);
oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wait for old local on-link prefix to expire.
while (oldPrefixLifetime > kMaxRaTxInterval)
while (oldPrefixLifetime > 2 * kMaxRaTxInterval)
{
// Ensure Network Data entries remain as before. Mainly we still
// see the deprecating local on-link prefix.
@@ -1635,21 +1685,22 @@ void TestExtPanIdChange(void)
AdvanceTime(kMaxRaTxInterval * 1000);
VerifyOrQuit(sRaValidated);
Log("Old on-link prefix is deprecating, remaining lifetime:%d", sOldOnLinkLifetime);
VerifyOrQuit(sOldOnLinkLifetime < oldPrefixLifetime);
oldPrefixLifetime = sOldOnLinkLifetime;
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
Log("Old on-link prefix is deprecating, remaining lifetime:%d", sDeprecatingPrefixes[0].mLifetime);
VerifyOrQuit(sDeprecatingPrefixes[0].mLifetime < oldPrefixLifetime);
oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The local on-link prefix must be expired now and should no
// longer be seen in the emitted RA message.
sRaValidated = false;
sExpectOldOnLinkPio = false;
sRaValidated = false;
AdvanceTime(kMaxRaTxInterval * 1000);
AdvanceTime(2 * kMaxRaTxInterval * 1000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.IsEmpty());
Log("Old on-link prefix is now expired");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1672,9 +1723,8 @@ void TestExtPanIdChange(void)
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate that the local on-link prefix is deprecated.
sRaValidated = false;
sExpectOldOnLinkPio = false;
sExpectedPio = kPioDeprecatingLocalOnLink;
sRaValidated = false;
sExpectedPio = kPioDeprecatingLocalOnLink;
AdvanceTime(30000);
@@ -1698,13 +1748,14 @@ void TestExtPanIdChange(void)
// Validate that the old local on-link prefix is still being included
// as PIO in the emitted RA.
sRaValidated = false;
sExpectOldOnLinkPio = true;
sExpectedPio = kNoPio;
sRaValidated = false;
sExpectedPio = kNoPio;
AdvanceTime(30000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate that Network Data contains the old local on-link
@@ -1717,7 +1768,7 @@ void TestExtPanIdChange(void)
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wait for old local on-link prefix to expire.
while (oldPrefixLifetime > kMaxRaTxInterval)
while (oldPrefixLifetime > 2 * kMaxRaTxInterval)
{
// Send same RA from router A to keep its on-link prefix alive.
@@ -1737,29 +1788,266 @@ void TestExtPanIdChange(void)
AdvanceTime(kMaxRaTxInterval * 1000);
VerifyOrQuit(sRaValidated);
Log("Old on-link prefix is deprecating, remaining lifetime:%d", sOldOnLinkLifetime);
VerifyOrQuit(sOldOnLinkLifetime < oldPrefixLifetime);
oldPrefixLifetime = sOldOnLinkLifetime;
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
Log("Old on-link prefix is deprecating, remaining lifetime:%d", sDeprecatingPrefixes[0].mLifetime);
VerifyOrQuit(sDeprecatingPrefixes[0].mLifetime < oldPrefixLifetime);
oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The old on-link prefix must be expired now and should no
// longer be seen in the emitted RA message.
sRaValidated = false;
sExpectOldOnLinkPio = false;
SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)});
sRaValidated = false;
AdvanceTime(kMaxRaTxInterval * 1000);
AdvanceTime(2 * kMaxRaTxInterval * 1000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.IsEmpty());
Log("Old on-link prefix is now expired");
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate the Network Data to now only contains entry from router A
// Validate the Network Data to now only contains entry from router A.
VerifyOmrPrefixInNetData(localOmr);
VerifyExternalRoutesInNetData({ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)});
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// Check behavior when ext PAN ID changes while the local on-link is not
// advertised.
Log("Changing ext PAN ID again");
oldLocalOnLink = localOnLink;
sRaValidated = false;
sExpectedPio = kNoPio;
dataset.mExtendedPanId = kExtPanId3;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
AdvanceTime(500);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(),
oldLocalOnLink.ToString().AsCString());
AdvanceTime(35000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.IsEmpty());
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate the Network Data to now only contains entry from router A.
VerifyOmrPrefixInNetData(localOmr);
VerifyExternalRoutesInNetData({ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Remove the on-link prefix PIO being advertised by router A
// and ensure local on-link prefix is advertised again.
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, 0)});
AdvanceTime(300000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.IsEmpty());
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wait for longer than valid lifetime of PIO entry from router A.
// Validate that it is unpublished from network data.
AdvanceTime(2000 * 1000);
VerifyOmrPrefixInNetData(localOmr);
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)});
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// Multiple PAN ID changes and multiple deprecating old prefixes.
oldPrefixes[0] = localOnLink;
dataset.mExtendedPanId = kExtPanId2;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
AdvanceTime(30000);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0]));
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[0], NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Change the prefix again. We should see two deprecating prefixes.
oldPrefixes[1] = localOnLink;
dataset.mExtendedPanId = kExtPanId1;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
AdvanceTime(30000);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 2);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1]));
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[0], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[1], NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wait for 15 minutes and then change ext PAN ID again.
// Now we should see three deprecating prefixes.
AdvanceTime(15 * 60 * 1000);
oldPrefixes[2] = localOnLink;
dataset.mExtendedPanId = kExtPanId4;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
AdvanceTime(30000);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[2]));
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[0], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[1], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[2], NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Change ext PAN ID back to previous value of `kExtPanId1`.
// We should still see three deprecating prefixes and the last prefix
// at `oldPrefixes[2]` should again be treated as local on-link prefix.
oldPrefixes[3] = localOnLink;
dataset.mExtendedPanId = kExtPanId1;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
AdvanceTime(30000);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1]));
VerifyOrQuit(oldPrefixes[2] == localOnLink);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[3]));
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[0], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[1], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[3], NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Stop BR and validate the final emitted RA to contain
// all deprecating prefixes.
sRaValidated = false;
sExpectedPio = kPioDeprecatingLocalOnLink;
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(false));
AdvanceTime(100);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[3]));
VerifyNoOmrPrefixInNetData();
VerifyNoExternalRouteInNetData();
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wait for 15 minutes while BR stays disabled and validate
// there are no emitted RAs. We want to check that deprecating
// prefixes continue to expire while BR is stopped.
sRaValidated = false;
AdvanceTime(15 * 60 * 1000);
VerifyOrQuit(!sRaValidated);
VerifyNoOmrPrefixInNetData();
VerifyNoExternalRouteInNetData();
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start BR again, and check that we only see the last deprecating prefix
// at `oldPrefixes[3]` in emitted RA and the other two are expired and
// no longer included as PIO and/or in network data.
sRaValidated = false;
sExpectedPio = kPioAdvertisingLocalOnLink;
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(true));
AdvanceTime(30000);
VerifyOrQuit(sRaValidated);
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[3]));
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[3], NetworkData::kRoutePreferenceMedium)});
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// Validate the oldest prefix is removed when we have too many
// back-to-back PAN ID changes.
// Remember the oldest deprecating prefix (associated with `kExtPanId4`).
oldLocalOnLink = oldPrefixes[3];
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(oldPrefixes[0]));
dataset.mExtendedPanId = kExtPanId2;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
AdvanceTime(30000);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(oldPrefixes[1]));
dataset.mExtendedPanId = kExtPanId3;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
AdvanceTime(30000);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(oldPrefixes[2]));
dataset.mExtendedPanId = kExtPanId5;
SuccessOrQuit(otDatasetSetActive(sInstance, &dataset));
sRaValidated = false;
AdvanceTime(30000);
VerifyOrQuit(sRaValidated);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3);
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1]));
VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[2]));
VerifyOrQuit(!sDeprecatingPrefixes.ContainsMatching(oldLocalOnLink));
VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[0], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[1], NetworkData::kRoutePreferenceMedium),
ExternalRoute(oldPrefixes[2], NetworkData::kRoutePreferenceMedium)});
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("End of TestExtPanIdChange");