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/instance.hpp"
43 #include "common/locator_getters.hpp"
44 #include "common/log.hpp"
45 #include "mac/mac.hpp"
46 #include "net/netif.hpp"
47 #include "thread/mesh_forwarder.hpp"
48 #include "thread/mle_router.hpp"
49 #include "thread/thread_netif.hpp"
50 #include "thread/thread_tlvs.hpp"
51 #include "thread/version.hpp"
52 
53 namespace ot {
54 
55 RegisterLogModule("NetDiag");
56 
57 namespace NetworkDiagnostic {
58 
59 const char Server::kVendorName[]      = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME;
60 const char Server::kVendorModel[]     = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL;
61 const char Server::kVendorSwVersion[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION;
62 
63 //---------------------------------------------------------------------------------------------------------------------
64 // Server
65 
Server(Instance & aInstance)66 Server::Server(Instance &aInstance)
67     : InstanceLocator(aInstance)
68 {
69     static_assert(sizeof(kVendorName) <= sizeof(VendorNameTlv::StringType), "VENDOR_NAME is too long");
70     static_assert(sizeof(kVendorModel) <= sizeof(VendorModelTlv::StringType), "VENDOR_MODEL is too long");
71     static_assert(sizeof(kVendorSwVersion) <= sizeof(VendorSwVersionTlv::StringType), "VENDOR_SW_VERSION is too long");
72 
73 #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
74     memcpy(mVendorName, kVendorName, sizeof(kVendorName));
75     memcpy(mVendorModel, kVendorModel, sizeof(kVendorModel));
76     memcpy(mVendorSwVersion, kVendorSwVersion, sizeof(kVendorSwVersion));
77 #endif
78 }
79 
80 #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
81 
SetVendorName(const char * aVendorName)82 Error Server::SetVendorName(const char *aVendorName)
83 {
84     return SetVendorString(mVendorName, sizeof(mVendorName), aVendorName);
85 }
86 
SetVendorModel(const char * aVendorModel)87 Error Server::SetVendorModel(const char *aVendorModel)
88 {
89     return SetVendorString(mVendorModel, sizeof(mVendorModel), aVendorModel);
90 }
91 
SetVendorSwVersion(const char * aVendorSwVersion)92 Error Server::SetVendorSwVersion(const char *aVendorSwVersion)
93 {
94     return SetVendorString(mVendorSwVersion, sizeof(mVendorSwVersion), aVendorSwVersion);
95 }
96 
SetVendorString(char * aDestString,uint16_t kMaxSize,const char * aSrcString)97 Error Server::SetVendorString(char *aDestString, uint16_t kMaxSize, const char *aSrcString)
98 {
99     Error    error = kErrorInvalidArgs;
100     uint16_t length;
101 
102     VerifyOrExit(aSrcString != nullptr);
103 
104     length = StringLength(aSrcString, kMaxSize);
105     VerifyOrExit(length < kMaxSize);
106 
107     VerifyOrExit(IsValidUtf8String(aSrcString));
108 
109     memcpy(aDestString, aSrcString, length + 1);
110     error = kErrorNone;
111 
112 exit:
113     return error;
114 }
115 
116 #endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
117 
PrepareMessageInfoForDest(const Ip6::Address & aDestination,Tmf::MessageInfo & aMessageInfo) const118 void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const
119 {
120     if (aDestination.IsMulticast())
121     {
122         aMessageInfo.SetMulticastLoop(true);
123     }
124 
125     if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast())
126     {
127         aMessageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
128     }
129     else
130     {
131         aMessageInfo.SetSockAddrToRloc();
132     }
133 
134     aMessageInfo.SetPeerAddr(aDestination);
135 }
136 
AppendIp6AddressList(Message & aMessage)137 Error Server::AppendIp6AddressList(Message &aMessage)
138 {
139     Error    error = kErrorNone;
140     uint16_t count = 0;
141 
142     for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
143     {
144         OT_UNUSED_VARIABLE(addr);
145         count++;
146     }
147 
148     if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength)
149     {
150         Tlv tlv;
151 
152         tlv.SetType(Tlv::kIp6AddressList);
153         tlv.SetLength(static_cast<uint8_t>(count * Ip6::Address::kSize));
154         SuccessOrExit(error = aMessage.Append(tlv));
155     }
156     else
157     {
158         ExtendedTlv extTlv;
159 
160         extTlv.SetType(Tlv::kIp6AddressList);
161         extTlv.SetLength(count * Ip6::Address::kSize);
162         SuccessOrExit(error = aMessage.Append(extTlv));
163     }
164 
165     for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
166     {
167         SuccessOrExit(error = aMessage.Append(addr.GetAddress()));
168     }
169 
170 exit:
171     return error;
172 }
173 
174 #if OPENTHREAD_FTD
AppendChildTable(Message & aMessage)175 Error Server::AppendChildTable(Message &aMessage)
176 {
177     Error    error = kErrorNone;
178     uint16_t count;
179 
180     VerifyOrExit(Get<Mle::MleRouter>().IsRouterOrLeader());
181 
182     count = Min(Get<ChildTable>().GetNumChildren(Child::kInStateValid), kMaxChildEntries);
183 
184     if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength)
185     {
186         Tlv tlv;
187 
188         tlv.SetType(Tlv::kChildTable);
189         tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
190         SuccessOrExit(error = aMessage.Append(tlv));
191     }
192     else
193     {
194         ExtendedTlv extTlv;
195 
196         extTlv.SetType(Tlv::kChildTable);
197         extTlv.SetLength(count * sizeof(ChildTableEntry));
198         SuccessOrExit(error = aMessage.Append(extTlv));
199     }
200 
201     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
202     {
203         uint8_t         timeout = 0;
204         ChildTableEntry entry;
205 
206         VerifyOrExit(count--);
207 
208         while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
209         {
210             timeout++;
211         }
212 
213         entry.Clear();
214         entry.SetTimeout(timeout + 4);
215         entry.SetLinkQuality(child.GetLinkQualityIn());
216         entry.SetChildId(Mle::ChildIdFromRloc16(child.GetRloc16()));
217         entry.SetMode(child.GetDeviceMode());
218 
219         SuccessOrExit(error = aMessage.Append(entry));
220     }
221 
222 exit:
223     return error;
224 }
225 #endif // OPENTHREAD_FTD
226 
AppendMacCounters(Message & aMessage)227 Error Server::AppendMacCounters(Message &aMessage)
228 {
229     MacCountersTlv       tlv;
230     const otMacCounters &counters = Get<Mac::Mac>().GetCounters();
231 
232     memset(&tlv, 0, sizeof(tlv));
233 
234     tlv.Init();
235     tlv.SetIfInUnknownProtos(counters.mRxOther);
236     tlv.SetIfInErrors(counters.mRxErrNoFrame + counters.mRxErrUnknownNeighbor + counters.mRxErrInvalidSrcAddr +
237                       counters.mRxErrSec + counters.mRxErrFcs + counters.mRxErrOther);
238     tlv.SetIfOutErrors(counters.mTxErrCca);
239     tlv.SetIfInUcastPkts(counters.mRxUnicast);
240     tlv.SetIfInBroadcastPkts(counters.mRxBroadcast);
241     tlv.SetIfInDiscards(counters.mRxAddressFiltered + counters.mRxDestAddrFiltered + counters.mRxDuplicated);
242     tlv.SetIfOutUcastPkts(counters.mTxUnicast);
243     tlv.SetIfOutBroadcastPkts(counters.mTxBroadcast);
244     tlv.SetIfOutDiscards(counters.mTxErrBusyChannel);
245 
246     return tlv.AppendTo(aMessage);
247 }
248 
AppendRequestedTlvs(const Message & aRequest,Message & aResponse)249 Error Server::AppendRequestedTlvs(const Message &aRequest, Message &aResponse)
250 {
251     Error    error;
252     uint16_t offset;
253     uint16_t length;
254     uint16_t endOffset;
255 
256     SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequest, Tlv::kTypeList, offset, length));
257     endOffset = offset + length;
258 
259     for (; offset < endOffset; offset++)
260     {
261         uint8_t tlvType;
262 
263         SuccessOrExit(error = aRequest.Read(offset, tlvType));
264         SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse));
265     }
266 
267 exit:
268     return error;
269 }
270 
AppendDiagTlv(uint8_t aTlvType,Message & aMessage)271 Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage)
272 {
273     Error error = kErrorNone;
274 
275     switch (aTlvType)
276     {
277     case Tlv::kExtMacAddress:
278         error = Tlv::Append<ExtMacAddressTlv>(aMessage, Get<Mac::Mac>().GetExtAddress());
279         break;
280 
281     case Tlv::kAddress16:
282         error = Tlv::Append<Address16Tlv>(aMessage, Get<Mle::MleRouter>().GetRloc16());
283         break;
284 
285     case Tlv::kMode:
286         error = Tlv::Append<ModeTlv>(aMessage, Get<Mle::MleRouter>().GetDeviceMode().Get());
287         break;
288 
289     case Tlv::kVersion:
290         error = Tlv::Append<VersionTlv>(aMessage, kThreadVersion);
291         break;
292 
293     case Tlv::kTimeout:
294         VerifyOrExit(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
295         error = Tlv::Append<TimeoutTlv>(aMessage, Get<Mle::MleRouter>().GetTimeout());
296         break;
297 
298     case Tlv::kLeaderData:
299     {
300         LeaderDataTlv tlv;
301 
302         tlv.Init();
303         tlv.Set(Get<Mle::MleRouter>().GetLeaderData());
304         error = tlv.AppendTo(aMessage);
305         break;
306     }
307 
308     case Tlv::kNetworkData:
309         error = Tlv::Append<NetworkDataTlv>(aMessage, Get<NetworkData::Leader>().GetBytes(),
310                                             Get<NetworkData::Leader>().GetLength());
311         break;
312 
313     case Tlv::kIp6AddressList:
314         error = AppendIp6AddressList(aMessage);
315         break;
316 
317     case Tlv::kMacCounters:
318         error = AppendMacCounters(aMessage);
319         break;
320 
321     case Tlv::kVendorName:
322         error = Tlv::Append<VendorNameTlv>(aMessage, GetVendorName());
323         break;
324 
325     case Tlv::kVendorModel:
326         error = Tlv::Append<VendorModelTlv>(aMessage, GetVendorModel());
327         break;
328 
329     case Tlv::kVendorSwVersion:
330         error = Tlv::Append<VendorSwVersionTlv>(aMessage, GetVendorSwVersion());
331         break;
332 
333     case Tlv::kThreadStackVersion:
334         error = Tlv::Append<ThreadStackVersionTlv>(aMessage, otGetVersionString());
335         break;
336 
337     case Tlv::kChannelPages:
338     {
339         ChannelPagesTlv tlv;
340         uint8_t         length = 0;
341 
342         tlv.Init();
343 
344         for (uint8_t page = 0; page < sizeof(Radio::kSupportedChannelPages) * CHAR_BIT; page++)
345         {
346             if (Radio::kSupportedChannelPages & (1 << page))
347             {
348                 tlv.GetChannelPages()[length++] = page;
349             }
350         }
351 
352         tlv.SetLength(length);
353         error = tlv.AppendTo(aMessage);
354 
355         break;
356     }
357 
358 #if OPENTHREAD_FTD
359 
360     case Tlv::kConnectivity:
361     {
362         ConnectivityTlv tlv;
363 
364         tlv.Init();
365         Get<Mle::MleRouter>().FillConnectivityTlv(tlv);
366         error = tlv.AppendTo(aMessage);
367         break;
368     }
369 
370     case Tlv::kRoute:
371     {
372         RouteTlv tlv;
373 
374         tlv.Init();
375         Get<RouterTable>().FillRouteTlv(tlv);
376         SuccessOrExit(error = tlv.AppendTo(aMessage));
377         break;
378     }
379 
380     case Tlv::kChildTable:
381         error = AppendChildTable(aMessage);
382         break;
383 
384     case Tlv::kMaxChildTimeout:
385     {
386         uint32_t maxTimeout;
387 
388         SuccessOrExit(Get<Mle::MleRouter>().GetMaxChildTimeout(maxTimeout));
389         error = Tlv::Append<MaxChildTimeoutTlv>(aMessage, maxTimeout);
390         break;
391     }
392 
393 #endif // OPENTHREAD_FTD
394 
395     default:
396         break;
397     }
398 
399 exit:
400     return error;
401 }
402 
403 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)404 void Server::HandleTmf<kUriDiagnosticGetQuery>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
405 {
406     Error            error    = kErrorNone;
407     Coap::Message   *response = nullptr;
408     Tmf::MessageInfo responseInfo(GetInstance());
409 
410     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
411 
412     LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetQuery>(),
413             aMessageInfo.GetPeerAddr().ToString().AsCString());
414 
415     // DIAG_GET.qry may be sent as a confirmable request.
416     if (aMessage.IsConfirmable())
417     {
418         IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
419     }
420 
421     response = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
422     VerifyOrExit(response != nullptr, error = kErrorNoBufs);
423 
424     SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
425 
426     PrepareMessageInfoForDest(aMessageInfo.GetPeerAddr(), responseInfo);
427     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, responseInfo));
428 
429 exit:
430     FreeMessageOnError(response, error);
431 }
432 
433 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)434 void Server::HandleTmf<kUriDiagnosticGetRequest>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
435 {
436     Error          error    = kErrorNone;
437     Coap::Message *response = nullptr;
438 
439     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
440 
441     LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetRequest>(),
442             aMessageInfo.GetPeerAddr().ToString().AsCString());
443 
444     response = Get<Tmf::Agent>().NewResponseMessage(aMessage);
445     VerifyOrExit(response != nullptr, error = kErrorNoBufs);
446 
447     SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
448     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, aMessageInfo));
449 
450 exit:
451     FreeMessageOnError(response, error);
452 }
453 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)454 template <> void Server::HandleTmf<kUriDiagnosticReset>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
455 {
456     uint16_t offset = 0;
457     uint8_t  type;
458     Tlv      tlv;
459 
460     VerifyOrExit(aMessage.IsConfirmablePostRequest());
461 
462     LogInfo("Received %s from %s", UriToString<kUriDiagnosticReset>(),
463             aMessageInfo.GetPeerAddr().ToString().AsCString());
464 
465     SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv));
466 
467     VerifyOrExit(tlv.GetType() == Tlv::kTypeList);
468 
469     offset = aMessage.GetOffset() + sizeof(Tlv);
470 
471     for (uint8_t i = 0; i < tlv.GetLength(); i++)
472     {
473         SuccessOrExit(aMessage.Read(offset + i, type));
474 
475         switch (type)
476         {
477         case Tlv::kMacCounters:
478             Get<Mac::Mac>().ResetCounters();
479             break;
480 
481         default:
482             break;
483         }
484     }
485 
486     IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
487 
488 exit:
489     return;
490 }
491 
492 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
493 
494 //---------------------------------------------------------------------------------------------------------------------
495 // Client
496 
Client(Instance & aInstance)497 Client::Client(Instance &aInstance)
498     : InstanceLocator(aInstance)
499 {
500 }
501 
SendDiagnosticGet(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,GetCallback aCallback,void * aContext)502 Error Client::SendDiagnosticGet(const Ip6::Address &aDestination,
503                                 const uint8_t       aTlvTypes[],
504                                 uint8_t             aCount,
505                                 GetCallback         aCallback,
506                                 void               *aContext)
507 {
508     Error error;
509 
510     if (aDestination.IsMulticast())
511     {
512         error = SendCommand(kUriDiagnosticGetQuery, aDestination, aTlvTypes, aCount);
513     }
514     else
515     {
516         error = SendCommand(kUriDiagnosticGetRequest, aDestination, aTlvTypes, aCount, &HandleGetResponse, this);
517     }
518 
519     SuccessOrExit(error);
520 
521     mGetCallback.Set(aCallback, aContext);
522 
523 exit:
524     return error;
525 }
526 
SendCommand(Uri aUri,const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,Coap::ResponseHandler aHandler,void * aContext)527 Error Client::SendCommand(Uri                   aUri,
528                           const Ip6::Address   &aDestination,
529                           const uint8_t         aTlvTypes[],
530                           uint8_t               aCount,
531                           Coap::ResponseHandler aHandler,
532                           void                 *aContext)
533 {
534     Error            error;
535     Coap::Message   *message = nullptr;
536     Tmf::MessageInfo messageInfo(GetInstance());
537 
538     switch (aUri)
539     {
540     case kUriDiagnosticGetQuery:
541         message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(aUri);
542         break;
543 
544     case kUriDiagnosticGetRequest:
545     case kUriDiagnosticReset:
546         message = Get<Tmf::Agent>().NewConfirmablePostMessage(aUri);
547         break;
548 
549     default:
550         OT_ASSERT(false);
551     }
552 
553     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
554 
555     if (aCount > 0)
556     {
557         SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
558     }
559 
560     Get<Server>().PrepareMessageInfoForDest(aDestination, messageInfo);
561 
562     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aHandler, aContext));
563 
564     LogInfo("Sent %s to %s", UriToString(aUri), aDestination.ToString().AsCString());
565 
566 exit:
567     FreeMessageOnError(message, error);
568     return error;
569 }
570 
HandleGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)571 void Client::HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
572 {
573     static_cast<Client *>(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
574                                                        aResult);
575 }
576 
HandleGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)577 void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
578 {
579     SuccessOrExit(aResult);
580     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed);
581 
582 exit:
583     mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo);
584 }
585 
586 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)587 void Client::HandleTmf<kUriDiagnosticGetAnswer>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
588 {
589     VerifyOrExit(aMessage.IsConfirmablePostRequest());
590 
591     LogInfo("Received %s from %s", ot::UriToString<kUriDiagnosticGetAnswer>(),
592             aMessageInfo.GetPeerAddr().ToString().AsCString());
593 
594     mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo);
595 
596     IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
597 
598 exit:
599     return;
600 }
601 
SendDiagnosticReset(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount)602 Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount)
603 {
604     return SendCommand(kUriDiagnosticReset, aDestination, aTlvTypes, aCount);
605 }
606 
ParseRoute(const RouteTlv & aRouteTlv,otNetworkDiagRoute & aNetworkDiagRoute)607 static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
608 {
609     uint8_t routeCount = 0;
610 
611     for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i)
612     {
613         if (!aRouteTlv.IsRouterIdSet(i))
614         {
615             continue;
616         }
617         aNetworkDiagRoute.mRouteData[routeCount].mRouterId       = i;
618         aNetworkDiagRoute.mRouteData[routeCount].mRouteCost      = aRouteTlv.GetRouteCost(routeCount);
619         aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn  = aRouteTlv.GetLinkQualityIn(routeCount);
620         aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount);
621         ++routeCount;
622     }
623     aNetworkDiagRoute.mRouteCount = routeCount;
624     aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence();
625 }
626 
ParseMacCounters(const MacCountersTlv & aMacCountersTlv,otNetworkDiagMacCounters & aMacCounters)627 static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters)
628 {
629     aMacCounters.mIfInUnknownProtos  = aMacCountersTlv.GetIfInUnknownProtos();
630     aMacCounters.mIfInErrors         = aMacCountersTlv.GetIfInErrors();
631     aMacCounters.mIfOutErrors        = aMacCountersTlv.GetIfOutErrors();
632     aMacCounters.mIfInUcastPkts      = aMacCountersTlv.GetIfInUcastPkts();
633     aMacCounters.mIfInBroadcastPkts  = aMacCountersTlv.GetIfInBroadcastPkts();
634     aMacCounters.mIfInDiscards       = aMacCountersTlv.GetIfInDiscards();
635     aMacCounters.mIfOutUcastPkts     = aMacCountersTlv.GetIfOutUcastPkts();
636     aMacCounters.mIfOutBroadcastPkts = aMacCountersTlv.GetIfOutBroadcastPkts();
637     aMacCounters.mIfOutDiscards      = aMacCountersTlv.GetIfOutDiscards();
638 }
639 
GetNextDiagTlv(const Coap::Message & aMessage,Iterator & aIterator,TlvInfo & aTlvInfo)640 Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo)
641 {
642     Error    error;
643     uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator;
644 
645     while (offset < aMessage.GetLength())
646     {
647         bool     skipTlv = false;
648         uint16_t valueOffset;
649         uint16_t tlvLength;
650         union
651         {
652             Tlv         tlv;
653             ExtendedTlv extTlv;
654         };
655 
656         SuccessOrExit(error = aMessage.Read(offset, tlv));
657 
658         if (tlv.IsExtended())
659         {
660             SuccessOrExit(error = aMessage.Read(offset, extTlv));
661             valueOffset = offset + sizeof(ExtendedTlv);
662             tlvLength   = extTlv.GetLength();
663         }
664         else
665         {
666             valueOffset = offset + sizeof(Tlv);
667             tlvLength   = tlv.GetLength();
668         }
669 
670         VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse);
671 
672         switch (tlv.GetType())
673         {
674         case Tlv::kExtMacAddress:
675             SuccessOrExit(error =
676                               Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mExtAddress)));
677             break;
678 
679         case Tlv::kAddress16:
680             SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aTlvInfo.mData.mAddr16));
681             break;
682 
683         case Tlv::kMode:
684         {
685             uint8_t mode;
686 
687             SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
688             Mle::DeviceMode(mode).Get(aTlvInfo.mData.mMode);
689             break;
690         }
691 
692         case Tlv::kTimeout:
693             SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aTlvInfo.mData.mTimeout));
694             break;
695 
696         case Tlv::kConnectivity:
697         {
698             ConnectivityTlv connectivityTlv;
699 
700             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
701             SuccessOrExit(error = aMessage.Read(offset, connectivityTlv));
702             VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
703             connectivityTlv.GetConnectivity(aTlvInfo.mData.mConnectivity);
704             break;
705         }
706 
707         case Tlv::kRoute:
708         {
709             RouteTlv routeTlv;
710             uint16_t bytesToRead = static_cast<uint16_t>(Min(tlv.GetSize(), static_cast<uint32_t>(sizeof(routeTlv))));
711 
712             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
713             SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead));
714             VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
715             ParseRoute(routeTlv, aTlvInfo.mData.mRoute);
716             break;
717         }
718 
719         case Tlv::kLeaderData:
720         {
721             LeaderDataTlv leaderDataTlv;
722 
723             VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
724             SuccessOrExit(error = aMessage.Read(offset, leaderDataTlv));
725             VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse);
726             leaderDataTlv.Get(AsCoreType(&aTlvInfo.mData.mLeaderData));
727             break;
728         }
729 
730         case Tlv::kNetworkData:
731             static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize,
732                           "NetworkData array in `otNetworkDiagTlv` is too small");
733 
734             VerifyOrExit(tlvLength <= NetworkData::NetworkData::kMaxSize, error = kErrorParse);
735             aTlvInfo.mData.mNetworkData.mCount = static_cast<uint8_t>(tlvLength);
736             aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength);
737             break;
738 
739         case Tlv::kIp6AddressList:
740         {
741             uint16_t      addrListLength = GetArrayLength(aTlvInfo.mData.mIp6AddrList.mList);
742             Ip6::Address *addrEntry      = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]);
743             uint8_t      &addrCount      = aTlvInfo.mData.mIp6AddrList.mCount;
744 
745             VerifyOrExit((tlvLength % Ip6::Address::kSize) == 0, error = kErrorParse);
746 
747             // `TlvInfo` has a fixed array for IPv6 addresses. If there
748             // are more addresses in the message, we read and return as
749             // many as can fit in array and ignore the rest.
750 
751             addrCount = 0;
752 
753             while ((tlvLength > 0) && (addrCount < addrListLength))
754             {
755                 SuccessOrExit(error = aMessage.Read(valueOffset, *addrEntry));
756                 addrCount++;
757                 addrEntry++;
758                 valueOffset += Ip6::Address::kSize;
759                 tlvLength -= Ip6::Address::kSize;
760             }
761 
762             break;
763         }
764 
765         case Tlv::kMacCounters:
766         {
767             MacCountersTlv macCountersTlv;
768 
769             SuccessOrExit(error = aMessage.Read(offset, macCountersTlv));
770             VerifyOrExit(macCountersTlv.IsValid(), error = kErrorParse);
771             ParseMacCounters(macCountersTlv, aTlvInfo.mData.mMacCounters);
772             break;
773         }
774 
775         case Tlv::kBatteryLevel:
776             SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aTlvInfo.mData.mBatteryLevel));
777             break;
778 
779         case Tlv::kSupplyVoltage:
780             SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aTlvInfo.mData.mSupplyVoltage));
781             break;
782 
783         case Tlv::kChildTable:
784         {
785             uint16_t   childInfoLength = GetArrayLength(aTlvInfo.mData.mChildTable.mTable);
786             ChildInfo *childInfo       = &aTlvInfo.mData.mChildTable.mTable[0];
787             uint8_t   &childCount      = aTlvInfo.mData.mChildTable.mCount;
788 
789             VerifyOrExit((tlvLength % sizeof(ChildTableEntry)) == 0, error = kErrorParse);
790 
791             // `TlvInfo` has a fixed array Child Table entries. If there
792             // are more entries in the message, we read and return as
793             // many as can fit in array and ignore the rest.
794 
795             childCount = 0;
796 
797             while ((tlvLength > 0) && (childCount < childInfoLength))
798             {
799                 ChildTableEntry entry;
800 
801                 SuccessOrExit(error = aMessage.Read(valueOffset, entry));
802 
803                 childInfo->mTimeout     = entry.GetTimeout();
804                 childInfo->mLinkQuality = entry.GetLinkQuality();
805                 childInfo->mChildId     = entry.GetChildId();
806                 entry.GetMode().Get(childInfo->mMode);
807 
808                 childCount++;
809                 childInfo++;
810                 tlvLength -= sizeof(ChildTableEntry);
811                 valueOffset += sizeof(ChildTableEntry);
812             }
813 
814             break;
815         }
816 
817         case Tlv::kChannelPages:
818             aTlvInfo.mData.mChannelPages.mCount =
819                 static_cast<uint8_t>(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8)));
820             aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount);
821             break;
822 
823         case Tlv::kMaxChildTimeout:
824             SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout));
825             break;
826 
827         case Tlv::kVersion:
828             SuccessOrExit(error = Tlv::Read<VersionTlv>(aMessage, offset, aTlvInfo.mData.mVersion));
829             break;
830 
831         case Tlv::kVendorName:
832             SuccessOrExit(error = Tlv::Read<VendorNameTlv>(aMessage, offset, aTlvInfo.mData.mVendorName));
833             break;
834 
835         case Tlv::kVendorModel:
836             SuccessOrExit(error = Tlv::Read<VendorModelTlv>(aMessage, offset, aTlvInfo.mData.mVendorModel));
837             break;
838 
839         case Tlv::kVendorSwVersion:
840             SuccessOrExit(error = Tlv::Read<VendorSwVersionTlv>(aMessage, offset, aTlvInfo.mData.mVendorSwVersion));
841             break;
842 
843         case Tlv::kThreadStackVersion:
844             SuccessOrExit(error =
845                               Tlv::Read<ThreadStackVersionTlv>(aMessage, offset, aTlvInfo.mData.mThreadStackVersion));
846             break;
847 
848         default:
849             // Skip unrecognized TLVs.
850             skipTlv = true;
851             break;
852         }
853 
854         offset += tlv.GetSize();
855 
856         if (!skipTlv)
857         {
858             // Exit if a TLV is recognized and parsed successfully.
859             aTlvInfo.mType = tlv.GetType();
860             aIterator      = offset;
861             error          = kErrorNone;
862             ExitNow();
863         }
864     }
865 
866     error = kErrorNotFound;
867 
868 exit:
869     return error;
870 }
871 
872 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
873 
UriToString(Uri aUri)874 const char *Client::UriToString(Uri aUri)
875 {
876     const char *str = "";
877 
878     switch (aUri)
879     {
880     case kUriDiagnosticGetQuery:
881         str = ot::UriToString<kUriDiagnosticGetQuery>();
882         break;
883     case kUriDiagnosticGetRequest:
884         str = ot::UriToString<kUriDiagnosticGetRequest>();
885         break;
886     case kUriDiagnosticReset:
887         str = ot::UriToString<kUriDiagnosticReset>();
888         break;
889     default:
890         break;
891     }
892 
893     return str;
894 }
895 
896 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
897 
898 #endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
899 
900 } // namespace NetworkDiagnostic
901 
902 } // namespace ot
903