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