• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, 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 Thread's Network Diagnostic processing.
32  */
33 
34 #include "network_diagnostic.hpp"
35 
36 #include "instance/instance.hpp"
37 
38 namespace ot {
39 
40 RegisterLogModule("NetDiag");
41 
42 namespace NetworkDiagnostic {
43 
44 const char Server::kVendorName[]      = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME;
45 const char Server::kVendorModel[]     = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL;
46 const char Server::kVendorSwVersion[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION;
47 const char Server::kVendorAppUrl[]    = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL;
48 
49 //---------------------------------------------------------------------------------------------------------------------
50 // Server
51 
Server(Instance & aInstance)52 Server::Server(Instance &aInstance)
53     : InstanceLocator(aInstance)
54 {
55     static_assert(sizeof(kVendorName) <= sizeof(VendorNameTlv::StringType), "VENDOR_NAME is too long");
56     static_assert(sizeof(kVendorModel) <= sizeof(VendorModelTlv::StringType), "VENDOR_MODEL is too long");
57     static_assert(sizeof(kVendorSwVersion) <= sizeof(VendorSwVersionTlv::StringType), "VENDOR_SW_VERSION is too long");
58     static_assert(sizeof(kVendorAppUrl) <= sizeof(VendorAppUrlTlv::StringType), "VENDOR_APP_URL is too long");
59 
60 #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
61     memcpy(mVendorName, kVendorName, sizeof(kVendorName));
62     memcpy(mVendorModel, kVendorModel, sizeof(kVendorModel));
63     memcpy(mVendorSwVersion, kVendorSwVersion, sizeof(kVendorSwVersion));
64     memcpy(mVendorAppUrl, kVendorAppUrl, sizeof(kVendorAppUrl));
65 #endif
66 }
67 
68 #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
69 
SetVendorName(const char * aVendorName)70 Error Server::SetVendorName(const char *aVendorName)
71 {
72     return StringCopy(mVendorName, aVendorName, kStringCheckUtf8Encoding);
73 }
74 
SetVendorModel(const char * aVendorModel)75 Error Server::SetVendorModel(const char *aVendorModel)
76 {
77     return StringCopy(mVendorModel, aVendorModel, kStringCheckUtf8Encoding);
78 }
79 
SetVendorSwVersion(const char * aVendorSwVersion)80 Error Server::SetVendorSwVersion(const char *aVendorSwVersion)
81 {
82     return StringCopy(mVendorSwVersion, aVendorSwVersion, kStringCheckUtf8Encoding);
83 }
84 
SetVendorAppUrl(const char * aVendorAppUrl)85 Error Server::SetVendorAppUrl(const char *aVendorAppUrl)
86 {
87     return StringCopy(mVendorAppUrl, aVendorAppUrl, kStringCheckUtf8Encoding);
88 }
89 
90 #endif
91 
PrepareMessageInfoForDest(const Ip6::Address & aDestination,Tmf::MessageInfo & aMessageInfo) const92 void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const
93 {
94     if (aDestination.IsMulticast())
95     {
96         aMessageInfo.SetMulticastLoop(true);
97     }
98 
99     if (aDestination.IsLinkLocalUnicastOrMulticast())
100     {
101         aMessageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
102     }
103     else
104     {
105         aMessageInfo.SetSockAddrToRloc();
106     }
107 
108     aMessageInfo.SetPeerAddr(aDestination);
109 }
110 
AppendIp6AddressList(Message & aMessage)111 Error Server::AppendIp6AddressList(Message &aMessage)
112 {
113     Error    error = kErrorNone;
114     uint16_t count = 0;
115 
116     for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
117     {
118         OT_UNUSED_VARIABLE(addr);
119         count++;
120     }
121 
122     if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength)
123     {
124         Tlv tlv;
125 
126         tlv.SetType(Tlv::kIp6AddressList);
127         tlv.SetLength(static_cast<uint8_t>(count * Ip6::Address::kSize));
128         SuccessOrExit(error = aMessage.Append(tlv));
129     }
130     else
131     {
132         ExtendedTlv extTlv;
133 
134         extTlv.SetType(Tlv::kIp6AddressList);
135         extTlv.SetLength(count * Ip6::Address::kSize);
136         SuccessOrExit(error = aMessage.Append(extTlv));
137     }
138 
139     for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
140     {
141         SuccessOrExit(error = aMessage.Append(addr.GetAddress()));
142     }
143 
144 exit:
145     return error;
146 }
147 
148 #if OPENTHREAD_FTD
AppendChildTable(Message & aMessage)149 Error Server::AppendChildTable(Message &aMessage)
150 {
151     Error    error = kErrorNone;
152     uint16_t count;
153 
154     VerifyOrExit(Get<Mle::MleRouter>().IsRouterOrLeader());
155 
156     count = Min(Get<ChildTable>().GetNumChildren(Child::kInStateValid), kMaxChildEntries);
157 
158     if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength)
159     {
160         Tlv tlv;
161 
162         tlv.SetType(Tlv::kChildTable);
163         tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
164         SuccessOrExit(error = aMessage.Append(tlv));
165     }
166     else
167     {
168         ExtendedTlv extTlv;
169 
170         extTlv.SetType(Tlv::kChildTable);
171         extTlv.SetLength(count * sizeof(ChildTableEntry));
172         SuccessOrExit(error = aMessage.Append(extTlv));
173     }
174 
175     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
176     {
177         uint8_t         timeout = 0;
178         ChildTableEntry entry;
179 
180         VerifyOrExit(count--);
181 
182         while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
183         {
184             timeout++;
185         }
186 
187         entry.Clear();
188         entry.SetTimeout(timeout + 4);
189         entry.SetLinkQuality(child.GetLinkQualityIn());
190         entry.SetChildId(Mle::ChildIdFromRloc16(child.GetRloc16()));
191         entry.SetMode(child.GetDeviceMode());
192 
193         SuccessOrExit(error = aMessage.Append(entry));
194     }
195 
196 exit:
197     return error;
198 }
199 #endif // OPENTHREAD_FTD
200 
AppendMacCounters(Message & aMessage)201 Error Server::AppendMacCounters(Message &aMessage)
202 {
203     MacCountersTlv       tlv;
204     const otMacCounters &counters = Get<Mac::Mac>().GetCounters();
205 
206     ClearAllBytes(tlv);
207 
208     tlv.Init();
209     tlv.SetIfInUnknownProtos(counters.mRxOther);
210     tlv.SetIfInErrors(counters.mRxErrNoFrame + counters.mRxErrUnknownNeighbor + counters.mRxErrInvalidSrcAddr +
211                       counters.mRxErrSec + counters.mRxErrFcs + counters.mRxErrOther);
212     tlv.SetIfOutErrors(counters.mTxErrCca);
213     tlv.SetIfInUcastPkts(counters.mRxUnicast);
214     tlv.SetIfInBroadcastPkts(counters.mRxBroadcast);
215     tlv.SetIfInDiscards(counters.mRxAddressFiltered + counters.mRxDestAddrFiltered + counters.mRxDuplicated);
216     tlv.SetIfOutUcastPkts(counters.mTxUnicast);
217     tlv.SetIfOutBroadcastPkts(counters.mTxBroadcast);
218     tlv.SetIfOutDiscards(counters.mTxErrBusyChannel);
219 
220     return tlv.AppendTo(aMessage);
221 }
222 
AppendRequestedTlvs(const Message & aRequest,Message & aResponse)223 Error Server::AppendRequestedTlvs(const Message &aRequest, Message &aResponse)
224 {
225     Error       error;
226     OffsetRange offsetRange;
227 
228     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kTypeList, offsetRange));
229 
230     while (!offsetRange.IsEmpty())
231     {
232         uint8_t tlvType;
233 
234         SuccessOrExit(error = aRequest.Read(offsetRange, tlvType));
235         offsetRange.AdvanceOffset(sizeof(tlvType));
236         SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse));
237     }
238 
239 exit:
240     return error;
241 }
242 
AppendDiagTlv(uint8_t aTlvType,Message & aMessage)243 Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage)
244 {
245     Error error = kErrorNone;
246 
247     switch (aTlvType)
248     {
249     case Tlv::kExtMacAddress:
250         error = Tlv::Append<ExtMacAddressTlv>(aMessage, Get<Mac::Mac>().GetExtAddress());
251         break;
252 
253     case Tlv::kAddress16:
254         error = Tlv::Append<Address16Tlv>(aMessage, Get<Mle::MleRouter>().GetRloc16());
255         break;
256 
257     case Tlv::kMode:
258         error = Tlv::Append<ModeTlv>(aMessage, Get<Mle::MleRouter>().GetDeviceMode().Get());
259         break;
260 
261     case Tlv::kEui64:
262     {
263         Mac::ExtAddress eui64;
264 
265         Get<Radio>().GetIeeeEui64(eui64);
266         error = Tlv::Append<Eui64Tlv>(aMessage, eui64);
267         break;
268     }
269 
270     case Tlv::kVersion:
271         error = Tlv::Append<VersionTlv>(aMessage, kThreadVersion);
272         break;
273 
274     case Tlv::kTimeout:
275         VerifyOrExit(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
276         error = Tlv::Append<TimeoutTlv>(aMessage, Get<Mle::MleRouter>().GetTimeout());
277         break;
278 
279     case Tlv::kLeaderData:
280     {
281         LeaderDataTlv tlv;
282 
283         tlv.Init();
284         tlv.Set(Get<Mle::MleRouter>().GetLeaderData());
285         error = tlv.AppendTo(aMessage);
286         break;
287     }
288 
289     case Tlv::kNetworkData:
290         error = Tlv::Append<NetworkDataTlv>(aMessage, Get<NetworkData::Leader>().GetBytes(),
291                                             Get<NetworkData::Leader>().GetLength());
292         break;
293 
294     case Tlv::kIp6AddressList:
295         error = AppendIp6AddressList(aMessage);
296         break;
297 
298     case Tlv::kMacCounters:
299         error = AppendMacCounters(aMessage);
300         break;
301 
302     case Tlv::kMleCounters:
303     {
304         MleCountersTlv tlv;
305 
306         tlv.Init(Get<Mle::Mle>().GetCounters());
307         error = tlv.AppendTo(aMessage);
308         break;
309     }
310 
311     case Tlv::kVendorName:
312         error = Tlv::Append<VendorNameTlv>(aMessage, GetVendorName());
313         break;
314 
315     case Tlv::kVendorModel:
316         error = Tlv::Append<VendorModelTlv>(aMessage, GetVendorModel());
317         break;
318 
319     case Tlv::kVendorSwVersion:
320         error = Tlv::Append<VendorSwVersionTlv>(aMessage, GetVendorSwVersion());
321         break;
322 
323     case Tlv::kVendorAppUrl:
324         error = Tlv::Append<VendorAppUrlTlv>(aMessage, GetVendorAppUrl());
325         break;
326 
327     case Tlv::kThreadStackVersion:
328         error = Tlv::Append<ThreadStackVersionTlv>(aMessage, otGetVersionString());
329         break;
330 
331     case Tlv::kChannelPages:
332     {
333         ChannelPagesTlv tlv;
334         uint8_t         length = 0;
335 
336         tlv.Init();
337 
338         for (uint8_t page : Radio::kSupportedChannelPages)
339         {
340             tlv.GetChannelPages()[length++] = page;
341         }
342 
343         tlv.SetLength(length);
344         error = tlv.AppendTo(aMessage);
345 
346         break;
347     }
348 
349 #if OPENTHREAD_FTD
350 
351     case Tlv::kConnectivity:
352     {
353         ConnectivityTlv tlv;
354 
355         tlv.Init();
356         Get<Mle::MleRouter>().FillConnectivityTlv(tlv);
357         error = tlv.AppendTo(aMessage);
358         break;
359     }
360 
361     case Tlv::kRoute:
362     {
363         RouteTlv tlv;
364 
365         tlv.Init();
366         Get<RouterTable>().FillRouteTlv(tlv);
367         SuccessOrExit(error = tlv.AppendTo(aMessage));
368         break;
369     }
370 
371     case Tlv::kChildTable:
372         error = AppendChildTable(aMessage);
373         break;
374 
375     case Tlv::kMaxChildTimeout:
376     {
377         uint32_t maxTimeout;
378 
379         SuccessOrExit(Get<Mle::MleRouter>().GetMaxChildTimeout(maxTimeout));
380         error = Tlv::Append<MaxChildTimeoutTlv>(aMessage, maxTimeout);
381         break;
382     }
383 
384 #endif // OPENTHREAD_FTD
385 
386     default:
387         break;
388     }
389 
390 exit:
391     return error;
392 }
393 
394 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)395 void Server::HandleTmf<kUriDiagnosticGetQuery>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
396 {
397     VerifyOrExit(aMessage.IsPostRequest());
398 
399     LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetQuery>(),
400             aMessageInfo.GetPeerAddr().ToString().AsCString());
401 
402     // DIAG_GET.qry may be sent as a confirmable request.
403     if (aMessage.IsConfirmable())
404     {
405         IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
406     }
407 
408 #if OPENTHREAD_MTD
409     SendAnswer(aMessageInfo.GetPeerAddr(), aMessage);
410 #elif OPENTHREAD_FTD
411     PrepareAndSendAnswers(aMessageInfo.GetPeerAddr(), aMessage);
412 #endif
413 
414 exit:
415     return;
416 }
417 
418 #if OPENTHREAD_MTD
419 
SendAnswer(const Ip6::Address & aDestination,const Message & aRequest)420 void Server::SendAnswer(const Ip6::Address &aDestination, const Message &aRequest)
421 {
422     Error            error  = kErrorNone;
423     Coap::Message   *answer = nullptr;
424     Tmf::MessageInfo messageInfo(GetInstance());
425     AnswerTlv        answerTlv;
426     uint16_t         queryId;
427 
428     answer = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
429     VerifyOrExit(answer != nullptr, error = kErrorNoBufs);
430 
431     IgnoreError(answer->SetPriority(aRequest.GetPriority()));
432 
433     if (Tlv::Find<QueryIdTlv>(aRequest, queryId) == kErrorNone)
434     {
435         SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*answer, queryId));
436     }
437 
438     SuccessOrExit(error = AppendRequestedTlvs(aRequest, *answer));
439 
440     answerTlv.Init(0, /* aIsLast */ true);
441     SuccessOrExit(answer->Append(answerTlv));
442 
443     PrepareMessageInfoForDest(aDestination, messageInfo);
444 
445     error = Get<Tmf::Agent>().SendMessage(*answer, messageInfo);
446 
447 exit:
448     FreeMessageOnError(answer, error);
449 }
450 
451 #endif // OPENTHREAD_MTD
452 
453 #if OPENTHREAD_FTD
454 
AllocateAnswer(Coap::Message * & aAnswer,AnswerInfo & aInfo)455 Error Server::AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo)
456 {
457     // Allocate an `Answer` message, adds it in `mAnswerQueue`,
458     // update the `aInfo.mFirstAnswer` if it is the first allocated
459     // messages, and appends `QueryIdTlv` to the message (if needed).
460 
461     Error error = kErrorNone;
462 
463     aAnswer = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
464     VerifyOrExit(aAnswer != nullptr, error = kErrorNoBufs);
465     IgnoreError(aAnswer->SetPriority(aInfo.mPriority));
466 
467     mAnswerQueue.Enqueue(*aAnswer);
468 
469     if (aInfo.mFirstAnswer == nullptr)
470     {
471         aInfo.mFirstAnswer = aAnswer;
472     }
473 
474     if (aInfo.mHasQueryId)
475     {
476         SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*aAnswer, aInfo.mQueryId));
477     }
478 
479 exit:
480     return error;
481 }
482 
IsLastAnswer(const Coap::Message & aAnswer) const483 bool Server::IsLastAnswer(const Coap::Message &aAnswer) const
484 {
485     // Indicates whether `aAnswer` is the last one associated with
486     // the same query.
487 
488     bool      isLast = true;
489     AnswerTlv answerTlv;
490 
491     // If there is no Answer TLV, we assume it is the last answer.
492 
493     SuccessOrExit(Tlv::FindTlv(aAnswer, answerTlv));
494     isLast = answerTlv.IsLast();
495 
496 exit:
497     return isLast;
498 }
499 
FreeAllRelatedAnswers(Coap::Message & aFirstAnswer)500 void Server::FreeAllRelatedAnswers(Coap::Message &aFirstAnswer)
501 {
502     // This method dequeues and frees all answer messages related to
503     // same query as `aFirstAnswer`. Note that related answers are
504     // enqueued in order.
505 
506     Coap::Message *answer = &aFirstAnswer;
507 
508     while (answer != nullptr)
509     {
510         Coap::Message *next = IsLastAnswer(*answer) ? nullptr : answer->GetNextCoapMessage();
511 
512         mAnswerQueue.DequeueAndFree(*answer);
513         answer = next;
514     }
515 }
516 
PrepareAndSendAnswers(const Ip6::Address & aDestination,const Message & aRequest)517 void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest)
518 {
519     Coap::Message *answer;
520     Error          error;
521     AnswerInfo     info;
522     OffsetRange    offsetRange;
523     AnswerTlv      answerTlv;
524 
525     if (Tlv::Find<QueryIdTlv>(aRequest, info.mQueryId) == kErrorNone)
526     {
527         info.mHasQueryId = true;
528     }
529 
530     info.mPriority = aRequest.GetPriority();
531 
532     SuccessOrExit(error = AllocateAnswer(answer, info));
533 
534     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kTypeList, offsetRange));
535 
536     while (!offsetRange.IsEmpty())
537     {
538         uint8_t tlvType;
539 
540         SuccessOrExit(error = aRequest.Read(offsetRange, tlvType));
541         offsetRange.AdvanceOffset(sizeof(tlvType));
542 
543         switch (tlvType)
544         {
545         case ChildTlv::kType:
546             SuccessOrExit(error = AppendChildTableAsChildTlvs(answer, info));
547             break;
548         case ChildIp6AddressListTlv::kType:
549             SuccessOrExit(error = AppendChildTableIp6AddressList(answer, info));
550             break;
551         case RouterNeighborTlv::kType:
552             SuccessOrExit(error = AppendRouterNeighborTlvs(answer, info));
553             break;
554         default:
555             SuccessOrExit(error = AppendDiagTlv(tlvType, *answer));
556             break;
557         }
558 
559         SuccessOrExit(error = CheckAnswerLength(answer, info));
560     }
561 
562     answerTlv.Init(info.mAnswerIndex, /* aIsLast */ true);
563     SuccessOrExit(error = answer->Append(answerTlv));
564 
565     SendNextAnswer(*info.mFirstAnswer, aDestination);
566 
567 exit:
568     if ((error != kErrorNone) && (info.mFirstAnswer != nullptr))
569     {
570         FreeAllRelatedAnswers(*info.mFirstAnswer);
571     }
572 }
573 
CheckAnswerLength(Coap::Message * & aAnswer,AnswerInfo & aInfo)574 Error Server::CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo)
575 {
576     // This method checks the length of the `aAnswer` message and if it
577     // is above the threshold, it enqueues the message for transmission
578     // after appending an Answer TLV with the current index to the
579     // message. In this case, it will also allocate a new answer
580     // message.
581 
582     Error     error = kErrorNone;
583     AnswerTlv answerTlv;
584 
585     VerifyOrExit(aAnswer->GetLength() >= kAnswerMessageLengthThreshold);
586 
587     answerTlv.Init(aInfo.mAnswerIndex++, /* aIsLast */ false);
588     SuccessOrExit(error = aAnswer->Append(answerTlv));
589 
590     error = AllocateAnswer(aAnswer, aInfo);
591 
592 exit:
593     return error;
594 }
595 
SendNextAnswer(Coap::Message & aAnswer,const Ip6::Address & aDestination)596 void Server::SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination)
597 {
598     // This method send the given next `aAnswer` associated with
599     // a query to the  `aDestination`.
600 
601     Error            error      = kErrorNone;
602     Coap::Message   *nextAnswer = IsLastAnswer(aAnswer) ? nullptr : aAnswer.GetNextCoapMessage();
603     Tmf::MessageInfo messageInfo(GetInstance());
604 
605     mAnswerQueue.Dequeue(aAnswer);
606 
607     PrepareMessageInfoForDest(aDestination, messageInfo);
608 
609     // When sending the message, we pass `nextAnswer` as `aContext`
610     // to be used when invoking callback `HandleAnswerResponse()`.
611 
612     error = Get<Tmf::Agent>().SendMessage(aAnswer, messageInfo, HandleAnswerResponse, nextAnswer);
613 
614     if (error != kErrorNone)
615     {
616         // If the `SendMessage()` fails, we `Free` the dequeued
617         // `aAnswer` and all the related next answers in the queue.
618 
619         aAnswer.Free();
620 
621         if (nextAnswer != nullptr)
622         {
623             FreeAllRelatedAnswers(*nextAnswer);
624         }
625     }
626 }
627 
HandleAnswerResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)628 void Server::HandleAnswerResponse(void                *aContext,
629                                   otMessage           *aMessage,
630                                   const otMessageInfo *aMessageInfo,
631                                   otError              aResult)
632 {
633     Coap::Message *nextAnswer = static_cast<Coap::Message *>(aContext);
634 
635     VerifyOrExit(nextAnswer != nullptr);
636 
637     nextAnswer->Get<Server>().HandleAnswerResponse(*nextAnswer, AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
638                                                    aResult);
639 
640 exit:
641     return;
642 }
643 
HandleAnswerResponse(Coap::Message & aNextAnswer,Coap::Message * aResponse,const Ip6::MessageInfo * aMessageInfo,Error aResult)644 void Server::HandleAnswerResponse(Coap::Message          &aNextAnswer,
645                                   Coap::Message          *aResponse,
646                                   const Ip6::MessageInfo *aMessageInfo,
647                                   Error                   aResult)
648 {
649     Error error = aResult;
650 
651     SuccessOrExit(error);
652     VerifyOrExit(aResponse != nullptr && aMessageInfo != nullptr, error = kErrorDrop);
653     VerifyOrExit(aResponse->GetCode() == Coap::kCodeChanged, error = kErrorDrop);
654 
655     SendNextAnswer(aNextAnswer, aMessageInfo->GetPeerAddr());
656 
657 exit:
658     if (error != kErrorNone)
659     {
660         FreeAllRelatedAnswers(aNextAnswer);
661     }
662 }
663 
AppendChildTableAsChildTlvs(Coap::Message * & aAnswer,AnswerInfo & aInfo)664 Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
665 {
666     Error    error = kErrorNone;
667     ChildTlv childTlv;
668 
669     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
670     {
671         childTlv.InitFrom(child);
672 
673         SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
674         SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
675     }
676 
677     // Add empty TLV to indicate end of the list
678 
679     childTlv.InitAsEmpty();
680     SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
681 
682 exit:
683     return error;
684 }
685 
AppendRouterNeighborTlvs(Coap::Message * & aAnswer,AnswerInfo & aInfo)686 Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
687 {
688     Error             error = kErrorNone;
689     RouterNeighborTlv neighborTlv;
690 
691     for (Router &router : Get<RouterTable>())
692     {
693         if (!router.IsStateValid())
694         {
695             continue;
696         }
697 
698         neighborTlv.InitFrom(router);
699 
700         SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
701         SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
702     }
703 
704     // Add empty TLV to indicate end of the list
705 
706     neighborTlv.InitAsEmpty();
707     SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
708 
709 exit:
710     return error;
711 }
712 
AppendChildTableIp6AddressList(Coap::Message * & aAnswer,AnswerInfo & aInfo)713 Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo)
714 {
715     Error error = kErrorNone;
716     Tlv   tlv;
717 
718     for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
719     {
720         SuccessOrExit(error = AppendChildIp6AddressListTlv(*aAnswer, child));
721         SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
722     }
723 
724     // Add empty TLV to indicate end of the list
725 
726     tlv.SetType(Tlv::kChildIp6AddressList);
727     tlv.SetLength(0);
728     SuccessOrExit(error = aAnswer->Append(tlv));
729 
730 exit:
731     return error;
732 }
733 
AppendChildIp6AddressListTlv(Coap::Message & aAnswer,const Child & aChild)734 Error Server::AppendChildIp6AddressListTlv(Coap::Message &aAnswer, const Child &aChild)
735 {
736     Error                       error      = kErrorNone;
737     uint16_t                    numIp6Addr = aChild.GetIp6Addresses().GetLength();
738     ChildIp6AddressListTlvValue tlvValue;
739     Ip6::Address                mlEid;
740 
741     if (aChild.GetMeshLocalIp6Address(mlEid) == kErrorNone)
742     {
743         numIp6Addr++;
744     }
745     else
746     {
747         mlEid.Clear();
748     }
749 
750     VerifyOrExit(numIp6Addr > 0);
751 
752     if ((numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)) <= Tlv::kBaseTlvMaxLength)
753     {
754         Tlv tlv;
755 
756         tlv.SetType(Tlv::kChildIp6AddressList);
757         tlv.SetLength(static_cast<uint8_t>(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)));
758         SuccessOrExit(error = aAnswer.Append(tlv));
759     }
760     else
761     {
762         ExtendedTlv extTlv;
763 
764         extTlv.SetType(Tlv::kChildIp6AddressList);
765         extTlv.SetLength(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue));
766         SuccessOrExit(error = aAnswer.Append(extTlv));
767     }
768 
769     tlvValue.SetRloc16(aChild.GetRloc16());
770 
771     SuccessOrExit(error = aAnswer.Append(tlvValue));
772 
773     if (!mlEid.IsUnspecified())
774     {
775         SuccessOrExit(error = aAnswer.Append(mlEid));
776     }
777 
778     for (const Ip6::Address &address : aChild.GetIp6Addresses())
779     {
780         SuccessOrExit(error = aAnswer.Append(address));
781     }
782 
783 exit:
784     return error;
785 }
786 
787 #endif // OPENTHREAD_FTD
788 
789 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)790 void Server::HandleTmf<kUriDiagnosticGetRequest>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
791 {
792     Error          error    = kErrorNone;
793     Coap::Message *response = nullptr;
794 
795     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
796 
797     LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetRequest>(),
798             aMessageInfo.GetPeerAddr().ToString().AsCString());
799 
800     response = Get<Tmf::Agent>().NewResponseMessage(aMessage);
801     VerifyOrExit(response != nullptr, error = kErrorNoBufs);
802 
803     IgnoreError(response->SetPriority(aMessage.GetPriority()));
804     SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
805     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, aMessageInfo));
806 
807 exit:
808     FreeMessageOnError(response, error);
809 }
810 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)811 template <> void Server::HandleTmf<kUriDiagnosticReset>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
812 {
813     uint16_t offset = 0;
814     uint8_t  type;
815     Tlv      tlv;
816 
817     VerifyOrExit(aMessage.IsConfirmablePostRequest());
818 
819     LogInfo("Received %s from %s", UriToString<kUriDiagnosticReset>(),
820             aMessageInfo.GetPeerAddr().ToString().AsCString());
821 
822     SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv));
823 
824     VerifyOrExit(tlv.GetType() == Tlv::kTypeList);
825 
826     offset = aMessage.GetOffset() + sizeof(Tlv);
827 
828     for (uint8_t i = 0; i < tlv.GetLength(); i++)
829     {
830         SuccessOrExit(aMessage.Read(offset + i, type));
831 
832         switch (type)
833         {
834         case Tlv::kMacCounters:
835             Get<Mac::Mac>().ResetCounters();
836             break;
837 
838         case Tlv::kMleCounters:
839             Get<Mle::Mle>().ResetCounters();
840             break;
841 
842         default:
843             break;
844         }
845     }
846 
847     IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
848 
849 exit:
850     return;
851 }
852 
853 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
854 
855 //---------------------------------------------------------------------------------------------------------------------
856 // Client
857 
Client(Instance & aInstance)858 Client::Client(Instance &aInstance)
859     : InstanceLocator(aInstance)
860     , mQueryId(Random::NonCrypto::GetUint16())
861 {
862 }
863 
SendDiagnosticGet(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,GetCallback aCallback,void * aContext)864 Error Client::SendDiagnosticGet(const Ip6::Address &aDestination,
865                                 const uint8_t       aTlvTypes[],
866                                 uint8_t             aCount,
867                                 GetCallback         aCallback,
868                                 void               *aContext)
869 {
870     Error error;
871 
872     if (aDestination.IsMulticast())
873     {
874         error = SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
875     }
876     else
877     {
878         error = SendCommand(kUriDiagnosticGetRequest, Message::kPriorityNormal, aDestination, aTlvTypes, aCount,
879                             &HandleGetResponse, this);
880     }
881 
882     SuccessOrExit(error);
883 
884     mGetCallback.Set(aCallback, aContext);
885 
886 exit:
887     return error;
888 }
889 
SendCommand(Uri aUri,Message::Priority aPriority,const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,Coap::ResponseHandler aHandler,void * aContext)890 Error Client::SendCommand(Uri                   aUri,
891                           Message::Priority     aPriority,
892                           const Ip6::Address   &aDestination,
893                           const uint8_t         aTlvTypes[],
894                           uint8_t               aCount,
895                           Coap::ResponseHandler aHandler,
896                           void                 *aContext)
897 {
898     Error            error;
899     Coap::Message   *message = nullptr;
900     Tmf::MessageInfo messageInfo(GetInstance());
901 
902     switch (aUri)
903     {
904     case kUriDiagnosticGetQuery:
905         message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(aUri);
906         break;
907 
908     case kUriDiagnosticGetRequest:
909     case kUriDiagnosticReset:
910         message = Get<Tmf::Agent>().NewConfirmablePostMessage(aUri);
911         break;
912 
913     default:
914         OT_ASSERT(false);
915     }
916 
917     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
918     IgnoreError(message->SetPriority(aPriority));
919 
920     if (aCount > 0)
921     {
922         SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
923     }
924 
925     if (aUri == kUriDiagnosticGetQuery)
926     {
927         SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*message, ++mQueryId));
928     }
929 
930     Get<Server>().PrepareMessageInfoForDest(aDestination, messageInfo);
931 
932     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aHandler, aContext));
933 
934     LogInfo("Sent %s to %s", UriToString(aUri), aDestination.ToString().AsCString());
935 
936 exit:
937     FreeMessageOnError(message, error);
938     return error;
939 }
940 
HandleGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)941 void Client::HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult)
942 {
943     static_cast<Client *>(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
944                                                        aResult);
945 }
946 
HandleGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)947 void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
948 {
949     SuccessOrExit(aResult);
950     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed);
951 
952 exit:
953     mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo);
954 }
955 
956 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)957 void Client::HandleTmf<kUriDiagnosticGetAnswer>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
958 {
959     VerifyOrExit(aMessage.IsConfirmablePostRequest());
960 
961     LogInfo("Received %s from %s", ot::UriToString<kUriDiagnosticGetAnswer>(),
962             aMessageInfo.GetPeerAddr().ToString().AsCString());
963 
964 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
965     // Let the `MeshDiag` process the message first.
966     if (!Get<Utils::MeshDiag>().HandleDiagnosticGetAnswer(aMessage, aMessageInfo))
967 #endif
968     {
969         mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo);
970     }
971 
972     IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
973 
974 exit:
975     return;
976 }
977 
SendDiagnosticReset(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount)978 Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount)
979 {
980     return SendCommand(kUriDiagnosticReset, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
981 }
982 
ParseRoute(const RouteTlv & aRouteTlv,otNetworkDiagRoute & aNetworkDiagRoute)983 static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
984 {
985     uint8_t routeCount = 0;
986 
987     for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i)
988     {
989         if (!aRouteTlv.IsRouterIdSet(i))
990         {
991             continue;
992         }
993         aNetworkDiagRoute.mRouteData[routeCount].mRouterId       = i;
994         aNetworkDiagRoute.mRouteData[routeCount].mRouteCost      = aRouteTlv.GetRouteCost(routeCount);
995         aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn  = aRouteTlv.GetLinkQualityIn(routeCount);
996         aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount);
997         ++routeCount;
998     }
999     aNetworkDiagRoute.mRouteCount = routeCount;
1000     aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence();
1001 }
1002 
ParseMacCounters(const MacCountersTlv & aMacCountersTlv,otNetworkDiagMacCounters & aMacCounters)1003 static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters)
1004 {
1005     aMacCounters.mIfInUnknownProtos  = aMacCountersTlv.GetIfInUnknownProtos();
1006     aMacCounters.mIfInErrors         = aMacCountersTlv.GetIfInErrors();
1007     aMacCounters.mIfOutErrors        = aMacCountersTlv.GetIfOutErrors();
1008     aMacCounters.mIfInUcastPkts      = aMacCountersTlv.GetIfInUcastPkts();
1009     aMacCounters.mIfInBroadcastPkts  = aMacCountersTlv.GetIfInBroadcastPkts();
1010     aMacCounters.mIfInDiscards       = aMacCountersTlv.GetIfInDiscards();
1011     aMacCounters.mIfOutUcastPkts     = aMacCountersTlv.GetIfOutUcastPkts();
1012     aMacCounters.mIfOutBroadcastPkts = aMacCountersTlv.GetIfOutBroadcastPkts();
1013     aMacCounters.mIfOutDiscards      = aMacCountersTlv.GetIfOutDiscards();
1014 }
1015 
GetNextDiagTlv(const Coap::Message & aMessage,Iterator & aIterator,TlvInfo & aTlvInfo)1016 Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo)
1017 {
1018     Error    error;
1019     uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator;
1020 
1021     while (offset < aMessage.GetLength())
1022     {
1023         bool     skipTlv = false;
1024         uint16_t valueOffset;
1025         uint16_t tlvLength;
1026         union
1027         {
1028             Tlv         tlv;
1029             ExtendedTlv extTlv;
1030         };
1031 
1032         SuccessOrExit(error = aMessage.Read(offset, tlv));
1033 
1034         if (tlv.IsExtended())
1035         {
1036             SuccessOrExit(error = aMessage.Read(offset, extTlv));
1037             valueOffset = offset + sizeof(ExtendedTlv);
1038             tlvLength   = extTlv.GetLength();
1039         }
1040         else
1041         {
1042             valueOffset = offset + sizeof(Tlv);
1043             tlvLength   = tlv.GetLength();
1044         }
1045 
1046         VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse);
1047 
1048         switch (tlv.GetType())
1049         {
1050         case Tlv::kExtMacAddress:
1051             SuccessOrExit(error =
1052                               Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mExtAddress)));
1053             break;
1054 
1055         case Tlv::kAddress16:
1056             SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aTlvInfo.mData.mAddr16));
1057             break;
1058 
1059         case Tlv::kMode:
1060         {
1061             uint8_t mode;
1062 
1063             SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
1064             Mle::DeviceMode(mode).Get(aTlvInfo.mData.mMode);
1065             break;
1066         }
1067 
1068         case Tlv::kTimeout:
1069             SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aTlvInfo.mData.mTimeout));
1070             break;
1071 
1072         case Tlv::kConnectivity:
1073         {
1074             ConnectivityTlv connectivityTlv;
1075 
1076             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1077             SuccessOrExit(error = aMessage.Read(offset, connectivityTlv));
1078             VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
1079             connectivityTlv.GetConnectivity(aTlvInfo.mData.mConnectivity);
1080             break;
1081         }
1082 
1083         case Tlv::kRoute:
1084         {
1085             RouteTlv routeTlv;
1086             uint16_t bytesToRead = static_cast<uint16_t>(Min(tlv.GetSize(), static_cast<uint32_t>(sizeof(routeTlv))));
1087 
1088             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1089             SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead));
1090             VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
1091             ParseRoute(routeTlv, aTlvInfo.mData.mRoute);
1092             break;
1093         }
1094 
1095         case Tlv::kLeaderData:
1096         {
1097             LeaderDataTlv leaderDataTlv;
1098 
1099             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1100             SuccessOrExit(error = aMessage.Read(offset, leaderDataTlv));
1101             VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse);
1102             leaderDataTlv.Get(AsCoreType(&aTlvInfo.mData.mLeaderData));
1103             break;
1104         }
1105 
1106         case Tlv::kNetworkData:
1107             static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize,
1108                           "NetworkData array in `otNetworkDiagTlv` is too small");
1109 
1110             VerifyOrExit(tlvLength <= NetworkData::NetworkData::kMaxSize, error = kErrorParse);
1111             aTlvInfo.mData.mNetworkData.mCount = static_cast<uint8_t>(tlvLength);
1112             aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength);
1113             break;
1114 
1115         case Tlv::kIp6AddressList:
1116         {
1117             uint16_t      addrListLength = GetArrayLength(aTlvInfo.mData.mIp6AddrList.mList);
1118             Ip6::Address *addrEntry      = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]);
1119             uint8_t      &addrCount      = aTlvInfo.mData.mIp6AddrList.mCount;
1120 
1121             VerifyOrExit((tlvLength % Ip6::Address::kSize) == 0, error = kErrorParse);
1122 
1123             // `TlvInfo` has a fixed array for IPv6 addresses. If there
1124             // are more addresses in the message, we read and return as
1125             // many as can fit in array and ignore the rest.
1126 
1127             addrCount = 0;
1128 
1129             while ((tlvLength > 0) && (addrCount < addrListLength))
1130             {
1131                 SuccessOrExit(error = aMessage.Read(valueOffset, *addrEntry));
1132                 addrCount++;
1133                 addrEntry++;
1134                 valueOffset += Ip6::Address::kSize;
1135                 tlvLength -= Ip6::Address::kSize;
1136             }
1137 
1138             break;
1139         }
1140 
1141         case Tlv::kMacCounters:
1142         {
1143             MacCountersTlv macCountersTlv;
1144 
1145             SuccessOrExit(error = aMessage.Read(offset, macCountersTlv));
1146             VerifyOrExit(macCountersTlv.IsValid(), error = kErrorParse);
1147             ParseMacCounters(macCountersTlv, aTlvInfo.mData.mMacCounters);
1148             break;
1149         }
1150 
1151         case Tlv::kMleCounters:
1152         {
1153             MleCountersTlv mleCoutersTlv;
1154 
1155             SuccessOrExit(error = aMessage.Read(offset, mleCoutersTlv));
1156             VerifyOrExit(mleCoutersTlv.IsValid(), error = kErrorParse);
1157             mleCoutersTlv.Read(aTlvInfo.mData.mMleCounters);
1158             break;
1159         }
1160 
1161         case Tlv::kBatteryLevel:
1162             SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aTlvInfo.mData.mBatteryLevel));
1163             break;
1164 
1165         case Tlv::kSupplyVoltage:
1166             SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aTlvInfo.mData.mSupplyVoltage));
1167             break;
1168 
1169         case Tlv::kChildTable:
1170         {
1171             uint16_t   childInfoLength = GetArrayLength(aTlvInfo.mData.mChildTable.mTable);
1172             ChildInfo *childInfo       = &aTlvInfo.mData.mChildTable.mTable[0];
1173             uint8_t   &childCount      = aTlvInfo.mData.mChildTable.mCount;
1174 
1175             VerifyOrExit((tlvLength % sizeof(ChildTableEntry)) == 0, error = kErrorParse);
1176 
1177             // `TlvInfo` has a fixed array Child Table entries. If there
1178             // are more entries in the message, we read and return as
1179             // many as can fit in array and ignore the rest.
1180 
1181             childCount = 0;
1182 
1183             while ((tlvLength > 0) && (childCount < childInfoLength))
1184             {
1185                 ChildTableEntry entry;
1186 
1187                 SuccessOrExit(error = aMessage.Read(valueOffset, entry));
1188 
1189                 childInfo->mTimeout     = entry.GetTimeout();
1190                 childInfo->mLinkQuality = entry.GetLinkQuality();
1191                 childInfo->mChildId     = entry.GetChildId();
1192                 entry.GetMode().Get(childInfo->mMode);
1193 
1194                 childCount++;
1195                 childInfo++;
1196                 tlvLength -= sizeof(ChildTableEntry);
1197                 valueOffset += sizeof(ChildTableEntry);
1198             }
1199 
1200             break;
1201         }
1202 
1203         case Tlv::kChannelPages:
1204             aTlvInfo.mData.mChannelPages.mCount =
1205                 static_cast<uint8_t>(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8)));
1206             aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount);
1207             break;
1208 
1209         case Tlv::kMaxChildTimeout:
1210             SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout));
1211             break;
1212 
1213         case Tlv::kEui64:
1214             SuccessOrExit(error = Tlv::Read<Eui64Tlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mEui64)));
1215             break;
1216 
1217         case Tlv::kVersion:
1218             SuccessOrExit(error = Tlv::Read<VersionTlv>(aMessage, offset, aTlvInfo.mData.mVersion));
1219             break;
1220 
1221         case Tlv::kVendorName:
1222             SuccessOrExit(error = Tlv::Read<VendorNameTlv>(aMessage, offset, aTlvInfo.mData.mVendorName));
1223             break;
1224 
1225         case Tlv::kVendorModel:
1226             SuccessOrExit(error = Tlv::Read<VendorModelTlv>(aMessage, offset, aTlvInfo.mData.mVendorModel));
1227             break;
1228 
1229         case Tlv::kVendorSwVersion:
1230             SuccessOrExit(error = Tlv::Read<VendorSwVersionTlv>(aMessage, offset, aTlvInfo.mData.mVendorSwVersion));
1231             break;
1232 
1233         case Tlv::kVendorAppUrl:
1234             SuccessOrExit(error = Tlv::Read<VendorAppUrlTlv>(aMessage, offset, aTlvInfo.mData.mVendorAppUrl));
1235             break;
1236 
1237         case Tlv::kThreadStackVersion:
1238             SuccessOrExit(error =
1239                               Tlv::Read<ThreadStackVersionTlv>(aMessage, offset, aTlvInfo.mData.mThreadStackVersion));
1240             break;
1241 
1242         default:
1243             // Skip unrecognized TLVs.
1244             skipTlv = true;
1245             break;
1246         }
1247 
1248         offset += tlv.GetSize();
1249 
1250         if (!skipTlv)
1251         {
1252             // Exit if a TLV is recognized and parsed successfully.
1253             aTlvInfo.mType = tlv.GetType();
1254             aIterator      = offset;
1255             error          = kErrorNone;
1256             ExitNow();
1257         }
1258     }
1259 
1260     error = kErrorNotFound;
1261 
1262 exit:
1263     return error;
1264 }
1265 
1266 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1267 
UriToString(Uri aUri)1268 const char *Client::UriToString(Uri aUri)
1269 {
1270     const char *str = "";
1271 
1272     switch (aUri)
1273     {
1274     case kUriDiagnosticGetQuery:
1275         str = ot::UriToString<kUriDiagnosticGetQuery>();
1276         break;
1277     case kUriDiagnosticGetRequest:
1278         str = ot::UriToString<kUriDiagnosticGetRequest>();
1279         break;
1280     case kUriDiagnosticReset:
1281         str = ot::UriToString<kUriDiagnosticReset>();
1282         break;
1283     default:
1284         break;
1285     }
1286 
1287     return str;
1288 }
1289 
1290 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1291 
1292 #endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
1293 
1294 } // namespace NetworkDiagnostic
1295 
1296 } // namespace ot
1297