• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "link_metrics_manager.hpp"
30 
31 #if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
32 
33 #include "instance/instance.hpp"
34 
35 namespace ot {
36 namespace Utils {
37 
38 RegisterLogModule("LinkMetricsMgr");
39 
40 /**
41  * @addtogroup utils-link-metrics-manager
42  *
43  * @brief
44  *   This module includes definitions for Link Metrics Manager.
45  *
46  * @{
47  */
48 
LinkMetricsManager(Instance & aInstance)49 LinkMetricsManager::LinkMetricsManager(Instance &aInstance)
50     : InstanceLocator(aInstance)
51     , mTimer(aInstance)
52     , mEnabled(OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT)
53 {
54 }
55 
SetEnabled(bool aEnable)56 void LinkMetricsManager::SetEnabled(bool aEnable)
57 {
58     VerifyOrExit(mEnabled != aEnable);
59     mEnabled = aEnable;
60 
61     if (mEnabled)
62     {
63         Start();
64     }
65     else
66     {
67         Stop();
68     }
69 
70 exit:
71     return;
72 }
73 
GetLinkMetricsValueByExtAddr(const Mac::ExtAddress & aExtAddress,LinkMetrics::MetricsValues & aMetricsValues)74 Error LinkMetricsManager::GetLinkMetricsValueByExtAddr(const Mac::ExtAddress      &aExtAddress,
75                                                        LinkMetrics::MetricsValues &aMetricsValues)
76 {
77     Error    error = kErrorNone;
78     Subject *subject;
79 
80     subject = mSubjectList.FindMatching(aExtAddress);
81     VerifyOrExit(subject != nullptr, error = kErrorNotFound);
82     VerifyOrExit(subject->mState == kActive || subject->mState == kRenewing, error = kErrorInvalidState);
83 
84     aMetricsValues.mLinkMarginValue = subject->mData.mLinkMargin;
85     aMetricsValues.mRssiValue       = subject->mData.mRssi;
86 
87 exit:
88     return error;
89 }
90 
Start(void)91 void LinkMetricsManager::Start(void)
92 {
93     LinkMetrics::Initiator &initiator = Get<LinkMetrics::Initiator>();
94 
95     VerifyOrExit(mEnabled && Get<Mle::Mle>().IsAttached());
96 
97     initiator.SetMgmtResponseCallback(HandleMgmtResponse, this);
98     initiator.SetEnhAckProbingCallback(HandleEnhAckIe, this);
99 
100     mTimer.Start(kTimeBeforeStartMilliSec);
101 exit:
102     return;
103 }
104 
Stop(void)105 void LinkMetricsManager::Stop(void)
106 {
107     LinkMetrics::Initiator &initiator = Get<LinkMetrics::Initiator>();
108 
109     mTimer.Stop();
110 
111     initiator.SetMgmtResponseCallback(nullptr, nullptr);
112     initiator.SetEnhAckProbingCallback(nullptr, nullptr);
113 
114     UnregisterAllSubjects();
115     ReleaseAllSubjects();
116 }
117 
Update(void)118 void LinkMetricsManager::Update(void)
119 {
120     UpdateSubjects();
121     UpdateLinkMetricsStates();
122 }
123 
124 // This method updates the Link Metrics Subject in the subject list. It adds new neighbors to the list.
UpdateSubjects(void)125 void LinkMetricsManager::UpdateSubjects(void)
126 {
127     Neighbor::Info         neighborInfo;
128     otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
129 
130     while (Get<NeighborTable>().GetNextNeighborInfo(iterator, neighborInfo) == kErrorNone)
131     {
132         // if not in the subject list, allocate and add
133         if (!mSubjectList.ContainsMatching(AsCoreType(&neighborInfo.mExtAddress)))
134         {
135             Subject *subject = mPool.Allocate();
136 
137             VerifyOrExit(subject != nullptr);
138 
139             subject->Clear();
140             subject->mExtAddress = AsCoreType(&neighborInfo.mExtAddress);
141             IgnoreError(mSubjectList.Add(*subject));
142         }
143     }
144 
145 exit:
146     return;
147 }
148 
149 // This method updates the state and take corresponding actions for all subjects and removes stale subjects.
UpdateLinkMetricsStates(void)150 void LinkMetricsManager::UpdateLinkMetricsStates(void)
151 {
152     LinkedList<Subject> staleSubjects;
153 
154     mSubjectList.RemoveAllMatching(staleSubjects, *this);
155 
156     while (!staleSubjects.IsEmpty())
157     {
158         Subject *subject = staleSubjects.Pop();
159 
160         mPool.Free(*subject);
161     }
162 }
163 
UnregisterAllSubjects(void)164 void LinkMetricsManager::UnregisterAllSubjects(void)
165 {
166     for (Subject &subject : mSubjectList)
167     {
168         IgnoreError(subject.UnregisterEap(GetInstance()));
169     }
170 }
171 
ReleaseAllSubjects(void)172 void LinkMetricsManager::ReleaseAllSubjects(void)
173 {
174     while (!mSubjectList.IsEmpty())
175     {
176         Subject *subject = mSubjectList.Pop();
177 
178         mPool.Free(*subject);
179     }
180 }
181 
HandleNotifierEvents(Events aEvents)182 void LinkMetricsManager::HandleNotifierEvents(Events aEvents)
183 {
184     if (aEvents.Contains(kEventThreadRoleChanged))
185     {
186         if (Get<Mle::Mle>().IsAttached())
187         {
188             Start();
189         }
190         else
191         {
192             Stop();
193         }
194     }
195 }
196 
HandleTimer(void)197 void LinkMetricsManager::HandleTimer(void)
198 {
199     if (Get<Mle::Mle>().IsAttached())
200     {
201         Update();
202         mTimer.Start(kStateUpdateIntervalMilliSec);
203     }
204 }
205 
HandleMgmtResponse(const otIp6Address * aAddress,otLinkMetricsStatus aStatus,void * aContext)206 void LinkMetricsManager::HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus, void *aContext)
207 {
208     static_cast<LinkMetricsManager *>(aContext)->HandleMgmtResponse(aAddress, aStatus);
209 }
210 
HandleMgmtResponse(const otIp6Address * aAddress,otLinkMetricsStatus aStatus)211 void LinkMetricsManager::HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus)
212 {
213     Mac::ExtAddress extAddress;
214     Subject        *subject;
215     Neighbor       *neighbor;
216 
217     AsCoreType(aAddress).GetIid().ConvertToExtAddress(extAddress);
218     neighbor = Get<NeighborTable>().FindNeighbor(extAddress);
219     VerifyOrExit(neighbor != nullptr);
220 
221     subject = mSubjectList.FindMatching(extAddress);
222     VerifyOrExit(subject != nullptr);
223 
224     switch (MapEnum(aStatus))
225     {
226     case LinkMetrics::Status::kStatusSuccess:
227         subject->mState = SubjectState::kActive;
228         break;
229     default:
230         subject->mState = SubjectState::kNotConfigured;
231         break;
232     }
233 
234 exit:
235     return;
236 }
237 
HandleEnhAckIe(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues,void * aContext)238 void LinkMetricsManager::HandleEnhAckIe(otShortAddress             aShortAddress,
239                                         const otExtAddress        *aExtAddress,
240                                         const otLinkMetricsValues *aMetricsValues,
241                                         void                      *aContext)
242 {
243     static_cast<LinkMetricsManager *>(aContext)->HandleEnhAckIe(aShortAddress, aExtAddress, aMetricsValues);
244 }
245 
HandleEnhAckIe(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues)246 void LinkMetricsManager::HandleEnhAckIe(otShortAddress             aShortAddress,
247                                         const otExtAddress        *aExtAddress,
248                                         const otLinkMetricsValues *aMetricsValues)
249 {
250     OT_UNUSED_VARIABLE(aShortAddress);
251 
252     Error    error   = kErrorNone;
253     Subject *subject = mSubjectList.FindMatching(AsCoreType(aExtAddress));
254 
255     VerifyOrExit(subject != nullptr, error = kErrorNotFound);
256 
257     VerifyOrExit(subject->mState == SubjectState::kActive || subject->mState == SubjectState::kRenewing);
258     subject->mLastUpdateTime = TimerMilli::GetNow();
259 
260     VerifyOrExit(aMetricsValues->mMetrics.mRssi && aMetricsValues->mMetrics.mLinkMargin, error = kErrorInvalidArgs);
261 
262     subject->mData.mRssi       = aMetricsValues->mRssiValue;
263     subject->mData.mLinkMargin = aMetricsValues->mLinkMarginValue;
264 
265 exit:
266     if (error == kErrorInvalidArgs)
267     {
268         LogWarn("Metrics received are unexpected!");
269     }
270 }
271 
272 // This special Match method is used for "iterating over list while removing some items"
Matches(const LinkMetricsManager & aLinkMetricsMgr)273 bool LinkMetricsManager::Subject::Matches(const LinkMetricsManager &aLinkMetricsMgr)
274 {
275     Error error = UpdateState(aLinkMetricsMgr.GetInstance());
276 
277     return error == kErrorUnknownNeighbor || error == kErrorNotCapable;
278 }
279 
ConfigureEap(Instance & aInstance)280 Error LinkMetricsManager::Subject::ConfigureEap(Instance &aInstance)
281 {
282     Error                    error    = kErrorNone;
283     Neighbor                *neighbor = aInstance.Get<NeighborTable>().FindNeighbor(mExtAddress);
284     Ip6::Address             destination;
285     LinkMetrics::EnhAckFlags enhAckFlags = LinkMetrics::kEnhAckRegister;
286     LinkMetrics::Metrics     metricsFlags;
287 
288     VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
289     destination.SetToLinkLocalAddress(neighbor->GetExtAddress());
290 
291     metricsFlags.Clear();
292     metricsFlags.mLinkMargin = 1;
293     metricsFlags.mRssi       = 1;
294     error =
295         aInstance.Get<LinkMetrics::Initiator>().SendMgmtRequestEnhAckProbing(destination, enhAckFlags, &metricsFlags);
296 
297 exit:
298     if (error == kErrorNone)
299     {
300         mState = (mState == SubjectState::kActive) ? SubjectState::kRenewing : SubjectState::kConfiguring;
301         mAttempts++;
302     }
303     return error;
304 }
305 
UnregisterEap(Instance & aInstance)306 Error LinkMetricsManager::Subject::UnregisterEap(Instance &aInstance)
307 {
308     Error                    error    = kErrorNone;
309     Neighbor                *neighbor = aInstance.Get<NeighborTable>().FindNeighbor(mExtAddress);
310     Ip6::Address             destination;
311     LinkMetrics::EnhAckFlags enhAckFlags = LinkMetrics::kEnhAckClear;
312 
313     VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
314     destination.SetToLinkLocalAddress(neighbor->GetExtAddress());
315 
316     error = aInstance.Get<LinkMetrics::Initiator>().SendMgmtRequestEnhAckProbing(destination, enhAckFlags, nullptr);
317 exit:
318     return error;
319 }
320 
UpdateState(Instance & aInstance)321 Error LinkMetricsManager::Subject::UpdateState(Instance &aInstance)
322 {
323     bool     shouldConfigure = false;
324     uint32_t pastTimeMs;
325     Error    error = kErrorNone;
326 
327     switch (mState)
328     {
329     case kNotConfigured:
330     case kConfiguring:
331     case kRenewing:
332         if (mAttempts >= kConfigureLinkMetricsMaxAttempts)
333         {
334             mState = kNotSupported;
335         }
336         else
337         {
338             shouldConfigure = true;
339         }
340         break;
341     case kActive:
342         pastTimeMs = TimerMilli::GetNow() - mLastUpdateTime;
343         if (pastTimeMs >= kStateUpdateIntervalMilliSec)
344         {
345             shouldConfigure = true;
346         }
347         break;
348     case kNotSupported:
349         ExitNow(error = kErrorNotCapable);
350         break;
351     default:
352         break;
353     }
354 
355     if (shouldConfigure)
356     {
357         error = ConfigureEap(aInstance);
358     }
359 
360 exit:
361     return error;
362 }
363 
364 /**
365  * @}
366  */
367 
368 } // namespace Utils
369 } // namespace ot
370 
371 #endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
372