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