[low-power] implement forward tracking series (#5608)

This commit implements the Forward Tracking Series feature in Link Metrics.

- Two new apis are added:
  - otLinkMetricsSendMgmtRequestForwardTrackingSeries, which is used
    to send an MLE Link Metrics Management Request
  - otLinkMetricsSendLinkProbe, which is used to send an MLE Link Probe

- A new class LinkMetricsSeriesInfo is used to maintain the data of
  one Series configured by a neighbor. This class inherits
  LinkedListEntry and each Neighbor has a list of
  LinkMetricsSeriesInfo. All LinkedListEntrys are allocated and freed
  by a Pool in LinkMetrics.

- To specify SeriesFlags in cli command for Forward Tracking Series, a
  similar approach with LinkMetricsFlags is used. Another character
  flags is used to represent SeriesFlags.

- Whenever the node receives a frame (including ACK) from a neighbor,
  it would call Neighbor::AggregateLinkMetrics which goes through each
  LinkMetricsSeriesInfo entry in the neighbor's list and aggregate the
  data if the frame type matches the entry.

- Two test scripts are added to test this function:
  - v1_2_LowPower_7_2_01_ForwardTrackingSeries.py
  - v1_2_LowPower_test_forward_tracking_series.py
This commit is contained in:
Li Cao
2020-10-29 20:04:39 -07:00
committed by GitHub
parent 1586c4c628
commit 42fb2e44be
25 changed files with 2299 additions and 265 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (42)
#define OPENTHREAD_API_VERSION (43)
/**
* @addtogroup api-instance
+83 -2
View File
@@ -54,7 +54,7 @@ extern "C" {
*
*/
/*
/**
* This structure represents what metrics are specified to query.
*
*/
@@ -66,7 +66,7 @@ typedef struct otLinkMetrics
bool mRssi : 1;
} otLinkMetrics;
/*
/**
* This structure represents the result (value) for a Link Metrics query.
*
*/
@@ -80,17 +80,54 @@ typedef struct otLinkMetricsValues
int8_t mRssiValue; ///< The value of Rssi.
} otLinkMetricsValues;
/**
* This structure represents which frames are accounted in a Forward Tracking Series.
*
*/
typedef struct otLinkMetricsSeriesFlags
{
bool mLinkProbe : 1; ///< MLE Link Probe.
bool mMacData : 1; ///< MAC Data frame.
bool mMacDataRequest : 1; ///< MAC Data Request.
bool mMacAck : 1; ///< MAC Ack.
} otLinkMetricsSeriesFlags;
/**
* Link Metrics Status values.
*
*/
typedef enum otLinkMetricsStatus : uint8_t
{
OT_LINK_METRICS_STATUS_SUCCESS = 0,
OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES = 1,
OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED = 2,
OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED = 3,
OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED = 4,
OT_LINK_METRICS_STATUS_OTHER_ERROR = 254,
} otLinkMetricsStatus;
/**
* This function pointer is called when a Link Metrics report is received.
*
* @param[in] aSource A pointer to the source address.
* @param[in] aMetricsValues A pointer to the Link Metrics values (the query result).
* @param[in] aStatus The status code in the report (only useful when @p aMetricsValues is NULL).
* @param[in] aContext A pointer to application-specific context.
*
*/
typedef void (*otLinkMetricsReportCallback)(const otIp6Address * aSource,
const otLinkMetricsValues *aMetricsValues,
uint8_t aStatus,
void * aContext);
/**
* This function pointer is called when a Link Metrics Management Response is received.
*
* @param[in] aSource A pointer to the source address.
* @param[in] aStatus The status code in the response.
* @param[in] aContext A pointer to application-specific context.
*
*/
typedef void (*otLinkMetricsMgmtResponseCallback)(const otIp6Address *aSource, uint8_t aStatus, void *aContext);
/**
* This function sends an MLE Data Request to query Link Metrics.
@@ -115,6 +152,50 @@ otError otLinkMetricsQuery(otInstance * aInstance,
otLinkMetricsReportCallback aCallback,
void * aCallbackContext);
/**
* This function sends an MLE Link Metrics Management Request to configure/clear a Forward Tracking Series.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aDestination A pointer to the destination address.
* @param[in] aSeriesId The Series ID to operate with.
* @param[in] aSeriesFlags The Series Flags that specifies which frames are to be accounted.
* @param[in] aLinkMetricsFlags A pointer to flags specifying what metrics to query. Should be `NULL` when
* `aSeriesFlags` is `0`.
* @param[in] aCallback A pointer to a function that is called when Link Metrics Management Response is
* received.
* @param[in] aCallbackContext A pointer to application-specific context.
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics Management Request message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Link Metrics Management Request message.
* @retval OT_ERROR_INVALID_ARGS @p aSeriesId is not within the valid range.
*
*/
otError otLinkMetricsConfigForwardTrackingSeries(otInstance * aInstance,
const otIp6Address * aDestination,
uint8_t aSeriesId,
otLinkMetricsSeriesFlags aSeriesFlags,
const otLinkMetrics * aLinkMetricsFlags,
otLinkMetricsMgmtResponseCallback aCallback,
void * aCallbackContext);
/**
* This function sends an MLE Link Probe message.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aDestination A pointer to the destination address.
* @param[in] aSeriesId The Series ID [1, 254] which the Probe message aims at.
* @param[in] aLength The length of the data payload in Link Probe TLV, [0, 64] (per Thread 1.2 spec, 4.4.37).
*
* @retval OT_ERROR_NONE Successfully sent a Link Probe message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Link Probe message.
* @retval OT_ERROR_INVALID_ARGS @p aSeriesId or @p aLength is not within the valid range.
*
*/
otError otLinkMetricsSendLinkProbe(otInstance * aInstance,
const otIp6Address *aDestination,
uint8_t aSeriesId,
uint8_t aLength);
/**
* @}
*
+1
View File
@@ -137,6 +137,7 @@ size_nrf52840_version()
"DUA=1"
"MLR=1"
"CSL_RECEIVER=1"
"LINK_METRICS=1"
)
fi
+57 -1
View File
@@ -59,7 +59,7 @@ Done
- [leaderdata](#leaderdata)
- [leaderpartitionid](#leaderpartitionid)
- [leaderweight](#leaderweight)
- [linkmetrics](#linkmetrics-query-ipaddr-single-pqmr)
- [linkmetrics](#linkmetrics-mgmt-ipaddr-forward-seriesid-ldraxpqmr)
- [linkquality](#linkquality-extaddr)
- [log](#log-filename-filename)
- [mac](#mac-retries-direct)
@@ -1143,6 +1143,44 @@ Set the Thread Leader Weight.
Done
```
### linkmetrics mgmt \<ipaddr\> forward \<seriesid\> [ldraX][pqmr]
Send a Link Metrics Management Request to configure a Forward Tracking Series.
- ipaddr: Peer address.
- seriesid: The Series ID.
- ldraX: This specifies which frames are to be accounted.
- l: MLE Link Probe.
- d: MAC Data.
- r: MAC Data Request.
- a: MAC Ack.
- X: This represents none of the above flags, i.e., to stop accounting and remove the series. This can only be used without any other flags.
- pqmr: This specifies what metrics to query.
- p: Layer 2 Number of PDUs received.
- q: Layer 2 LQI.
- m: Link Margin.
- r: RSSI.
```bash
> linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr
Done
> Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
Status: SUCCESS
```
### linkmetrics probe \<ipaddr\> \<seriesid\> \<length\>
Send a MLE Link Probe message to the peer.
- ipaddr: Peer address.
- seriesid: The Series ID for which this Probe message targets at.
- length: The length of the Probe message, valid range: [0, 64].
```bash
> linkmetrics probe fe80:0:0:0:3092:f334:1455:1ad2 1 10
Done
```
### linkmetrics query \<ipaddr\> single [pqmr]
Perform a Link Metrics query (Single Probe).
@@ -1164,6 +1202,24 @@ Done
- RSSI: -18 (dBm) (Exponential Moving Average)
```
### linkmetrics query \<ipaddr\> forward \<seriesid\>
Perform a Link Metrics query (Forward Tracking Series).
- ipaddr: Peer address.
- seriesid: The Series ID.
```bash
> linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1
Done
> Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2
- PDU Counter: 2 (Count/Summation)
- LQI: 76 (Exponential Moving Average)
- Margin: 82 (dB) (Exponential Moving Average)
- RSSI: -18 (dBm) (Exponential Moving Average)
```
### linkquality \<extaddr\>
Get the link quality on the link to a given extended address.
+222 -48
View File
@@ -1904,12 +1904,15 @@ exit:
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
const otLinkMetricsValues *aMetricsValues,
uint8_t aStatus,
void * aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues);
static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus);
}
void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues)
void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
const otLinkMetricsValues *aMetricsValues,
uint8_t aStatus)
{
const char kLinkMetricsTypeCount[] = "(Count/Summation)";
const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
@@ -1918,25 +1921,87 @@ void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress, const ot
OutputIp6Address(*aAddress);
OutputLine("");
if (aMetricsValues->mMetrics.mPduCount)
if (aMetricsValues != nullptr)
{
OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount);
if (aMetricsValues->mMetrics.mPduCount)
{
OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount);
}
if (aMetricsValues->mMetrics.mLqi)
{
OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mLinkMargin)
{
OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mRssi)
{
OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
}
}
else
{
OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
}
}
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus);
}
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus)
{
OutputFormat("Received Link Metrics Management Response from: ");
OutputIp6Address(*aAddress);
OutputLine("");
OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus));
}
const char *Interpreter::LinkMetricsStatusToStr(uint8_t aStatus)
{
uint8_t strIndex = 0;
static const char *linkMetricsStatusText[] = {
"Success",
"Cannot support new series",
"Series ID already registered",
"Series ID not recognized",
"No matching series ID",
"Other error",
"Unknown error",
};
switch (aStatus)
{
case OT_LINK_METRICS_STATUS_SUCCESS:
strIndex = 0;
break;
case OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES:
strIndex = 1;
break;
case OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED:
strIndex = 2;
break;
case OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED:
strIndex = 3;
break;
case OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED:
strIndex = 4;
break;
case OT_LINK_METRICS_STATUS_OTHER_ERROR:
strIndex = 5;
break;
default:
strIndex = 6;
break;
}
if (aMetricsValues->mMetrics.mLqi)
{
OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mLinkMargin)
{
OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mRssi)
{
OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
}
return linkMetricsStatusText[strIndex];
}
otError Interpreter::ProcessLinkMetrics(uint8_t aArgsLength, char *aArgs[])
@@ -1949,6 +2014,14 @@ otError Interpreter::ProcessLinkMetrics(uint8_t aArgsLength, char *aArgs[])
{
error = ProcessLinkMetricsQuery(aArgsLength - 1, aArgs + 1);
}
else if (strcmp(aArgs[0], "mgmt") == 0)
{
error = ProcessLinkMetricsMgmt(aArgsLength - 1, aArgs + 1);
}
else if (strcmp(aArgs[0], "probe") == 0)
{
error = ProcessLinkMetricsProbe(aArgsLength - 1, aArgs + 1);
}
exit:
return error;
@@ -1959,49 +2032,150 @@ otError Interpreter::ProcessLinkMetricsQuery(uint8_t aArgsLength, char *aArgs[])
otError error = OT_ERROR_INVALID_ARGS;
otIp6Address address;
otLinkMetrics linkMetrics;
long seriesId = 0;
VerifyOrExit(aArgsLength >= 2);
VerifyOrExit(aArgsLength == 3);
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address));
memset(&linkMetrics, 0, sizeof(otLinkMetrics));
if (strcmp(aArgs[1], "single") == 0)
{
VerifyOrExit(aArgsLength == 3);
for (char *arg = aArgs[2]; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
linkMetrics.mPduCount = true;
break;
case 'q':
linkMetrics.mLqi = true;
break;
case 'm':
linkMetrics.mLinkMargin = true;
break;
case 'r':
linkMetrics.mRssi = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
error = otLinkMetricsQuery(mInstance, &address, static_cast<uint8_t>(seriesId), &linkMetrics,
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[2]));
error = otLinkMetricsQuery(mInstance, &address, /* aSeriesId */ 0, &linkMetrics,
&Interpreter::HandleLinkMetricsReport, this);
}
else if (strcmp(aArgs[1], "forward") == 0)
{
uint8_t seriesId;
SuccessOrExit(error = ParseAsUint8(aArgs[2], seriesId));
error = otLinkMetricsQuery(mInstance, &address, seriesId, nullptr, &Interpreter::HandleLinkMetricsReport, this);
}
exit:
return error;
}
otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, char *aFlags)
{
otError error = OT_ERROR_NONE;
memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
for (char *arg = aFlags; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
aLinkMetrics.mPduCount = true;
break;
case 'q':
aLinkMetrics.mLqi = true;
break;
case 'm':
aLinkMetrics.mLinkMargin = true;
break;
case 'r':
aLinkMetrics.mRssi = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
exit:
return error;
}
otError Interpreter::ProcessLinkMetricsMgmt(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
otIp6Address address;
otLinkMetricsSeriesFlags seriesFlags;
bool clear = false;
VerifyOrExit(aArgsLength >= 2);
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address));
memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
if (strcmp(aArgs[1], "forward") == 0)
{
uint8_t seriesId;
otLinkMetrics linkMetrics;
VerifyOrExit(aArgsLength >= 4);
memset(&linkMetrics, 0, sizeof(otLinkMetrics));
SuccessOrExit(error = ParseAsUint8(aArgs[2], seriesId));
for (char *arg = aArgs[3]; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'l':
seriesFlags.mLinkProbe = true;
break;
case 'd':
seriesFlags.mMacData = true;
break;
case 'r':
seriesFlags.mMacDataRequest = true;
break;
case 'a':
seriesFlags.mMacAck = true;
break;
case 'X':
VerifyOrExit(arg == aArgs[3] && *(arg + 1) == '\0' && aArgsLength == 4,
error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X'
clear = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
if (!clear)
{
VerifyOrExit(aArgsLength == 5);
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
}
error = otLinkMetricsConfigForwardTrackingSeries(mInstance, &address, seriesId, seriesFlags,
clear ? nullptr : &linkMetrics,
&Interpreter::HandleLinkMetricsMgmtResponse, this);
}
exit:
return error;
}
otError Interpreter::ProcessLinkMetricsProbe(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
otIp6Address address;
uint8_t seriesId = 0;
uint8_t length = 0;
VerifyOrExit(aArgsLength == 3);
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address));
SuccessOrExit(error = ParseAsUint8(aArgs[1], seriesId));
SuccessOrExit(error = ParseAsUint8(aArgs[2], length));
error = otLinkMetricsSendLinkProbe(mInstance, &address, seriesId, length);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#if OPENTHREAD_FTD
+14 -1
View File
@@ -358,6 +358,10 @@ private:
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
otError ProcessLinkMetrics(uint8_t aArgsLength, char *aArgs[]);
otError ProcessLinkMetricsQuery(uint8_t aArgsLength, char *aArgs[]);
otError ProcessLinkMetricsMgmt(uint8_t aArgsLength, char *aArgs[]);
otError ProcessLinkMetricsProbe(uint8_t aArgsLength, char *aArgs[]);
otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, char *aFlags);
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
otError ProcessMlr(uint8_t aArgsLength, char *aArgs[]);
@@ -526,9 +530,18 @@ private:
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
static void HandleLinkMetricsReport(const otIp6Address * aAddress,
const otLinkMetricsValues *aMetricsValues,
uint8_t aStatus,
void * aContext);
void HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues);
void HandleLinkMetricsReport(const otIp6Address * aAddress,
const otLinkMetricsValues *aMetricsValues,
uint8_t aStatus);
static void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext);
void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus);
const char *LinkMetricsStatusToStr(uint8_t aStatus);
#endif
static Interpreter &GetOwner(OwnerLocator &aOwnerLocator);
+30 -2
View File
@@ -49,12 +49,40 @@ otError otLinkMetricsQuery(otInstance * aInstance,
otLinkMetricsReportCallback aCallback,
void * aCallbackContext)
{
OT_ASSERT(aDestination != nullptr && aLinkMetricsFlags != nullptr);
OT_ASSERT(aDestination != nullptr);
static_cast<Instance *>(aInstance)->Get<LinkMetrics>().SetLinkMetricsReportCallback(aCallback, aCallbackContext);
return static_cast<Instance *>(aInstance)->Get<LinkMetrics>().LinkMetricsQuery(
static_cast<const Ip6::Address &>(*aDestination), aSeriesId, *aLinkMetricsFlags);
static_cast<const Ip6::Address &>(*aDestination), aSeriesId, aLinkMetricsFlags);
}
otError otLinkMetricsConfigForwardTrackingSeries(otInstance * aInstance,
const otIp6Address * aDestination,
uint8_t aSeriesId,
const otLinkMetricsSeriesFlags aSeriesFlags,
const otLinkMetrics * aLinkMetricsFlags,
otLinkMetricsMgmtResponseCallback aCallback,
void * aCallbackContext)
{
OT_ASSERT(aDestination != nullptr);
static_cast<Instance *>(aInstance)->Get<LinkMetrics>().SetLinkMetricsMgmtResponseCallback(aCallback,
aCallbackContext);
return static_cast<Instance *>(aInstance)->Get<LinkMetrics>().SendMgmtRequestForwardTrackingSeries(
static_cast<const Ip6::Address &>(*aDestination), aSeriesId, aSeriesFlags, aLinkMetricsFlags);
}
otError otLinkMetricsSendLinkProbe(otInstance * aInstance,
const otIp6Address *aDestination,
uint8_t aSeriesId,
uint8_t aLength)
{
OT_ASSERT(aDestination != nullptr);
return static_cast<Instance *>(aInstance)->Get<LinkMetrics>().SendLinkProbe(
static_cast<const Ip6::Address &>(*aDestination), aSeriesId, aLength);
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
+10
View File
@@ -250,4 +250,14 @@
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED
*
* The max number of series that a Link Metrics Subject can track simultaneously.
*
*/
#ifndef OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED 10
#endif
#endif // CONFIG_MLE_H_
+7
View File
@@ -1298,6 +1298,10 @@ void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame,
if ((aError == OT_ERROR_NONE) && ackRequested && (aAckFrame != nullptr) && (neighbor != nullptr))
{
neighbor->GetLinkInfo().AddRss(aAckFrame->GetRssi());
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
neighbor->AggregateLinkMetrics(/* aSeriesId */ 0, aAckFrame->GetType(), aAckFrame->GetLqi(),
aAckFrame->GetRssi());
#endif
#if OPENTHREAD_FTD
if (aAckFrame->GetVersion() == Frame::kFcfFrameVersion2015)
{
@@ -1903,6 +1907,9 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, otError aError)
if (neighbor != nullptr)
{
neighbor->GetLinkInfo().AddRss(aFrame->GetRssi());
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
neighbor->AggregateLinkMetrics(/* aSeriesId */ 0, aFrame->GetType(), aFrame->GetLqi(), aFrame->GetRssi());
#endif
if (aFrame->GetSecurityEnabled())
{
+522 -137
View File
@@ -44,39 +44,182 @@
namespace ot {
void LinkMetricsSeriesInfo::Init(uint8_t aSeriesId,
const SeriesFlags & aSeriesFlags,
const otLinkMetrics &aLinkMetricsFlags)
{
mSeriesId = aSeriesId;
mSeriesFlags = aSeriesFlags;
mLinkMetrics.mPduCount = aLinkMetricsFlags.mPduCount;
mLinkMetrics.mLqi = aLinkMetricsFlags.mLqi;
mLinkMetrics.mLinkMargin = aLinkMetricsFlags.mLinkMargin;
mLinkMetrics.mRssi = aLinkMetricsFlags.mRssi;
mRssAverager.Reset();
mLqiAverager.Reset();
mPduCount = 0;
}
void LinkMetricsSeriesInfo::AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss)
{
if (IsFrameTypeMatch(aFrameType))
{
mPduCount++;
mLqiAverager.Add(aLqi);
IgnoreError(mRssAverager.Add(aRss));
}
}
void LinkMetricsSeriesInfo::GetLinkMetrics(otLinkMetrics &aLinkMetrics) const
{
aLinkMetrics.mPduCount = mLinkMetrics.mPduCount;
aLinkMetrics.mLqi = mLinkMetrics.mLqi;
aLinkMetrics.mLinkMargin = mLinkMetrics.mLinkMargin;
aLinkMetrics.mRssi = mLinkMetrics.mRssi;
}
bool LinkMetricsSeriesInfo::IsFrameTypeMatch(uint8_t aFrameType) const
{
bool match = false;
switch (aFrameType)
{
case kSeriesTypeLinkProbe:
VerifyOrExit(!mSeriesFlags.IsMacDataFlagSet()); // Ignore this when Mac Data is accounted
match = mSeriesFlags.IsLinkProbeFlagSet();
break;
case Mac::Frame::kFcfFrameData:
match = mSeriesFlags.IsMacDataFlagSet();
break;
case Mac::Frame::kFcfFrameMacCmd:
match = mSeriesFlags.IsMacDataRequestFlagSet();
break;
case Mac::Frame::kFcfFrameAck:
match = mSeriesFlags.IsMacAckFlagSet();
break;
default:
break;
}
exit:
return match;
}
LinkMetrics::LinkMetrics(Instance &aInstance)
: InstanceLocator(aInstance)
, mLinkMetricsReportCallback(nullptr)
, mLinkMetricsReportCallbackContext(nullptr)
, mLinkMetricsSeriesInfoPool()
{
}
otError LinkMetrics::LinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const otLinkMetrics &aLinkMetricsFlags)
const otLinkMetrics *aLinkMetricsFlags)
{
otError error;
LinkMetricsTypeIdFlags typeIdFlags[kMaxTypeIdFlags];
uint8_t typeIdFlagsCount = TypeIdFlagsFromLinkMetricsFlags(typeIdFlags, aLinkMetricsFlags);
uint8_t typeIdFlagsCount = 0;
if (aLinkMetricsFlags != nullptr)
{
typeIdFlagsCount = TypeIdFlagsFromLinkMetricsFlags(typeIdFlags, *aLinkMetricsFlags);
}
if (aSeriesId != 0)
{
VerifyOrExit(typeIdFlagsCount == 0, error = OT_ERROR_INVALID_ARGS);
}
error = SendLinkMetricsQuery(aDestination, aSeriesId, typeIdFlags, typeIdFlagsCount);
exit:
return error;
}
otError LinkMetrics::AppendLinkMetricsReport(Message &aMessage, const Message &aRequestMessage)
otError LinkMetrics::SendMgmtRequestForwardTrackingSeries(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const otLinkMetricsSeriesFlags &aSeriesFlags,
const otLinkMetrics * aLinkMetricsFlags)
{
otError error = OT_ERROR_NONE;
Tlv tlv;
uint8_t queryId;
bool hasQueryId = false;
uint8_t length = 0;
uint16_t startOffset = aMessage.GetLength();
uint16_t offset;
uint16_t endOffset;
otLinkMetrics linkMetrics;
otError error = OT_ERROR_NONE;
uint8_t subTlvs[sizeof(Tlv) + sizeof(uint8_t) * 2 + sizeof(LinkMetricsTypeIdFlags) * kMaxTypeIdFlags];
Tlv * forwardProbingRegistrationSubTlv = reinterpret_cast<Tlv *>(subTlvs);
SeriesFlags *seriesFlags = reinterpret_cast<SeriesFlags *>(subTlvs + sizeof(Tlv) + sizeof(aSeriesId));
uint8_t typeIdFlagsOffset = sizeof(Tlv) + sizeof(uint8_t) * 2;
uint8_t typeIdFlagsCount = 0;
memset(&linkMetrics, 0, sizeof(linkMetrics));
// Directly transform `aLinkMetricsFlags` into LinkMetricsTypeIdFlags and put them into `subTlvs`
if (aLinkMetricsFlags != nullptr)
{
typeIdFlagsCount = TypeIdFlagsFromLinkMetricsFlags(
reinterpret_cast<LinkMetricsTypeIdFlags *>(subTlvs + typeIdFlagsOffset), *aLinkMetricsFlags);
}
VerifyOrExit(aSeriesId > kQueryIdSingleProbe, error = OT_ERROR_INVALID_ARGS);
forwardProbingRegistrationSubTlv->SetType(kForwardProbingRegistration);
forwardProbingRegistrationSubTlv->SetLength(
sizeof(uint8_t) * 2 +
sizeof(LinkMetricsTypeIdFlags) *
typeIdFlagsCount); // SeriesId + SeriesFlags + typeIdFlagsCount * LinkMetricsTypeIdFlags
memcpy(subTlvs + sizeof(Tlv), &aSeriesId, sizeof(aSeriesId));
seriesFlags->Clear();
if (aSeriesFlags.mLinkProbe)
{
seriesFlags->SetLinkProbeFlag();
}
if (aSeriesFlags.mMacData)
{
seriesFlags->SetMacDataFlag();
}
if (aSeriesFlags.mMacDataRequest)
{
seriesFlags->SetMacDataRequestFlag();
}
if (aSeriesFlags.mMacAck)
{
seriesFlags->SetMacAckFlag();
}
error = Get<Mle::MleRouter>().SendLinkMetricsManagementRequest(aDestination, subTlvs,
forwardProbingRegistrationSubTlv->GetSize());
exit:
otLogDebgMle("SendMgmtRequestForwardTrackingSeries, error:%s, Series ID:%u", otThreadErrorToString(error),
aSeriesId);
return error;
}
otError LinkMetrics::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t aLength)
{
otError error = OT_ERROR_NONE;
uint8_t buf[kLinkProbeMaxLen];
VerifyOrExit(aLength <= LinkMetrics::kLinkProbeMaxLen && aSeriesId != kQueryIdSingleProbe &&
aSeriesId != kSeriesIdAllSeries,
error = OT_ERROR_INVALID_ARGS);
error = Get<Mle::MleRouter>().SendLinkProbe(aDestination, aSeriesId, buf, aLength);
exit:
otLogDebgMle("SendLinkProbe, error:%s, Series ID:%u", otThreadErrorToString(error), aSeriesId);
return error;
}
otError LinkMetrics::AppendLinkMetricsReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor)
{
otError error = OT_ERROR_NONE;
Tlv tlv;
uint8_t queryId;
bool hasQueryId = false;
uint8_t length = 0;
uint16_t startOffset = aMessage.GetLength();
uint16_t offset;
uint16_t endOffset;
otLinkMetricsValues linkMetricsValues;
memset(&linkMetricsValues, 0, sizeof(linkMetricsValues));
SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offset,
endOffset)); // `endOffset` is used to store tlv length here
@@ -95,43 +238,9 @@ otError LinkMetrics::AppendLinkMetricsReport(Message &aMessage, const Message &a
break;
case kLinkMetricsQueryOptions:
for (uint16_t index = offset + sizeof(tlv), endIndex = static_cast<uint16_t>(offset + tlv.GetSize());
index < endIndex; index += sizeof(LinkMetricsTypeIdFlags))
{
LinkMetricsTypeIdFlags typeIdFlags;
SuccessOrExit(error = aRequestMessage.Read(index, typeIdFlags));
switch (typeIdFlags.GetRawValue())
{
case kTypeIdFlagPdu:
VerifyOrExit(!linkMetrics.mPduCount, error = OT_ERROR_PARSE);
linkMetrics.mPduCount = true;
break;
case kTypeIdFlagLqi:
VerifyOrExit(!linkMetrics.mLqi, error = OT_ERROR_PARSE);
linkMetrics.mLqi = true;
break;
case kTypeIdFlagLinkMargin:
VerifyOrExit(!linkMetrics.mLinkMargin, error = OT_ERROR_PARSE);
linkMetrics.mLinkMargin = true;
break;
case kTypeIdFlagRssi:
VerifyOrExit(!linkMetrics.mRssi, error = OT_ERROR_PARSE);
linkMetrics.mRssi = true;
break;
default:
if (typeIdFlags.IsExtendedFlagSet())
{
index += sizeof(uint8_t); // Skip the additional second flags byte.
}
break;
}
}
SuccessOrExit(error = ReadTypeIdFlagsFromMessage(aRequestMessage, offset + sizeof(tlv),
static_cast<uint16_t>(offset + tlv.GetSize()),
linkMetricsValues.mMetrics));
break;
default:
@@ -147,13 +256,44 @@ otError LinkMetrics::AppendLinkMetricsReport(Message &aMessage, const Message &a
tlv.SetType(Mle::Tlv::kLinkMetricsReport);
SuccessOrExit(error = aMessage.Append(tlv));
if (queryId == 0)
if (queryId == kQueryIdSingleProbe)
{
SuccessOrExit(error = AppendSingleProbeLinkMetricsReport(aMessage, length, linkMetrics, aRequestMessage));
linkMetricsValues.mPduCountValue = aRequestMessage.GetPsduCount();
linkMetricsValues.mLqiValue = aRequestMessage.GetAverageLqi();
linkMetricsValues.mLinkMarginValue =
LinkQualityInfo::ConvertRssToLinkMargin(Get<Mac::Mac>().GetNoiseFloor(), aRequestMessage.GetAverageRss()) *
255 / 130; // Linear scale Link Margin from [0, 130] to [0, 255]
linkMetricsValues.mRssiValue =
(aRequestMessage.GetAverageRss() + 130) * 255 / 130; // Linear scale rss from [-130, 0] to [0, 255]
SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, length, linkMetricsValues));
}
else
{
ExitNow(error = OT_ERROR_NOT_IMPLEMENTED);
LinkMetricsSeriesInfo *seriesInfo = aNeighbor.GetForwardTrackingSeriesInfo(queryId);
if (seriesInfo == nullptr)
{
SuccessOrExit(
error = AppendStatusSubTlvToMessage(aMessage, length, OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED));
}
else if (seriesInfo->GetPduCount() == 0)
{
SuccessOrExit(error = AppendStatusSubTlvToMessage(aMessage, length,
OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED));
}
else
{
seriesInfo->GetLinkMetrics(linkMetricsValues.mMetrics);
linkMetricsValues.mPduCountValue = seriesInfo->GetPduCount();
linkMetricsValues.mLqiValue = seriesInfo->GetAverageLqi();
linkMetricsValues.mLinkMarginValue =
LinkQualityInfo::ConvertRssToLinkMargin(Get<Mac::Mac>().GetNoiseFloor(),
seriesInfo->GetAverageRss()) *
255 / 130; // Linear scale Link Margin from [0, 130] to [0, 255]
linkMetricsValues.mRssiValue =
(seriesInfo->GetAverageRss() + 130) * 255 / 130; // Linear scale RSSI from [-130, 0] to [0, 255]
SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, length, linkMetricsValues));
}
}
tlv.SetLength(length);
@@ -164,92 +304,239 @@ exit:
return error;
}
otError LinkMetrics::HandleLinkMetricsManagementRequest(const Message & aMessage,
Neighbor & aNeighbor,
otLinkMetricsStatus &aStatus)
{
otError error = OT_ERROR_NONE;
Tlv tlv;
uint8_t seriesId;
SeriesFlags seriesFlags;
otLinkMetrics linkMetrics;
bool hasForwardProbingRegistrationTlv = false;
uint16_t offset;
uint16_t length;
uint16_t index = 0;
SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, length));
while (index < length)
{
uint16_t pos = offset + index;
SuccessOrExit(aMessage.Read(pos, tlv));
pos += sizeof(tlv);
switch (tlv.GetType())
{
case kForwardProbingRegistration:
VerifyOrExit(!hasForwardProbingRegistrationTlv, error = OT_ERROR_PARSE);
VerifyOrExit(tlv.GetLength() >= sizeof(seriesId) + sizeof(seriesFlags), error = OT_ERROR_PARSE);
SuccessOrExit(aMessage.Read(pos, seriesId));
pos += sizeof(seriesId);
SuccessOrExit(aMessage.Read(pos, seriesFlags));
pos += sizeof(seriesFlags);
SuccessOrExit(error = ReadTypeIdFlagsFromMessage(
aMessage, pos, static_cast<uint16_t>(offset + index + tlv.GetSize()), linkMetrics));
hasForwardProbingRegistrationTlv = true;
break;
default:
break;
}
index += tlv.GetSize();
}
if (hasForwardProbingRegistrationTlv)
{
aStatus = ConfigureForwardTrackingSeries(seriesId, seriesFlags, linkMetrics, aNeighbor);
}
exit:
return error;
}
otError LinkMetrics::HandleLinkMetricsManagementResponse(const Message &aMessage, const Ip6::Address &aAddress)
{
otError error = OT_ERROR_NONE;
Tlv tlv;
uint16_t offset;
uint16_t length;
uint16_t index = 0;
otLinkMetricsStatus status;
bool hasStatus = false;
VerifyOrExit(mLinkMetricsMgmtResponseCallback != nullptr);
SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, length));
while (index < length)
{
SuccessOrExit(aMessage.Read(offset + index, tlv));
switch (tlv.GetType())
{
case kLinkMetricsStatus:
VerifyOrExit(!hasStatus, error = OT_ERROR_PARSE);
VerifyOrExit(tlv.GetLength() == sizeof(status), error = OT_ERROR_PARSE);
SuccessOrExit(aMessage.Read(offset + index + sizeof(tlv), status));
hasStatus = true;
break;
default:
break;
}
index += tlv.GetSize();
}
VerifyOrExit(hasStatus, error = OT_ERROR_PARSE);
mLinkMetricsMgmtResponseCallback(&aAddress, status, mLinkMetricsMgmtResponseCallbackContext);
exit:
return error;
}
void LinkMetrics::HandleLinkMetricsReport(const Message & aMessage,
uint16_t aOffset,
uint16_t aLength,
const Ip6::Address &aAddress)
{
otError error = OT_ERROR_NONE;
otLinkMetricsValues metricsValues;
uint8_t metricsRawValue;
uint16_t pos = aOffset;
uint16_t endPos = aOffset + aLength;
Tlv tlv;
LinkMetricsTypeIdFlags typeIdFlags;
bool hasStatus = false;
bool hasReport = false;
otLinkMetricsStatus status;
OT_UNUSED_VARIABLE(error);
VerifyOrExit(mLinkMetricsReportCallback != nullptr);
memset(&metricsValues, 0, sizeof(metricsValues));
otLogDebgMle("Received Link Metrics Report");
while (pos < endPos)
{
SuccessOrExit(aMessage.Read(pos, tlv));
VerifyOrExit(tlv.GetType() == kLinkMetricsReportSub);
pos += sizeof(Tlv);
VerifyOrExit(pos + tlv.GetLength() <= endPos);
IgnoreError(aMessage.Read(pos, typeIdFlags));
VerifyOrExit(pos + tlv.GetLength() <= endPos, error = OT_ERROR_PARSE);
if (typeIdFlags.IsExtendedFlagSet())
switch (tlv.GetType())
{
pos += tlv.GetLength(); // Skip the whole sub-TLV if `E` flag is set
continue;
}
pos += sizeof(LinkMetricsTypeIdFlags);
switch (typeIdFlags.GetRawValue())
{
case kTypeIdFlagPdu:
metricsValues.mMetrics.mPduCount = true;
IgnoreError(aMessage.Read(pos, metricsValues.mPduCountValue));
pos += sizeof(uint32_t);
otLogDebgMle(" - PDU Counter: %d (Count/Summation)", metricsValues.mPduCountValue);
case kLinkMetricsStatus:
VerifyOrExit(!hasStatus && !hasReport,
error = OT_ERROR_DROP); // There should be either: one Status TLV or some Report-Sub TLVs
VerifyOrExit(tlv.GetLength() == sizeof(status), error = OT_ERROR_PARSE);
SuccessOrExit(aMessage.Read(pos, status));
hasStatus = true;
pos += sizeof(status);
break;
case kTypeIdFlagLqi:
metricsValues.mMetrics.mLqi = true;
IgnoreError(aMessage.Read(pos, metricsValues.mLqiValue));
pos += sizeof(uint8_t);
otLogDebgMle(" - LQI: %d (Exponential Moving Average)", metricsValues.mLqiValue);
break;
case kLinkMetricsReportSub:
VerifyOrExit(!hasStatus,
error = OT_ERROR_DROP); // There shouldn't be any Report-Sub TLV when there's a Status TLV
VerifyOrExit(tlv.GetLength() == sizeof(typeIdFlags), error = OT_ERROR_PARSE);
SuccessOrExit(aMessage.Read(pos, typeIdFlags));
if (typeIdFlags.IsExtendedFlagSet())
{
pos += tlv.GetLength(); // Skip the whole sub-TLV if `E` flag is set
continue;
}
hasReport = true;
pos += sizeof(LinkMetricsTypeIdFlags);
case kTypeIdFlagLinkMargin:
metricsValues.mMetrics.mLinkMargin = true;
IgnoreError(aMessage.Read(pos, metricsRawValue));
metricsValues.mLinkMarginValue =
metricsRawValue * 130 / 255; // Reverse operation for linear scale, map from [0, 255] to [0, 130]
pos += sizeof(uint8_t);
otLogDebgMle(" - Margin: %d (dB) (Exponential Moving Average)", metricsValues.mLinkMarginValue);
break;
switch (typeIdFlags.GetRawValue())
{
case kTypeIdFlagPdu:
metricsValues.mMetrics.mPduCount = true;
SuccessOrExit(aMessage.Read(pos, metricsValues.mPduCountValue));
pos += sizeof(uint32_t);
otLogDebgMle(" - PDU Counter: %d (Count/Summation)", metricsValues.mPduCountValue);
break;
case kTypeIdFlagRssi:
metricsValues.mMetrics.mRssi = true;
IgnoreError(aMessage.Read(pos, metricsRawValue));
metricsValues.mRssiValue =
metricsRawValue * 130 / 255 - 130; // Reverse operation for linear scale, map from [0, 255] to [-130, 0]
pos += sizeof(uint8_t);
otLogDebgMle(" - RSSI: %d (dBm) (Exponential Moving Average)", metricsValues.mRssiValue);
break;
case kTypeIdFlagLqi:
metricsValues.mMetrics.mLqi = true;
SuccessOrExit(aMessage.Read(pos, metricsValues.mLqiValue));
pos += sizeof(uint8_t);
otLogDebgMle(" - LQI: %d (Exponential Moving Average)", metricsValues.mLqiValue);
break;
default:
case kTypeIdFlagLinkMargin:
metricsValues.mMetrics.mLinkMargin = true;
SuccessOrExit(aMessage.Read(pos, metricsRawValue));
metricsValues.mLinkMarginValue =
metricsRawValue * 130 / 255; // Reverse operation for linear scale, map from [0, 255] to [0, 130]
pos += sizeof(uint8_t);
otLogDebgMle(" - Margin: %d (dB) (Exponential Moving Average)", metricsValues.mLinkMarginValue);
break;
case kTypeIdFlagRssi:
metricsValues.mMetrics.mRssi = true;
SuccessOrExit(aMessage.Read(pos, metricsRawValue));
metricsValues.mRssiValue = metricsRawValue * 130 / 255 -
130; // Reverse operation for linear scale, map from [0, 255] to [-130, 0]
pos += sizeof(uint8_t);
otLogDebgMle(" - RSSI: %d (dBm) (Exponential Moving Average)", metricsValues.mRssiValue);
break;
default:
break;
}
break;
}
}
mLinkMetricsReportCallback(&aAddress, &metricsValues, mLinkMetricsReportCallbackContext);
if (hasStatus)
{
mLinkMetricsReportCallback(&aAddress, nullptr, status, mLinkMetricsReportCallbackContext);
}
else if (hasReport)
{
mLinkMetricsReportCallback(&aAddress, &metricsValues, OT_LINK_METRICS_STATUS_SUCCESS,
mLinkMetricsReportCallbackContext);
}
exit:
otLogDebgMle("HandleLinkMetricsReport, error:%s", otThreadErrorToString(error));
return;
}
otError LinkMetrics::HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId)
{
otError error = OT_ERROR_NONE;
uint16_t offset;
uint16_t length;
SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkProbe, offset, length));
VerifyOrExit(length >= sizeof(aSeriesId), error = OT_ERROR_PARSE);
SuccessOrExit(error = aMessage.Read(offset, aSeriesId));
VerifyOrExit(aSeriesId >= kQueryIdSingleProbe && aSeriesId <= kSeriesIdAllSeries, error = OT_ERROR_INVALID_ARGS);
exit:
return error;
}
void LinkMetrics::SetLinkMetricsReportCallback(otLinkMetricsReportCallback aCallback, void *aCallbackContext)
{
mLinkMetricsReportCallback = aCallback;
mLinkMetricsReportCallbackContext = aCallbackContext;
}
void LinkMetrics::SetLinkMetricsMgmtResponseCallback(otLinkMetricsMgmtResponseCallback aCallback,
void * aCallbackContext)
{
mLinkMetricsMgmtResponseCallback = aCallback;
mLinkMetricsMgmtResponseCallbackContext = aCallbackContext;
}
otError LinkMetrics::SendLinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const LinkMetricsTypeIdFlags *aTypeIdFlags,
@@ -299,58 +586,41 @@ exit:
return error;
}
otError LinkMetrics::AppendSingleProbeLinkMetricsReport(Message & aMessage,
uint8_t & aLength,
const otLinkMetrics &aLinkMetrics,
const Message & aRequestMessage)
otLinkMetricsStatus LinkMetrics::ConfigureForwardTrackingSeries(uint8_t aSeriesId,
const SeriesFlags & aSeriesFlags,
const otLinkMetrics &aLinkMetrics,
Neighbor & aNeighbor)
{
otError error = OT_ERROR_NONE;
LinkMetricsReportSubTlv metric;
otLinkMetricsStatus status = OT_LINK_METRICS_STATUS_SUCCESS;
aLength = 0;
// Link Metrics Report sub-TLVs
if (aLinkMetrics.mPduCount)
VerifyOrExit(0 < aSeriesId, status = OT_LINK_METRICS_STATUS_OTHER_ERROR);
if (aSeriesFlags.GetRawValue() == 0) // Remove the series
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagPdu));
metric.SetMetricsValue32(aRequestMessage.GetPsduCount());
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
if (aSeriesId == kSeriesIdAllSeries) // Remove all
{
aNeighbor.RemoveAllForwardTrackingSeriesInfo();
}
else
{
LinkMetricsSeriesInfo *seriesInfo = aNeighbor.RemoveForwardTrackingSeriesInfo(aSeriesId);
VerifyOrExit(seriesInfo != nullptr, status = OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED);
mLinkMetricsSeriesInfoPool.Free(*seriesInfo);
}
}
if (aLinkMetrics.mLqi)
else // Add a new series
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagLqi));
metric.SetMetricsValue8(aRequestMessage.GetAverageLqi()); // IEEE 802.15.4 LQI is in scale 0-255
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
LinkMetricsSeriesInfo *seriesInfo = aNeighbor.GetForwardTrackingSeriesInfo(aSeriesId);
VerifyOrExit(seriesInfo == nullptr, status = OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED);
seriesInfo = mLinkMetricsSeriesInfoPool.Allocate();
VerifyOrExit(seriesInfo != nullptr, status = OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES);
if (aLinkMetrics.mLinkMargin)
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagLinkMargin));
metric.SetMetricsValue8(
LinkQualityInfo::ConvertRssToLinkMargin(Get<Mac::Mac>().GetNoiseFloor(), aRequestMessage.GetAverageRss()) *
255 / 130); // Linear scale Link Margin from [0, 130] to [0, 255]
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
seriesInfo->Init(aSeriesId, aSeriesFlags, aLinkMetrics);
if (aLinkMetrics.mRssi)
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagRssi));
metric.SetMetricsValue8((aRequestMessage.GetAverageRss() + 130) * 255 /
130); // Linear scale rss from [-130, 0] to [0, 255]
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
aNeighbor.AddForwardTrackingSeriesInfo(*seriesInfo);
}
exit:
return error;
return status;
}
uint8_t LinkMetrics::TypeIdFlagsFromLinkMetricsFlags(LinkMetricsTypeIdFlags *aTypeIdFlags,
@@ -381,6 +651,121 @@ uint8_t LinkMetrics::TypeIdFlagsFromLinkMetricsFlags(LinkMetricsTypeIdFlags *aTy
return count;
}
otError LinkMetrics::ReadTypeIdFlagsFromMessage(const Message &aMessage,
uint8_t aStartPos,
uint8_t aEndPos,
otLinkMetrics &aLinkMetrics)
{
otError error = OT_ERROR_NONE;
memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
for (uint16_t pos = aStartPos; pos < aEndPos; pos += sizeof(LinkMetricsTypeIdFlags))
{
LinkMetricsTypeIdFlags typeIdFlags;
SuccessOrExit(aMessage.Read(pos, typeIdFlags));
switch (typeIdFlags.GetRawValue())
{
case kTypeIdFlagPdu:
VerifyOrExit(!aLinkMetrics.mPduCount, error = OT_ERROR_PARSE);
aLinkMetrics.mPduCount = true;
break;
case kTypeIdFlagLqi:
VerifyOrExit(!aLinkMetrics.mLqi, error = OT_ERROR_PARSE);
aLinkMetrics.mLqi = true;
break;
case kTypeIdFlagLinkMargin:
VerifyOrExit(!aLinkMetrics.mLinkMargin, error = OT_ERROR_PARSE);
aLinkMetrics.mLinkMargin = true;
break;
case kTypeIdFlagRssi:
VerifyOrExit(!aLinkMetrics.mRssi, error = OT_ERROR_PARSE);
aLinkMetrics.mRssi = true;
break;
default:
if (typeIdFlags.IsExtendedFlagSet())
{
pos += sizeof(uint8_t); // Skip the additional second flags byte.
}
break;
}
}
exit:
return error;
}
otError LinkMetrics::AppendReportSubTlvToMessage(Message & aMessage,
uint8_t & aLength,
const otLinkMetricsValues &aValues)
{
otError error = OT_ERROR_NONE;
LinkMetricsReportSubTlv metric;
aLength = 0;
// Link Metrics Report sub-TLVs
if (aValues.mMetrics.mPduCount)
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagPdu));
metric.SetMetricsValue32(aValues.mPduCountValue);
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
if (aValues.mMetrics.mLqi)
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagLqi));
metric.SetMetricsValue8(aValues.mLqiValue);
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
if (aValues.mMetrics.mLinkMargin)
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagLinkMargin));
metric.SetMetricsValue8(aValues.mLinkMarginValue);
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
if (aValues.mMetrics.mRssi)
{
metric.Init();
metric.SetMetricsTypeId(LinkMetricsTypeIdFlags(kTypeIdFlagRssi));
metric.SetMetricsValue8(aValues.mRssiValue);
SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
exit:
return error;
}
otError LinkMetrics::AppendStatusSubTlvToMessage(Message &aMessage, uint8_t &aLength, otLinkMetricsStatus aStatus)
{
otError error = OT_ERROR_NONE;
Tlv statusTlv;
statusTlv.SetType(kLinkMetricsStatus);
statusTlv.SetLength(sizeof(uint8_t));
SuccessOrExit(error = aMessage.AppendBytes(&statusTlv, sizeof(statusTlv)));
SuccessOrExit(error = aMessage.AppendBytes(&aStatus, sizeof(aStatus)));
aLength += statusTlv.GetSize();
exit:
return error;
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
+234 -17
View File
@@ -39,12 +39,15 @@
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#include <openthread/ip6.h>
#include <openthread/link.h>
#include "common/code_utils.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/pool.hpp"
#include "link_metrics_tlvs.hpp"
#include "link_quality.hpp"
#include "topology.hpp"
namespace ot {
@@ -58,8 +61,111 @@ namespace ot {
* @{
*/
/**
* This class represents one Series that is being tracked by the Subject.
*
* When an Initiator successfully configured a Forward Tracking Series, the Subject would use an instance of this class
* to track the information of the Series. The Subject has a `Pool` of `LinkMetricsSeriesInfo`. It would allocate one
* when a new Series comes, and free it when a Series finishes.
*
* This class inherits `LinkedListEntry` and each `Neighbor` has a list of `LinkMetricsSeriesInfo` so that the Subject
* could track per Series initiated by neighbors as long as it has available resources.
*
*/
class LinkMetricsSeriesInfo : public LinkedListEntry<LinkMetricsSeriesInfo>
{
friend class LinkedList<LinkMetricsSeriesInfo>;
friend class LinkedListEntry<LinkMetricsSeriesInfo>;
public:
///< This represents Link Probe when filtering frames to be accounted using Series Flag. There's
///< already `kFcfFrameData`, `kFcfFrameAck` and `kFcfFrameMacCmd`. This item is added so that we can
///< filter a Link Probe for series in the same way as other frames.
enum
{
kSeriesTypeLinkProbe = 0,
};
/**
* This method initializes the object.
*
* @param[in] aSeriesId The Series ID.
* @param[in] aSeriesFlags A reference to the Series Flags which specify what types of frames are to be
* accounted.
* @param[in] aLinkMetricsFlags A reference to flags specifying what metrics to query.
*
*/
void Init(uint8_t aSeriesId, const SeriesFlags &aSeriesFlags, const otLinkMetrics &aLinkMetrics);
/**
* This method gets the Series ID.
*
* @returns The Series ID.
*
*/
uint8_t GetSeriesId() const { return mSeriesId; }
/**
* This method gets the PDU count.
*
* @returns The PDU count.
*
*/
uint32_t GetPduCount() const { return mPduCount; }
/**
* This method gets the average LQI.
*
* @returns The average LQI.
*
*/
uint8_t GetAverageLqi() const { return mLqiAverager.GetAverage(); }
/**
* This method gets the average RSS.
*
* @returns The average RSS.
*
*/
int8_t GetAverageRss() const { return mRssAverager.GetAverage(); }
/**
* This method aggregates the Link Metrics data of a frame into this series.
*
* @param[in] aFrameType The type of the frame.
* @param[in] aLqi The LQI value.
* @param[in] aRss Ths RSS value.
*
*/
void AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss);
/**
* This methods gets the Link Metrics Flags.
*
* @param[out] aLinkMetrics A reference to a `LinkMetrics` object to get the values.
*
*/
void GetLinkMetrics(otLinkMetrics &aLinkMetrics) const;
private:
LinkMetricsSeriesInfo *mNext;
uint8_t mSeriesId;
SeriesFlags mSeriesFlags;
otLinkMetrics mLinkMetrics;
RssAverager mRssAverager;
LqiAverager mLqiAverager;
uint32_t mPduCount;
bool Matches(const uint8_t &aSeriesId) const { return mSeriesId == aSeriesId; }
bool IsFrameTypeMatch(uint8_t aFrameType) const;
};
class LinkMetrics : public InstanceLocator, private NonCopyable
{
friend class Neighbor;
public:
/**
* This constructor initializes an instance of the LinkMetrics class.
@@ -70,13 +176,13 @@ public:
explicit LinkMetrics(Instance &aInstance);
/**
* This function sends an MLE Data Request containing Link Metrics Query TLV to query Link Metrics data.
* This method sends an MLE Data Request containing Link Metrics Query TLV to query Link Metrics data.
*
* It could be either a Single Probe or a Forward Tracking Series.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aSeriesId The Series ID to query, 0 for single probe.
* @param[in] aLinkMetricsFlags A reference to flags specifying what metrics to query.
* @param[in] aLinkMetricsFlags A pointer to flags specifying what metrics to query.
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics query message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Data Request message.
@@ -85,20 +191,81 @@ public:
*/
otError LinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const otLinkMetrics &aLinkMetricsFlags);
const otLinkMetrics *aLinkMetricsFlags);
/**
* This method sends an MLE Link Metrics Management Request to configure/clear a Forward Tracking Series.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aSeriesId The Series ID.
* @param[in] aSeriesFlags A reference to the Series Flags which specify what types of frames are to be
* accounted.
* @param[in] aLinkMetricsFlags A pointer to flags specifying what metrics to query.
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics Management Request message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Link Metrics Management Request message.
* @retval OT_ERROR_INVALID_ARGS @p aSeriesId is not within the valid range.
*
*/
otError SendMgmtRequestForwardTrackingSeries(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const otLinkMetricsSeriesFlags &aSeriesFlags,
const otLinkMetrics * aLinkMetricsFlags);
/**
* This method sends an MLE Link Probe message.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aSeriesId The Series ID which the Probe message targets at.
* @param[in] aLength The length of the data payload in Link Probe TLV, [0, 64].
*
* @retval OT_ERROR_NONE Successfully sent a Link Probe message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Link Probe message.
* @retval OT_ERROR_INVALID_ARGS @p aSeriesId or @p aLength is not within the valid range.
*
*/
otError SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t aLength);
/**
* This method appends a Link Metrics Report to a message according to the Link Metrics query.
*
* @param[out] aMessage A reference to the message to append report.
* @param[in] aRequestMessage A reference to the message of the Data Request.
* @param[in] aNeighbor A reference to the neighbor who queries the report.
*
* @retval OT_ERROR_NONE Successfully appended the Thread Discovery TLV.
* @retval OT_ERROR_PARSE Cannot parse query sub TLV successfully.
* @retval OT_ERROR_INVALID_ARGS QueryId is invalid or any Type ID is invalid.
*
*/
otError AppendLinkMetricsReport(Message &aMessage, const Message &aRequestMessage);
otError AppendLinkMetricsReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor);
/**
* This method handles the received Link Metrics Management Request contained in @p aMessage and return a status.
*
* @param[in] aMessage A reference to the message that contains the Link Metrics Management Request.
* @param[in] aNeighbor A reference to the neighbor who sends the request.
* @param[out] aStatus A reference to the status which indicates the handling result.
*
* @retval OT_ERROR_NONE Successfully handled the Link Metrics Management Request.
* @retval OT_ERROR_PARSE Cannot parse sub-TLVs from @p aMessage successfully.
*
*/
otError HandleLinkMetricsManagementRequest(const Message & aMessage,
Neighbor & aNeighbor,
otLinkMetricsStatus &aStatus);
/**
* This method handles the received Link Metrics Management Response contained in @p aMessage.
*
* @param[in] aMessage A reference to the message that contains the Link Metrics Management Response.
* @param[in] aAddress A reference to the source address of the message.
*
* @retval OT_ERROR_NONE Successfully handled the Link Metrics Management Response.
* @retval OT_ERROR_PARSE Cannot parse sub-TLVs from @p aMessage successfully.
*
*/
otError HandleLinkMetricsManagementResponse(const Message &aMessage, const Ip6::Address &aAddress);
/**
* This method handles the received Link Metrics report contained in @p aMessage.
@@ -114,43 +281,93 @@ public:
uint16_t aLength,
const Ip6::Address &aAddress);
/**
* This method handles the Link Probe contained in @p aMessage.
*
* @param[in] aMessage A reference to the message that contains the Link Probe Message.
* @param[out] aSeriesId A reference to Series ID that parsed from the message.
*
* @retval OT_ERROR_NONE Successfully handled the Link Metrics Management Response.
* @retval OT_ERROR_PARSE Cannot parse sub-TLVs from @p aMessage successfully.
*
*/
otError HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId);
/**
* This method registers a callback to handle Link Metrics report received.
*
* @param[in] aCallback A pointer to a function that is called when link probing report is received
* @param[in] aCallback A pointer to a function that is called when a Link Metrics report is received.
* @param[in] aCallbackContext A pointer to application-specific context.
*
*/
void SetLinkMetricsReportCallback(otLinkMetricsReportCallback aCallback, void *aCallbackContext);
/**
* This method registers a callback to handle Link Metrics Management Response received.
*
* @param[in] aCallback A pointer to a function that is called when a Link Metrics Management Response is
* received.
* @param[in] aCallbackContext A pointer to application-specific context.
*
*/
void SetLinkMetricsMgmtResponseCallback(otLinkMetricsMgmtResponseCallback aCallback, void *aCallbackContext);
private:
/**
* TypeIdFlagPdu: 0x0_1_000_000 -> 0x40 ==> L bit set, type = 0 (count/summation), metric-enum = 0 (PDU rxed).
* TypeIdFlagLqi: 0x0_0_001_001 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 1 (LQI).
* TypeIdFlagLinkMargin: 0x0_0_001_010 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 2 (Link Margin).
* TypeIdFlagRssi: 0x0_0_001_011 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 3 (RSSI).
*
*/
enum
{
kMaxTypeIdFlags = 4,
kTypeIdFlagPdu =
0x40, ///< 0x0_1_000_000 -> 0x40 ==> L bit set, type = 0 (count/summation), metric-enum = 0 (PDU rxed)
kTypeIdFlagLqi = 0x09, ///< 0x0_0_001_001 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 1 (LQI)
kTypeIdFlagLinkMargin =
0x0a, ///< 0x0_0_001_010 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 2 (Link Margin)
kTypeIdFlagRssi = 0x0b, ///< 0x0_0_001_011 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 3 (RSSI)
kTypeIdFlagPdu = 0x40,
kTypeIdFlagLqi = 0x09,
kTypeIdFlagLinkMargin = 0x0a,
kTypeIdFlagRssi = 0x0b,
kMaxSeriesSupported =
OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED, ///< Max number of LinkMetricsSeriesInfo that could
///< be allocated by the pool.
kQueryIdSingleProbe = 0, ///< This query ID represents Single Probe.
kSeriesIdAllSeries = 255, ///< This series ID represents all series.
kLinkProbeMaxLen = 64, ///< Max length of data payload in Link Probe TLV.
};
otLinkMetricsReportCallback mLinkMetricsReportCallback;
void * mLinkMetricsReportCallbackContext;
otLinkMetricsReportCallback mLinkMetricsReportCallback;
void * mLinkMetricsReportCallbackContext;
otLinkMetricsMgmtResponseCallback mLinkMetricsMgmtResponseCallback;
void * mLinkMetricsMgmtResponseCallbackContext;
Pool<LinkMetricsSeriesInfo, kMaxSeriesSupported> mLinkMetricsSeriesInfoPool;
otError SendLinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const LinkMetricsTypeIdFlags *aTypeIdFlags,
uint8_t aTypeIdFlagsCount);
otError AppendSingleProbeLinkMetricsReport(Message & aMessage,
uint8_t & aLength,
const otLinkMetrics &aLinkMetrics,
const Message & aRequestMessage);
otLinkMetricsStatus ConfigureForwardTrackingSeries(uint8_t aSeriesId,
const SeriesFlags & aSeriesFlags,
const otLinkMetrics &aLinkMetrics,
Neighbor & aNeighbor);
static uint8_t TypeIdFlagsFromLinkMetricsFlags(LinkMetricsTypeIdFlags *aTypeIdFlags,
const otLinkMetrics & aLinkMetricsFlags);
static otError ReadTypeIdFlagsFromMessage(const Message &aMessage,
uint8_t aStartPos,
uint8_t aEndPos,
otLinkMetrics &aLinkMetrics);
static otError AppendReportSubTlvToMessage(Message &aMessage, uint8_t &aLength, const otLinkMetricsValues &aValues);
static otError AppendStatusSubTlvToMessage(Message &aMessage, uint8_t &aLength, otLinkMetricsStatus aStatus);
};
/**
+139 -5
View File
@@ -41,12 +41,14 @@
#include "common/message.hpp"
#include "common/tlvs.hpp"
#include "mac/mac_frame.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
namespace ot {
/**
* Link Metrics parameters
* Link Metrics parameters.
*
*/
enum
@@ -55,18 +57,16 @@ enum
};
/**
* Link Metrics Sub-TLV types
* Link Metrics Sub-TLV types.
*
*/
enum Type
enum Type : uint8_t
{
kLinkMetricsReportSub = 0, ///< Link Metrics Report Sub-TLV
kLinkMetricsQueryId = 1, ///< Link Metrics Query ID Sub-TLV
kLinkMetricsQueryOptions = 2, ///< Link Metrics Query Options Sub-TLV
kForwardProbingRegistration = 3, ///< Forward Probing Registration Sub-TLV
kReverseProbingRegistration = 4, ///< Reverse Probing Registration Sub-TLV
kLinkMetricsStatus = 5, ///< Link Metrics Status Sub-TLV
kSeriesTrackingCapabilities = 6, ///< Series Tracking Capabilities Sub-TLV
kEnhancedACKConfiguration = 7, ///< Enhanced ACK Configuration Sub-TLV
};
@@ -330,6 +330,140 @@ public:
} OT_TOOL_PACKED_END;
/**
* This class implements Series Flags for Forward Tracking Series.
*
*/
OT_TOOL_PACKED_BEGIN
class SeriesFlags
{
public:
/**
* Default constructor
*
*/
SeriesFlags(void) { mSeriesFlags = 0; }
/**
* Copy constructor
*
*/
SeriesFlags(const SeriesFlags &aSeriesFlags) { mSeriesFlags = aSeriesFlags.mSeriesFlags; }
/**
* This method clears the Link Probe flag.
*
*/
void ClearLinkProbeFlag(void) { mSeriesFlags &= ~kLinkProbeFlag; }
/**
* This method sets the Link Probe flag.
*
*/
void SetLinkProbeFlag(void) { mSeriesFlags |= kLinkProbeFlag; }
/**
* This method indicates whether or not the Link Probe flag is set.
*
* @retval true The Link Probe flag is set.
* @retval false The Link Probe flag is not set.
*
*/
bool IsLinkProbeFlagSet(void) const { return (mSeriesFlags & kLinkProbeFlag) != 0; }
/**
* This method clears the MAC Data flag.
*
*/
void ClearMacDataFlag(void) { mSeriesFlags &= ~kMacDataFlag; }
/**
* This method sets the MAC Data flag.
*
*/
void SetMacDataFlag(void) { mSeriesFlags |= kMacDataFlag; }
/**
* This method indicates whether or not the MAC Data flag is set.
*
* @retval true The MAC Data flag is set.
* @retval false The MAC Data flag is not set.
*
*/
bool IsMacDataFlagSet(void) const { return (mSeriesFlags & kMacDataFlag) != 0; }
/**
* This method clears the MAC Data Request flag.
*
*/
void ClearMacDataRequestFlag(void) { mSeriesFlags &= ~kMacDataRequestFlag; }
/**
* This method sets the MAC Data Request flag.
*
*/
void SetMacDataRequestFlag(void) { mSeriesFlags |= kMacDataRequestFlag; }
/**
* This method indicates whether or not the MAC Data Request flag is set.
*
* @retval true The MAC Data Request flag is set.
* @retval false The MAC Data Request flag is not set.
*
*/
bool IsMacDataRequestFlagSet(void) const { return (mSeriesFlags & kMacDataRequestFlag) != 0; }
/**
* This method clears the Mac Ack flag.
*
*/
void ClearMacAckFlag(void) { mSeriesFlags &= ~kMacAckFlag; }
/**
* This method sets the Mac Ack flag.
*
*/
void SetMacAckFlag(void) { mSeriesFlags |= kMacAckFlag; }
/**
* This method indicates whether or not the Mac Ack flag is set.
*
* @retval true The Mac Ack flag is set.
* @retval false The Mac Ack flag is not set.
*
*/
bool IsMacAckFlagSet(void) const { return (mSeriesFlags & kMacAckFlag) != 0; }
/**
* This method returns the raw value of mSeriesFlags.
*
*/
uint8_t GetRawValue(void) const { return mSeriesFlags; }
/**
* This method clears the all the flags.
*
*/
void Clear(void) { mSeriesFlags = 0; }
/**
* This method overloads the assignment operator.
*
*/
void operator=(const SeriesFlags &aSeriesFlags) { mSeriesFlags = aSeriesFlags.mSeriesFlags; }
private:
enum
{
kLinkProbeFlag = 1 << 0,
kMacDataFlag = 1 << 1,
kMacDataRequestFlag = 1 << 2,
kMacAckFlag = 1 << 3,
};
uint8_t mSeriesFlags;
} OT_TOOL_PACKED_END;
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
+150
View File
@@ -51,6 +51,7 @@
#include "net/udp6.hpp"
#include "thread/address_resolver.hpp"
#include "thread/key_manager.hpp"
#include "thread/link_metrics.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
#include "thread/time_sync_service.hpp"
@@ -2420,6 +2421,53 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
otError Mle::SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, otLinkMetricsStatus aStatus)
{
otError error = OT_ERROR_NONE;
Message *message;
Tlv tlv;
ot::Tlv statusSubTlv;
VerifyOrExit((message = NewMleMessage()) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = AppendHeader(*message, kCommandLinkMetricsManagementResponse));
tlv.SetType(Tlv::kLinkMetricsManagement);
statusSubTlv.SetType(kLinkMetricsStatus);
statusSubTlv.SetLength(sizeof(aStatus));
tlv.SetLength(statusSubTlv.GetSize());
SuccessOrExit(error = message->Append(tlv));
SuccessOrExit(error = message->Append(statusSubTlv));
SuccessOrExit(error = message->Append(aStatus));
SuccessOrExit(error = SendMessage(*message, aDestination));
exit:
return error;
}
otError Mle::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t *aBuf, uint8_t aLength)
{
otError error = OT_ERROR_NONE;
Message *message;
Tlv tlv;
VerifyOrExit((message = NewMleMessage()) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = AppendHeader(*message, kCommandLinkProbe));
tlv.SetType(Tlv::kLinkProbe);
tlv.SetLength(sizeof(aSeriesId) + aLength);
SuccessOrExit(error = message->Append(tlv));
SuccessOrExit(error = message->Append(aSeriesId));
SuccessOrExit(error = message->AppendBytes(aBuf, aLength));
SuccessOrExit(error = SendMessage(*message, aDestination));
exit:
return error;
}
#endif
otError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination)
{
otError error = OT_ERROR_NONE;
@@ -2716,6 +2764,20 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
case kCommandLinkMetricsManagementRequest:
HandleLinkMetricsManagementRequest(aMessage, aMessageInfo, neighbor);
break;
case kCommandLinkMetricsManagementResponse:
HandleLinkMetricsManagementResponse(aMessage, aMessageInfo, neighbor);
break;
case kCommandLinkProbe:
HandleLinkProbe(aMessage, aMessageInfo, neighbor);
break;
#endif
default:
ExitNow(error = OT_ERROR_DROP);
}
@@ -3689,6 +3751,57 @@ exit:
LogProcessError(kTypeAnnounce, error);
}
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
void Mle::HandleLinkMetricsManagementRequest(const Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
Neighbor * aNeighbor)
{
otError error = OT_ERROR_NONE;
otLinkMetricsStatus status;
Log(kMessageReceive, kTypeLinkMetricsManagementRequest, aMessageInfo.GetPeerAddr());
VerifyOrExit(aNeighbor != nullptr, error = OT_ERROR_INVALID_STATE);
SuccessOrExit(error = Get<LinkMetrics>().HandleLinkMetricsManagementRequest(aMessage, *aNeighbor, status));
error = SendLinkMetricsManagementResponse(aMessageInfo.GetPeerAddr(), status);
exit:
LogProcessError(kTypeLinkMetricsManagementRequest, error);
}
void Mle::HandleLinkMetricsManagementResponse(const Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
Neighbor * aNeighbor)
{
otError error = OT_ERROR_NONE;
Log(kMessageReceive, kTypeLinkMetricsManagementResponse, aMessageInfo.GetPeerAddr());
VerifyOrExit(aNeighbor != nullptr, error = OT_ERROR_INVALID_STATE);
error = Get<LinkMetrics>().HandleLinkMetricsManagementResponse(aMessage, aMessageInfo.GetPeerAddr());
exit:
LogProcessError(kTypeLinkMetricsManagementResponse, error);
}
void Mle::HandleLinkProbe(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor)
{
otError error = OT_ERROR_NONE;
uint8_t seriesId;
Log(kMessageReceive, kTypeLinkProbe, aMessageInfo.GetPeerAddr());
SuccessOrExit(error = Get<LinkMetrics>().HandleLinkProbe(aMessage, seriesId));
aNeighbor->AggregateLinkMetrics(seriesId, LinkMetricsSeriesInfo::kSeriesTypeLinkProbe, aMessage.GetAverageLqi(),
aMessage.GetAverageRss());
exit:
LogProcessError(kTypeLinkProbe, error);
}
#endif
void Mle::ProcessAnnounce(void)
{
uint8_t newChannel = mAlternateChannel;
@@ -4046,6 +4159,17 @@ const char *Mle::MessageTypeToString(MessageType aType)
break;
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
case kTypeLinkMetricsManagementRequest:
str = "Link Metrics Management Request";
break;
case kTypeLinkMetricsManagementResponse:
str = "Link Metrics Management Response";
break;
case kTypeLinkProbe:
str = "Link Probe";
break;
#endif
}
return str;
@@ -4221,6 +4345,32 @@ const char *Mle::ReattachStateToString(ReattachState aState)
// LCOV_EXCL_STOP
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
otError Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination,
const uint8_t * aSubTlvs,
uint8_t aLength)
{
otError error = OT_ERROR_NONE;
Message *message;
Tlv tlv;
VerifyOrExit((message = NewMleMessage()) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = AppendHeader(*message, kCommandLinkMetricsManagementRequest));
// Link Metrics Management TLV
tlv.SetType(Tlv::kLinkMetricsManagement);
tlv.SetLength(aLength);
SuccessOrExit(error = message->AppendBytes(&tlv, sizeof(tlv)));
SuccessOrExit(error = message->AppendBytes(aSubTlvs, aLength));
SuccessOrExit(error = SendMessage(*message, aDestination));
exit:
return error;
}
#endif
void Mle::RegisterParentResponseStatsCallback(otThreadParentResponseCallback aCallback, void *aContext)
{
mParentResponseCb = aCallback;
+73 -19
View File
@@ -45,6 +45,7 @@
#include "meshcop/meshcop.hpp"
#include "net/udp6.hpp"
#include "thread/link_metrics.hpp"
#include "thread/link_metrics_tlvs.hpp"
#include "thread/mle_tlvs.hpp"
#include "thread/mle_types.hpp"
#include "thread/neighbor_table.hpp"
@@ -719,25 +720,28 @@ protected:
*/
enum Command : uint8_t
{
kCommandLinkRequest = 0, ///< Link Request
kCommandLinkAccept = 1, ///< Link Accept
kCommandLinkAcceptAndRequest = 2, ///< Link Accept and Reject
kCommandLinkReject = 3, ///< Link Reject
kCommandAdvertisement = 4, ///< Advertisement
kCommandUpdate = 5, ///< Update
kCommandUpdateRequest = 6, ///< Update Request
kCommandDataRequest = 7, ///< Data Request
kCommandDataResponse = 8, ///< Data Response
kCommandParentRequest = 9, ///< Parent Request
kCommandParentResponse = 10, ///< Parent Response
kCommandChildIdRequest = 11, ///< Child ID Request
kCommandChildIdResponse = 12, ///< Child ID Response
kCommandChildUpdateRequest = 13, ///< Child Update Request
kCommandChildUpdateResponse = 14, ///< Child Update Response
kCommandAnnounce = 15, ///< Announce
kCommandDiscoveryRequest = 16, ///< Discovery Request
kCommandDiscoveryResponse = 17, ///< Discovery Response
kCommandTimeSync = 99, ///< Time Sync (applicable when OPENTHREAD_CONFIG_TIME_SYNC_ENABLE enabled)
kCommandLinkRequest = 0, ///< Link Request
kCommandLinkAccept = 1, ///< Link Accept
kCommandLinkAcceptAndRequest = 2, ///< Link Accept and Reject
kCommandLinkReject = 3, ///< Link Reject
kCommandAdvertisement = 4, ///< Advertisement
kCommandUpdate = 5, ///< Update
kCommandUpdateRequest = 6, ///< Update Request
kCommandDataRequest = 7, ///< Data Request
kCommandDataResponse = 8, ///< Data Response
kCommandParentRequest = 9, ///< Parent Request
kCommandParentResponse = 10, ///< Parent Response
kCommandChildIdRequest = 11, ///< Child ID Request
kCommandChildIdResponse = 12, ///< Child ID Response
kCommandChildUpdateRequest = 13, ///< Child Update Request
kCommandChildUpdateResponse = 14, ///< Child Update Response
kCommandAnnounce = 15, ///< Announce
kCommandDiscoveryRequest = 16, ///< Discovery Request
kCommandDiscoveryResponse = 17, ///< Discovery Response
kCommandLinkMetricsManagementRequest = 18, ///< Link Metrics Management Request
kCommandLinkMetricsManagementResponse = 19, ///< Link Metrics Management Response
kCommandLinkProbe = 20, ///< Link Probe
kCommandTimeSync = 99, ///< Time Sync (applicable when OPENTHREAD_CONFIG_TIME_SYNC_ENABLE enabled)
};
/**
@@ -833,6 +837,11 @@ protected:
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
kTypeTimeSync,
#endif
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
kTypeLinkMetricsManagementRequest,
kTypeLinkMetricsManagementResponse,
kTypeLinkProbe,
#endif
};
@@ -1485,6 +1494,39 @@ protected:
static const char *ReattachStateToString(ReattachState aState);
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
/**
* This method sends a Link Metrics Management Request message.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aSubTlvs A pointer to the buffer of the sub-TLVs in the message.
* @param[in] aLength The overall length of @p aSubTlvs.
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics Management Request.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Link Metrics Management Request message.
*
*/
otError SendLinkMetricsManagementRequest(const Ip6::Address &aDestination,
const uint8_t * aSubTlvs,
uint8_t aLength);
/**
* This method sends an MLE Link Probe message.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aSeriesId The Series ID [1, 254] which the Probe message targets at.
* @param[in] aBuf A pointer to the data payload.
* @param[in] aLength The length of the data payload in Link Probe TLV, [0, 64].
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics Management Request.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Link Metrics Management Request message.
* @retval OT_ERROR_INVALID_ARGS Series ID is not a valid value, not within range [1, 254].
*
*/
otError SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t *aBuf, uint8_t aLength);
#endif
Ip6::NetifUnicastAddress mLeaderAloc; ///< Leader anycast locator
LeaderData mLeaderData; ///< Last received Leader Data TLV.
@@ -1666,6 +1708,15 @@ private:
void HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor);
void HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence);
void HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
void HandleLinkMetricsManagementRequest(const Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
Neighbor * aNeighbor);
void HandleLinkMetricsManagementResponse(const Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
Neighbor * aNeighbor);
void HandleLinkProbe(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor);
#endif
otError HandleLeaderData(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void ProcessAnnounce(void);
bool HasUnregisteredAddress(void);
@@ -1676,6 +1727,9 @@ private:
otError SendOrphanAnnounce(void);
bool PrepareAnnounceState(void);
void SendAnnounce(uint8_t aChannel, bool aOrphanAnnounce, const Ip6::Address &aDestination);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
otError SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, otLinkMetricsStatus aStatus);
#endif
uint32_t Reattach(void);
bool IsBetterParent(uint16_t aRloc16,
+6 -1
View File
@@ -3256,7 +3256,9 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination,
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
case Tlv::kLinkMetricsReport:
OT_ASSERT(aRequestMessage != nullptr);
SuccessOrExit(error = Get<LinkMetrics>().AppendLinkMetricsReport(*message, *aRequestMessage));
neighbor = mNeighborTable.FindNeighbor(aDestination);
VerifyOrExit(neighbor != nullptr, error = OT_ERROR_INVALID_STATE);
SuccessOrExit(error = Get<LinkMetrics>().AppendLinkMetricsReport(*message, *aRequestMessage, *neighbor));
break;
#endif
}
@@ -3367,6 +3369,9 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor)
aNeighbor.GetLinkInfo().Clear();
aNeighbor.SetState(Neighbor::kStateInvalid);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
aNeighbor.RemoveAllForwardTrackingSeriesInfo();
#endif
exit:
return;
+33 -31
View File
@@ -75,37 +75,39 @@ public:
*/
enum Type
{
kSourceAddress = 0, ///< Source Address TLV
kMode = 1, ///< Mode TLV
kTimeout = 2, ///< Timeout TLV
kChallenge = 3, ///< Challenge TLV
kResponse = 4, ///< Response TLV
kLinkFrameCounter = 5, ///< Link-Layer Frame Counter TLV
kLinkQuality = 6, ///< Link Quality TLV
kNetworkParameter = 7, ///< Network Parameter TLV
kMleFrameCounter = 8, ///< MLE Frame Counter TLV
kRoute = 9, ///< Route64 TLV
kAddress16 = 10, ///< Address16 TLV
kLeaderData = 11, ///< Leader Data TLV
kNetworkData = 12, ///< Network Data TLV
kTlvRequest = 13, ///< TLV Request TLV
kScanMask = 14, ///< Scan Mask TLV
kConnectivity = 15, ///< Connectivity TLV
kLinkMargin = 16, ///< Link Margin TLV
kStatus = 17, ///< Status TLV
kVersion = 18, ///< Version TLV
kAddressRegistration = 19, ///< Address Registration TLV
kChannel = 20, ///< Channel TLV
kPanId = 21, ///< PAN ID TLV
kActiveTimestamp = 22, ///< Active Timestamp TLV
kPendingTimestamp = 23, ///< Pending Timestamp TLV
kActiveDataset = 24, ///< Active Operational Dataset TLV
kPendingDataset = 25, ///< Pending Operational Dataset TLV
kDiscovery = 26, ///< Thread Discovery TLV
kCslChannel = 80, ///< CSL Channel TLV
kCslTimeout = 85, ///< CSL Timeout TLV
kLinkMetricsQuery = 87, ///< Link Metrics Query TLV
kLinkMetricsReport = 89, ///< Link Metrics Report TLV
kSourceAddress = 0, ///< Source Address TLV
kMode = 1, ///< Mode TLV
kTimeout = 2, ///< Timeout TLV
kChallenge = 3, ///< Challenge TLV
kResponse = 4, ///< Response TLV
kLinkFrameCounter = 5, ///< Link-Layer Frame Counter TLV
kLinkQuality = 6, ///< Link Quality TLV
kNetworkParameter = 7, ///< Network Parameter TLV
kMleFrameCounter = 8, ///< MLE Frame Counter TLV
kRoute = 9, ///< Route64 TLV
kAddress16 = 10, ///< Address16 TLV
kLeaderData = 11, ///< Leader Data TLV
kNetworkData = 12, ///< Network Data TLV
kTlvRequest = 13, ///< TLV Request TLV
kScanMask = 14, ///< Scan Mask TLV
kConnectivity = 15, ///< Connectivity TLV
kLinkMargin = 16, ///< Link Margin TLV
kStatus = 17, ///< Status TLV
kVersion = 18, ///< Version TLV
kAddressRegistration = 19, ///< Address Registration TLV
kChannel = 20, ///< Channel TLV
kPanId = 21, ///< PAN ID TLV
kActiveTimestamp = 22, ///< Active Timestamp TLV
kPendingTimestamp = 23, ///< Pending Timestamp TLV
kActiveDataset = 24, ///< Active Operational Dataset TLV
kPendingDataset = 25, ///< Pending Operational Dataset TLV
kDiscovery = 26, ///< Thread Discovery TLV
kCslChannel = 80, ///< CSL Channel TLV
kCslTimeout = 85, ///< CSL Timeout TLV
kLinkMetricsQuery = 87, ///< Link Metrics Query TLV
kLinkMetricsManagement = 88, ///< Link Metrics Management TLV
kLinkMetricsReport = 89, ///< Link Metrics Report TLV
kLinkProbe = 90, ///< Link Probe TLV
/**
* Applicable/Required only when time synchronization service
+39
View File
@@ -159,6 +159,45 @@ void Neighbor::GenerateChallenge(void)
Random::Crypto::FillBuffer(mValidPending.mPending.mChallenge, sizeof(mValidPending.mPending.mChallenge)));
}
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
void Neighbor::AggregateLinkMetrics(uint8_t aSeriesId, uint8_t aFrameType, uint8_t aLqi, int8_t aRss)
{
for (LinkMetricsSeriesInfo *entry = mLinkMetricsSeriesInfoList.GetHead(); entry != nullptr;
entry = entry->GetNext())
{
if (aSeriesId == 0 || aSeriesId == entry->GetSeriesId())
{
entry->AggregateLinkMetrics(aFrameType, aLqi, aRss);
}
}
}
LinkMetricsSeriesInfo *Neighbor::GetForwardTrackingSeriesInfo(const uint8_t &aSeriesId)
{
LinkMetricsSeriesInfo *prev;
return mLinkMetricsSeriesInfoList.FindMatching(aSeriesId, prev);
}
void Neighbor::AddForwardTrackingSeriesInfo(LinkMetricsSeriesInfo &aLinkMetricsSeriesInfo)
{
mLinkMetricsSeriesInfoList.Push(aLinkMetricsSeriesInfo);
}
LinkMetricsSeriesInfo *Neighbor::RemoveForwardTrackingSeriesInfo(const uint8_t &aSeriesId)
{
return mLinkMetricsSeriesInfoList.RemoveMatching(aSeriesId);
}
void Neighbor::RemoveAllForwardTrackingSeriesInfo(void)
{
while (!mLinkMetricsSeriesInfoList.IsEmpty())
{
LinkMetricsSeriesInfo *seriesInfo = mLinkMetricsSeriesInfoList.Pop();
Get<LinkMetrics>().mLinkMetricsSeriesInfoPool.Free(*seriesInfo);
}
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
void Child::Info::SetFrom(const Child &aChild)
{
Clear();
+60
View File
@@ -39,6 +39,7 @@
#include <openthread/thread_ftd.h>
#include "common/clearable.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
@@ -47,12 +48,17 @@
#include "net/ip6.hpp"
#include "thread/csl_tx_scheduler.hpp"
#include "thread/indirect_sender.hpp"
#include "thread/link_metrics.hpp"
#include "thread/link_quality.hpp"
#include "thread/mle_tlvs.hpp"
#include "thread/mle_types.hpp"
namespace ot {
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
class LinkMetricsSeriesInfo; ///< Forward declaration for including each other with `link_metrics.hpp`
#endif
/**
* This class represents a Thread neighbor.
*
@@ -589,6 +595,56 @@ public:
void SetTimeSyncEnabled(bool aEnabled) { mTimeSyncEnabled = aEnabled; }
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
/**
* This method aggregates the Link Metrics data into all the series that is running for this neighbor.
*
* If a series wants to account frames of @p aFrameType, it would add count by 1 and aggregrate @p aLqi and
* @p aRss into its averagers.
*
* @param[in] aSeriesId Series ID for Link Probe. Should be `0` if this method is not called by Link Probe.
* @param[in] aFrameType Type of the frame that carries Link Metrics data.
* @param[in] aLqi The LQI value.
* @param[in] aRss The Rss value.
*
*/
void AggregateLinkMetrics(uint8_t aSeriesId, uint8_t aFrameType, uint8_t aLqi, int8_t aRss);
/**
* This method adds a new LinkMetricsSeriesInfo to the neighbor's list.
*
* @param[in] A reference to the new LinkMetricsSeriesInfo.
*
*/
void AddForwardTrackingSeriesInfo(LinkMetricsSeriesInfo &aLinkMetricsSeriesInfo);
/**
* This method finds a specific LinkMetricsSeriesInfo by Series ID.
*
* @param[in] aSeriesId A reference to the Series ID.
*
* @returns The pointer to the LinkMetricsSeriesInfo. `nullptr` if not found.
*
*/
LinkMetricsSeriesInfo *GetForwardTrackingSeriesInfo(const uint8_t &aSeriesId);
/**
* This method removes a specific LinkMetricsSeriesInfo by Series ID.
*
* @param[in] aSeriesId A reference to the Series ID to remove.
*
* @returns The pointer to the LinkMetricsSeriesInfo. `nullptr` if not found.
*
*/
LinkMetricsSeriesInfo *RemoveForwardTrackingSeriesInfo(const uint8_t &aSeriesId);
/**
* This method removes all the Series and return the data structures to the Pool
*
*/
void RemoveAllForwardTrackingSeriesInfo(void);
#endif
protected:
/**
* This method initializes the `Neighbor` object.
@@ -629,6 +685,10 @@ private:
#endif
uint8_t mVersion; ///< The MLE version
LinkQualityInfo mLinkInfo; ///< Link quality info (contains average RSS, link margin and link quality)
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
LinkedList<LinkMetricsSeriesInfo> mLinkMetricsSeriesInfoList; ///< A list of Link Metrics Forward Tracking Series
///< that is being tracked for this neighbor.
#endif
};
/**
+2
View File
@@ -259,7 +259,9 @@ def create_default_mle_tlvs_factories():
mle.TlvType.TIME_PARAMETER: mle.TimeParameterFactory(),
mle.TlvType.THREAD_DISCOVERY: create_default_mle_tlv_thread_discovery_factory(),
mle.TlvType.LINK_METRICS_QUERY: mle.LinkMetricsQueryFactory(),
mle.TlvType.LINK_METRICS_MANAGEMENT: mle.LinkMetricsManagementFactory(),
mle.TlvType.LINK_METRICS_REPORT: mle.LinkMetricsReportFactory(),
mle.TlvType.LINK_PROBE: mle.LinkProbeFactory(),
}
+40
View File
@@ -58,6 +58,9 @@ class CommandType(IntEnum):
ANNOUNCE = 15
DISCOVERY_REQUEST = 16
DISCOVERY_RESPONSE = 17
LINK_METRICS_MANAGEMENT_REQUEST = 18
LINK_METRICS_MANAGEMENT_RESPONSE = 19
LINK_PROBE = 20
TIME_SYNC = 99
@@ -90,11 +93,22 @@ class TlvType(IntEnum):
CSL_CHANNEL = 80
CSL_SYNCHRONIZED_TIMEOUT = 85
LINK_METRICS_QUERY = 87
LINK_METRICS_MANAGEMENT = 88
LINK_METRICS_REPORT = 89
LINK_PROBE = 90
TIME_REQUEST = 252
TIME_PARAMETER = 253
class LinkMetricsSubTlvType(IntEnum):
LINK_METRICS_REPORT = 0
LINK_METRICS_QUERY_ID = 1
LINK_METRICS_QUERY_OPTIONS = 2
FORWARD_PROBING_REGISTRATION = 3
LINK_METRICS_STATUS = 5
ENHANCED_ACK_LINK_METRICS_CONFIGURATION = 7
class SourceAddress(object):
def __init__(self, address):
@@ -1117,6 +1131,19 @@ class LinkMetricsQueryFactory:
return LinkMetricsQuery()
class LinkMetricsManagement:
# TODO: Not implemented yet
def __init__(self):
print("LinkMetricsManagement is not implemented yet.")
class LinkMetricsManagementFactory:
def parse(self, data, message_info):
return LinkMetricsManagement()
class LinkMetricsReport:
# TODO: Not implemented yet
@@ -1130,6 +1157,19 @@ class LinkMetricsReportFactory:
return LinkMetricsReport()
class LinkProbe:
# TODO: Not implemented yet
def __init__(self):
print("LinkProbe is not implemented yet.")
class LinkProbeFactory:
def parse(self, data, message_info):
return LinkProbe()
class MleCommand(object):
def __init__(self, _type, tlvs):
+16
View File
@@ -1920,6 +1920,22 @@ class NodeImpl:
self.send_command(cmd)
self._expect('Done')
def link_metrics_query_forward_tracking_series(self, dst_addr: str, series_id: int):
cmd = 'linkmetrics query %s forward %d' % (dst_addr, series_id)
self.send_command(cmd)
self._expect('Done')
def link_metrics_mgmt_req_forward_tracking_series(self, dst_addr: str, series_id: int, series_flags: str,
metrics_flags: str):
cmd = "linkmetrics mgmt %s forward %d %s %s" % (dst_addr, series_id, series_flags, metrics_flags)
self.send_command(cmd)
self._expect('Done')
def link_metrics_send_link_probe(self, dst_addr: str, series_id: int, length: int):
cmd = "linkmetrics probe %s %d %d" % (dst_addr, series_id, length)
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)
@@ -75,6 +75,9 @@ MLE_CHILD_UPDATE_RESPONSE = 14
MLE_ANNOUNCE = 15
MLE_DISCOVERY_REQUEST = 16
MLE_DISCOVERY_RESPONSE = 17
MLE_LINK_METRICS_MANAGEMENT_REQUEST = 18
MLE_LINK_METRICS_MANAGEMENT_RESPONSE = 19
MLE_LINK_PROBE = 20
# COAP URIs
ADDR_QRY_URI = '/a/aq'
@@ -285,6 +288,22 @@ THREAD_VERSION_1_2 = 3
# ICMPv6 Types
ICMPV6_TYPE_DESTINATION_UNREACHABLE = 1
# Link Metrics
LINK_METRICS_STATUS_SUCCESS = 0
LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES = 1
LINK_METRICS_STATUS_SERIES_ID_ALREADY_REGISTERED = 2
LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED = 3
LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED = 4
LINK_METRICS_STATUS_OTHER_ERROR = 254
LINK_METRICS_TYPE_AVERAGE_ENUM_COUNT = 0
LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL = 1
LINK_METRICS_METRIC_TYPE_ENUM_PDU_COUNT = 0
LINK_METRICS_METRIC_TYPE_ENUM_LQI = 1
LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN = 2
LINK_METRICS_METRIC_TYPE_ENUM_RSSI = 3
if __name__ == '__main__':
from pktverify.addrs import Ipv6Addr
@@ -293,6 +293,13 @@ _LAYER_FIELDS = {
'mle.tlv.addr16': _auto,
'mle.tlv.channel': _auto,
'mle.tlv.addr_reg_iid': _list(_auto),
'mle.tlv.link_forward_series': _list(_auto),
'mle.tlv.link_sub_tlv': _auto,
'mle.tlv.link_status_sub_tlv': _auto,
'mle.tlv.query_id': _auto,
'mle.tlv.metric_type_id_flags.type': _list(_hex),
'mle.tlv.metric_type_id_flags.metric': _list(_hex),
'mle.tlv.metric_type_id_flags.l': _list(_hex),
# IP
'ip.version': _auto,
@@ -0,0 +1,340 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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 thread_cert
LEADER = 1
SED_1 = 2
SSED_1 = 3
SERIES_ID = 1
SERIES_ID_2 = 2
POLL_PERIOD = 2000 # 2s
class LowPower_7_2_01_ForwardTrackingSeries(thread_cert.TestCase):
TOPOLOGY = {
LEADER: {
'version': '1.2',
'name': 'LEADER',
'mode': 'rdn',
'panid': 0xface,
'allowlist': [SED_1, SSED_1],
},
SED_1: {
'version': '1.2',
'name': 'SED_1',
'mode': '-',
'panid': 0xface,
'allowlist': [LEADER],
},
SSED_1: {
'version': '1.2',
'name': 'SSED_1',
'mode': '-',
'panid': 0xface,
'allowlist': [LEADER],
}
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[LEADER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[SED_1].set_pollperiod(POLL_PERIOD)
self.nodes[SED_1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[SED_1].get_state(), 'child')
self.nodes[SSED_1].set_csl_period(consts.CSL_DEFAULT_PERIOD)
self.nodes[SSED_1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[SSED_1].get_state(), 'child')
leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
# Step 3 - Verify connectivity by instructing each device to send an ICMPv6 Echo Request to the DUT
self.assertTrue(self.nodes[SED_1].ping(leader_addr, timeout=POLL_PERIOD))
self.assertTrue(self.nodes[SSED_1].ping(leader_addr, timeout=2 * consts.CSL_DEFAULT_PERIOD))
self.simulator.go(5)
# Step 4 - SED_1 requests a Forward Tracking Series by sending a Link Metrics Management Request
# Forward Series Flags = 0x04:
# - Bit 2 = 1, MAC Data Request
# - Bits 0,1 & 3-7 = 0
# Concatenation of Link Metric Type ID Flags = 0x00:
# - Item1: (0)(1)(000)(000) = 0x40
# -- E = 0
# -- L = 1
# -- Type/Average Enum = 0 (count)
# -- Metrics Enum = 0 (count)
self.nodes[SED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID, 'r', 'p')
self.simulator.go(5)
# Step 6 - Configures SED_1 to send MAC Data Request every 2 seconds and wait for 30 seconds
self.simulator.go(30)
# Step 7 - SED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results
self.nodes[SED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID)
self.simulator.go(5)
# Step 9 - SED_1 clears the Forward Tracking Series
# Forward Series Flags = 0x00:
# - Bits 0-7 = 0
# Concatenation of Link Metric Type ID Flags = NULL:
self.nodes[SED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID, 'X', '')
self.simulator.go(5)
# Step 16 - SSED_1 requests a Forward Tracking Series by sending a Link Metrics Management Request
# Forward Series Flags = 0x02:
# - Bit 1 = 1, (all Link Layer Data Frames)
# - Bits 0, 2-7 = 0
# Concatenation of Link Metric Type ID Flags = 0x0a:
# - Item1: (0)(0)(001)(010) = 0x0a
# -- E = 0
# -- L = 0
# -- Type/Average Enum = 1 (Exponential Moving Average)
# -- Metrics Enum = 2 (Link Margin)
self.nodes[SSED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'm')
self.simulator.go(5)
# Step 18 - SSED_1 sends MAC Data packet every 1 second for 30 seconds
# Note: As the api to send MAC Data frame hasn't been implemented, we send MLE Link Probe message here
for i in range(30):
self.nodes[SSED_1].link_metrics_send_link_probe(leader_addr, SERIES_ID_2, 1)
self.simulator.go(1)
# Step 19 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results
self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2)
self.simulator.go(5)
# Step 21 - SSED_1 clears the Forward Series Link Metrics
# Forward Series Flags = 0x00:
# - Bits 0-7 = 0
# Concatenation of Link Metric Type ID Flags = NULL
self.nodes[SSED_1].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '')
self.simulator.go(5)
# Step 23 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results
self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2)
self.simulator.go(5)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
SED_1 = pv.vars['SED_1']
SSED_1 = pv.vars['SSED_1']
# Step 3 - The DUT MUST send ICMPv6 Echo Responses to both SED1 & SSED1
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SED_1) \
.filter_ping_reply() \
.must_next()
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SSED_1) \
.filter_ping_reply() \
.must_next()
# Step 4 - SED_1 requests a Forward Series Link Metrics by sending a Link Metrics Management Request
# Forward Series Flags = 0x04:
# - Bit 2 = 1, MAC Data Request
# - Bits 0,1 & 3-7 = 0
# Concatenation of Link Metric Type ID Flags = 0x40:
# - Item1: (0)(1)(000)(000) = 0x40
# -- E = 0
# -- L = 1
# -- Type/Average Enum = 0 (count)
# -- Metrics Enum = 0 (count)
pkts.filter_wpan_src64(SED_1) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \
.filter(lambda p: 4 in p.mle.tlv.link_forward_series) \
.filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_COUNT in p.mle.tlv.metric_type_id_flags.type) \
.filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_PDU_COUNT in p.mle.tlv.metric_type_id_flags.metric) \
.filter(lambda p: 1 in p.mle.tlv.metric_type_id_flags.l) \
.must_next()
# Step 5 - Leader responds with a Link Metrics Management Response
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SED_1) \
.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 8 - Leader responds with an MLE Data Response
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SED_1) \
.filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
.filter(lambda p: TlvType.LINK_METRICS_REPORT in p.mle.tlv.type) \
.filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_COUNT in p.mle.tlv.metric_type_id_flags.type) \
.filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_PDU_COUNT in p.mle.tlv.metric_type_id_flags.metric) \
.must_next()
# Step 9 - SED_1 clears the Forward Series Link Metrics
# Forward Series Flags = 0x00:
# - Bits 0-7 = 0
# Concatenation of Link Metric Type ID Flags = NULL:
pkts.filter_wpan_src64(SED_1) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \
.filter(lambda p: 0 in p.mle.tlv.link_forward_series) \
.filter(lambda p: p.mle.tlv.metric_type_id_flags.type is nullField) \
.filter(lambda p: p.mle.tlv.metric_type_id_flags.metric is nullField) \
.must_next()
# Step 10 - Leader responds with a Link Metrics Management Response
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SED_1) \
.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 16 - SSED_1 requests a Forward Series Link Metrics by sending a Link Metrics Management Request
# Forward Series Flags = 0x02:
# - Bit 1 = 1, (all Link Layer Data Frames)
# - Bits 0, 2-7 = 0
# Concatenation of Link Metric Type ID Flags = 0x0a:
# - Item1: (0)(0)(001)(010) = 0x0a
# -- E = 0
# -- L = 0
# -- Type/Average Enum = 1 (Exponential Moving Average)
# -- Metrics Enum = 2 (Link Margin)
pkts.filter_wpan_src64(SSED_1) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \
.filter(lambda p: 2 in p.mle.tlv.link_forward_series) \
.filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
.filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \
.must_next()
# Step 17 - Leader responds with a Link Metrics Management Response
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SSED_1) \
.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 19 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series results
# - TLV Request TLV(Link Metrics Report TLV specified
# - Link Metrics Query TLV
# -- Link Metrics Query ID Sub-TLV
# --- Query ID: (ID set in step 16)
# - Verify that the Link Metrics Query Options Sub-TLV is NOT present
pkts.filter_wpan_src64(SSED_1) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_DATA_REQUEST) \
.filter(lambda p: TlvType.LINK_METRICS_QUERY in p.mle.tlv.type) \
.filter(lambda p: p.mle.tlv.query_id == SERIES_ID_2) \
.must_next()
# Step 20 - Leader MUST reply to SSED_1 with an MLE Data Response with the following:
# - Link Metrics Report TLV
# -- Link Metrics Report Sub-TLV
# --- Metric Type ID Flags
# ---- Type / Average Enum = 1 (Exponential Moving Average)
# ---- Metric Enum = 2 (Link Margin)
# --- Value (1 bytes)
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SSED_1) \
.filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
.filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \
.filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.LINK_METRICS_REPORT) \
.filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
.filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \
.must_next()
# Step 21 - SSED_1 clears the Forward Series Link Metrics
# Forward Series Flags = 0x00:
# - Bits 0-7 = 0
# Concatenation of Link Metric Type ID Flags = NULL
pkts.filter_wpan_src64(SSED_1) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
.filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \
.filter(lambda p: 0 in p.mle.tlv.link_forward_series) \
.filter(lambda p: p.mle.tlv.metric_type_id_flags.type is nullField) \
.filter(lambda p: p.mle.tlv.metric_type_id_flags.metric is nullField) \
.must_next()
# Step 22 - Leader responds with a Link Metrics Management Response
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SSED_1) \
.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 23 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series results
# - TLV Request TLV(Link Metrics Report TLV specified
# - Link Metrics Query TLV
# -- Link Metrics Query ID Sub-TLV
# --- Query ID: (ID set in step 16)
# This is a negative step
pkts.filter_wpan_src64(SSED_1) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_DATA_REQUEST) \
.filter(lambda p: TlvType.LINK_METRICS_QUERY in p.mle.tlv.type) \
.filter(lambda p: p.mle.tlv.query_id == SERIES_ID_2) \
.must_next()
# Step 24 - Leader MUST reply to SSED_1 with an MLE Data Response with the following:
# - Link Metrics Report TLV
# -- Link Metrics Status Sub-TLV
# --- Status = 3 (Series ID not recognized)
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(SSED_1) \
.filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
.filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \
.filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.LINK_METRICS_STATUS) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED) \
.must_next()
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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 thread_cert
LEADER = 1
CHILD = 2
SERIES_ID_1 = 1
SERIES_ID_2 = 2
class LowPower_test_ForwardTrackingSeries(thread_cert.TestCase):
TOPOLOGY = {
LEADER: {
'version': '1.2',
'name': 'LEADER',
'mode': 'rdn',
'panid': 0xface,
'allowlist': [CHILD],
},
CHILD: {
'version': '1.2',
'name': 'CHILD',
'mode': 'rn',
'panid': 0xface,
'allowlist': [LEADER],
}
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[LEADER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[CHILD].start()
self.simulator.go(5)
self.assertEqual(self.nodes[CHILD].get_state(), 'child')
leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
# 1. Child configures a Forward Tracking Series successfully.
# The Series tracks the count of MAC Data Request.
# Child should get a response with status 0 (SUCCESS).
self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p')
self.simulator.go(1)
# 2. Child configures the same Forward Tracking Series again.
# Child should get a response with status 2 (SERIES_ID_ALREADY_REGISTERED).
self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p')
self.simulator.go(1)
# 3. Child queries a Series that doesn't exist (using a wrong Series ID).
# Child should get a report with status 3 (SERIES_ID_NOT_RECOGNIZED).
self.nodes[CHILD].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2)
self.simulator.go(1)
# 4. Child queries a Series that don't have matched frames yet.
# Child should get a report with status 4 (NO_MATCHING_FRAMES_RECEIVED).
self.nodes[CHILD].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_1)
self.simulator.go(1)
# 5. Child clears a Forward Tracking Series that doesn't exist.
# Child should get a response with status 3 (SERIES_ID_NOT_RECOGNIZED).
self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '')
self.simulator.go(1)
# 6. Child clears a Forward Tracking Series successfully.
# Child should get a response with status 0 (SUCCESS).
self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'X', '')
self.simulator.go(1)
# 7. Child configures a new Forward Tracking Series successfully.
# The Series tracks the count of all MAC Data frames.
# Child should get a response with status 0 (SUCCESS).
self.nodes[CHILD].link_metrics_mgmt_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'pqmr')
self.simulator.go(1)
# 8. Child sends an MLE Link Probe message to the Subject for the newly configured Series.
self.nodes[CHILD].link_metrics_send_link_probe(leader_addr, SERIES_ID_2, 1)
# 9. Child queries the newly configured Series successfully.
# Child should get a report with valid values.
self.nodes[CHILD].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2)
self.simulator.go(1)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
CHILD = pv.vars['CHILD']
# 1. Child should get a response with status 0 (SUCCESS).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.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()
# 2. Child should get a response with status 2 (SERIES_ID_ALREADY_REGISTERED).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_ALREADY_REGISTERED) \
.must_next()
# 3. Child should get a report with status 3 (SERIES_ID_NOT_RECOGNIZED).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
.filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED) \
.must_next()
# 4. Child should get a report with status 4 (NO_MATCHING_FRAMES_RECEIVED).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
.filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED) \
.must_next()
# 5. Child should get a response with status 3 (SERIES_ID_NOT_RECOGNIZED).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED) \
.must_next()
# 6. Child should get a response with status 0 (SUCCESS).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.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()
# 7. Child should get a response with status 0 (SUCCESS).
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.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()
# 8. Child sends an MLE Link Probe message to the Subject for the newly configured Series.
pkts.filter_wpan_src64(CHILD) \
.filter_wpan_dst64(LEADER) \
.filter_mle_cmd(consts.MLE_LINK_PROBE) \
.must_next()
# 9. Child should get a report with valid values.
pkts.filter_wpan_src64(LEADER) \
.filter_wpan_dst64(CHILD) \
.filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
.filter(lambda p: TlvType.LINK_METRICS_REPORT in p.mle.tlv.type) \
.must_next()
if __name__ == '__main__':
unittest.main()