xref: /aosp_15_r20/external/openthread/src/core/utils/mesh_diag.cpp (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
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 /**
30  * @file
31  *   This file implements the Mesh Diag module.
32  */
33 
34 #include "mesh_diag.hpp"
35 
36 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
37 
38 #include "common/as_core_type.hpp"
39 #include "common/code_utils.hpp"
40 #include "common/debug.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/log.hpp"
43 #include "instance/instance.hpp"
44 
45 namespace ot {
46 namespace Utils {
47 
48 using namespace NetworkDiagnostic;
49 
50 RegisterLogModule("MeshDiag");
51 
52 //---------------------------------------------------------------------------------------------------------------------
53 // MeshDiag
54 
MeshDiag(Instance & aInstance)55 MeshDiag::MeshDiag(Instance &aInstance)
56     : InstanceLocator(aInstance)
57     , mState(kStateIdle)
58     , mExpectedQueryId(0)
59     , mExpectedAnswerIndex(0)
60     , mTimer(aInstance)
61 {
62 }
63 
DiscoverTopology(const DiscoverConfig & aConfig,DiscoverCallback aCallback,void * aContext)64 Error MeshDiag::DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext)
65 {
66     static constexpr uint8_t kMaxTlvsToRequest = 6;
67 
68     Error   error = kErrorNone;
69     uint8_t tlvs[kMaxTlvsToRequest];
70     uint8_t tlvsLength = 0;
71 
72     VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
73     VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
74 
75     tlvs[tlvsLength++] = Address16Tlv::kType;
76     tlvs[tlvsLength++] = ExtMacAddressTlv::kType;
77     tlvs[tlvsLength++] = RouteTlv::kType;
78     tlvs[tlvsLength++] = VersionTlv::kType;
79 
80     if (aConfig.mDiscoverIp6Addresses)
81     {
82         tlvs[tlvsLength++] = Ip6AddressListTlv::kType;
83     }
84 
85     if (aConfig.mDiscoverChildTable)
86     {
87         tlvs[tlvsLength++] = ChildTableTlv::kType;
88     }
89 
90     Get<RouterTable>().GetRouterIdSet(mDiscover.mExpectedRouterIdSet);
91 
92     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
93     {
94         Ip6::Address destination;
95 
96         if (!mDiscover.mExpectedRouterIdSet.Contains(routerId))
97         {
98             continue;
99         }
100 
101         destination.SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(), Mle::Rloc16FromRouterId(routerId));
102 
103         SuccessOrExit(error = Get<Client>().SendCommand(kUriDiagnosticGetRequest, Message::kPriorityLow, destination,
104                                                         tlvs, tlvsLength, HandleDiagGetResponse, this));
105     }
106 
107     mDiscover.mCallback.Set(aCallback, aContext);
108     mState = kStateDiscoverTopology;
109     mTimer.Start(kResponseTimeout);
110 
111 exit:
112     return error;
113 }
114 
HandleDiagGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)115 void MeshDiag::HandleDiagGetResponse(void                *aContext,
116                                      otMessage           *aMessage,
117                                      const otMessageInfo *aMessageInfo,
118                                      Error                aResult)
119 {
120     static_cast<MeshDiag *>(aContext)->HandleDiagGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
121                                                              aResult);
122 }
123 
HandleDiagGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)124 void MeshDiag::HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
125 {
126     OT_UNUSED_VARIABLE(aMessageInfo);
127 
128     Error           error;
129     RouterInfo      routerInfo;
130     Ip6AddrIterator ip6AddrIterator;
131     ChildIterator   childIterator;
132 
133     SuccessOrExit(aResult);
134     VerifyOrExit(aMessage != nullptr);
135     VerifyOrExit(mState == kStateDiscoverTopology);
136 
137     SuccessOrExit(routerInfo.ParseFrom(*aMessage));
138 
139     if (ip6AddrIterator.InitFrom(*aMessage) == kErrorNone)
140     {
141         routerInfo.mIp6AddrIterator = &ip6AddrIterator;
142     }
143 
144     if (childIterator.InitFrom(*aMessage, routerInfo.mRloc16) == kErrorNone)
145     {
146         routerInfo.mChildIterator = &childIterator;
147     }
148 
149     mDiscover.mExpectedRouterIdSet.Remove(routerInfo.mRouterId);
150 
151     if (mDiscover.mExpectedRouterIdSet.GetNumberOfAllocatedIds() == 0)
152     {
153         error  = kErrorNone;
154         mState = kStateIdle;
155         mTimer.Stop();
156     }
157     else
158     {
159         error = kErrorPending;
160     }
161 
162     mDiscover.mCallback.InvokeIfSet(error, &routerInfo);
163 
164 exit:
165     return;
166 }
167 
SendQuery(uint16_t aRloc16,const uint8_t * aTlvs,uint8_t aTlvsLength)168 Error MeshDiag::SendQuery(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength)
169 {
170     Error        error = kErrorNone;
171     Ip6::Address destination;
172 
173     VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
174     VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
175     VerifyOrExit(Mle::IsRouterRloc16(aRloc16), error = kErrorInvalidArgs);
176     VerifyOrExit(Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(aRloc16)), error = kErrorNotFound);
177 
178     destination.SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(), aRloc16);
179 
180     SuccessOrExit(error = Get<Client>().SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, destination,
181                                                     aTlvs, aTlvsLength));
182 
183     mExpectedQueryId     = Get<Client>().GetLastQueryId();
184     mExpectedAnswerIndex = 0;
185 
186     mTimer.Start(kResponseTimeout);
187 
188 exit:
189     return error;
190 }
191 
QueryChildTable(uint16_t aRloc16,QueryChildTableCallback aCallback,void * aContext)192 Error MeshDiag::QueryChildTable(uint16_t aRloc16, QueryChildTableCallback aCallback, void *aContext)
193 {
194     static const uint8_t kTlvTypes[] = {ChildTlv::kType};
195 
196     Error error;
197 
198     SuccessOrExit(error = SendQuery(aRloc16, kTlvTypes, sizeof(kTlvTypes)));
199 
200     mQueryChildTable.mCallback.Set(aCallback, aContext);
201     mQueryChildTable.mRouterRloc16 = aRloc16;
202     mState                         = kStateQueryChildTable;
203 
204 exit:
205     return error;
206 }
207 
QueryChildrenIp6Addrs(uint16_t aRloc16,ChildIp6AddrsCallback aCallback,void * aContext)208 Error MeshDiag::QueryChildrenIp6Addrs(uint16_t aRloc16, ChildIp6AddrsCallback aCallback, void *aContext)
209 {
210     static const uint8_t kTlvTypes[] = {ChildIp6AddressListTlv::kType};
211 
212     Error error;
213 
214     SuccessOrExit(error = SendQuery(aRloc16, kTlvTypes, sizeof(kTlvTypes)));
215 
216     mQueryChildrenIp6Addrs.mCallback.Set(aCallback, aContext);
217     mQueryChildrenIp6Addrs.mParentRloc16 = aRloc16;
218     mState                               = kStateQueryChildrenIp6Addrs;
219 
220 exit:
221     return error;
222 }
223 
QueryRouterNeighborTable(uint16_t aRloc16,RouterNeighborTableCallback aCallback,void * aContext)224 Error MeshDiag::QueryRouterNeighborTable(uint16_t aRloc16, RouterNeighborTableCallback aCallback, void *aContext)
225 {
226     static const uint8_t kTlvTypes[] = {RouterNeighborTlv::kType};
227 
228     Error error;
229 
230     SuccessOrExit(error = SendQuery(aRloc16, kTlvTypes, sizeof(kTlvTypes)));
231 
232     mQueryRouterNeighborTable.mCallback.Set(aCallback, aContext);
233     mQueryRouterNeighborTable.mRouterRloc16 = aRloc16;
234     mState                                  = kStateQueryRouterNeighborTable;
235 
236 exit:
237     return error;
238 }
239 
HandleDiagnosticGetAnswer(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)240 bool MeshDiag::HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
241 {
242     bool didPorcess = false;
243 
244     switch (mState)
245     {
246     case kStateQueryChildTable:
247         didPorcess = ProcessChildTableAnswer(aMessage, aMessageInfo);
248         break;
249 
250     case kStateQueryChildrenIp6Addrs:
251         didPorcess = ProcessChildrenIp6AddrsAnswer(aMessage, aMessageInfo);
252         break;
253 
254     case kStateQueryRouterNeighborTable:
255         didPorcess = ProcessRouterNeighborTableAnswer(aMessage, aMessageInfo);
256         break;
257 
258     default:
259         break;
260     }
261 
262     return didPorcess;
263 }
264 
ProcessMessage(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo,uint16_t aSenderRloc16)265 Error MeshDiag::ProcessMessage(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aSenderRloc16)
266 {
267     // This method processes the received answer message to
268     // check whether it is from the intended sender and matches
269     // the expected query ID and answer index.
270 
271     Error     error = kErrorFailed;
272     AnswerTlv answerTlv;
273     uint16_t  queryId;
274 
275     VerifyOrExit(Get<Mle::Mle>().IsRoutingLocator(aMessageInfo.GetPeerAddr()));
276     VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().GetLocator() == aSenderRloc16);
277 
278     SuccessOrExit(Tlv::Find<QueryIdTlv>(aMessage, queryId));
279     VerifyOrExit(queryId == mExpectedQueryId);
280 
281     SuccessOrExit(Tlv::FindTlv(aMessage, answerTlv));
282 
283     if (answerTlv.GetIndex() != mExpectedAnswerIndex)
284     {
285         Finalize(kErrorResponseTimeout);
286         ExitNow();
287     }
288 
289     mExpectedAnswerIndex++;
290     error = kErrorNone;
291 
292 exit:
293     return error;
294 }
295 
ProcessChildTableAnswer(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)296 bool MeshDiag::ProcessChildTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
297 {
298     bool       didPorcess = false;
299     ChildTlv   childTlv;
300     ChildEntry entry;
301     uint16_t   offset;
302 
303     SuccessOrExit(ProcessMessage(aMessage, aMessageInfo, mQueryChildTable.mRouterRloc16));
304 
305     while (true)
306     {
307         SuccessOrExit(Tlv::FindTlv(aMessage, childTlv, offset));
308         VerifyOrExit(!childTlv.IsExtended());
309 
310         didPorcess = true;
311 
312         if (childTlv.GetLength() == 0)
313         {
314             // We reached end of the list.
315             mState = kStateIdle;
316             mTimer.Stop();
317             mQueryChildTable.mCallback.InvokeIfSet(kErrorNone, nullptr);
318             ExitNow();
319         }
320 
321         VerifyOrExit(childTlv.GetLength() >= sizeof(ChildTlv) - sizeof(Tlv));
322         IgnoreError(aMessage.Read(offset, childTlv));
323 
324         entry.SetFrom(childTlv);
325         mQueryChildTable.mCallback.InvokeIfSet(kErrorPending, &entry);
326 
327         // Make sure query operation is not canceled from the
328         // callback.
329         VerifyOrExit(mState == kStateQueryChildTable);
330 
331         aMessage.SetOffset(static_cast<uint16_t>(offset + childTlv.GetSize()));
332     }
333 
334 exit:
335     return didPorcess;
336 }
337 
ProcessRouterNeighborTableAnswer(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)338 bool MeshDiag::ProcessRouterNeighborTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
339 {
340     bool                didPorcess = false;
341     RouterNeighborTlv   neighborTlv;
342     RouterNeighborEntry entry;
343     uint16_t            offset;
344 
345     SuccessOrExit(ProcessMessage(aMessage, aMessageInfo, mQueryRouterNeighborTable.mRouterRloc16));
346 
347     while (true)
348     {
349         SuccessOrExit(Tlv::FindTlv(aMessage, neighborTlv, offset));
350         VerifyOrExit(!neighborTlv.IsExtended());
351 
352         didPorcess = true;
353 
354         if (neighborTlv.GetLength() == 0)
355         {
356             // We reached end of the list.
357             mState = kStateIdle;
358             mTimer.Stop();
359             mQueryRouterNeighborTable.mCallback.InvokeIfSet(kErrorNone, nullptr);
360             ExitNow();
361         }
362 
363         VerifyOrExit(neighborTlv.GetLength() >= sizeof(RouterNeighborTlv) - sizeof(Tlv));
364 
365         entry.SetFrom(neighborTlv);
366         mQueryRouterNeighborTable.mCallback.InvokeIfSet(kErrorPending, &entry);
367 
368         // Make sure query operation is not canceled from the
369         // callback.
370         VerifyOrExit(mState == kStateQueryRouterNeighborTable);
371 
372         aMessage.SetOffset(static_cast<uint16_t>(offset + neighborTlv.GetSize()));
373     }
374 
375 exit:
376     return didPorcess;
377 }
378 
ProcessChildrenIp6AddrsAnswer(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)379 bool MeshDiag::ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
380 {
381     bool                        didPorcess = false;
382     OffsetRange                 offsetRange;
383     ChildIp6AddressListTlvValue tlvValue;
384     Ip6AddrIterator             ip6AddrIterator;
385 
386     SuccessOrExit(ProcessMessage(aMessage, aMessageInfo, mQueryChildrenIp6Addrs.mParentRloc16));
387 
388     while (true)
389     {
390         SuccessOrExit(Tlv::FindTlvValueOffsetRange(aMessage, ChildIp6AddressListTlv::kType, offsetRange));
391 
392         didPorcess = true;
393 
394         if (offsetRange.IsEmpty())
395         {
396             // We reached end of the list
397             mState = kStateIdle;
398             mTimer.Stop();
399             mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(kErrorNone, Mle::kInvalidRloc16, nullptr);
400             ExitNow();
401         }
402 
403         // Read the `ChildIp6AddressListTlvValue` (which contains the
404         // child RLOC16) and then prepare the `Ip6AddrIterator`.
405 
406         SuccessOrExit(aMessage.Read(offsetRange, tlvValue));
407         offsetRange.AdvanceOffset(sizeof(tlvValue));
408 
409         ip6AddrIterator.mMessage     = &aMessage;
410         ip6AddrIterator.mOffsetRange = offsetRange;
411 
412         mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(kErrorPending, tlvValue.GetRloc16(), &ip6AddrIterator);
413 
414         // Make sure query operation is not canceled from the
415         // callback.
416         VerifyOrExit(mState == kStateQueryChildrenIp6Addrs);
417 
418         aMessage.SetOffset(offsetRange.GetEndOffset());
419     }
420 
421 exit:
422     return didPorcess;
423 }
424 
Cancel(void)425 void MeshDiag::Cancel(void)
426 {
427     switch (mState)
428     {
429     case kStateIdle:
430     case kStateQueryChildTable:
431     case kStateQueryChildrenIp6Addrs:
432     case kStateQueryRouterNeighborTable:
433         break;
434 
435     case kStateDiscoverTopology:
436         IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleDiagGetResponse, this));
437         break;
438     }
439 
440     mState = kStateIdle;
441     mTimer.Stop();
442 }
443 
Finalize(Error aError)444 void MeshDiag::Finalize(Error aError)
445 {
446     // Finalize an ongoing query operation (if any) invoking
447     // the corresponding callback with `aError`.
448 
449     State oldState = mState;
450 
451     Cancel();
452 
453     switch (oldState)
454     {
455     case kStateIdle:
456         break;
457 
458     case kStateDiscoverTopology:
459         mDiscover.mCallback.InvokeIfSet(aError, nullptr);
460         break;
461 
462     case kStateQueryChildTable:
463         mQueryChildTable.mCallback.InvokeIfSet(aError, nullptr);
464         break;
465 
466     case kStateQueryChildrenIp6Addrs:
467         mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(aError, Mle::kInvalidRloc16, nullptr);
468         break;
469 
470     case kStateQueryRouterNeighborTable:
471         mQueryRouterNeighborTable.mCallback.InvokeIfSet(aError, nullptr);
472         break;
473     }
474 }
475 
HandleTimer(void)476 void MeshDiag::HandleTimer(void) { Finalize(kErrorResponseTimeout); }
477 
478 //---------------------------------------------------------------------------------------------------------------------
479 // MeshDiag::RouterInfo
480 
ParseFrom(const Message & aMessage)481 Error MeshDiag::RouterInfo::ParseFrom(const Message &aMessage)
482 {
483     Error     error = kErrorNone;
484     Mle::Mle &mle   = aMessage.Get<Mle::Mle>();
485     RouteTlv  routeTlv;
486 
487     Clear();
488 
489     SuccessOrExit(error = Tlv::Find<Address16Tlv>(aMessage, mRloc16));
490     SuccessOrExit(error = Tlv::Find<ExtMacAddressTlv>(aMessage, AsCoreType(&mExtAddress)));
491     SuccessOrExit(error = Tlv::FindTlv(aMessage, routeTlv));
492 
493     switch (error = Tlv::Find<VersionTlv>(aMessage, mVersion))
494     {
495     case kErrorNone:
496         break;
497     case kErrorNotFound:
498         mVersion = kVersionUnknown;
499         error    = kErrorNone;
500         break;
501     default:
502         ExitNow();
503     }
504 
505     mRouterId           = Mle::RouterIdFromRloc16(mRloc16);
506     mIsThisDevice       = mle.HasRloc16(mRloc16);
507     mIsThisDeviceParent = mle.IsChild() && (mRloc16 == mle.GetParent().GetRloc16());
508     mIsLeader           = (mRouterId == mle.GetLeaderId());
509     mIsBorderRouter     = aMessage.Get<NetworkData::Leader>().ContainsBorderRouterWithRloc(mRloc16);
510 
511     for (uint8_t id = 0, index = 0; id <= Mle::kMaxRouterId; id++)
512     {
513         if (routeTlv.IsRouterIdSet(id))
514         {
515             mLinkQualities[id] = routeTlv.GetLinkQualityIn(index);
516             index++;
517         }
518     }
519 
520 exit:
521     return error;
522 }
523 
524 //---------------------------------------------------------------------------------------------------------------------
525 // MeshDiag::Ip6AddrIterator
526 
InitFrom(const Message & aMessage)527 Error MeshDiag::Ip6AddrIterator::InitFrom(const Message &aMessage)
528 {
529     Error error;
530 
531     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Ip6AddressListTlv::kType, mOffsetRange));
532     mMessage = &aMessage;
533 
534 exit:
535     return error;
536 }
537 
GetNextAddress(Ip6::Address & aAddress)538 Error MeshDiag::Ip6AddrIterator::GetNextAddress(Ip6::Address &aAddress)
539 {
540     Error error = kErrorNone;
541 
542     VerifyOrExit(mMessage != nullptr, error = kErrorNotFound);
543 
544     VerifyOrExit(mMessage->Read(mOffsetRange, aAddress) == kErrorNone, error = kErrorNotFound);
545     mOffsetRange.AdvanceOffset(sizeof(Ip6::Address));
546 
547 exit:
548     return error;
549 }
550 
551 //---------------------------------------------------------------------------------------------------------------------
552 // MeshDiag::ChildIterator
553 
InitFrom(const Message & aMessage,uint16_t aParentRloc16)554 Error MeshDiag::ChildIterator::InitFrom(const Message &aMessage, uint16_t aParentRloc16)
555 {
556     Error error;
557 
558     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, ChildTableTlv::kType, mOffsetRange));
559 
560     mMessage      = &aMessage;
561     mParentRloc16 = aParentRloc16;
562 
563 exit:
564     return error;
565 }
566 
GetNextChildInfo(ChildInfo & aChildInfo)567 Error MeshDiag::ChildIterator::GetNextChildInfo(ChildInfo &aChildInfo)
568 {
569     Error           error = kErrorNone;
570     ChildTableEntry entry;
571 
572     VerifyOrExit(mMessage != nullptr, error = kErrorNotFound);
573 
574     VerifyOrExit(mMessage->Read(mOffsetRange, entry) == kErrorNone, error = kErrorNotFound);
575     mOffsetRange.AdvanceOffset(sizeof(ChildTableEntry));
576 
577     aChildInfo.mRloc16 = mParentRloc16 + entry.GetChildId();
578     entry.GetMode().Get(aChildInfo.mMode);
579     aChildInfo.mLinkQuality = entry.GetLinkQuality();
580 
581     aChildInfo.mIsThisDevice   = mMessage->Get<Mle::Mle>().HasRloc16(aChildInfo.mRloc16);
582     aChildInfo.mIsBorderRouter = mMessage->Get<NetworkData::Leader>().ContainsBorderRouterWithRloc(aChildInfo.mRloc16);
583 
584 exit:
585     return error;
586 }
587 
588 //---------------------------------------------------------------------------------------------------------------------
589 // MeshDiag::ChildEntry
590 
SetFrom(const ChildTlv & aChildTlv)591 void MeshDiag::ChildEntry::SetFrom(const ChildTlv &aChildTlv)
592 {
593     mRxOnWhenIdle        = (aChildTlv.GetFlags() & ChildTlv::kFlagsRxOnWhenIdle);
594     mDeviceTypeFtd       = (aChildTlv.GetFlags() & ChildTlv::kFlagsFtd);
595     mFullNetData         = (aChildTlv.GetFlags() & ChildTlv::kFlagsFullNetdta);
596     mCslSynchronized     = (aChildTlv.GetFlags() & ChildTlv::kFlagsCslSync);
597     mSupportsErrRate     = (aChildTlv.GetFlags() & ChildTlv::kFlagsTrackErrRate);
598     mRloc16              = aChildTlv.GetRloc16();
599     mExtAddress          = aChildTlv.GetExtAddress();
600     mVersion             = aChildTlv.GetVersion();
601     mTimeout             = aChildTlv.GetTimeout();
602     mAge                 = aChildTlv.GetAge();
603     mConnectionTime      = aChildTlv.GetConnectionTime();
604     mSupervisionInterval = aChildTlv.GetSupervisionInterval();
605     mLinkMargin          = aChildTlv.GetLinkMargin();
606     mAverageRssi         = aChildTlv.GetAverageRssi();
607     mLastRssi            = aChildTlv.GetLastRssi();
608     mFrameErrorRate      = aChildTlv.GetFrameErrorRate();
609     mMessageErrorRate    = aChildTlv.GetMessageErrorRate();
610     mQueuedMessageCount  = aChildTlv.GetQueuedMessageCount();
611     mCslPeriod           = aChildTlv.GetCslPeriod();
612     mCslTimeout          = aChildTlv.GetCslTimeout();
613     mCslChannel          = aChildTlv.GetCslChannel();
614 }
615 
616 //---------------------------------------------------------------------------------------------------------------------
617 // MeshDiag::RouterNeighborEntry
618 
SetFrom(const RouterNeighborTlv & aTlv)619 void MeshDiag::RouterNeighborEntry::SetFrom(const RouterNeighborTlv &aTlv)
620 {
621     mSupportsErrRate  = (aTlv.GetFlags() & RouterNeighborTlv::kFlagsTrackErrRate);
622     mRloc16           = aTlv.GetRloc16();
623     mExtAddress       = aTlv.GetExtAddress();
624     mVersion          = aTlv.GetVersion();
625     mConnectionTime   = aTlv.GetConnectionTime();
626     mLinkMargin       = aTlv.GetLinkMargin();
627     mAverageRssi      = aTlv.GetAverageRssi();
628     mLastRssi         = aTlv.GetLastRssi();
629     mFrameErrorRate   = aTlv.GetFrameErrorRate();
630     mMessageErrorRate = aTlv.GetMessageErrorRate();
631 }
632 
633 } // namespace Utils
634 } // namespace ot
635 
636 #endif // #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
637