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