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