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