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