[link-metrics] implement link metrics manager (#9375)

This commit implements a new module LinkMetricsManager, which utilizes
the Link Metrics feature to get the Link Metrics data from neighboring
devices.

The commit also adds a few tests to the module:
- Unit Test: tests/unit/test_link_metrics_manager.cpp, will be run in
  `unit-tests` in `unit.yml`.
- Expect Test: tests/scripts/expect/v1_2-linkmetricsmgr.exp, will be
  run in `expects` in `simulation-1.2.yml`.
- Simulation Test:
  tests/scripts/thread-cert/v1_2_LowPower_test_link_metrics_manager.py,
  will be run in `packet-verification-low-power` in
  `simulation-1.2.yml`.
This commit is contained in:
Li Cao
2023-09-02 08:14:24 -07:00
committed by GitHub
parent 830572f816
commit 8c2abba2f8
25 changed files with 1419 additions and 2 deletions
+1
View File
@@ -207,6 +207,7 @@ ot_option(OT_IP6_FRAGM OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE "ipv6 fragment
ot_option(OT_JAM_DETECTION OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE "jam detection")
ot_option(OT_JOINER OPENTHREAD_CONFIG_JOINER_ENABLE "joiner")
ot_option(OT_LINK_METRICS_INITIATOR OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE "link metrics initiator")
ot_option(OT_LINK_METRICS_MANAGER OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE "link metrics manager")
ot_option(OT_LINK_METRICS_SUBJECT OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE "link metrics subject")
ot_option(OT_LINK_RAW OPENTHREAD_CONFIG_LINK_RAW_ENABLE "link raw service")
ot_option(OT_LOG_LEVEL_DYNAMIC OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE "dynamic log level control")
@@ -66,6 +66,7 @@
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 1
#define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 1
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1
@@ -66,6 +66,7 @@
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 1
#define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 1
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 0
@@ -66,6 +66,7 @@
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 0
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 0
#define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 1
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 0
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (355)
#define OPENTHREAD_API_VERSION (356)
/**
* @addtogroup api-instance
+25
View File
@@ -249,6 +249,31 @@ otError otLinkMetricsSendLinkProbe(otInstance *aInstance,
uint8_t aSeriesId,
uint8_t aLength);
/**
* Enable or disable Link Metrics Manager.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aEnable A boolean indicating to enable or disable.
*
*/
void otLinkMetricsManagerSetEnabled(otInstance *aInstance, bool aEnable);
/**
* Get Link Metrics data of a neighbor by its extended address.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aExtAddress A pointer to the Mac extended address of the Probing Subject.
* @param[out] aLinkMetricsValues A pointer to the Link Metrics values of the subject.
*
* @retval OT_ERROR_NONE Successfully got the Link Metrics data.
* @retval OT_ERROR_INVALID_ARGS The arguments are invalid.
* @retval OT_ERROR_NOT_FOUND No neighbor with the given extended address is found.
*
*/
otError otLinkMetricsManagerGetMetricsValueByExtAddr(otInstance *aInstance,
const otExtAddress *aExtAddress,
otLinkMetricsValues *aLinkMetricsValues);
/**
* @}
*
+4
View File
@@ -129,6 +129,7 @@ build_simulation()
options+=("-DOT_CSL_RECEIVER=ON")
options+=("-DOT_LINK_METRICS_INITIATOR=ON")
options+=("-DOT_LINK_METRICS_SUBJECT=ON")
options+=("-DOT_LINK_METRICS_MANAGER=ON")
fi
if [[ ${ot_extra_options[*]+x} ]]; then
@@ -162,6 +163,9 @@ build_posix()
if [[ ${version} != "1.1" ]]; then
options+=("-DOT_DUA=ON")
options+=("-DOT_MLR=ON")
options+=("-DOT_LINK_METRICS_INITIATOR=ON")
options+=("-DOT_LINK_METRICS_SUBJECT=ON")
options+=("-DOT_LINK_METRICS_MANAGER=ON")
fi
if [[ ${FULL_LOGS} == 1 ]]; then
+36
View File
@@ -66,6 +66,7 @@ Done
- [leaderdata](#leaderdata)
- [leaderweight](#leaderweight)
- [linkmetrics](#linkmetrics-mgmt-ipaddr-enhanced-ack-clear)
- [linkmetricsmgr](#linkmetricsmgr-disable)
- [locate](#locate)
- [log](#log-filename-filename)
- [mac](#mac-retries-direct)
@@ -1877,6 +1878,41 @@ Done
- RSSI: -18 (dBm) (Exponential Moving Average)
```
### linkmetricsmgr disable
Disable the Link Metrics Manager.
`OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE` is required.
```bash
> linkmetricsmgr disable
Done
```
### linkmetricsmgr enable
Enable the Link Metrics Manager.
`OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE` is required.
```bash
> linkmetricsmgr enable
Done
```
### linkmetricsmgr show
Display the Link Metrics data of all subjects. The subjects are identified by its extended address.
`OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE` is required.
```bash
> linkmetricsmgr show
ExtAddr:827aa7f7f63e1234, LinkMargin:80, Rssi:-20
Done
```
### locate
Gets the current state (`In Progress` or `Idle`) of anycast locator.
+69
View File
@@ -3863,6 +3863,72 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
template <> otError Interpreter::Process<Cmd("linkmetricsmgr")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
/**
* @cli linkmetricsmgr (enable,disable)
* @code
* linkmetricmgr enable
* Done
* @endcode
* @code
* linkmetricmgr disable
* Done
* @endcode
* @cparam linkmetricsmgr @ca{enable|disable}
* @par api_copy
* #otLinkMetricsManagerSetEnabled
*
*/
if (ProcessEnableDisable(aArgs, otLinkMetricsManagerSetEnabled) == OT_ERROR_NONE)
{
}
/**
* @cli linkmetricsmgr show
* @code
* linkmetricsmgr show
* ExtAddr:827aa7f7f63e1234, LinkMargin:80, Rssi:-20
* Done
* @endcode
* @par api_copy
* #otLinkMetricsManagerGetMetricsValueByExtAddr
*
*/
else if (aArgs[0] == "show")
{
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
otNeighborInfo neighborInfo;
while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
{
otLinkMetricsValues linkMetricsValues;
if (otLinkMetricsManagerGetMetricsValueByExtAddr(GetInstancePtr(), &neighborInfo.mExtAddress,
&linkMetricsValues) != OT_ERROR_NONE)
{
continue;
}
OutputFormat("ExtAddr:");
OutputExtAddress(neighborInfo.mExtAddress);
OutputLine(", LinkMargin:%u, Rssi:%d", linkMetricsValues.mLinkMarginValue, linkMetricsValues.mRssiValue);
}
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
{
otError error = OT_ERROR_NONE;
@@ -8352,6 +8418,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
CmdEntry("linkmetrics"),
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
CmdEntry("linkmetricsmgr"),
#endif
#endif
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
CmdEntry("locate"),
+3
View File
@@ -706,6 +706,8 @@ openthread_core_files = [
"utils/history_tracker.hpp",
"utils/jam_detector.cpp",
"utils/jam_detector.hpp",
"utils/link_metrics_manager.cpp",
"utils/link_metrics_manager.hpp",
"utils/mesh_diag.cpp",
"utils/mesh_diag.hpp",
"utils/otns.cpp",
@@ -792,6 +794,7 @@ source_set("libopenthread_core_config") {
"config/history_tracker.h",
"config/ip6.h",
"config/joiner.h",
"config/link_metrics_manager.h",
"config/link_quality.h",
"config/link_raw.h",
"config/logging.h",
+1
View File
@@ -244,6 +244,7 @@ set(COMMON_SOURCES
utils/heap.cpp
utils/history_tracker.cpp
utils/jam_detector.cpp
utils/link_metrics_manager.cpp
utils/mesh_diag.cpp
utils/otns.cpp
utils/parse_cmdline.cpp
+20 -1
View File
@@ -54,7 +54,6 @@ otError otLinkMetricsQuery(otInstance *aInstance,
AsCoreTypePtr(aLinkMetricsFlags));
}
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
otError otLinkMetricsConfigForwardTrackingSeries(otInstance *aInstance,
const otIp6Address *aDestination,
uint8_t aSeriesId,
@@ -98,6 +97,26 @@ otError otLinkMetricsSendLinkProbe(otInstance *aInstance,
return initiator.SendLinkProbe(AsCoreType(aDestination), aSeriesId, aLength);
}
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
void otLinkMetricsManagerSetEnabled(otInstance *aInstance, bool aEnable)
{
AsCoreType(aInstance).Get<Utils::LinkMetricsManager>().SetEnabled(aEnable);
}
otError otLinkMetricsManagerGetMetricsValueByExtAddr(otInstance *aInstance,
const otExtAddress *aExtAddress,
otLinkMetricsValues *aLinkMetricsValues)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aExtAddress != nullptr && aLinkMetricsValues != nullptr, error = OT_ERROR_INVALID_ARGS);
error = AsCoreType(aInstance).Get<Utils::LinkMetricsManager>().GetLinkMetricsValueByExtAddr(
AsCoreType(aExtAddress), AsCoreType(aLinkMetricsValues));
exit:
return error;
}
#endif
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
+3
View File
@@ -216,6 +216,9 @@ Instance::Instance(void)
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
, mHistoryTracker(*this)
#endif
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
, mLinkMetricsManager(*this)
#endif
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
, mDatasetUpdater(*this)
#endif
+9
View File
@@ -127,6 +127,7 @@
#include "utils/heap.hpp"
#include "utils/history_tracker.hpp"
#include "utils/jam_detector.hpp"
#include "utils/link_metrics_manager.hpp"
#include "utils/mesh_diag.hpp"
#include "utils/ping_sender.hpp"
#include "utils/slaac_address.hpp"
@@ -610,6 +611,10 @@ private:
Utils::HistoryTracker mHistoryTracker;
#endif
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
Utils::LinkMetricsManager mLinkMetricsManager;
#endif
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
MeshCoP::DatasetUpdater mDatasetUpdater;
#endif
@@ -901,6 +906,10 @@ template <> inline Utils::MeshDiag &Instance::Get(void) { return mMeshDiag; }
template <> inline Utils::HistoryTracker &Instance::Get(void) { return mHistoryTracker; }
#endif
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
template <> inline Utils::LinkMetricsManager &Instance::Get(void) { return mLinkMetricsManager; }
#endif
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
template <> inline MeshCoP::DatasetUpdater &Instance::Get(void) { return mDatasetUpdater; }
#endif
+3
View File
@@ -186,6 +186,9 @@ void Notifier::EmitEvents(void)
// being published (if needed).
Get<NetworkData::Publisher>().HandleNotifierEvents(events);
#endif
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
Get<Utils::LinkMetricsManager>().HandleNotifierEvents(events);
#endif
for (ExternalCallback &callback : mExternalCallbacks)
{
+58
View File
@@ -0,0 +1,58 @@
/*
* 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.
*/
/**
* @file
* This file includes compile-time configurations for Link Metrics Manager.
*
*/
#ifndef CONFIG_LINK_METRICS_MANAGER_H_
#define CONFIG_LINK_METRICS_MANAGER_H_
/**
* @def OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
*
* Define as 1 to enable Link Metrics Manager support.
*
*/
#ifndef OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT
*
* Define as 1 to make Link Metrics Manager function on by default.
*
*/
#ifndef OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT 0
#endif
#endif // CONFIG_LINK_METRICS_MANAGER_H_
+1
View File
@@ -92,6 +92,7 @@
#include "config/history_tracker.h"
#include "config/ip6.h"
#include "config/joiner.h"
#include "config/link_metrics_manager.h"
#include "config/link_quality.h"
#include "config/link_raw.h"
#include "config/logging.h"
+2
View File
@@ -75,6 +75,7 @@ namespace ot {
*/
class SupervisionListener;
class UnitTester;
/**
* @namespace ot::Mle
@@ -111,6 +112,7 @@ class Mle : public InstanceLocator, private NonCopyable
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
friend class ot::LinkMetrics::Initiator;
#endif
friend class ot::UnitTester;
public:
/**
+377
View File
@@ -0,0 +1,377 @@
/*
* 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.
*/
#include "link_metrics_manager.hpp"
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
#include "common/as_core_type.hpp"
#include "common/error.hpp"
#include "common/locator_getters.hpp"
#include "common/log.hpp"
#include "common/notifier.hpp"
#include "thread/mle.hpp"
#include "thread/neighbor_table.hpp"
namespace ot {
namespace Utils {
RegisterLogModule("LinkMetricsMgr");
/**
* @addtogroup utils-link-metrics-manager
*
* @brief
* This module includes definitions for Link Metrics Manager.
*
* @{
*/
LinkMetricsManager::LinkMetricsManager(Instance &aInstance)
: InstanceLocator(aInstance)
, mTimer(aInstance)
, mEnabled(OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT)
{
}
void LinkMetricsManager::SetEnabled(bool aEnable)
{
VerifyOrExit(mEnabled != aEnable);
mEnabled = aEnable;
if (mEnabled)
{
Start();
}
else
{
Stop();
}
exit:
return;
}
Error LinkMetricsManager::GetLinkMetricsValueByExtAddr(const Mac::ExtAddress &aExtAddress,
LinkMetrics::MetricsValues &aMetricsValues)
{
Error error = kErrorNone;
Subject *subject;
subject = mSubjectList.FindMatching(aExtAddress);
VerifyOrExit(subject != nullptr, error = kErrorNotFound);
aMetricsValues.mLinkMarginValue = subject->mData.mLinkMargin;
aMetricsValues.mRssiValue = subject->mData.mRssi;
exit:
return error;
}
void LinkMetricsManager::Start(void)
{
LinkMetrics::Initiator &initiator = Get<LinkMetrics::Initiator>();
VerifyOrExit(mEnabled && Get<Mle::Mle>().IsAttached());
initiator.SetMgmtResponseCallback(HandleMgmtResponse, this);
initiator.SetEnhAckProbingCallback(HandleEnhAckIe, this);
mTimer.Start(kTimeBeforeStartMilliSec);
exit:
return;
}
void LinkMetricsManager::Stop(void)
{
LinkMetrics::Initiator &initiator = Get<LinkMetrics::Initiator>();
mTimer.Stop();
initiator.SetMgmtResponseCallback(nullptr, nullptr);
initiator.SetEnhAckProbingCallback(nullptr, nullptr);
UnregisterAllSubjects();
ReleaseAllSubjects();
}
void LinkMetricsManager::Update(void)
{
UpdateSubjects();
UpdateLinkMetricsStates();
}
// This method updates the Link Metrics Subject in the subject list. It adds new neighbors to the list.
void LinkMetricsManager::UpdateSubjects(void)
{
Neighbor::Info neighborInfo;
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
while (Get<NeighborTable>().GetNextNeighborInfo(iterator, neighborInfo) == kErrorNone)
{
// if not in the subject list, allocate and add
if (!mSubjectList.ContainsMatching(AsCoreType(&neighborInfo.mExtAddress)))
{
Subject *subject = mPool.Allocate();
VerifyOrExit(subject != nullptr);
subject->Clear();
subject->mExtAddress = AsCoreType(&neighborInfo.mExtAddress);
IgnoreError(mSubjectList.Add(*subject));
}
}
exit:
return;
}
// This method updates the state and take corresponding actions for all subjects and removes stale subjects.
void LinkMetricsManager::UpdateLinkMetricsStates(void)
{
LinkedList<Subject> staleSubjects;
mSubjectList.RemoveAllMatching(*this, staleSubjects);
while (!staleSubjects.IsEmpty())
{
Subject *subject = staleSubjects.Pop();
mPool.Free(*subject);
}
}
void LinkMetricsManager::UnregisterAllSubjects(void)
{
for (Subject &subject : mSubjectList)
{
IgnoreError(subject.UnregisterEap(GetInstance()));
}
}
void LinkMetricsManager::ReleaseAllSubjects(void)
{
while (!mSubjectList.IsEmpty())
{
Subject *subject = mSubjectList.Pop();
mPool.Free(*subject);
}
}
void LinkMetricsManager::HandleNotifierEvents(Events aEvents)
{
if (aEvents.Contains(kEventThreadRoleChanged))
{
if (Get<Mle::Mle>().IsAttached())
{
Start();
}
else
{
Stop();
}
}
}
void LinkMetricsManager::HandleTimer(void)
{
if (Get<Mle::Mle>().IsAttached())
{
Update();
mTimer.Start(kStateUpdateIntervalMilliSec);
}
}
void LinkMetricsManager::HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus, void *aContext)
{
static_cast<LinkMetricsManager *>(aContext)->HandleMgmtResponse(aAddress, aStatus);
}
void LinkMetricsManager::HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus)
{
Mac::ExtAddress extAddress;
Subject *subject;
Neighbor *neighbor;
AsCoreType(aAddress).GetIid().ConvertToExtAddress(extAddress);
neighbor = Get<NeighborTable>().FindNeighbor(extAddress);
VerifyOrExit(neighbor != nullptr);
subject = mSubjectList.FindMatching(extAddress);
VerifyOrExit(subject != nullptr);
switch (MapEnum(aStatus))
{
case LinkMetrics::Status::kStatusSuccess:
subject->mState = SubjectState::kActive;
break;
default:
subject->mState = SubjectState::kNotConfigured;
break;
}
exit:
return;
}
void LinkMetricsManager::HandleEnhAckIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void *aContext)
{
static_cast<LinkMetricsManager *>(aContext)->HandleEnhAckIe(aShortAddress, aExtAddress, aMetricsValues);
}
void LinkMetricsManager::HandleEnhAckIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues)
{
OT_UNUSED_VARIABLE(aShortAddress);
Error error = kErrorNone;
Subject *subject = mSubjectList.FindMatching(AsCoreType(aExtAddress));
VerifyOrExit(subject != nullptr, error = kErrorNotFound);
VerifyOrExit(subject->mState == SubjectState::kActive || subject->mState == SubjectState::kRenewing);
subject->mLastUpdateTime = TimerMilli::GetNow();
VerifyOrExit(aMetricsValues->mMetrics.mRssi && aMetricsValues->mMetrics.mLinkMargin, error = kErrorInvalidArgs);
subject->mData.mRssi = aMetricsValues->mRssiValue;
subject->mData.mLinkMargin = aMetricsValues->mLinkMarginValue;
exit:
if (error == kErrorInvalidArgs)
{
LogWarn("Metrics received are unexpected!");
}
}
// This special Match method is used for "iterating over list while removing some items"
bool LinkMetricsManager::Subject::Matches(const LinkMetricsManager &aLinkMetricsMgr)
{
Error error = UpdateState(aLinkMetricsMgr.GetInstance());
return error == kErrorUnknownNeighbor || error == kErrorNotCapable;
}
Error LinkMetricsManager::Subject::ConfigureEap(Instance &aInstance)
{
Error error = kErrorNone;
Neighbor *neighbor = aInstance.Get<NeighborTable>().FindNeighbor(mExtAddress);
Ip6::Address destination;
LinkMetrics::EnhAckFlags enhAckFlags = LinkMetrics::kEnhAckRegister;
LinkMetrics::Metrics metricsFlags;
VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
destination.SetToLinkLocalAddress(neighbor->GetExtAddress());
metricsFlags.Clear();
metricsFlags.mLinkMargin = 1;
metricsFlags.mRssi = 1;
error =
aInstance.Get<LinkMetrics::Initiator>().SendMgmtRequestEnhAckProbing(destination, enhAckFlags, &metricsFlags);
exit:
if (error == kErrorNone)
{
mState = (mState == SubjectState::kActive) ? SubjectState::kRenewing : SubjectState::kConfiguring;
mAttempts++;
}
return error;
}
Error LinkMetricsManager::Subject::UnregisterEap(Instance &aInstance)
{
Error error = kErrorNone;
Neighbor *neighbor = aInstance.Get<NeighborTable>().FindNeighbor(mExtAddress);
Ip6::Address destination;
LinkMetrics::EnhAckFlags enhAckFlags = LinkMetrics::kEnhAckClear;
VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
destination.SetToLinkLocalAddress(neighbor->GetExtAddress());
error = aInstance.Get<LinkMetrics::Initiator>().SendMgmtRequestEnhAckProbing(destination, enhAckFlags, nullptr);
exit:
return error;
}
Error LinkMetricsManager::Subject::UpdateState(Instance &aInstance)
{
bool shouldConfigure = false;
uint32_t pastTimeMs;
Error error = kErrorNone;
switch (mState)
{
case kNotConfigured:
case kConfiguring:
case kRenewing:
if (mAttempts >= kConfigureLinkMetricsMaxAttempts)
{
mState = kNotSupported;
}
else
{
shouldConfigure = true;
}
break;
case kActive:
pastTimeMs = TimerMilli::GetNow() - mLastUpdateTime;
if (pastTimeMs >= kStateUpdateIntervalMilliSec)
{
shouldConfigure = true;
}
break;
case kNotSupported:
ExitNow(error = kErrorNotCapable);
break;
default:
break;
}
if (shouldConfigure)
{
error = ConfigureEap(aInstance);
}
exit:
return error;
}
/**
* @}
*
*/
} // namespace Utils
} // namespace ot
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
+223
View File
@@ -0,0 +1,223 @@
/*
* 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.
*/
#ifndef LINK_METRICS_MANAGER_HPP_
#define LINK_METRICS_MANAGER_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE == 0
#error \
"OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE can only be used when OPENTHREAD_CONFIG_LINK_METRICS_INITIATOR_ENABLE is true"
#endif
#include <openthread/link_metrics.h>
#include "common/clearable.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/notifier.hpp"
#include "common/pool.hpp"
#include "common/time.hpp"
#include "common/timer.hpp"
#include "mac/mac_types.hpp"
#include "thread/link_metrics_types.hpp"
namespace ot {
class UnitTester;
namespace Utils {
/**
* @addtogroup utils-link-metrics-manager
*
* @brief
* This module includes definitions for Link Metrics Manager.
*
* @{
*/
/**
*
* Link Metrics Manager feature utilizes the Enhanced-ACK Based
* Probing (abbreviated as "EAP" below) to get the Link Metrics
* data of neighboring devices. It is a user of the Link Metrics
* feature.
*
* The feature works as follow:
* - Start/Stop
* The switch `OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT`
* controls enabling/disabling this feature by default. The feature
* will only start to work after it joins a Thread network.
*
* A CLI interface is provided to enable/disable this feature.
*
* Once enabled, it will regularly check current neighbors (all
* devices in the neighbor table, including 'Child' and 'Router')
* and configure the probing with them if haven't done that.
* If disabled, it will clear the configuration with its subjects
* and the local data.
*
* - Maintenance
* The manager will regularly check the status of each subject. If
* it finds that the link metrics data for one subject hasn't been
* updated for `kStateUpdateIntervalMilliSec`, it will configure
* EAP with the subject again.
* The manager may find that some subject (neighbor) no longer
* exist when trying to configure EAP. It will remove the stale
* subject then.
*
* - Show data
* An OT API is provided to get the link metrics data of any
* subject (neighbor) by its extended address. In production, this
* data may be fetched by some other means like RPC.
*
*/
class LinkMetricsManager : public InstanceLocator, private NonCopyable
{
friend class ot::Notifier;
friend class ot::UnitTester;
struct LinkMetricsData
{
uint8_t mLqi; ///< Link Quality Indicator. Value range: [0, 255].
int8_t mRssi; ///< Receive Signal Strength Indicator. Value range: [-128, 0].
uint8_t mLinkMargin; ///< Link Margin. The relative signal strength is recorded as
///< db above the local noise floor. Value range: [0, 130].
};
enum SubjectState : uint8_t
{
kNotConfigured = 0,
kConfiguring,
kActive,
kRenewing,
kNotSupported,
};
struct Subject : LinkedListEntry<Subject>, Clearable<Subject>
{
Mac::ExtAddress mExtAddress; ///< Use the extended address to identify the neighbor.
SubjectState mState; ///< Current State of the Subject
uint8_t mAttempts; ///< The count of attempt that has been made to
///< configure EAP
TimeMilli mLastUpdateTime; ///< The time `mData` was updated last time
LinkMetricsData mData;
Subject *mNext;
bool Matches(const Mac::ExtAddress &aExtAddress) const { return mExtAddress == aExtAddress; }
bool Matches(const LinkMetricsManager &aLinkMetricsMgr);
Error ConfigureEap(Instance &aInstance);
Error UnregisterEap(Instance &aInstance);
Error UpdateState(Instance &aInstance);
};
public:
/**
* Initializes a `LinkMetricsManager` object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit LinkMetricsManager(Instance &aInstance);
/**
* Enable/Disable the LinkMetricsManager feature.
*
* @param[in] aEnable A boolean to indicate enable or disable.
*
*/
void SetEnabled(bool aEnable);
/**
* Get Link Metrics data of subject by the extended address.
*
* @param[in] aExtAddress A reference to the extended address of the subject.
* @param[out] aMetricsValues A reference to the MetricsValues object to place the result.
*
* @retval kErrorNone Successfully got the metrics value.
* @retval kErrorInvalidArgs The arguments are invalid.
* @retval kNotFound No neighbor with the given extended address is found.
*
*/
Error GetLinkMetricsValueByExtAddr(const Mac::ExtAddress &aExtAddress, LinkMetrics::MetricsValues &aMetricsValues);
private:
static constexpr uint16_t kTimeBeforeStartMilliSec = 5000;
static constexpr uint32_t kStateUpdateIntervalMilliSec = 150000;
static constexpr uint8_t kConfigureLinkMetricsMaxAttempts = 3;
#if OPENTHREAD_FTD
static constexpr uint8_t kMaximumSubjectToTrack = 128;
#elif OPENTHREAD_MTD
static constexpr uint8_t kMaximumSubjectToTrack = 1;
#endif
void Start(void);
void Stop(void);
void Update(void);
void UpdateSubjects(void);
void UpdateLinkMetricsStates(void);
void UnregisterAllSubjects(void);
void ReleaseAllSubjects(void);
void HandleNotifierEvents(Events aEvents);
void HandleTimer(void);
static void HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus, void *aContext);
void HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus);
static void HandleEnhAckIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void *aContext);
void HandleEnhAckIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues);
using LinkMetricsMgrTimer = TimerMilliIn<LinkMetricsManager, &LinkMetricsManager::HandleTimer>;
Pool<Subject, kMaximumSubjectToTrack> mPool;
LinkedList<Subject> mSubjectList;
LinkMetricsMgrTimer mTimer;
bool mEnabled;
};
/**
* @}
*
*/
} // namespace Utils
} // namespace ot
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
#endif // LINK_METRICS_MANAGER_HPP_
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/expect -f
#
# 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.
#
source "tests/scripts/expect/_common.exp"
source "tests/scripts/expect/_multinode.exp"
setup_two_nodes
switch_node 1
send "linkmetricsmgr enable\n"
expect_line "Done"
sleep 10
switch_node 2
set addr [get_ipaddr mleid]
switch_node 1
send "ping $addr\n"
expect "16 bytes from $addr: icmp_seq=1"
send "linkmetricsmgr show\n"
expect -re {ExtAddr:([0-9a-f]){16}, LinkMargin:\d+, Rssi:\-?\d+}
expect "Done"
sleep 5
send "linkmetricsmgr disable\n"
expect_line "Done"
send "linkmetricsmgr show\n"
expect_line "Done"
send "linkmetricsmgr xxx\n"
expect_line "InvalidCommand"
dispose_all
+6
View File
@@ -3257,6 +3257,12 @@ class NodeImpl:
self.send_command(cmd)
self._expect_done()
def link_metrics_mgr_set_enabled(self, enable: bool):
op_str = "enable" if enable else "disable"
cmd = f'linkmetricsmgr {op_str}'
self.send_command(cmd)
self._expect_done()
def send_address_notification(self, dst: str, target: str, mliid: str):
cmd = f'fake /a/an {dst} {target} {mliid}'
self.send_command(cmd)
@@ -0,0 +1,271 @@
#!/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.
#
import unittest
from config import ADDRESS_TYPE
from mle import LinkMetricsSubTlvType, TlvType
from pktverify import consts
from pktverify.null_field import nullField
from pktverify.packet_verifier import PacketVerifier
import config
import thread_cert
LEADER = 1
ROUTER = 2
CHILD = 3
"""
Acronyms:
- EAP - Enhanced-ACK Based Probing
Test Process:
1. Initiate a leader and a router at the beginning.
2. Enable Link Metrics Manager on leader.
3. Wait a few seconds.
- At this moment, leader should have configured EAP successfully at router.
4. Instruct the leader ping the router.
- Enhanced ACK sent by router should have Thread IE containing Link Metrics data.
5. Add a child into the network. The child should attach to the leader.
6. Wait 150 seconds.
- At this moment, leader should have configured EAP successfully at the child.
7. Instruct both the router and the child ping the leader.
- Enhanced ACK sent by router and child should have Thread IE containing Link Metrics data.
8. Shutdown the child.
9. Wait 300 seconds.
- The leader should not send a Link Management Request to the child.
- The leader should have sent a Link Management Request to the router and get a response.
10. Disable Link Metrics Manager on leader.
- The leader shonld send a Link Management Request to the router to unregister EAP.
"""
class LowPower_test_LinkMetricsManager(thread_cert.TestCase):
TOPOLOGY = {
LEADER: {
'version': '1.2',
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER, CHILD],
},
ROUTER: {
'version': '1.2',
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER],
},
CHILD: {
'version': '1.2',
'name': 'CHILD',
'mode': 'r',
'allowlist': [LEADER],
}
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
router_addr = self.nodes[ROUTER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
# Step 2 - Enable Link Metrics Manager on leader.
self.nodes[LEADER].link_metrics_mgr_set_enabled(True)
self.simulator.go(10)
# Step 4 - Instruct the leader ping the router.
self.nodes[LEADER].ping(router_addr)
# Step 5 - Add a child into the network.
self.nodes[CHILD].start()
self.simulator.go(10)
self.assertEqual(self.nodes[CHILD].get_state(), 'child')
child_addr = self.nodes[CHILD].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
# Step 6 - Wait the leader to configure EAP at the child.
self.simulator.go(150)
# Step 7 - Instruct both the router and the child ping the leader.
self.nodes[LEADER].ping(router_addr)
self.nodes[LEADER].ping(child_addr)
# Step 8 - Shutdown the child.
self.nodes[CHILD].stop()
# Step 9 - Wait the leader to refresh EAP.
self.simulator.go(300)
# Step 10 - Disable Link Metrics Manager on leader.
self.nodes[LEADER].link_metrics_mgr_set_enabled(False)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
CHILD = pv.vars['CHILD']
# Step 3 - Leader enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
# Request to the Router
# MLE Link Metrics Management TLV Payload:
# - Enhanced ACK Configuration Sub-TLV
# -- Enh-ACK Flags = 1 (register a configuration)
# -- Concatenation of Link Metric Type ID Flags = 0x00:
# --- Item1: (0)(0)(001)(010) = 0x0a
# ---- E = 0
# ---- L = 0
# ---- Type/Average Enum = 1 (Exponential Moving Avg)
# ---- Metrics Enum = 2 (Link Margin)
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(ROUTER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
.filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
.must_next()
# Step 3 - The Router MUST send a Link Metrics Management Response to Leader containing the following TLVs:
# - MLE LInk Metrics Management TLV
# -- Link Metrics Status Sub-TLV = 0 (Success)
pkts.filter_wpan_src64(ROUTER) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
.must_next()
# Step 4 - The Enhanced Ack sent by both Router should contain Thread specific IE
pkt = pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(ROUTER) \
.filter_ping_request() \
.must_next()
ack_seq_no = pkt.wpan.seq_no
pkts.filter_wpan_ack() \
.filter_wpan_seq(ack_seq_no) \
.filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
.must_next()
# Step 6 - Leader enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
# Request to the Child
# MLE Link Metrics Management TLV Payload:
# - Enhanced ACK Configuration Sub-TLV
# -- Enh-ACK Flags = 1 (register a configuration)
# -- Concatenation of Link Metric Type ID Flags = 0x00:
# --- Item1: (0)(0)(001)(010) = 0x0a
# ---- E = 0
# ---- L = 0
# ---- Type/Average Enum = 1 (Exponential Moving Avg)
# ---- Metrics Enum = 2 (Link Margin)
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
.filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
.must_next()
# Step 6 - The Child MUST send a Link Metrics Management Response to Leader containing the following TLVs:
# - MLE LInk Metrics Management TLV
# -- Link Metrics Status Sub-TLV = 0 (Success)
pkts.filter_wpan_src64(CHILD) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
.must_next()
# Step 7 - The Enhanced Ack sent by both Router and Child should contain Thread specific IE
pkt1 = pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(ROUTER) \
.filter_ping_request() \
.must_next()
ack_seq_no = pkt1.wpan.seq_no
pkts.filter_wpan_ack() \
.filter_wpan_seq(ack_seq_no) \
.filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
.must_next()
pkt2 = pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_ping_request() \
.must_next()
ack_seq_no = pkt2.wpan.seq_no
pkts.filter_wpan_ack() \
.filter_wpan_seq(ack_seq_no) \
.filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
.must_next()
# Step 9 - Leader refreshes IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
# Request to the Router
# MLE Link Metrics Management TLV Payload:
# - Enhanced ACK Configuration Sub-TLV
# -- Enh-ACK Flags = 1 (register a configuration)
# -- Concatenation of Link Metric Type ID Flags = 0x00:
# --- Item1: (0)(0)(001)(010) = 0x0a
# ---- E = 0
# ---- L = 0
# ---- Type/Average Enum = 1 (Exponential Moving Avg)
# ---- Metrics Enum = 2 (Link Margin)
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(ROUTER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
.filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
.must_next()
# Step 9 - The Router MUST send a Link Metrics Management Response to Leader containing the following TLVs:
# - MLE LInk Metrics Management TLV
# -- Link Metrics Status Sub-TLV = 0 (Success)
pkts.filter_wpan_src64(ROUTER) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
.must_next()
# Step 10 - Leader unregisters IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
# Request to the Router
# MLE Link Metrics Management TLV Payload:
# - Enhanced ACK Configuration Sub-TLV
# -- Enh-ACK Flags = 0 (unregister a configuration)
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(ROUTER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_CLEAR) \
.must_next()
if __name__ == '__main__':
unittest.main()
+21
View File
@@ -538,6 +538,27 @@ target_link_libraries(ot-test-ip-address
add_test(NAME ot-test-ip-address COMMAND ot-test-ip-address)
add_executable(ot-test-link-metrics-manager
test_link_metrics_manager.cpp
)
target_include_directories(ot-test-link-metrics-manager
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-link-metrics-manager
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-link-metrics-manager
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-link-metrics-manager COMMAND ot-test-link-metrics-manager)
add_executable(ot-test-link-quality
test_link_quality.cpp
)
+222
View File
@@ -0,0 +1,222 @@
/*
* 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.
*/
#include "test_platform.h"
#include <openthread/config.h>
#include "test_util.h"
#include "common/array.hpp"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "mac/mac_types.hpp"
#include "thread/child_table.hpp"
namespace ot {
static ot::Instance *sInstance;
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
using namespace Utils;
static uint32_t sNow = 10000;
extern "C" {
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
} // extern "C"
struct TestChild
{
Child::State mState;
otExtAddress mExtAddress;
uint16_t mThreadVersion;
};
class UnitTester
{
public:
static void TestLinkMetricsManager(void);
private:
static void SetTestLinkMetricsValues(otLinkMetricsValues &aLinkMetricsValues,
uint8_t aLinkMarginValue,
int8_t aRssiValue);
static const TestChild mTestChildList[];
};
const TestChild UnitTester::mTestChildList[] = {
{
Child::kStateValid,
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}},
ot::kThreadVersion1p2,
},
{
Child::kStateValid,
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}},
ot::kThreadVersion1p2,
},
{
Child::kStateParentRequest,
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}},
ot::kThreadVersion1p2,
},
};
void UnitTester::SetTestLinkMetricsValues(otLinkMetricsValues &aLinkMetricsValues,
uint8_t aLinkMarginValue,
int8_t aRssiValue)
{
aLinkMetricsValues.mMetrics.mLinkMargin = true;
aLinkMetricsValues.mMetrics.mRssi = true;
aLinkMetricsValues.mLinkMarginValue = aLinkMarginValue;
aLinkMetricsValues.mRssiValue = aRssiValue;
}
void UnitTester::TestLinkMetricsManager(void)
{
ChildTable *childTable;
const uint16_t testListLength = GetArrayLength(mTestChildList);
Ip6::Address linkLocalAddr;
LinkMetricsManager::Subject *subject1 = nullptr;
LinkMetricsManager::Subject *subject2 = nullptr;
LinkMetricsManager *linkMetricsMgr = nullptr;
otLinkMetricsValues linkMetricsValues;
otShortAddress anyShortAddress = 0x1234;
sInstance = testInitInstance();
VerifyOrQuit(sInstance != nullptr);
childTable = &sInstance->Get<ChildTable>();
sInstance->Get<Mle::Mle>().SetRole(Mle::kRoleRouter);
// Add the child entries from test list
for (uint16_t i = 0; i < testListLength; i++)
{
Child *child;
child = childTable->GetNewChild();
VerifyOrQuit(child != nullptr, "GetNewChild() failed");
child->SetState(mTestChildList[i].mState);
child->SetExtAddress((AsCoreType(&mTestChildList[i].mExtAddress)));
child->SetVersion(mTestChildList[i].mThreadVersion);
}
linkMetricsMgr = &sInstance->Get<LinkMetricsManager>();
linkMetricsMgr->SetEnabled(true);
// Update the subjects for the first time.
linkMetricsMgr->UpdateSubjects();
// Expect there are 2 subjects in NotConfigured state.
{
uint16_t index = 0;
for (LinkMetricsManager::Subject &subject : linkMetricsMgr->mSubjectList)
{
VerifyOrQuit(subject.mExtAddress == AsCoreType(&mTestChildList[1 - index].mExtAddress));
VerifyOrQuit(subject.mState == LinkMetricsManager::SubjectState::kNotConfigured);
index++;
}
}
subject1 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[0].mExtAddress));
subject2 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[1].mExtAddress));
VerifyOrQuit(subject1 != nullptr);
VerifyOrQuit(subject2 != nullptr);
// Update the state of the subjects
linkMetricsMgr->UpdateLinkMetricsStates();
// Expect the 2 subjects are in Configuring state.
for (LinkMetricsManager::Subject &subject : linkMetricsMgr->mSubjectList)
{
VerifyOrQuit(subject.mState == LinkMetricsManager::SubjectState::kConfiguring);
}
// subject1 received a response with a success status code
linkLocalAddr.SetToLinkLocalAddress(AsCoreType(&mTestChildList[0].mExtAddress));
linkMetricsMgr->HandleMgmtResponse(&linkLocalAddr, MapEnum(LinkMetrics::Status::kStatusSuccess));
VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kActive);
// subject1 received Enhanced ACK IE and updated the link metrics data
{
constexpr uint8_t kTestLinkMargin = 100;
constexpr int8_t kTestRssi = -30;
SetTestLinkMetricsValues(linkMetricsValues, kTestLinkMargin, kTestRssi);
linkMetricsMgr->HandleEnhAckIe(anyShortAddress, &mTestChildList[0].mExtAddress, &linkMetricsValues);
VerifyOrQuit(subject1->mData.mLinkMargin == kTestLinkMargin);
VerifyOrQuit(subject1->mData.mRssi == kTestRssi);
}
// subject2 didn't receive any responses after a few attempts and marked as UnSupported.
for (uint8_t i = 0; i < LinkMetricsManager::kConfigureLinkMetricsMaxAttempts; i++)
{
linkMetricsMgr->Update();
}
VerifyOrQuit(subject2->mState == LinkMetricsManager::SubjectState::kNotSupported);
// neighbor2 is removed and subject2 should also be removed.
Child *child2 = childTable->FindChild(subject2->mExtAddress, Child::kInStateValid);
child2->SetState(Child::kStateInvalid);
linkMetricsMgr->Update();
subject2 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[1].mExtAddress));
VerifyOrQuit(subject2 == nullptr);
// subject1 still existed
subject1 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[0].mExtAddress));
VerifyOrQuit(subject1 != nullptr);
// Make subject1 renew
sNow += LinkMetricsManager::kStateUpdateIntervalMilliSec + 1;
linkMetricsMgr->Update();
VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kRenewing);
// subject1 got Enh-ACK IE when in kRenewing state
sNow += 1;
linkMetricsMgr->HandleEnhAckIe(anyShortAddress, &mTestChildList[0].mExtAddress, &linkMetricsValues);
VerifyOrQuit(subject1->mLastUpdateTime == TimeMilli(sNow));
// subject1 got response and become active again
linkMetricsMgr->HandleMgmtResponse(&linkLocalAddr, MapEnum(LinkMetrics::Status::kStatusSuccess));
VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kActive);
}
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
} // namespace ot
int main(void)
{
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
ot::UnitTester::TestLinkMetricsManager();
#endif
printf("\nAll tests passed.\n");
return 0;
}