mirror of
https://github.com/espressif/openthread.git
synced 2026-08-22 18:39:50 +00:00
[netdata] simplify tracking of lowpan context IDs (#8876)
This commit simplifies how the 6LoWPAN Context IDs are assigned and tracked by `NetworkData::Leader` using a new nested class `ContextIds`. A Context ID can be in one of the 3 states: - It is unallocated. - It is allocated and in-use. - It is scheduled to be removed (after reuse delay interval). This commit also adds a test-case `test-019-netdata-context-id.py` which validates the assignment and removal of Context IDs under different scenarios.
This commit is contained in:
@@ -61,6 +61,7 @@ RegisterLogModule("NetworkData");
|
||||
Leader::Leader(Instance &aInstance)
|
||||
: LeaderBase(aInstance)
|
||||
, mWaitingForNetDataSync(false)
|
||||
, mContextIds(aInstance)
|
||||
, mTimer(aInstance)
|
||||
{
|
||||
Reset();
|
||||
@@ -70,9 +71,7 @@ void Leader::Reset(void)
|
||||
{
|
||||
LeaderBase::Reset();
|
||||
|
||||
memset(reinterpret_cast<void *>(mContextLastUsed), 0, sizeof(mContextLastUsed));
|
||||
mContextUsed = 0;
|
||||
mContextIdReuseDelay = kContextIdReuseDelay;
|
||||
mContextIds.Clear();
|
||||
}
|
||||
|
||||
void Leader::Start(Mle::LeaderStartMode aStartMode)
|
||||
@@ -852,10 +851,10 @@ Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &a
|
||||
|
||||
if (dstContext == nullptr)
|
||||
{
|
||||
// Allocate a Context ID first. This ensure that if we cannot
|
||||
// allocate, we fail and exit before potentially inserting a
|
||||
// Border Router sub-TLV.
|
||||
SuccessOrExit(error = AllocateContextId(contextId));
|
||||
// Get a new Context ID first. This ensure that if we cannot
|
||||
// get new Context ID, we fail and exit before potentially
|
||||
// inserting a Border Router sub-TLV.
|
||||
SuccessOrExit(error = mContextIds.GetUnallocatedId(contextId));
|
||||
}
|
||||
|
||||
if (dstBorderRouter == nullptr)
|
||||
@@ -894,7 +893,7 @@ Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &a
|
||||
}
|
||||
|
||||
dstContext->SetCompress();
|
||||
StopContextReuseTimer(dstContext->GetContextId());
|
||||
mContextIds.MarkAsInUse(dstContext->GetContextId());
|
||||
|
||||
VerifyOrExit(!ContainsMatchingEntry(dstBorderRouter, *entry));
|
||||
|
||||
@@ -974,58 +973,6 @@ const ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) const
|
||||
return service;
|
||||
}
|
||||
|
||||
Error Leader::AllocateContextId(uint8_t &aContextId)
|
||||
{
|
||||
Error error = kErrorNotFound;
|
||||
|
||||
for (uint8_t contextId = kMinContextId; contextId < kMinContextId + kNumContextIds; contextId++)
|
||||
{
|
||||
if ((mContextUsed & (1 << contextId)) == 0)
|
||||
{
|
||||
mContextUsed |= (1 << contextId);
|
||||
aContextId = contextId;
|
||||
error = kErrorNone;
|
||||
LogInfo("Allocated Context ID = %d", contextId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void Leader::FreeContextId(uint8_t aContextId)
|
||||
{
|
||||
LogInfo("Free Context Id = %d", aContextId);
|
||||
RemoveContext(aContextId);
|
||||
mContextUsed &= ~(1 << aContextId);
|
||||
IncrementVersions(/* aIncludeStable */ true);
|
||||
}
|
||||
|
||||
void Leader::StartContextReuseTimer(uint8_t aContextId)
|
||||
{
|
||||
// Start the reuse timer for `aContextId` if it is not already
|
||||
// scheduled.
|
||||
|
||||
VerifyOrExit(mContextLastUsed[aContextId - kMinContextId].GetValue() == 0);
|
||||
|
||||
mContextLastUsed[aContextId - kMinContextId] = TimerMilli::GetNow();
|
||||
|
||||
if (mContextLastUsed[aContextId - kMinContextId].GetValue() == 0)
|
||||
{
|
||||
mContextLastUsed[aContextId - kMinContextId].SetValue(1);
|
||||
}
|
||||
|
||||
if (!mTimer.IsRunning())
|
||||
{
|
||||
mTimer.Start(kStateUpdatePeriod);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Leader::StopContextReuseTimer(uint8_t aContextId) { mContextLastUsed[aContextId - kMinContextId].SetValue(0); }
|
||||
|
||||
void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags)
|
||||
{
|
||||
NetworkData excludeNetworkData(GetInstance()); // Empty network data.
|
||||
@@ -1147,15 +1094,15 @@ void Leader::RemoveRlocInPrefix(PrefixTlv &aPrefix,
|
||||
|
||||
if ((context = aPrefix.FindSubTlv<ContextTlv>()) != nullptr)
|
||||
{
|
||||
if (aPrefix.GetSubTlvsLength() == sizeof(ContextTlv))
|
||||
if (aPrefix.FindSubTlv<BorderRouterTlv>() == nullptr)
|
||||
{
|
||||
context->ClearCompress();
|
||||
StartContextReuseTimer(context->GetContextId());
|
||||
mContextIds.ScheduleToRemove(context->GetContextId());
|
||||
}
|
||||
else
|
||||
{
|
||||
context->SetCompress();
|
||||
StopContextReuseTimer(context->GetContextId());
|
||||
mContextIds.MarkAsInUse(context->GetContextId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1263,6 +1210,8 @@ void Leader::RemoveContext(uint8_t aContextId)
|
||||
|
||||
start = prefix->GetNext();
|
||||
}
|
||||
|
||||
IncrementVersions(/* aIncludeStable */ true);
|
||||
}
|
||||
|
||||
void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId)
|
||||
@@ -1303,55 +1252,26 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
mContextUsed |= 1 << context->GetContextId();
|
||||
mContextIds.MarkAsInUse(context->GetContextId());
|
||||
|
||||
if (context->IsCompress())
|
||||
if (!context->IsCompress())
|
||||
{
|
||||
StopContextReuseTimer(context->GetContextId());
|
||||
}
|
||||
else
|
||||
{
|
||||
StartContextReuseTimer(context->GetContextId());
|
||||
mContextIds.ScheduleToRemove(context->GetContextId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Leader::HandleTimer(void)
|
||||
{
|
||||
bool contextsWaiting = false;
|
||||
|
||||
if (mWaitingForNetDataSync)
|
||||
{
|
||||
LogInfo("Timed out waiting for netdata on restoring leader role after reset");
|
||||
IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < kNumContextIds; i++)
|
||||
else
|
||||
{
|
||||
if (mContextLastUsed[i].GetValue() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TimerMilli::GetNow() - mContextLastUsed[i] >= Time::SecToMsec(mContextIdReuseDelay))
|
||||
{
|
||||
mContextLastUsed[i].SetValue(0);
|
||||
FreeContextId(kMinContextId + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
contextsWaiting = true;
|
||||
}
|
||||
mContextIds.HandleTimer();
|
||||
}
|
||||
|
||||
if (contextsWaiting)
|
||||
{
|
||||
mTimer.Start(kStateUpdatePeriod);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
@@ -1393,6 +1313,86 @@ exit:
|
||||
}
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// Leader::ContextIds
|
||||
|
||||
void Leader::ContextIds::Clear(void)
|
||||
{
|
||||
for (uint8_t id = kMinId; id <= kMaxId; id++)
|
||||
{
|
||||
MarkAsUnallocated(id);
|
||||
}
|
||||
}
|
||||
|
||||
Error Leader::ContextIds::GetUnallocatedId(uint8_t &aId)
|
||||
{
|
||||
Error error = kErrorNotFound;
|
||||
|
||||
for (uint8_t id = kMinId; id <= kMaxId; id++)
|
||||
{
|
||||
if (IsUnallocated(id))
|
||||
{
|
||||
aId = id;
|
||||
error = kErrorNone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void Leader::ContextIds::ScheduleToRemove(uint8_t aId)
|
||||
{
|
||||
VerifyOrExit(IsInUse(aId));
|
||||
|
||||
SetRemoveTime(aId, TimerMilli::GetNow() + Time::SecToMsec(mReuseDelay));
|
||||
Get<Leader>().mTimer.FireAtIfEarlier(GetRemoveTime(aId));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Leader::ContextIds::SetRemoveTime(uint8_t aId, TimeMilli aTime)
|
||||
{
|
||||
uint32_t time = aTime.GetValue();
|
||||
|
||||
while ((time == kUnallocated) || (time == kInUse))
|
||||
{
|
||||
time++;
|
||||
}
|
||||
|
||||
mRemoveTimes[aId - kMinId].SetValue(time);
|
||||
}
|
||||
|
||||
void Leader::ContextIds::HandleTimer(void)
|
||||
{
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
TimeMilli nextTime = now.GetDistantFuture();
|
||||
|
||||
for (uint8_t id = kMinId; id <= kMaxId; id++)
|
||||
{
|
||||
if (IsUnallocated(id) || IsInUse(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (now >= GetRemoveTime(id))
|
||||
{
|
||||
MarkAsUnallocated(id);
|
||||
Get<Leader>().RemoveContext(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextTime = Min(nextTime, GetRemoveTime(id));
|
||||
}
|
||||
}
|
||||
|
||||
if (nextTime != now.GetDistantFuture())
|
||||
{
|
||||
Get<Leader>().mTimer.FireAt(nextTime);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace NetworkData
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/non_copyable.hpp"
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
#include "thread/mle_router.hpp"
|
||||
@@ -122,20 +123,20 @@ public:
|
||||
/**
|
||||
* This method returns CONTEXT_ID_RESUSE_DELAY value.
|
||||
*
|
||||
* @returns The CONTEXT_ID_REUSE_DELAY value.
|
||||
* @returns The CONTEXT_ID_REUSE_DELAY value (in seconds).
|
||||
*
|
||||
*/
|
||||
uint32_t GetContextIdReuseDelay(void) const { return mContextIdReuseDelay; }
|
||||
uint32_t GetContextIdReuseDelay(void) const { return mContextIds.GetReuseDelay(); }
|
||||
|
||||
/**
|
||||
* This method sets CONTEXT_ID_RESUSE_DELAY value.
|
||||
*
|
||||
* @warning This method should only be used for testing.
|
||||
*
|
||||
* @param[in] aDelay The CONTEXT_ID_REUSE_DELAY value.
|
||||
* @param[in] aDelay The CONTEXT_ID_REUSE_DELAY value (in seconds).
|
||||
*
|
||||
*/
|
||||
void SetContextIdReuseDelay(uint32_t aDelay) { mContextIdReuseDelay = aDelay; }
|
||||
void SetContextIdReuseDelay(uint32_t aDelay) { mContextIds.SetReuseDelay(aDelay); }
|
||||
|
||||
/**
|
||||
* This method removes Network Data entries matching with a given RLOC16.
|
||||
@@ -177,6 +178,8 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync in msec.
|
||||
|
||||
class ChangedFlags
|
||||
{
|
||||
public:
|
||||
@@ -206,6 +209,55 @@ private:
|
||||
kTlvUpdated, // TLV stable flag is updated based on its sub TLVs.
|
||||
};
|
||||
|
||||
class ContextIds : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
// This class tracks Context IDs. A Context ID can be in one
|
||||
// of the 3 states: It is unallocated, or it is allocated
|
||||
// and in-use, or it scheduled to be removed (after reuse delay
|
||||
// interval is passed).
|
||||
|
||||
static constexpr uint8_t kInvalidId = NumericLimits<uint8_t>::kMax;
|
||||
|
||||
explicit ContextIds(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mReuseDelay(kReuseDelay)
|
||||
{
|
||||
}
|
||||
|
||||
void Clear(void);
|
||||
Error GetUnallocatedId(uint8_t &aId);
|
||||
void MarkAsInUse(uint8_t aId) { mRemoveTimes[aId - kMinId].SetValue(kInUse); }
|
||||
void ScheduleToRemove(uint8_t aId);
|
||||
uint32_t GetReuseDelay(void) const { return mReuseDelay; }
|
||||
void SetReuseDelay(uint32_t aDelay) { mReuseDelay = aDelay; }
|
||||
void HandleTimer(void);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kReuseDelay = 48 * 60 * 60; // in seconds
|
||||
|
||||
static constexpr uint8_t kMinId = 1;
|
||||
static constexpr uint8_t kMaxId = 15;
|
||||
|
||||
// The `mRemoveTimes[id]` is used to track the state of a
|
||||
// Context ID and its remove time. Two specific values
|
||||
// `kUnallocated` and `kInUse` are used to indicate ID is in
|
||||
// unallocated or in-use states. Other values indicate we
|
||||
// are in remove state waiting to remove it at `mRemoveTime`.
|
||||
|
||||
static constexpr uint32_t kUnallocated = 0;
|
||||
static constexpr uint32_t kInUse = 1;
|
||||
|
||||
bool IsUnallocated(uint8_t aId) const { return mRemoveTimes[aId - kMinId].GetValue() == kUnallocated; }
|
||||
bool IsInUse(uint8_t aId) const { return mRemoveTimes[aId - kMinId].GetValue() == kInUse; }
|
||||
TimeMilli GetRemoveTime(uint8_t aId) const { return mRemoveTimes[aId - kMinId]; }
|
||||
void SetRemoveTime(uint8_t aId, TimeMilli aTime);
|
||||
void MarkAsUnallocated(uint8_t aId) { mRemoveTimes[aId - kMinId].SetValue(kUnallocated); }
|
||||
|
||||
TimeMilli mRemoveTimes[kMaxId - kMinId + 1];
|
||||
uint32_t mReuseDelay;
|
||||
};
|
||||
|
||||
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
|
||||
void HandleTimer(void);
|
||||
@@ -220,11 +272,6 @@ private:
|
||||
|
||||
Error AllocateServiceId(uint8_t &aServiceId) const;
|
||||
|
||||
Error AllocateContextId(uint8_t &aContextId);
|
||||
void FreeContextId(uint8_t aContextId);
|
||||
void StartContextReuseTimer(uint8_t aContextId);
|
||||
void StopContextReuseTimer(uint8_t aContextId);
|
||||
|
||||
void RemoveContext(uint8_t aContextId);
|
||||
void RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId);
|
||||
|
||||
@@ -285,16 +332,8 @@ private:
|
||||
|
||||
using UpdateTimer = TimerMilliIn<Leader, &Leader::HandleTimer>;
|
||||
|
||||
static constexpr uint8_t kMinContextId = 1; // Minimum Context ID (0 is used for Mesh Local)
|
||||
static constexpr uint8_t kNumContextIds = 15; // Maximum Context ID
|
||||
static constexpr uint32_t kContextIdReuseDelay = 48 * 60 * 60; // in seconds
|
||||
static constexpr uint32_t kStateUpdatePeriod = 60 * 1000; // State update period in milliseconds
|
||||
static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync.
|
||||
|
||||
bool mWaitingForNetDataSync;
|
||||
uint16_t mContextUsed;
|
||||
TimeMilli mContextLastUsed[kNumContextIds];
|
||||
uint32_t mContextIdReuseDelay;
|
||||
ContextIds mContextIds;
|
||||
UpdateTimer mTimer;
|
||||
};
|
||||
|
||||
|
||||
+369
@@ -0,0 +1,369 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2023, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from cli import verify
|
||||
from cli import verify_within
|
||||
import cli
|
||||
import time
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test description: Network Data Context ID assignment and reuse delay.
|
||||
#
|
||||
# Network topology
|
||||
#
|
||||
# r1 ---- r2 ---- r3
|
||||
#
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print('-' * 120)
|
||||
print('Starting \'{}\''.format(test_name))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `cli.Node` instances
|
||||
|
||||
speedup = 40
|
||||
cli.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
r1 = cli.Node()
|
||||
r2 = cli.Node()
|
||||
r3 = cli.Node()
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Form topology
|
||||
|
||||
r1.allowlist_node(r2)
|
||||
|
||||
r2.allowlist_node(r1)
|
||||
r2.allowlist_node(r3)
|
||||
|
||||
r3.allowlist_node(r2)
|
||||
|
||||
r1.form('netdata')
|
||||
r2.join(r1)
|
||||
r3.join(r1)
|
||||
|
||||
verify(r1.get_state() == 'leader')
|
||||
verify(r2.get_state() == 'router')
|
||||
verify(r3.get_state() == 'router')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test Implementation
|
||||
|
||||
# Change the reuse delay on `r1 (leader) to 5 seconds.
|
||||
|
||||
r1.cli('contextreusedelay 5')
|
||||
verify(int(r1.cli('contextreusedelay')[0]) == 5)
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Add an on-mesh prefix from each router `r1`, r2`, and `r3`.
|
||||
# Validate that netdata is updated and Context IDs are assigned.
|
||||
|
||||
r1.add_prefix('fd00:1::/64', 'poas')
|
||||
r1.register_netdata()
|
||||
|
||||
r2.add_prefix('fd00:2::/64', 'poas')
|
||||
r2.register_netdata()
|
||||
|
||||
r3.add_prefix('fd00:3::/64', 'poas')
|
||||
r3.add_route('fd00:beef::/64', 's')
|
||||
r3.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_1():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 3)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
|
||||
|
||||
verify_within(check_netdata_1, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 3)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
|
||||
for context in contexts:
|
||||
verify(context.endswith('c'))
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Remove prefix on `r3`. Validate that Context compress flag
|
||||
# is cleared for the removed prefix.
|
||||
|
||||
r3.remove_prefix('fd00:3::/64')
|
||||
r3.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_2():
|
||||
global netdata, versions
|
||||
versions = r1.get_netdata_versions()
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 2)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
|
||||
|
||||
verify_within(check_netdata_2, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 3)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
|
||||
for context in contexts:
|
||||
if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'):
|
||||
verify(context.endswith('c'))
|
||||
else:
|
||||
verify(context.endswith('-'))
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Validate that the prefix context is removed within reuse delay
|
||||
# time (which is set to 5 seconds on leader).
|
||||
|
||||
|
||||
def check_netdata_3():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 2)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
verify(len(netdata['contexts']) == 2)
|
||||
|
||||
|
||||
verify_within(check_netdata_3, 5)
|
||||
old_versions = versions
|
||||
versions = r1.get_netdata_versions()
|
||||
verify(versions != old_versions)
|
||||
|
||||
# Make sure netdata does not change afterwards
|
||||
|
||||
time.sleep(5 * 3 / speedup)
|
||||
|
||||
verify(versions == r1.get_netdata_versions())
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 2)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
verify(len(netdata['contexts']) == 2)
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Add two new on-mesh prefixes from `r3`. Validate that they get
|
||||
# assigned Context IDs.
|
||||
|
||||
r3.add_prefix('fd00:4::/64', 'poas')
|
||||
r3.register_netdata()
|
||||
|
||||
r3.add_prefix('fd00:3::/64', 'poas')
|
||||
r3.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_4():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 4)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
verify(len(netdata['contexts']) == 4)
|
||||
|
||||
|
||||
verify_within(check_netdata_4, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 4)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
|
||||
for context in contexts:
|
||||
verify(context.endswith('c'))
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Remove prefixes on `r1` and `r2` and re-add them back quickly both
|
||||
# from `r2`. Validate that they get the same context IDs as before.
|
||||
|
||||
for context in contexts:
|
||||
if context.startswith('fd00:1:0:0::/64'):
|
||||
cid1 = int(context.split()[1])
|
||||
elif context.startswith('fd00:2:0:0::/64'):
|
||||
cid2 = int(context.split()[1])
|
||||
|
||||
r1.remove_prefix('fd00:1:0:0::/64')
|
||||
r1.register_netdata()
|
||||
|
||||
r2.remove_prefix('fd00:2:0:0::/64')
|
||||
r2.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_5():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 2)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
|
||||
|
||||
verify_within(check_netdata_5, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 4)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
|
||||
for context in contexts:
|
||||
if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'):
|
||||
verify(context.endswith('-'))
|
||||
else:
|
||||
verify(context.endswith('c'))
|
||||
|
||||
# Re-add both prefixes (now from `r2`) before CID remove delay time
|
||||
# is expired.
|
||||
|
||||
r2.add_prefix('fd00:1::/64', 'poas')
|
||||
r2.add_prefix('fd00:2::/64', 'poas')
|
||||
r2.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_6():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 4)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
|
||||
|
||||
verify_within(check_netdata_6, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 4)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
|
||||
for context in contexts:
|
||||
verify(context.endswith('c'))
|
||||
if context.startswith('fd00:1:0:0::/64'):
|
||||
verify(int(context.split()[1]) == cid1)
|
||||
elif context.startswith('fd00:2:0:0::/64'):
|
||||
verify(int(context.split()[1]) == cid2)
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Remove two prefixes on `r3`. Add one back as an external route from
|
||||
# `r1`. Also add a new prefix from `r1`. Validate that the context IDs
|
||||
# for the removed on-mesh prefixes are removed after reuse delay time
|
||||
# and that the new prefix gets a different Context ID.
|
||||
|
||||
# Remember the CID used.
|
||||
for context in contexts:
|
||||
if context.startswith('fd00:3:0:0::/64'):
|
||||
cid3 = int(context.split()[1])
|
||||
elif context.startswith('fd00:4:0:0::/64'):
|
||||
cid4 = int(context.split()[1])
|
||||
|
||||
r3.remove_prefix('fd00:3:0:0::/64')
|
||||
r3.remove_prefix('fd00:4:0:0::/64')
|
||||
r3.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_7():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 2)
|
||||
verify(len(netdata['routes']) == 1)
|
||||
|
||||
|
||||
verify_within(check_netdata_7, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 4)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
|
||||
for context in contexts:
|
||||
if context.startswith('fd00:3:0:0::/64') or context.startswith('fd00:4:0:0::/64'):
|
||||
verify(context.endswith('-'))
|
||||
else:
|
||||
verify(context.endswith('c'))
|
||||
|
||||
# Add first one removed as route and add a new prefix.
|
||||
|
||||
r1.add_route('fd00:3:0:0::/64', 's')
|
||||
r1.add_prefix('fd00:5:0:0::/64', 'poas')
|
||||
r1.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_8():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 3)
|
||||
verify(len(netdata['routes']) == 2)
|
||||
verify(len(netdata['contexts']) == 3)
|
||||
|
||||
|
||||
verify_within(check_netdata_8, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 3)
|
||||
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
|
||||
verify(any([context.startswith('fd00:5:0:0::/64') for context in contexts]))
|
||||
|
||||
for context in contexts:
|
||||
verify(context.endswith('c'))
|
||||
if context.startswith('fd00:5:0:0::/64'):
|
||||
verify(not int(context.split()[1]) in [cid3, cid4])
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Remove a prefix on `r2`, wait one second and remove a second
|
||||
# prefix. Validate the Context IDs for both are removed.
|
||||
|
||||
r2.remove_prefix('fd00:1::/64')
|
||||
r2.register_netdata()
|
||||
|
||||
time.sleep(1 / speedup)
|
||||
|
||||
r2.remove_prefix('fd00:2::/64')
|
||||
r2.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_9():
|
||||
global netdata
|
||||
netdata = r1.get_netdata()
|
||||
verify(len(netdata['prefixes']) == 1)
|
||||
verify(len(netdata['routes']) == 2)
|
||||
verify(len(netdata['contexts']) == 1)
|
||||
|
||||
|
||||
verify_within(check_netdata_9, 5)
|
||||
|
||||
contexts = netdata['contexts']
|
||||
verify(len(contexts) == 1)
|
||||
verify(contexts[0].startswith('fd00:5:0:0::/64'))
|
||||
verify(contexts[0].endswith('c'))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
cli.Node.finalize_all_nodes()
|
||||
|
||||
print('\'{}\' passed.'.format(test_name))
|
||||
@@ -183,6 +183,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
|
||||
run cli/test-016-child-mode-change.py
|
||||
run cli/test-017-network-data-versions.py
|
||||
run cli/test-018-next-hop-and-path-cost.py
|
||||
run cli/test-019-netdata-context-id.py
|
||||
run cli/test-400-srp-client-server.py
|
||||
run cli/test-601-channel-manager-channel-change.py
|
||||
# Skip the "channel-select" test on a TREL only radio link, since it
|
||||
|
||||
Reference in New Issue
Block a user