mirror of
https://github.com/espressif/openthread.git
synced 2026-08-01 08:37:47 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -137,6 +137,7 @@ size_nrf52840_version()
|
||||
"DUA=1"
|
||||
"MLR=1"
|
||||
"CSL_RECEIVER=1"
|
||||
"LINK_METRICS=1"
|
||||
)
|
||||
fi
|
||||
|
||||
|
||||
+57
-1
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user