[netdata] mechanism to detect & signal when network data gets full (#9073)

This commit adds a new mechanism to detect when Network Data (local or
leader) becomes full. When this happens, a callback function is
invoked. This callback function can be set using a newly added OT
API. The callback is invoked whenever:
- The device is acting as the leader and receives a Network Data
  registration from a Border Router (BR) that it cannot add to
  Network Data (running out of space).
- The device is acting as a BR and new entries cannot be added to its
  local Network Data.
- The device is acting as a BR and tries to register its local Network
  Data entries with the leader, but determines that its local entries
  will not fit.

The `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL` config
controls the new mechanism and its API. It is enabled by default when
`OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` is enabled.

This commit also adds CLI support for the new mechanism and adds a
test case to validate its behavior.
This commit is contained in:
Abtin Keshavarzian
2023-06-26 18:08:13 -07:00
committed by GitHub
parent 1a682fe2c8
commit 8e9bbbc88e
15 changed files with 548 additions and 17 deletions
+29
View File
@@ -168,6 +168,35 @@ otError otBorderRouterGetNextRoute(otInstance *aInstance,
*/
otError otBorderRouterRegister(otInstance *aInstance);
/**
* Function pointer callback which is invoked when Network Data (local or leader) gets full.
*
* @param[in] aContext A pointer to arbitrary context information.
*
*/
typedef void (*otBorderRouterNetDataFullCallback)(void *aContext);
/**
* Sets the callback to indicate when Network Data gets full.
*
* Requires `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL`.
*
* The callback is invoked whenever:
* - The device is acting as a leader and receives a Network Data registration from a Border Router (BR) that it cannot
* add to Network Data (running out of space).
* - The device is acting as a BR and new entries cannot be added to its local Network Data.
* - The device is acting as a BR and tries to register its local Network Data entries with the leader, but determines
* that its local entries will not fit.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aCallback The callback.
* @param[in] aContext A pointer to arbitrary context information used with @p aCallback.
*
*/
void otBorderRouterSetNetDataFullCallback(otInstance *aInstance,
otBorderRouterNetDataFullCallback aCallback,
void *aContext);
/**
* @}
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (334)
#define OPENTHREAD_API_VERSION (335)
/**
* @addtogroup api-instance
+35
View File
@@ -142,6 +142,7 @@ After the device successfully attaches to a Thread network, the device will retr
## Command List
- [help](#help)
- [full](#full)
- [length](#length)
- [maxlength](#maxlength)
- [publish](#publish)
@@ -160,6 +161,7 @@ Print netdata help menu.
```bash
> netdata help
full
length
maxlength
publish
@@ -170,6 +172,39 @@ unpublish
Done
```
### full
Usage: `netdata full`
Print "yes" or "no" flag tracking whether or not the "net data full" callback has been invoked since start of Thread operation or since the last time `netdata full reset` was used to reset the flag.
This command requires `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL`.
The "net data full" callback is invoked whenever:
- The device is acting as a leader and receives a Network Data registration from a Border Router (BR) that it cannot add to Network Data (running out of space).
- The device is acting as a BR and new entries cannot be added to its local Network Data.
- The device is acting as a BR and tries to register its local Network Data entries with the leader, but determines that its local entries will not fit.
```
> netdata full
no
Done
```
### full reset
Usage: `netdata full reset`
Reset the flag tracking whether "net data full" callback was invoked.
This command requires `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL`.
```
> netdata full reset
Done
```
### length
Usage: `netdata length`
+64
View File
@@ -43,6 +43,15 @@
namespace ot {
namespace Cli {
NetworkData::NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer)
: Output(aInstance, aOutputImplementer)
{
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
mFullCallbackWasCalled = false;
otBorderRouterSetNetDataFullCallback(aInstance, HandleNetdataFull, this);
#endif
}
void NetworkData::PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString)
{
char *flagsPtr = &aString[0];
@@ -802,6 +811,58 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
template <> otError NetworkData::Process<Cmd("full")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
/**
* @cli netdata full
* @code
* netdata full
* no
* Done
* @endcode
* @par
* Print "yes" or "no" indicating whether or not the "net data full" callback has been invoked since start of
* Thread operation or since the last time `netdata full reset` was used to reset the flag.
* This command requires `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL`.
* The "net data full" callback is invoked whenever:
* - The device is acting as a leader and receives a Network Data registration from a Border Router (BR) that it
* cannot add to Network Data (running out of space).
* - The device is acting as a BR and new entries cannot be added to its local Network Data.
* - The device is acting as a BR and tries to register its local Network Data entries with the leader, but
* determines that its local entries will not fit.
* @sa otBorderRouterSetNetDataFullCallback
*/
if (aArgs[0].IsEmpty())
{
OutputLine(mFullCallbackWasCalled ? "yes" : "no");
}
/**
* @cli netdata full reset
* @code
* netdata full reset
* Done
* @endcode
* @par
* Reset the flag tracking whether "net data full" callback was invoked.
*/
else if (aArgs[0] == "reset")
{
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
mFullCallbackWasCalled = false;
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
otError NetworkData::Process(Arg aArgs[])
{
#define CmdEntry(aCommandString) \
@@ -810,6 +871,9 @@ otError NetworkData::Process(Arg aArgs[])
}
static constexpr Command kCommands[] = {
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
CmdEntry("full"),
#endif
CmdEntry("length"),
CmdEntry("maxlength"),
#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
+8 -4
View File
@@ -71,10 +71,7 @@ public:
* @param[in] aOutputImplementer An `OutputImplementer`.
*
*/
NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer)
: Output(aInstance, aOutputImplementer)
{
}
NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer);
/**
* Processes a CLI sub-command.
@@ -146,6 +143,13 @@ private:
void OutputRoutes(bool aLocal);
void OutputServices(bool aLocal);
void OutputLowpanContexts(bool aLocal);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
static void HandleNetdataFull(void *aContext) { static_cast<NetworkData *>(aContext)->HandleNetdataFull(); }
void HandleNetdataFull(void) { mFullCallbackWasCalled = true; }
bool mFullCallbackWasCalled;
#endif
};
} // namespace Cli
+9
View File
@@ -118,4 +118,13 @@ otError otBorderRouterRegister(otInstance *aInstance)
return kErrorNone;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void otBorderRouterSetNetDataFullCallback(otInstance *aInstance,
otBorderRouterNetDataFullCallback aCallback,
void *aContext)
{
AsCoreType(aInstance).Get<NetworkData::Notifier>().SetNetDataFullCallback(aCallback, aContext);
}
#endif
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
+12
View File
@@ -35,6 +35,8 @@
#ifndef CONFIG_BORDER_ROUTER_H_
#define CONFIG_BORDER_ROUTER_H_
#include <config/border_routing.h>
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
*
@@ -71,4 +73,14 @@
#define OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
*
* Define as 1 to enable mechanism to detect and signal when local or leader Network Data gets full.
*
*/
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
#define OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#endif
#endif // CONFIG_BORDER_ROUTER_H_
+113 -4
View File
@@ -60,6 +60,9 @@ RegisterLogModule("NetworkData");
Leader::Leader(Instance &aInstance)
: LeaderBase(aInstance)
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
, mIsClone(false)
#endif
, mWaitingForNetDataSync(false)
, mContextIds(aInstance)
, mTimer(aInstance)
@@ -76,6 +79,10 @@ void Leader::Reset(void)
void Leader::Start(Mle::LeaderStartMode aStartMode)
{
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
OT_ASSERT(!mIsClone);
#endif
mWaitingForNetDataSync = (aStartMode == Mle::kRestoringLeaderRoleAfterReset);
if (mWaitingForNetDataSync)
@@ -110,6 +117,10 @@ void Leader::IncrementVersions(const ChangedFlags &aFlags)
void Leader::IncrementVersions(bool aIncludeStable)
{
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
VerifyOrExit(!mIsClone);
#endif
if (aIncludeStable)
{
mStableVersion++;
@@ -117,6 +128,10 @@ void Leader::IncrementVersions(bool aIncludeStable)
mVersion++;
SignalNetDataChanged();
ExitNow();
exit:
return;
}
void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode)
@@ -124,6 +139,7 @@ void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode)
ChangedFlags flags;
RemoveRloc(aRloc16, aMatchMode, flags);
IncrementVersions(flags);
}
@@ -672,6 +688,52 @@ exit:
return status;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void Leader::CheckForNetDataGettingFull(const NetworkData &aNetworkData, uint16_t aOldRloc16)
{
// Determines whether there is still room in Network Data to register
// `aNetworkData` entries. The `aNetworkData` MUST follow the format of
// local Network Data (e.g., all entries associated with the RLOC16 of
// this device). Network data getting full is signaled by invoking the
// `Get<Notifier>().SignalNetworkDataFull()` method.
//
// Input `aOldRloc16` can be used to indicate the old RLOC16 of the
// device. If provided, then entries matching old RLOC16 are first
// removed, before checking if new entries from @p aNetworkData can fit.
if (!Get<Mle::MleRouter>().IsLeader())
{
// Create a clone of the leader's network data, and try to register
// `aNetworkData` into the copy (as if this device itself is the
// leader). `mIsClone` flag is used to mark the clone and ensure
// that the cloned instance does interact with other OT modules,
// e.g., does not start timer, or does not signal version change
// using `Get<ot::Notifier>().Signal()`, or allocate service or
// context ID.
Leader leaderClone(GetInstance());
leaderClone.MarkAsClone();
SuccessOrAssert(CopyNetworkData(kFullSet, leaderClone));
if (aOldRloc16 != Mac::kShortAddrInvalid)
{
leaderClone.RemoveBorderRouter(aOldRloc16, kMatchModeRloc16);
}
leaderClone.RegisterNetworkData(Get<Mle::Mle>().GetRloc16(), aNetworkData);
}
}
void Leader::MarkAsClone(void)
{
mIsClone = true;
mContextIds.MarkAsClone();
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void Leader::RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkData)
{
Error error = kErrorNone;
@@ -710,9 +772,19 @@ void Leader::RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkDa
exit:
IncrementVersions(flags);
if (error != kErrorNone)
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
if (error == kErrorNoBufs)
{
LogNote("Failed to register network data: %s", ErrorToString(error));
Get<Notifier>().SignalNetworkDataFull();
}
if (!mIsClone)
#endif
{
if (error != kErrorNone)
{
LogNote("Failed to register network data: %s", ErrorToString(error));
}
}
}
@@ -943,6 +1015,15 @@ Error Leader::AllocateServiceId(uint8_t &aServiceId) const
Error error = kErrorNotFound;
uint8_t serviceId;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
if (mIsClone)
{
aServiceId = Mle::kServiceMinId;
error = kErrorNone;
ExitNow();
}
#endif
for (serviceId = Mle::kServiceMinId; serviceId <= Mle::kServiceMaxId; serviceId++)
{
if (FindServiceById(serviceId) == nullptr)
@@ -950,10 +1031,11 @@ Error Leader::AllocateServiceId(uint8_t &aServiceId) const
aServiceId = serviceId;
error = kErrorNone;
LogInfo("Allocated Service ID = %d", serviceId);
break;
ExitNow();
}
}
exit:
return error;
}
@@ -1316,6 +1398,15 @@ exit:
//---------------------------------------------------------------------------------------------------------------------
// Leader::ContextIds
Leader::ContextIds::ContextIds(Instance &aInstance)
: InstanceLocator(aInstance)
, mReuseDelay(kReuseDelay)
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
, mIsClone(false)
#endif
{
}
void Leader::ContextIds::Clear(void)
{
for (uint8_t id = kMinId; id <= kMaxId; id++)
@@ -1328,21 +1419,35 @@ Error Leader::ContextIds::GetUnallocatedId(uint8_t &aId)
{
Error error = kErrorNotFound;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
if (mIsClone)
{
aId = kMinId;
error = kErrorNone;
ExitNow();
}
#endif
for (uint8_t id = kMinId; id <= kMaxId; id++)
{
if (IsUnallocated(id))
{
aId = id;
error = kErrorNone;
break;
ExitNow();
}
}
exit:
return error;
}
void Leader::ContextIds::ScheduleToRemove(uint8_t aId)
{
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
VerifyOrExit(!mIsClone);
#endif
VerifyOrExit(IsInUse(aId));
SetRemoveTime(aId, TimerMilli::GetNow() + Time::SecToMsec(mReuseDelay));
@@ -1369,6 +1474,10 @@ void Leader::ContextIds::HandleTimer(void)
TimeMilli now = TimerMilli::GetNow();
TimeMilli nextTime = now.GetDistantFuture();
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
OT_ASSERT(!mIsClone);
#endif
for (uint8_t id = kMinId; id <= kMaxId; id++)
{
if (IsUnallocated(id) || IsInUse(id))
+18 -5
View File
@@ -52,6 +52,8 @@ namespace ot {
namespace NetworkData {
class Notifier;
/**
* @addtogroup core-netdata-leader
*
@@ -69,6 +71,7 @@ namespace NetworkData {
class Leader : public LeaderBase, private NonCopyable
{
friend class Tmf::Agent;
friend class Notifier;
public:
/**
@@ -219,11 +222,7 @@ private:
static constexpr uint8_t kInvalidId = NumericLimits<uint8_t>::kMax;
explicit ContextIds(Instance &aInstance)
: InstanceLocator(aInstance)
, mReuseDelay(kReuseDelay)
{
}
explicit ContextIds(Instance &aInstance);
void Clear(void);
Error GetUnallocatedId(uint8_t &aId);
@@ -232,6 +231,9 @@ private:
uint32_t GetReuseDelay(void) const { return mReuseDelay; }
void SetReuseDelay(uint32_t aDelay) { mReuseDelay = aDelay; }
void HandleTimer(void);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void MarkAsClone(void) { mIsClone = true; }
#endif
private:
static constexpr uint32_t kReuseDelay = 5 * 60; // 5 minutes (in seconds).
@@ -256,6 +258,9 @@ private:
TimeMilli mRemoveTimes[kMaxId - kMinId + 1];
uint32_t mReuseDelay;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
bool mIsClone;
#endif
};
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
@@ -330,8 +335,16 @@ private:
void IncrementVersions(bool aIncludeStable);
void IncrementVersions(const ChangedFlags &aFlags);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void CheckForNetDataGettingFull(const NetworkData &aNetworkData, uint16_t aOldRloc16);
void MarkAsClone(void);
#endif
using UpdateTimer = TimerMilliIn<Leader, &Leader::HandleTimer>;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
bool mIsClone;
#endif
bool mWaitingForNetDataSync;
ContextIds mContextIds;
UpdateTimer mTimer;
+14
View File
@@ -134,6 +134,13 @@ Error Local::AddPrefix(const Ip6::Prefix &aPrefix, NetworkDataTlv::Type aSubTlvT
DumpDebg("AddPrefix", GetBytes(), GetLength());
exit:
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
if (error == kErrorNoBufs)
{
Get<Notifier>().SignalNetworkDataFull();
}
#endif
return error;
}
@@ -211,6 +218,13 @@ Error Local::AddService(uint32_t aEnterpriseNumber,
DumpDebg("AddService", GetBytes(), GetLength());
exit:
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
if (error == kErrorNoBufs)
{
Get<Notifier>().SignalNetworkDataFull();
}
#endif
return error;
}
+16
View File
@@ -53,6 +53,9 @@ Notifier::Notifier(Instance &aInstance)
: InstanceLocator(aInstance)
, mTimer(aInstance)
, mSynchronizeDataTask(aInstance)
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
, mNetDataFullTask(aInstance)
#endif
, mNextDelay(0)
, mOldRloc(Mac::kShortAddrInvalid)
, mWaitingForResponse(false)
@@ -201,6 +204,10 @@ Error Notifier::SendServerDataNotification(uint16_t aOldRloc16, const NetworkDat
tlv.SetLength(aNetworkData->GetLength());
SuccessOrExit(error = message->Append(tlv));
SuccessOrExit(error = message->AppendBytes(aNetworkData->GetBytes(), aNetworkData->GetLength()));
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
Get<Leader>().CheckForNetDataGettingFull(*aNetworkData, aOldRloc16);
#endif
}
if (aOldRloc16 != Mac::kShortAddrInvalid)
@@ -273,6 +280,15 @@ void Notifier::HandleCoapResponse(Error aResult)
}
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void Notifier::SetNetDataFullCallback(NetDataCallback aCallback, void *aContext)
{
mNetDataFullCallback.Set(aCallback, aContext);
}
void Notifier::HandleNetDataFull(void) { mNetDataFullCallback.InvokeIfSet(); }
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
bool Notifier::IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const
+35 -3
View File
@@ -38,6 +38,8 @@
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
#include <openthread/border_router.h>
#include "coap/coap.hpp"
#include "common/message.hpp"
#include "common/non_copyable.hpp"
@@ -78,6 +80,25 @@ public:
*/
void HandleServerDataUpdated(void);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
typedef otBorderRouterNetDataFullCallback NetDataCallback; ///< Network Data full callback.
/**
* Sets the callback to indicate when Network Data gets full.
*
* @param[in] aCallback The callback.
* @param[in] aContext The context to use with @p aCallback.
*
*/
void SetNetDataFullCallback(NetDataCallback aCallback, void *aContext);
/**
* Signals that network data (local or leader) is getting full.
*
*/
void SignalNetworkDataFull(void) { mNetDataFullTask.Post(); }
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
/**
* Indicates whether the device as border router is eligible for router role upgrade request using the
@@ -123,6 +144,10 @@ private:
Error aResult);
void HandleCoapResponse(Error aResult);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
void HandleNetDataFull(void);
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
void ScheduleRouterRoleUpgradeIfEligible(void);
void HandleTimeTick(void);
@@ -130,12 +155,19 @@ private:
using SynchronizeDataTask = TaskletIn<Notifier, &Notifier::SynchronizeServerData>;
using DelayTimer = TimerMilliIn<Notifier, &Notifier::HandleTimer>;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
using NetDataFullTask = TaskletIn<Notifier, &Notifier::HandleNetDataFull>;
#endif
DelayTimer mTimer;
SynchronizeDataTask mSynchronizeDataTask;
uint32_t mNextDelay;
uint16_t mOldRloc;
bool mWaitingForResponse : 1;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
NetDataFullTask mNetDataFullTask;
Callback<NetDataCallback> mNetDataFullCallback;
#endif
uint32_t mNextDelay;
uint16_t mOldRloc;
bool mWaitingForResponse : 1;
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
bool mDidRequestRouterRoleUpgrade : 1;
+9
View File
@@ -411,6 +411,9 @@ class Node(object):
leaderdata = Node.parse_list(self.cli('leaderdata'))
return (int(leaderdata['Data Version']), int(leaderdata['Stable Data Version']))
def get_netdata_length(self):
return self._cli_single_output('netdata length')
def add_prefix(self, prefix, flags=None, prf=None):
return self._cli_no_output('prefix add', prefix, flags, prf)
@@ -423,6 +426,12 @@ class Node(object):
def register_netdata(self):
self._cli_no_output('netdata register')
def get_netdata_full(self):
return self._cli_single_output('netdata full')
def reset_netdata_full(self):
self._cli_no_output('netdata full reset')
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# ping and counters
+184
View File
@@ -0,0 +1,184 @@
#!/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: Validate mechanism to detect when Network Data gets full and related callback.
#
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)
leader = cli.Node()
r1 = cli.Node()
r2 = cli.Node()
r3 = cli.Node()
# -----------------------------------------------------------------------------------------------------------------------
# Form topology
leader.form('netdata-full')
r1.join(leader)
r2.join(r1)
r3.join(r1)
verify(leader.get_state() == 'leader')
verify(r1.get_state() == 'router')
verify(r2.get_state() == 'router')
verify(r2.get_state() == 'router')
# -----------------------------------------------------------------------------------------------------------------------
# Test Implementation
verify(leader.get_netdata_full() == 'no')
verify(r1.get_netdata_full() == 'no')
verify(r2.get_netdata_full() == 'no')
verify(r3.get_netdata_full() == 'no')
# Add 7 routes from `r1` to netdata and make sure they are all
# successfully registered with leader.
r1.add_route('fd00:1:0:1::/64', 's', 'med')
r1.add_route('fd00:1:0:2::/64', 's', 'med')
r1.add_route('fd00:1:0:3::/64', 's', 'med')
r1.add_route('fd00:1:0:4::/64', 's', 'med')
r1.add_route('fd00:1:0:5::/64', 's', 'med')
r1.add_route('fd00:1:0:6::/64', 's', 'med')
r1.add_route('fd00:1:0:7::/64', 's', 'med')
r1.register_netdata()
def check_netdata_after_r1():
netdata = leader.get_netdata()
verify(len(netdata['routes']) == 7)
verify_within(check_netdata_after_r1, 5)
verify(leader.get_netdata_full() == 'no')
verify(r1.get_netdata_full() == 'no')
verify(r2.get_netdata_full() == 'no')
verify(r3.get_netdata_full() == 'no')
prev_len = int(leader.get_netdata_length())
# Now add 7 new routes from `r2` to netdata and make sure they are all
# successfully registered with leader.
r2.add_route('fd00:2:0:1::/64', 's', 'med')
r2.add_route('fd00:2:0:2::/64', 's', 'med')
r2.add_route('fd00:2:0:3::/64', 's', 'med')
r2.add_route('fd00:2:0:4::/64', 's', 'med')
r2.add_route('fd00:2:0:5::/64', 's', 'med')
r2.add_route('fd00:2:0:6::/64', 's', 'med')
r2.add_route('fd00:2:0:7::/64', 's', 'med')
r2.register_netdata()
def check_netdata_after_r2():
netdata = leader.get_netdata()
verify(len(netdata['routes']) == 14)
verify_within(check_netdata_after_r2, 5)
verify(leader.get_netdata_full() == 'no')
verify(r1.get_netdata_full() == 'no')
verify(r2.get_netdata_full() == 'no')
verify(r3.get_netdata_full() == 'no')
verify(int(leader.get_netdata_length()) > prev_len)
# Try adding 3 routes from `r3`, this should cause netdata to go
# over its size limit. Make sure that both `r3` and `leader`
# detect that
r3.add_route('fd00:3:0:1::/64', 's', 'med')
r3.add_route('fd00:3:0:2::/64', 's', 'med')
r3.add_route('fd00:3:0:3::/64', 's', 'med')
r3.register_netdata()
def check_netdata_after_r3():
verify(leader.get_netdata_full() == 'yes')
verify(r1.get_netdata_full() == 'no')
verify(r2.get_netdata_full() == 'no')
verify(r3.get_netdata_full() == 'yes')
verify_within(check_netdata_after_r3, 5)
r3.remove_prefix('fd00:3:0:1::/64')
r3.remove_prefix('fd00:3:0:2::/64')
r3.remove_prefix('fd00:3:0:3::/64')
r3.register_netdata()
leader.reset_netdata_full()
r3.reset_netdata_full()
def check_netdata_after_remove_on_r3():
verify(leader.get_netdata_full() == 'no')
verify(r1.get_netdata_full() == 'no')
verify(r2.get_netdata_full() == 'no')
verify(r3.get_netdata_full() == 'no')
verify_within(check_netdata_after_remove_on_r3, 5)
r2.add_route('fd00:2:0:8::/64', 's', 'med')
r2.add_route('fd00:2:0:9::/64', 's', 'med')
r2.register_netdata()
def check_netdata_after_more_routes_on_r2():
verify(leader.get_netdata_full() == 'yes')
verify(r1.get_netdata_full() == 'no')
verify(r2.get_netdata_full() == 'yes')
verify(r3.get_netdata_full() == 'no')
verify_within(check_netdata_after_more_routes_on_r2, 5)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
cli.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
+1
View File
@@ -185,6 +185,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
run cli/test-018-next-hop-and-path-cost.py
run cli/test-019-netdata-context-id.py
run cli/test-020-net-diag-vendor-info.py
run cli/test-022-netdata-full.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