• 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 "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.IsLinkLocal() || aDestination.IsLinkLocalMulticast())
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     uint16_t offset;
244     uint16_t endOffset;
245 
246     SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRequest, Tlv::kTypeList, offset, endOffset));
247 
248     for (; offset < endOffset; offset++)
249     {
250         uint8_t tlvType;
251 
252         SuccessOrExit(error = aRequest.Read(offset, 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     uint16_t       offset;
540     uint16_t       length;
541     uint16_t       endOffset;
542     AnswerTlv      answerTlv;
543 
544     if (Tlv::Find<QueryIdTlv>(aRequest, info.mQueryId) == kErrorNone)
545     {
546         info.mHasQueryId = true;
547     }
548 
549     info.mPriority = aRequest.GetPriority();
550 
551     SuccessOrExit(error = AllocateAnswer(answer, info));
552 
553     SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequest, Tlv::kTypeList, offset, length));
554     endOffset = offset + length;
555 
556     for (; offset < endOffset; offset++)
557     {
558         uint8_t tlvType;
559 
560         SuccessOrExit(error = aRequest.Read(offset, tlvType));
561 
562         switch (tlvType)
563         {
564         case ChildTlv::kType:
565             SuccessOrExit(error = AppendChildTableAsChildTlvs(answer, info));
566             break;
567         case ChildIp6AddressListTlv::kType:
568             SuccessOrExit(error = AppendChildTableIp6AddressList(answer, info));
569             break;
570         case RouterNeighborTlv::kType:
571             SuccessOrExit(error = AppendRouterNeighborTlvs(answer, info));
572             break;
573         default:
574             SuccessOrExit(error = AppendDiagTlv(tlvType, *answer));
575             break;
576         }
577 
578         SuccessOrExit(error = CheckAnswerLength(answer, info));
579     }
580 
581     answerTlv.Init(info.mAnswerIndex, /* aIsLast */ true);
582     SuccessOrExit(error = answer->Append(answerTlv));
583 
584     SendNextAnswer(*info.mFirstAnswer, aDestination);
585 
586 exit:
587     if ((error != kErrorNone) && (info.mFirstAnswer != nullptr))
588     {
589         FreeAllRelatedAnswers(*info.mFirstAnswer);
590     }
591 }
592 
CheckAnswerLength(Coap::Message * & aAnswer,AnswerInfo & aInfo)593 Error Server::CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo)
594 {
595     // This method checks the length of the `aAnswer` message and if it
596     // is above the threshold, it enqueues the message for transmission
597     // after appending an Answer TLV with the current index to the
598     // message. In this case, it will also allocate a new answer
599     // message.
600 
601     Error     error = kErrorNone;
602     AnswerTlv answerTlv;
603 
604     VerifyOrExit(aAnswer->GetLength() >= kAnswerMessageLengthThreshold);
605 
606     answerTlv.Init(aInfo.mAnswerIndex++, /* aIsLast */ false);
607     SuccessOrExit(error = aAnswer->Append(answerTlv));
608 
609     error = AllocateAnswer(aAnswer, aInfo);
610 
611 exit:
612     return error;
613 }
614 
SendNextAnswer(Coap::Message & aAnswer,const Ip6::Address & aDestination)615 void Server::SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination)
616 {
617     // This method send the given next `aAnswer` associated with
618     // a query to the  `aDestination`.
619 
620     Error            error      = kErrorNone;
621     Coap::Message   *nextAnswer = IsLastAnswer(aAnswer) ? nullptr : aAnswer.GetNextCoapMessage();
622     Tmf::MessageInfo messageInfo(GetInstance());
623 
624     mAnswerQueue.Dequeue(aAnswer);
625 
626     PrepareMessageInfoForDest(aDestination, messageInfo);
627 
628     // When sending the message, we pass `nextAnswer` as `aContext`
629     // to be used when invoking callback `HandleAnswerResponse()`.
630 
631     error = Get<Tmf::Agent>().SendMessage(aAnswer, messageInfo, HandleAnswerResponse, nextAnswer);
632 
633     if (error != kErrorNone)
634     {
635         // If the `SendMessage()` fails, we `Free` the dequeued
636         // `aAnswer` and all the related next answers in the queue.
637 
638         aAnswer.Free();
639 
640         if (nextAnswer != nullptr)
641         {
642             FreeAllRelatedAnswers(*nextAnswer);
643         }
644     }
645 }
646 
HandleAnswerResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)647 void Server::HandleAnswerResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
648 {
649     Coap::Message *nextAnswer = static_cast<Coap::Message *>(aContext);
650 
651     VerifyOrExit(nextAnswer != nullptr);
652 
653     nextAnswer->Get<Server>().HandleAnswerResponse(*nextAnswer, AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
654                                                    aResult);
655 
656 exit:
657     return;
658 }
659 
HandleAnswerResponse(Coap::Message & aNextAnswer,Coap::Message * aResponse,const Ip6::MessageInfo * aMessageInfo,Error aResult)660 void Server::HandleAnswerResponse(Coap::Message          &aNextAnswer,
661                                   Coap::Message          *aResponse,
662                                   const Ip6::MessageInfo *aMessageInfo,
663                                   Error                   aResult)
664 {
665     Error error = aResult;
666 
667     SuccessOrExit(error);
668     VerifyOrExit(aResponse != nullptr && aMessageInfo != nullptr, error = kErrorDrop);
669     VerifyOrExit(aResponse->GetCode() == Coap::kCodeChanged, error = kErrorDrop);
670 
671     SendNextAnswer(aNextAnswer, aMessageInfo->GetPeerAddr());
672 
673 exit:
674     if (error != kErrorNone)
675     {
676         FreeAllRelatedAnswers(aNextAnswer);
677     }
678 }
679 
AppendChildTableAsChildTlvs(Coap::Message * & aAnswer,AnswerInfo & aInfo)680 Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
681 {
682     Error    error = kErrorNone;
683     ChildTlv childTlv;
684 
685     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
686     {
687         childTlv.InitFrom(child);
688 
689         SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
690         SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
691     }
692 
693     // Add empty TLV to indicate end of the list
694 
695     childTlv.InitAsEmpty();
696     SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
697 
698 exit:
699     return error;
700 }
701 
AppendRouterNeighborTlvs(Coap::Message * & aAnswer,AnswerInfo & aInfo)702 Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
703 {
704     Error             error = kErrorNone;
705     RouterNeighborTlv neighborTlv;
706 
707     for (Router &router : Get<RouterTable>())
708     {
709         if (!router.IsStateValid())
710         {
711             continue;
712         }
713 
714         neighborTlv.InitFrom(router);
715 
716         SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
717         SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
718     }
719 
720     // Add empty TLV to indicate end of the list
721 
722     neighborTlv.InitAsEmpty();
723     SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
724 
725 exit:
726     return error;
727 }
728 
AppendChildTableIp6AddressList(Coap::Message * & aAnswer,AnswerInfo & aInfo)729 Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo)
730 {
731     Error error = kErrorNone;
732     Tlv   tlv;
733 
734     for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
735     {
736         SuccessOrExit(error = AppendChildIp6AddressListTlv(*aAnswer, child));
737         SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
738     }
739 
740     // Add empty TLV to indicate end of the list
741 
742     tlv.SetType(Tlv::kChildIp6AddressList);
743     tlv.SetLength(0);
744     SuccessOrExit(error = aAnswer->Append(tlv));
745 
746 exit:
747     return error;
748 }
749 
AppendChildIp6AddressListTlv(Coap::Message & aAnswer,const Child & aChild)750 Error Server::AppendChildIp6AddressListTlv(Coap::Message &aAnswer, const Child &aChild)
751 {
752     Error                       error      = kErrorNone;
753     uint16_t                    numIp6Addr = 0;
754     ChildIp6AddressListTlvValue tlvValue;
755 
756     for (const Ip6::Address &address : aChild.IterateIp6Addresses())
757     {
758         OT_UNUSED_VARIABLE(address);
759         numIp6Addr++;
760     }
761 
762     VerifyOrExit(numIp6Addr > 0);
763 
764     if ((numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)) <= Tlv::kBaseTlvMaxLength)
765     {
766         Tlv tlv;
767 
768         tlv.SetType(Tlv::kChildIp6AddressList);
769         tlv.SetLength(static_cast<uint8_t>(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)));
770         SuccessOrExit(error = aAnswer.Append(tlv));
771     }
772     else
773     {
774         ExtendedTlv extTlv;
775 
776         extTlv.SetType(Tlv::kChildIp6AddressList);
777         extTlv.SetLength(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue));
778         SuccessOrExit(error = aAnswer.Append(extTlv));
779     }
780 
781     tlvValue.SetRloc16(aChild.GetRloc16());
782 
783     SuccessOrExit(error = aAnswer.Append(tlvValue));
784 
785     for (const Ip6::Address &address : aChild.IterateIp6Addresses())
786     {
787         SuccessOrExit(error = aAnswer.Append(address));
788     }
789 
790 exit:
791     return error;
792 }
793 
794 #endif // OPENTHREAD_FTD
795 
796 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)797 void Server::HandleTmf<kUriDiagnosticGetRequest>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
798 {
799     Error          error    = kErrorNone;
800     Coap::Message *response = nullptr;
801 
802     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
803 
804     LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetRequest>(),
805             aMessageInfo.GetPeerAddr().ToString().AsCString());
806 
807     response = Get<Tmf::Agent>().NewResponseMessage(aMessage);
808     VerifyOrExit(response != nullptr, error = kErrorNoBufs);
809 
810     IgnoreError(response->SetPriority(aMessage.GetPriority()));
811     SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
812     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, aMessageInfo));
813 
814 exit:
815     FreeMessageOnError(response, error);
816 }
817 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)818 template <> void Server::HandleTmf<kUriDiagnosticReset>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
819 {
820     uint16_t offset = 0;
821     uint8_t  type;
822     Tlv      tlv;
823 
824     VerifyOrExit(aMessage.IsConfirmablePostRequest());
825 
826     LogInfo("Received %s from %s", UriToString<kUriDiagnosticReset>(),
827             aMessageInfo.GetPeerAddr().ToString().AsCString());
828 
829     SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv));
830 
831     VerifyOrExit(tlv.GetType() == Tlv::kTypeList);
832 
833     offset = aMessage.GetOffset() + sizeof(Tlv);
834 
835     for (uint8_t i = 0; i < tlv.GetLength(); i++)
836     {
837         SuccessOrExit(aMessage.Read(offset + i, type));
838 
839         switch (type)
840         {
841         case Tlv::kMacCounters:
842             Get<Mac::Mac>().ResetCounters();
843             break;
844 
845         case Tlv::kMleCounters:
846             Get<Mle::Mle>().ResetCounters();
847             break;
848 
849         default:
850             break;
851         }
852     }
853 
854     IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
855 
856 exit:
857     return;
858 }
859 
860 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
861 
862 //---------------------------------------------------------------------------------------------------------------------
863 // Client
864 
Client(Instance & aInstance)865 Client::Client(Instance &aInstance)
866     : InstanceLocator(aInstance)
867     , mQueryId(Random::NonCrypto::GetUint16())
868 {
869 }
870 
SendDiagnosticGet(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,GetCallback aCallback,void * aContext)871 Error Client::SendDiagnosticGet(const Ip6::Address &aDestination,
872                                 const uint8_t       aTlvTypes[],
873                                 uint8_t             aCount,
874                                 GetCallback         aCallback,
875                                 void               *aContext)
876 {
877     Error error;
878 
879     if (aDestination.IsMulticast())
880     {
881         error = SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
882     }
883     else
884     {
885         error = SendCommand(kUriDiagnosticGetRequest, Message::kPriorityNormal, aDestination, aTlvTypes, aCount,
886                             &HandleGetResponse, this);
887     }
888 
889     SuccessOrExit(error);
890 
891     mGetCallback.Set(aCallback, aContext);
892 
893 exit:
894     return error;
895 }
896 
SendCommand(Uri aUri,Message::Priority aPriority,const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,Coap::ResponseHandler aHandler,void * aContext)897 Error Client::SendCommand(Uri                   aUri,
898                           Message::Priority     aPriority,
899                           const Ip6::Address   &aDestination,
900                           const uint8_t         aTlvTypes[],
901                           uint8_t               aCount,
902                           Coap::ResponseHandler aHandler,
903                           void                 *aContext)
904 {
905     Error            error;
906     Coap::Message   *message = nullptr;
907     Tmf::MessageInfo messageInfo(GetInstance());
908 
909     switch (aUri)
910     {
911     case kUriDiagnosticGetQuery:
912         message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(aUri);
913         break;
914 
915     case kUriDiagnosticGetRequest:
916     case kUriDiagnosticReset:
917         message = Get<Tmf::Agent>().NewConfirmablePostMessage(aUri);
918         break;
919 
920     default:
921         OT_ASSERT(false);
922     }
923 
924     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
925     IgnoreError(message->SetPriority(aPriority));
926 
927     if (aCount > 0)
928     {
929         SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
930     }
931 
932     if (aUri == kUriDiagnosticGetQuery)
933     {
934         SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*message, ++mQueryId));
935     }
936 
937     Get<Server>().PrepareMessageInfoForDest(aDestination, messageInfo);
938 
939     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aHandler, aContext));
940 
941     LogInfo("Sent %s to %s", UriToString(aUri), aDestination.ToString().AsCString());
942 
943 exit:
944     FreeMessageOnError(message, error);
945     return error;
946 }
947 
HandleGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)948 void Client::HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
949 {
950     static_cast<Client *>(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
951                                                        aResult);
952 }
953 
HandleGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)954 void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
955 {
956     SuccessOrExit(aResult);
957     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed);
958 
959 exit:
960     mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo);
961 }
962 
963 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)964 void Client::HandleTmf<kUriDiagnosticGetAnswer>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
965 {
966     VerifyOrExit(aMessage.IsConfirmablePostRequest());
967 
968     LogInfo("Received %s from %s", ot::UriToString<kUriDiagnosticGetAnswer>(),
969             aMessageInfo.GetPeerAddr().ToString().AsCString());
970 
971 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
972     // Let the `MeshDiag` process the message first.
973     if (!Get<Utils::MeshDiag>().HandleDiagnosticGetAnswer(aMessage, aMessageInfo))
974 #endif
975     {
976         mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo);
977     }
978 
979     IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
980 
981 exit:
982     return;
983 }
984 
SendDiagnosticReset(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount)985 Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount)
986 {
987     return SendCommand(kUriDiagnosticReset, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
988 }
989 
ParseRoute(const RouteTlv & aRouteTlv,otNetworkDiagRoute & aNetworkDiagRoute)990 static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
991 {
992     uint8_t routeCount = 0;
993 
994     for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i)
995     {
996         if (!aRouteTlv.IsRouterIdSet(i))
997         {
998             continue;
999         }
1000         aNetworkDiagRoute.mRouteData[routeCount].mRouterId       = i;
1001         aNetworkDiagRoute.mRouteData[routeCount].mRouteCost      = aRouteTlv.GetRouteCost(routeCount);
1002         aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn  = aRouteTlv.GetLinkQualityIn(routeCount);
1003         aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount);
1004         ++routeCount;
1005     }
1006     aNetworkDiagRoute.mRouteCount = routeCount;
1007     aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence();
1008 }
1009 
ParseMacCounters(const MacCountersTlv & aMacCountersTlv,otNetworkDiagMacCounters & aMacCounters)1010 static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters)
1011 {
1012     aMacCounters.mIfInUnknownProtos  = aMacCountersTlv.GetIfInUnknownProtos();
1013     aMacCounters.mIfInErrors         = aMacCountersTlv.GetIfInErrors();
1014     aMacCounters.mIfOutErrors        = aMacCountersTlv.GetIfOutErrors();
1015     aMacCounters.mIfInUcastPkts      = aMacCountersTlv.GetIfInUcastPkts();
1016     aMacCounters.mIfInBroadcastPkts  = aMacCountersTlv.GetIfInBroadcastPkts();
1017     aMacCounters.mIfInDiscards       = aMacCountersTlv.GetIfInDiscards();
1018     aMacCounters.mIfOutUcastPkts     = aMacCountersTlv.GetIfOutUcastPkts();
1019     aMacCounters.mIfOutBroadcastPkts = aMacCountersTlv.GetIfOutBroadcastPkts();
1020     aMacCounters.mIfOutDiscards      = aMacCountersTlv.GetIfOutDiscards();
1021 }
1022 
GetNextDiagTlv(const Coap::Message & aMessage,Iterator & aIterator,TlvInfo & aTlvInfo)1023 Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo)
1024 {
1025     Error    error;
1026     uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator;
1027 
1028     while (offset < aMessage.GetLength())
1029     {
1030         bool     skipTlv = false;
1031         uint16_t valueOffset;
1032         uint16_t tlvLength;
1033         union
1034         {
1035             Tlv         tlv;
1036             ExtendedTlv extTlv;
1037         };
1038 
1039         SuccessOrExit(error = aMessage.Read(offset, tlv));
1040 
1041         if (tlv.IsExtended())
1042         {
1043             SuccessOrExit(error = aMessage.Read(offset, extTlv));
1044             valueOffset = offset + sizeof(ExtendedTlv);
1045             tlvLength   = extTlv.GetLength();
1046         }
1047         else
1048         {
1049             valueOffset = offset + sizeof(Tlv);
1050             tlvLength   = tlv.GetLength();
1051         }
1052 
1053         VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse);
1054 
1055         switch (tlv.GetType())
1056         {
1057         case Tlv::kExtMacAddress:
1058             SuccessOrExit(error =
1059                               Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mExtAddress)));
1060             break;
1061 
1062         case Tlv::kAddress16:
1063             SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aTlvInfo.mData.mAddr16));
1064             break;
1065 
1066         case Tlv::kMode:
1067         {
1068             uint8_t mode;
1069 
1070             SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
1071             Mle::DeviceMode(mode).Get(aTlvInfo.mData.mMode);
1072             break;
1073         }
1074 
1075         case Tlv::kTimeout:
1076             SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aTlvInfo.mData.mTimeout));
1077             break;
1078 
1079         case Tlv::kConnectivity:
1080         {
1081             ConnectivityTlv connectivityTlv;
1082 
1083             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1084             SuccessOrExit(error = aMessage.Read(offset, connectivityTlv));
1085             VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
1086             connectivityTlv.GetConnectivity(aTlvInfo.mData.mConnectivity);
1087             break;
1088         }
1089 
1090         case Tlv::kRoute:
1091         {
1092             RouteTlv routeTlv;
1093             uint16_t bytesToRead = static_cast<uint16_t>(Min(tlv.GetSize(), static_cast<uint32_t>(sizeof(routeTlv))));
1094 
1095             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1096             SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead));
1097             VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
1098             ParseRoute(routeTlv, aTlvInfo.mData.mRoute);
1099             break;
1100         }
1101 
1102         case Tlv::kLeaderData:
1103         {
1104             LeaderDataTlv leaderDataTlv;
1105 
1106             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1107             SuccessOrExit(error = aMessage.Read(offset, leaderDataTlv));
1108             VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse);
1109             leaderDataTlv.Get(AsCoreType(&aTlvInfo.mData.mLeaderData));
1110             break;
1111         }
1112 
1113         case Tlv::kNetworkData:
1114             static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize,
1115                           "NetworkData array in `otNetworkDiagTlv` is too small");
1116 
1117             VerifyOrExit(tlvLength <= NetworkData::NetworkData::kMaxSize, error = kErrorParse);
1118             aTlvInfo.mData.mNetworkData.mCount = static_cast<uint8_t>(tlvLength);
1119             aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength);
1120             break;
1121 
1122         case Tlv::kIp6AddressList:
1123         {
1124             uint16_t      addrListLength = GetArrayLength(aTlvInfo.mData.mIp6AddrList.mList);
1125             Ip6::Address *addrEntry      = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]);
1126             uint8_t      &addrCount      = aTlvInfo.mData.mIp6AddrList.mCount;
1127 
1128             VerifyOrExit((tlvLength % Ip6::Address::kSize) == 0, error = kErrorParse);
1129 
1130             // `TlvInfo` has a fixed array for IPv6 addresses. If there
1131             // are more addresses in the message, we read and return as
1132             // many as can fit in array and ignore the rest.
1133 
1134             addrCount = 0;
1135 
1136             while ((tlvLength > 0) && (addrCount < addrListLength))
1137             {
1138                 SuccessOrExit(error = aMessage.Read(valueOffset, *addrEntry));
1139                 addrCount++;
1140                 addrEntry++;
1141                 valueOffset += Ip6::Address::kSize;
1142                 tlvLength -= Ip6::Address::kSize;
1143             }
1144 
1145             break;
1146         }
1147 
1148         case Tlv::kMacCounters:
1149         {
1150             MacCountersTlv macCountersTlv;
1151 
1152             SuccessOrExit(error = aMessage.Read(offset, macCountersTlv));
1153             VerifyOrExit(macCountersTlv.IsValid(), error = kErrorParse);
1154             ParseMacCounters(macCountersTlv, aTlvInfo.mData.mMacCounters);
1155             break;
1156         }
1157 
1158         case Tlv::kMleCounters:
1159         {
1160             MleCountersTlv mleCoutersTlv;
1161 
1162             SuccessOrExit(error = aMessage.Read(offset, mleCoutersTlv));
1163             VerifyOrExit(mleCoutersTlv.IsValid(), error = kErrorParse);
1164             mleCoutersTlv.Read(aTlvInfo.mData.mMleCounters);
1165             break;
1166         }
1167 
1168         case Tlv::kBatteryLevel:
1169             SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aTlvInfo.mData.mBatteryLevel));
1170             break;
1171 
1172         case Tlv::kSupplyVoltage:
1173             SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aTlvInfo.mData.mSupplyVoltage));
1174             break;
1175 
1176         case Tlv::kChildTable:
1177         {
1178             uint16_t   childInfoLength = GetArrayLength(aTlvInfo.mData.mChildTable.mTable);
1179             ChildInfo *childInfo       = &aTlvInfo.mData.mChildTable.mTable[0];
1180             uint8_t   &childCount      = aTlvInfo.mData.mChildTable.mCount;
1181 
1182             VerifyOrExit((tlvLength % sizeof(ChildTableEntry)) == 0, error = kErrorParse);
1183 
1184             // `TlvInfo` has a fixed array Child Table entries. If there
1185             // are more entries in the message, we read and return as
1186             // many as can fit in array and ignore the rest.
1187 
1188             childCount = 0;
1189 
1190             while ((tlvLength > 0) && (childCount < childInfoLength))
1191             {
1192                 ChildTableEntry entry;
1193 
1194                 SuccessOrExit(error = aMessage.Read(valueOffset, entry));
1195 
1196                 childInfo->mTimeout     = entry.GetTimeout();
1197                 childInfo->mLinkQuality = entry.GetLinkQuality();
1198                 childInfo->mChildId     = entry.GetChildId();
1199                 entry.GetMode().Get(childInfo->mMode);
1200 
1201                 childCount++;
1202                 childInfo++;
1203                 tlvLength -= sizeof(ChildTableEntry);
1204                 valueOffset += sizeof(ChildTableEntry);
1205             }
1206 
1207             break;
1208         }
1209 
1210         case Tlv::kChannelPages:
1211             aTlvInfo.mData.mChannelPages.mCount =
1212                 static_cast<uint8_t>(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8)));
1213             aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount);
1214             break;
1215 
1216         case Tlv::kMaxChildTimeout:
1217             SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout));
1218             break;
1219 
1220         case Tlv::kEui64:
1221             SuccessOrExit(error = Tlv::Read<Eui64Tlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mEui64)));
1222             break;
1223 
1224         case Tlv::kVersion:
1225             SuccessOrExit(error = Tlv::Read<VersionTlv>(aMessage, offset, aTlvInfo.mData.mVersion));
1226             break;
1227 
1228         case Tlv::kVendorName:
1229             SuccessOrExit(error = Tlv::Read<VendorNameTlv>(aMessage, offset, aTlvInfo.mData.mVendorName));
1230             break;
1231 
1232         case Tlv::kVendorModel:
1233             SuccessOrExit(error = Tlv::Read<VendorModelTlv>(aMessage, offset, aTlvInfo.mData.mVendorModel));
1234             break;
1235 
1236         case Tlv::kVendorSwVersion:
1237             SuccessOrExit(error = Tlv::Read<VendorSwVersionTlv>(aMessage, offset, aTlvInfo.mData.mVendorSwVersion));
1238             break;
1239 
1240         case Tlv::kVendorAppUrl:
1241             SuccessOrExit(error = Tlv::Read<VendorAppUrlTlv>(aMessage, offset, aTlvInfo.mData.mVendorAppUrl));
1242             break;
1243 
1244         case Tlv::kThreadStackVersion:
1245             SuccessOrExit(error =
1246                               Tlv::Read<ThreadStackVersionTlv>(aMessage, offset, aTlvInfo.mData.mThreadStackVersion));
1247             break;
1248 
1249         default:
1250             // Skip unrecognized TLVs.
1251             skipTlv = true;
1252             break;
1253         }
1254 
1255         offset += tlv.GetSize();
1256 
1257         if (!skipTlv)
1258         {
1259             // Exit if a TLV is recognized and parsed successfully.
1260             aTlvInfo.mType = tlv.GetType();
1261             aIterator      = offset;
1262             error          = kErrorNone;
1263             ExitNow();
1264         }
1265     }
1266 
1267     error = kErrorNotFound;
1268 
1269 exit:
1270     return error;
1271 }
1272 
1273 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1274 
UriToString(Uri aUri)1275 const char *Client::UriToString(Uri aUri)
1276 {
1277     const char *str = "";
1278 
1279     switch (aUri)
1280     {
1281     case kUriDiagnosticGetQuery:
1282         str = ot::UriToString<kUriDiagnosticGetQuery>();
1283         break;
1284     case kUriDiagnosticGetRequest:
1285         str = ot::UriToString<kUriDiagnosticGetRequest>();
1286         break;
1287     case kUriDiagnosticReset:
1288         str = ot::UriToString<kUriDiagnosticReset>();
1289         break;
1290     default:
1291         break;
1292     }
1293 
1294     return str;
1295 }
1296 
1297 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1298 
1299 #endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
1300 
1301 } // namespace NetworkDiagnostic
1302 
1303 } // namespace ot
1304