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