1 /*
2  *  Copyright (c) 2020, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file includes definitions for Thread Link Metrics query and management.
32  */
33 
34 #ifndef LINK_METRICS_HPP_
35 #define LINK_METRICS_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
40 
41 #if (OPENTHREAD_CONFIG_THREAD_VERSION < OT_THREAD_VERSION_1_2)
42 #error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE" \
43        "and OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE."
44 #endif
45 
46 #include <openthread/link.h>
47 
48 #include "common/clearable.hpp"
49 #include "common/locator.hpp"
50 #include "common/message.hpp"
51 #include "common/non_copyable.hpp"
52 #include "common/pool.hpp"
53 #include "net/ip6_address.hpp"
54 #include "thread/link_metrics_tlvs.hpp"
55 #include "thread/link_quality.hpp"
56 
57 namespace ot {
58 class Neighbor;
59 
60 namespace LinkMetrics {
61 
62 /**
63  * @addtogroup core-link-metrics
64  *
65  * @brief
66  *   This module includes definitions for Thread Link Metrics query and management.
67  *
68  * @{
69  */
70 
71 /**
72  * This type represents the results (values) for a set of metrics.
73  *
74  * @sa otLinkMetricsValues.
75  *
76  */
77 class MetricsValues : public otLinkMetricsValues, public Clearable<MetricsValues>
78 {
79 public:
80     /**
81      * This method gets the metrics flags.
82      *
83      * @returns The metrics flags.
84      *
85      */
GetMetrics(void)86     Metrics &GetMetrics(void) { return static_cast<Metrics &>(mMetrics); }
87 
88     /**
89      * This method gets the metrics flags.
90      *
91      * @returns The metrics flags.
92      *
93      */
GetMetrics(void) const94     const Metrics &GetMetrics(void) const { return static_cast<const Metrics &>(mMetrics); }
95 
96     /**
97      * This method set the metrics flags.
98      *
99      * @param[in] aMetrics  The metrics flags to set from.
100      *
101      */
SetMetrics(const Metrics & aMetrics)102     void SetMetrics(const Metrics &aMetrics) { mMetrics = aMetrics; }
103 };
104 
105 /**
106  * This class represents one Series that is being tracked by the Subject.
107  *
108  * When an Initiator successfully configured a Forward Tracking Series, the Subject would use an instance of this class
109  * to track the information of the Series. The Subject has a `Pool` of `SeriesInfo`. It would allocate one when a new
110  * Series comes, and free it when a Series finishes.
111  *
112  * This class inherits `LinkedListEntry` and each `Neighbor` has a list of `SeriesInfo` so that the Subject could track
113  * per Series initiated by neighbors as long as it has available resources.
114  *
115  */
116 class SeriesInfo : public LinkedListEntry<SeriesInfo>
117 {
118     friend class LinkedList<SeriesInfo>;
119     friend class LinkedListEntry<SeriesInfo>;
120 
121 public:
122     /**
123      * This constant represents Link Probe when filtering frames to be accounted using Series Flag. There's
124      * already `kFcfFrameData`, `kFcfFrameAck` and `kFcfFrameMacCmd`. This item is added so that we can
125      * filter a Link Probe for series in the same way as other frames.
126      *
127      */
128     static constexpr uint8_t kSeriesTypeLinkProbe = 0;
129 
130     /**
131      * This method initializes the SeriesInfo object.
132      *
133      * @param[in]  aSeriesId      The Series ID.
134      * @param[in]  aSeriesFlags   The Series Flags which specify what types of frames are to be accounted.
135      * @param[in]  aMetrics       Metrics to query.
136      *
137      */
138     void Init(uint8_t aSeriesId, const SeriesFlags &aSeriesFlags, const Metrics &aMetrics);
139 
140     /**
141      * This method gets the Series ID.
142      *
143      * @returns  The Series ID.
144      *
145      */
GetSeriesId(void) const146     uint8_t GetSeriesId(void) const { return mSeriesId; }
147 
148     /**
149      * This method gets the PDU count.
150      *
151      * @returns  The PDU count.
152      *
153      */
GetPduCount(void) const154     uint32_t GetPduCount(void) const { return mPduCount; }
155 
156     /**
157      * This method gets the average LQI.
158      *
159      * @returns  The average LQI.
160      *
161      */
GetAverageLqi(void) const162     uint8_t GetAverageLqi(void) const { return mLqiAverager.GetAverage(); }
163 
164     /**
165      * This method gets the average RSS.
166      *
167      * @returns  The average RSS.
168      *
169      */
GetAverageRss(void) const170     int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); }
171 
172     /**
173      * This method aggregates the Link Metrics data of a frame into this series.
174      *
175      * @param[in]  aFrameType    The type of the frame.
176      * @param[in]  aLqi          The LQI value.
177      * @param[in]  aRss          The RSS value.
178      *
179      */
180     void AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss);
181 
182     /**
183      * This methods gets the metrics.
184      *
185      * @returns  The metrics associated with `SeriesInfo`.
186      *
187      */
GetLinkMetrics(void) const188     const Metrics &GetLinkMetrics(void) const { return mMetrics; }
189 
190 private:
Matches(const uint8_t & aSeriesId) const191     bool Matches(const uint8_t &aSeriesId) const { return mSeriesId == aSeriesId; }
192     bool IsFrameTypeMatch(uint8_t aFrameType) const;
193 
194     SeriesInfo *mNext;
195     uint8_t     mSeriesId;
196     SeriesFlags mSeriesFlags;
197     Metrics     mMetrics;
198     RssAverager mRssAverager;
199     LqiAverager mLqiAverager;
200     uint32_t    mPduCount;
201 };
202 
203 /**
204  * This enumeration type represent Link Metrics Status.
205  *
206  */
207 enum Status : uint8_t
208 {
209     kStatusSuccess                   = OT_LINK_METRICS_STATUS_SUCCESS,
210     kStatusCannotSupportNewSeries    = OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES,
211     kStatusSeriesIdAlreadyRegistered = OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED,
212     kStatusSeriesIdNotRecognized     = OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED,
213     kStatusNoMatchingFramesReceived  = OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED,
214     kStatusOtherError                = OT_LINK_METRICS_STATUS_OTHER_ERROR,
215 };
216 
217 /**
218  * This class implements Thread Link Metrics query and management.
219  *
220  */
221 class LinkMetrics : public InstanceLocator, private NonCopyable
222 {
223     friend class ot::Neighbor;
224 
225 public:
226     typedef otLinkMetricsReportCallback                ReportCallback;
227     typedef otLinkMetricsMgmtResponseCallback          MgmtResponseCallback;
228     typedef otLinkMetricsEnhAckProbingIeReportCallback EnhAckProbingIeReportCallback;
229 
230     /**
231      * This constructor initializes an instance of the LinkMetrics class.
232      *
233      * @param[in]  aInstance  A reference to the OpenThread interface.
234      *
235      */
236     explicit LinkMetrics(Instance &aInstance);
237 
238     /**
239      * This method sends an MLE Data Request containing Link Metrics Query TLV to query Link Metrics data.
240      *
241      * It could be either a Single Probe or a Forward Tracking Series.
242      *
243      * @param[in]  aDestination       A reference to the IPv6 address of the destination.
244      * @param[in]  aSeriesId          The Series ID to query, 0 for single probe.
245      * @param[in]  aMetrics           A pointer to metrics to query.
246      *
247      * @retval kErrorNone             Successfully sent a Link Metrics query message.
248      * @retval kErrorNoBufs           Insufficient buffers to generate the MLE Data Request message.
249      * @retval kErrorInvalidArgs      TypeIdFlags are not valid or exceed the count limit.
250      * @retval kErrorUnknownNeighbor  @p aDestination is not link-local or the neighbor is not found.
251      *
252      */
253     Error Query(const Ip6::Address &aDestination, uint8_t aSeriesId, const Metrics *aMetrics);
254 
255     /**
256      * This method sends an MLE Link Metrics Management Request to configure/clear a Forward Tracking Series.
257      *
258      * @param[in] aDestination       A reference to the IPv6 address of the destination.
259      * @param[in] aSeriesId          The Series ID.
260      * @param[in] aSeriesFlags       The Series Flags info which specify what types of frames are to be accounted.
261      * @param[in] aMetrics           A pointer to flags specifying what metrics to query.
262      *
263      * @retval kErrorNone             Successfully sent a Link Metrics Management Request message.
264      * @retval kErrorNoBufs           Insufficient buffers to generate the MLE Link Metrics Management Request message.
265      * @retval kErrorInvalidArgs      @p aSeriesId is not within the valid range.
266      * @retval kErrorUnknownNeighbor  @p aDestination is not link-local or the neighbor is not found.
267      *
268      */
269     Error SendMgmtRequestForwardTrackingSeries(const Ip6::Address &     aDestination,
270                                                uint8_t                  aSeriesId,
271                                                const SeriesFlags::Info &aSeriesFlags,
272                                                const Metrics *          aMetrics);
273 
274 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
275     /**
276      * This method sends an MLE Link Metrics Management Request to configure/clear a Enhanced-ACK Based Probing.
277      *
278      * @param[in] aDestination       A reference to the IPv6 address of the destination.
279      * @param[in] aEnhAckFlags       Enh-ACK Flags to indicate whether to register or clear the probing. `0` to clear
280      *                               and `1` to register. Other values are reserved.
281      * @param[in] aMetrics           A pointer to flags specifying what metrics to query. Should be `nullptr` when
282      *                               `aEnhAckFlags` is `0`.
283      *
284      * @retval kErrorNone             Successfully sent a Link Metrics Management Request message.
285      * @retval kErrorNoBufs           Insufficient buffers to generate the MLE Link Metrics Management Request message.
286      * @retval kErrorInvalidArgs      @p aEnhAckFlags is not a valid value or @p aMetrics isn't correct.
287      * @retval kErrorUnknownNeighbor  @p aDestination is not link-local or the neighbor is not found.
288      *
289      */
290     Error SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination,
291                                        EnhAckFlags         aEnhAckFlags,
292                                        const Metrics *     aMetrics);
293 
294     /**
295      * This method sends an MLE Link Probe message.
296      *
297      * @param[in] aDestination    A reference to the IPv6 address of the destination.
298      * @param[in] aSeriesId       The Series ID which the Probe message targets at.
299      * @param[in] aLength         The length of the data payload in Link Probe TLV, [0, 64].
300      *
301      * @retval kErrorNone             Successfully sent a Link Probe message.
302      * @retval kErrorNoBufs           Insufficient buffers to generate the MLE Link Probe message.
303      * @retval kErrorInvalidArgs      @p aSeriesId or @p aLength is not within the valid range.
304      * @retval kErrorUnknownNeighbor  @p aDestination is not link-local or the neighbor is not found.
305      *
306      */
307     Error SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t aLength);
308 #endif
309 
310 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
311     /**
312      * This method appends a Link Metrics Report to a message according to the Link Metrics query.
313      *
314      * @param[out]  aMessage           A reference to the message to append report.
315      * @param[in]   aRequestMessage    A reference to the message of the Data Request.
316      * @param[in]   aNeighbor          A reference to the neighbor who queries the report.
317      *
318      * @retval kErrorNone         Successfully appended the Thread Discovery TLV.
319      * @retval kErrorParse        Cannot parse query sub TLV successfully.
320      * @retval kErrorInvalidArgs  QueryId is invalid or any Type ID is invalid.
321      *
322      */
323     Error AppendReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor);
324 #endif
325     /**
326      * This method handles the received Link Metrics Management Request contained in @p aMessage and return a status.
327      *
328      * @param[in]   aMessage     A reference to the message that contains the Link Metrics Management Request.
329      * @param[in]   aNeighbor    A reference to the neighbor who sends the request.
330      * @param[out]  aStatus      A reference to the status which indicates the handling result.
331      *
332      * @retval kErrorNone     Successfully handled the Link Metrics Management Request.
333      * @retval kErrorParse    Cannot parse sub-TLVs from @p aMessage successfully.
334      *
335      */
336     Error HandleManagementRequest(const Message &aMessage, Neighbor &aNeighbor, Status &aStatus);
337 
338     /**
339      * This method handles the received Link Metrics Management Response contained in @p aMessage.
340      *
341      * @param[in]  aMessage    A reference to the message that contains the Link Metrics Management Response.
342      * @param[in]  aAddress    A reference to the source address of the message.
343      *
344      * @retval kErrorNone     Successfully handled the Link Metrics Management Response.
345      * @retval kErrorParse    Cannot parse sub-TLVs from @p aMessage successfully.
346      *
347      */
348     Error HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress);
349 
350     /**
351      * This method handles the received Link Metrics report contained in @p aMessage.
352      *
353      * @param[in]  aMessage      A reference to the message.
354      * @param[in]  aOffset       The offset in bytes where the metrics report sub-TLVs start.
355      * @param[in]  aLength       The length of the metrics report sub-TLVs in bytes.
356      * @param[in]  aAddress      A reference to the source address of the message.
357      *
358      */
359     void HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t aLength, const Ip6::Address &aAddress);
360 
361     /**
362      * This method handles the Link Probe contained in @p aMessage.
363      *
364      * @param[in]   aMessage     A reference to the message that contains the Link Probe Message.
365      * @param[out]  aSeriesId    A reference to Series ID that parsed from the message.
366      *
367      * @retval kErrorNone     Successfully handled the Link Metrics Management Response.
368      * @retval kErrorParse    Cannot parse sub-TLVs from @p aMessage successfully.
369      *
370      */
371     Error HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId);
372 
373     /**
374      * This method registers a callback to handle Link Metrics report received.
375      *
376      * @param[in]  aCallback  A pointer to a function that is called when a Link Metrics report is received.
377      * @param[in]  aContext   A pointer to application-specific context.
378      *
379      */
380     void SetReportCallback(ReportCallback aCallback, void *aContext);
381 
382     /**
383      * This method registers a callback to handle Link Metrics Management Response received.
384      *
385      * @param[in]  aCallback A pointer to a function that is called when a Link Metrics Management Response is received.
386      * @param[in]  aContext  A pointer to application-specific context.
387      *
388      */
389     void SetMgmtResponseCallback(MgmtResponseCallback aCallback, void *aContext);
390 
391     /**
392      * This method registers a callback to handle Link Metrics when Enh-ACK Probing IE is received.
393      *
394      * @param[in]  aCallback A pointer to a function that is called when Enh-ACK Probing IE is received is received.
395      * @param[in]  aContext  A pointer to application-specific context.
396      *
397      */
398     void SetEnhAckProbingCallback(EnhAckProbingIeReportCallback aCallback, void *aContext);
399 
400     /**
401      * This method processes received Enh-ACK Probing IE data.
402      *
403      * @param[in] aData      A pointer to buffer containing the Enh-ACK Probing IE data.
404      * @param[in] aLen       The length of @p aData.
405      * @param[in] aNeighbor  The neighbor from which the Enh-ACK Probing IE was received.
406      *
407      */
408     void ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, const Neighbor &aNeighbor);
409 
410 private:
411     static constexpr uint8_t kMaxTypeIdFlags = 4;
412 
413     // Max number of SeriesInfo that could be allocated by the pool.
414     static constexpr uint16_t kMaxSeriesSupported = OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED;
415 
416     static constexpr uint8_t kQueryIdSingleProbe = 0;   // This query ID represents Single Probe.
417     static constexpr uint8_t kSeriesIdAllSeries  = 255; // This series ID represents all series.
418     static constexpr uint8_t kLinkProbeMaxLen    = 64;  // Max length of data payload in Link Probe TLV.
419 
420     Error SendLinkMetricsQuery(const Ip6::Address &aDestination,
421                                uint8_t             aSeriesId,
422                                const TypeIdFlags * aTypeIdFlags,
423                                uint8_t             aTypeIdFlagsCount);
424 
425     Status ConfigureForwardTrackingSeries(uint8_t            aSeriesId,
426                                           const SeriesFlags &aSeriesFlags,
427                                           const Metrics &    aMetrics,
428                                           Neighbor &         aNeighbor);
429 
430     Status ConfigureEnhAckProbing(EnhAckFlags aEnhAckFlags, const Metrics &aMetrics, Neighbor &aNeighbor);
431 
432     Neighbor *GetNeighborFromLinkLocalAddr(const Ip6::Address &aDestination);
433 
434     static Error ReadTypeIdFlagsFromMessage(const Message &aMessage,
435                                             uint8_t        aStartPos,
436                                             uint8_t        aEndPos,
437                                             Metrics &      aMetrics);
438     static Error AppendReportSubTlvToMessage(Message &aMessage, uint8_t &aLength, const MetricsValues &aValues);
439     static Error AppendStatusSubTlvToMessage(Message &aMessage, uint8_t &aLength, Status aStatus);
440 
441     ReportCallback                mReportCallback;
442     void *                        mReportCallbackContext;
443     MgmtResponseCallback          mMgmtResponseCallback;
444     void *                        mMgmtResponseCallbackContext;
445     EnhAckProbingIeReportCallback mEnhAckProbingIeReportCallback;
446     void *                        mEnhAckProbingIeReportCallbackContext;
447 
448     Pool<SeriesInfo, kMaxSeriesSupported> mSeriesInfoPool;
449 };
450 
451 /**
452  * @}
453  */
454 
455 } // namespace LinkMetrics
456 } // namespace ot
457 
458 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
459 
460 #endif // LINK_METRICS_HPP
461