mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[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:
committed by
GitHub
parent
1a682fe2c8
commit
8e9bbbc88e
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Executable
+184
@@ -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))
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user