1 /*
2  *  Copyright (c) 2023, 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 #ifndef LINK_METRICS_MANAGER_HPP_
30 #define LINK_METRICS_MANAGER_HPP_
31 
32 #include "openthread-core-config.h"
33 
34 #if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
35 
36 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE == 0
37 #error \
38     "OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE can only be used when OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE is true"
39 #endif
40 
41 #include <openthread/link_metrics.h>
42 
43 #include "common/clearable.hpp"
44 #include "common/linked_list.hpp"
45 #include "common/locator.hpp"
46 #include "common/non_copyable.hpp"
47 #include "common/notifier.hpp"
48 #include "common/pool.hpp"
49 #include "common/time.hpp"
50 #include "common/timer.hpp"
51 #include "mac/mac_types.hpp"
52 #include "thread/link_metrics_types.hpp"
53 
54 namespace ot {
55 class UnitTester;
56 namespace Utils {
57 
58 /**
59  * @addtogroup utils-link-metrics-manager
60  *
61  * @brief
62  *   This module includes definitions for Link Metrics Manager.
63  *
64  * @{
65  */
66 
67 /**
68  *
69  * Link Metrics Manager feature utilizes the Enhanced-ACK Based
70  * Probing (abbreviated as "EAP" below) to get the Link Metrics
71  * data of neighboring devices. It is a user of the Link Metrics
72  * feature.
73  *
74  * The feature works as follow:
75  * - Start/Stop
76  *   The switch `OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT`
77  *   controls enabling/disabling this feature by default. The feature
78  *   will only start to work after it joins a Thread network.
79  *
80  *   A CLI interface is provided to enable/disable this feature.
81  *
82  *   Once enabled, it will regularly check current neighbors (all
83  *   devices in the neighbor table, including 'Child' and 'Router')
84  *   and configure the probing with them if haven't done that.
85  *   If disabled, it will clear the configuration with its subjects
86  *   and the local data.
87  *
88  * - Maintenance
89  *   The manager will regularly check the status of each subject. If
90  *   it finds that the link metrics data for one subject hasn't been
91  *   updated for `kStateUpdateIntervalMilliSec`, it will configure
92  *   EAP with the subject again.
93  *   The manager may find that some subject (neighbor) no longer
94  *   exist when trying to configure EAP. It will remove the stale
95  *   subject then.
96  *
97  * - Show data
98  *   An OT API is provided to get the link metrics data of any
99  *   subject (neighbor) by its extended address. In production, this
100  *   data may be fetched by some other means like RPC.
101  *
102  */
103 
104 class LinkMetricsManager : public InstanceLocator, private NonCopyable
105 {
106     friend class ot::Notifier;
107     friend class ot::UnitTester;
108 
109     struct LinkMetricsData
110     {
111         uint8_t mLqi;        ///< Link Quality Indicator. Value range: [0, 255].
112         int8_t  mRssi;       ///< Receive Signal Strength Indicator. Value range: [-128, 0].
113         uint8_t mLinkMargin; ///< Link Margin. The relative signal strength is recorded as
114                              ///< db above the local noise floor. Value range: [0, 130].
115     };
116 
117     enum SubjectState : uint8_t
118     {
119         kNotConfigured = 0,
120         kConfiguring,
121         kActive,
122         kRenewing,
123         kNotSupported,
124     };
125 
126     struct Subject : LinkedListEntry<Subject>, Clearable<Subject>
127     {
128         Mac::ExtAddress mExtAddress;     ///< Use the extended address to identify the neighbor.
129         SubjectState    mState;          ///< Current State of the Subject
130         uint8_t         mAttempts;       ///< The count of attempt that has been made to
131                                          ///< configure EAP
132         TimeMilli       mLastUpdateTime; ///< The time `mData` was updated last time
133         LinkMetricsData mData;
134 
135         Subject *mNext;
136 
Matchesot::Utils::LinkMetricsManager::Subject137         bool Matches(const Mac::ExtAddress &aExtAddress) const { return mExtAddress == aExtAddress; }
138         bool Matches(const LinkMetricsManager &aLinkMetricsMgr);
139 
140         Error ConfigureEap(Instance &aInstance);
141         Error UnregisterEap(Instance &aInstance);
142         Error UpdateState(Instance &aInstance);
143     };
144 
145 public:
146     /**
147      * Initializes a `LinkMetricsManager` object.
148      *
149      * @param[in]   aInstance  A reference to the OpenThread instance.
150      *
151      */
152     explicit LinkMetricsManager(Instance &aInstance);
153 
154     /**
155      * Is the LinkMetricsManager feature enabled.
156      *
157      * @retval TRUE   Link Metrics Manager is enabled.
158      * @retval FALSE  Link Metrics Manager is not enabled.
159      *
160      */
IsEnabled(void)161     bool IsEnabled(void) { return mEnabled; }
162 
163     /**
164      * Enable/Disable the LinkMetricsManager feature.
165      *
166      * @param[in]   aEnable  A boolean to indicate enable or disable.
167      *
168      */
169     void SetEnabled(bool aEnable);
170 
171     /**
172      * Get Link Metrics data of subject by the extended address.
173      *
174      * @param[in]  aExtAddress     A reference to the extended address of the subject.
175      * @param[out] aMetricsValues  A reference to the MetricsValues object to place the result.
176      *
177      * @retval kErrorNone             Successfully got the metrics value.
178      * @retval kErrorInvalidArgs      The arguments are invalid.
179      * @retval kNotFound              No neighbor with the given extended address is found.
180      *
181      */
182     Error GetLinkMetricsValueByExtAddr(const Mac::ExtAddress &aExtAddress, LinkMetrics::MetricsValues &aMetricsValues);
183 
184 private:
185     static constexpr uint16_t kTimeBeforeStartMilliSec         = 5000;
186     static constexpr uint32_t kStateUpdateIntervalMilliSec     = 150000;
187     static constexpr uint8_t  kConfigureLinkMetricsMaxAttempts = 3;
188 #if OPENTHREAD_FTD
189     static constexpr uint8_t kMaximumSubjectToTrack = 128;
190 #elif OPENTHREAD_MTD
191     static constexpr uint8_t kMaximumSubjectToTrack = 1;
192 #endif
193 
194     void Start(void);
195     void Stop(void);
196     void Update(void);
197     void UpdateSubjects(void);
198     void UpdateLinkMetricsStates(void);
199     void UnregisterAllSubjects(void);
200     void ReleaseAllSubjects(void);
201 
202     void        HandleNotifierEvents(Events aEvents);
203     void        HandleTimer(void);
204     static void HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus, void *aContext);
205     void        HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus);
206     static void HandleEnhAckIe(otShortAddress             aShortAddress,
207                                const otExtAddress        *aExtAddress,
208                                const otLinkMetricsValues *aMetricsValues,
209                                void                      *aContext);
210     void        HandleEnhAckIe(otShortAddress             aShortAddress,
211                                const otExtAddress        *aExtAddress,
212                                const otLinkMetricsValues *aMetricsValues);
213 
214     using LinkMetricsMgrTimer = TimerMilliIn<LinkMetricsManager, &LinkMetricsManager::HandleTimer>;
215 
216     Pool<Subject, kMaximumSubjectToTrack> mPool;
217     LinkedList<Subject>                   mSubjectList;
218     LinkMetricsMgrTimer                   mTimer;
219     bool                                  mEnabled;
220 };
221 
222 /**
223  * @}
224  *
225  */
226 
227 } // namespace Utils
228 } // namespace ot
229 
230 #endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
231 
232 #endif // LINK_METRICS_MANAGER_HPP_
233