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 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
37
38 #include "coap/coap_message.hpp"
39 #include "common/array.hpp"
40 #include "common/as_core_type.hpp"
41 #include "common/code_utils.hpp"
42 #include "common/debug.hpp"
43 #include "common/encoding.hpp"
44 #include "common/instance.hpp"
45 #include "common/locator_getters.hpp"
46 #include "common/log.hpp"
47 #include "mac/mac.hpp"
48 #include "net/netif.hpp"
49 #include "thread/mesh_forwarder.hpp"
50 #include "thread/mle_router.hpp"
51 #include "thread/thread_netif.hpp"
52 #include "thread/thread_tlvs.hpp"
53 #include "thread/uri_paths.hpp"
54
55 namespace ot {
56
57 RegisterLogModule("NetDiag");
58
59 namespace NetworkDiagnostic {
60
NetworkDiagnostic(Instance & aInstance)61 NetworkDiagnostic::NetworkDiagnostic(Instance &aInstance)
62 : InstanceLocator(aInstance)
63 , mDiagnosticGetRequest(UriPath::kDiagnosticGetRequest, &NetworkDiagnostic::HandleDiagnosticGetRequest, this)
64 , mDiagnosticGetQuery(UriPath::kDiagnosticGetQuery, &NetworkDiagnostic::HandleDiagnosticGetQuery, this)
65 , mDiagnosticGetAnswer(UriPath::kDiagnosticGetAnswer, &NetworkDiagnostic::HandleDiagnosticGetAnswer, this)
66 , mDiagnosticReset(UriPath::kDiagnosticReset, &NetworkDiagnostic::HandleDiagnosticReset, this)
67 , mReceiveDiagnosticGetCallback(nullptr)
68 , mReceiveDiagnosticGetCallbackContext(nullptr)
69 {
70 Get<Tmf::Agent>().AddResource(mDiagnosticGetRequest);
71 Get<Tmf::Agent>().AddResource(mDiagnosticGetQuery);
72 Get<Tmf::Agent>().AddResource(mDiagnosticGetAnswer);
73 Get<Tmf::Agent>().AddResource(mDiagnosticReset);
74 }
75
SendDiagnosticGet(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount,otReceiveDiagnosticGetCallback aCallback,void * aCallbackContext)76 Error NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address & aDestination,
77 const uint8_t aTlvTypes[],
78 uint8_t aCount,
79 otReceiveDiagnosticGetCallback aCallback,
80 void * aCallbackContext)
81 {
82 Error error;
83 Coap::Message * message = nullptr;
84 Tmf::MessageInfo messageInfo(GetInstance());
85 otCoapResponseHandler handler = nullptr;
86
87 if (aDestination.IsMulticast())
88 {
89 message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(UriPath::kDiagnosticGetQuery);
90 messageInfo.SetMulticastLoop(true);
91 }
92 else
93 {
94 handler = &NetworkDiagnostic::HandleDiagnosticGetResponse;
95 message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticGetRequest);
96 }
97
98 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
99
100 if (aCount > 0)
101 {
102 SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
103 }
104
105 if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast())
106 {
107 messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
108 }
109 else
110 {
111 messageInfo.SetSockAddrToRloc();
112 }
113
114 messageInfo.SetPeerAddr(aDestination);
115
116 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, handler, this));
117
118 mReceiveDiagnosticGetCallback = aCallback;
119 mReceiveDiagnosticGetCallbackContext = aCallbackContext;
120
121 LogInfo("Sent diagnostic get");
122
123 exit:
124 FreeMessageOnError(message, error);
125 return error;
126 }
127
HandleDiagnosticGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)128 void NetworkDiagnostic::HandleDiagnosticGetResponse(void * aContext,
129 otMessage * aMessage,
130 const otMessageInfo *aMessageInfo,
131 Error aResult)
132 {
133 static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetResponse(AsCoapMessagePtr(aMessage),
134 AsCoreTypePtr(aMessageInfo), aResult);
135 }
136
HandleDiagnosticGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)137 void NetworkDiagnostic::HandleDiagnosticGetResponse(Coap::Message * aMessage,
138 const Ip6::MessageInfo *aMessageInfo,
139 Error aResult)
140 {
141 SuccessOrExit(aResult);
142 VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed);
143
144 exit:
145 if (mReceiveDiagnosticGetCallback)
146 {
147 mReceiveDiagnosticGetCallback(aResult, aMessage, aMessageInfo, mReceiveDiagnosticGetCallbackContext);
148 }
149 else
150 {
151 LogDebg("Received diagnostic get response, error = %s", ErrorToString(aResult));
152 }
153 return;
154 }
155
HandleDiagnosticGetAnswer(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)156 void NetworkDiagnostic::HandleDiagnosticGetAnswer(void * aContext,
157 otMessage * aMessage,
158 const otMessageInfo *aMessageInfo)
159 {
160 static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetAnswer(AsCoapMessage(aMessage),
161 AsCoreType(aMessageInfo));
162 }
163
HandleDiagnosticGetAnswer(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)164 void NetworkDiagnostic::HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
165 {
166 VerifyOrExit(aMessage.IsConfirmablePostRequest());
167
168 LogInfo("Diagnostic get answer received");
169
170 if (mReceiveDiagnosticGetCallback)
171 {
172 mReceiveDiagnosticGetCallback(kErrorNone, &aMessage, &aMessageInfo, mReceiveDiagnosticGetCallbackContext);
173 }
174
175 SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
176
177 LogInfo("Sent diagnostic answer acknowledgment");
178
179 exit:
180 return;
181 }
182
AppendIp6AddressList(Message & aMessage)183 Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage)
184 {
185 Error error = kErrorNone;
186 Ip6AddressListTlv tlv;
187 uint8_t count = 0;
188
189 tlv.Init();
190
191 for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
192 {
193 OT_UNUSED_VARIABLE(addr);
194 count++;
195 }
196
197 tlv.SetLength(count * sizeof(Ip6::Address));
198 SuccessOrExit(error = aMessage.Append(tlv));
199
200 for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
201 {
202 SuccessOrExit(error = aMessage.Append(addr.GetAddress()));
203 }
204
205 exit:
206
207 return error;
208 }
209
210 #if OPENTHREAD_FTD
AppendChildTable(Message & aMessage)211 Error NetworkDiagnostic::AppendChildTable(Message &aMessage)
212 {
213 Error error = kErrorNone;
214 uint16_t count = 0;
215 uint8_t timeout = 0;
216 ChildTableTlv tlv;
217 ChildTableEntry entry;
218
219 tlv.Init();
220
221 count = Get<ChildTable>().GetNumChildren(Child::kInStateValid);
222
223 // The length of the Child Table TLV may exceed the outgoing link's MTU (1280B).
224 // As a workaround we limit the number of entries in the Child Table TLV,
225 // also to avoid using extended TLV format. The issue is processed by the
226 // Thread Group (SPEC-894).
227 if (count > (Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry)))
228 {
229 count = Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry);
230 }
231
232 tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
233
234 SuccessOrExit(error = aMessage.Append(tlv));
235
236 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
237 {
238 VerifyOrExit(count--);
239
240 timeout = 0;
241
242 while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
243 {
244 timeout++;
245 }
246
247 entry.SetReserved(0);
248 entry.SetTimeout(timeout + 4);
249
250 entry.SetChildId(Mle::Mle::ChildIdFromRloc16(child.GetRloc16()));
251 entry.SetMode(child.GetDeviceMode());
252
253 SuccessOrExit(error = aMessage.Append(entry));
254 }
255
256 exit:
257
258 return error;
259 }
260 #endif // OPENTHREAD_FTD
261
FillMacCountersTlv(MacCountersTlv & aMacCountersTlv)262 void NetworkDiagnostic::FillMacCountersTlv(MacCountersTlv &aMacCountersTlv)
263 {
264 const otMacCounters &macCounters = Get<Mac::Mac>().GetCounters();
265
266 aMacCountersTlv.SetIfInUnknownProtos(macCounters.mRxOther);
267 aMacCountersTlv.SetIfInErrors(macCounters.mRxErrNoFrame + macCounters.mRxErrUnknownNeighbor +
268 macCounters.mRxErrInvalidSrcAddr + macCounters.mRxErrSec + macCounters.mRxErrFcs +
269 macCounters.mRxErrOther);
270 aMacCountersTlv.SetIfOutErrors(macCounters.mTxErrCca);
271 aMacCountersTlv.SetIfInUcastPkts(macCounters.mRxUnicast);
272 aMacCountersTlv.SetIfInBroadcastPkts(macCounters.mRxBroadcast);
273 aMacCountersTlv.SetIfInDiscards(macCounters.mRxAddressFiltered + macCounters.mRxDestAddrFiltered +
274 macCounters.mRxDuplicated);
275 aMacCountersTlv.SetIfOutUcastPkts(macCounters.mTxUnicast);
276 aMacCountersTlv.SetIfOutBroadcastPkts(macCounters.mTxBroadcast);
277 aMacCountersTlv.SetIfOutDiscards(macCounters.mTxErrBusyChannel);
278 }
279
FillRequestedTlvs(const Message & aRequest,Message & aResponse,NetworkDiagnosticTlv & aNetworkDiagnosticTlv)280 Error NetworkDiagnostic::FillRequestedTlvs(const Message & aRequest,
281 Message & aResponse,
282 NetworkDiagnosticTlv &aNetworkDiagnosticTlv)
283 {
284 Error error = kErrorNone;
285 uint16_t offset = 0;
286 uint8_t type;
287
288 offset = aRequest.GetOffset() + sizeof(NetworkDiagnosticTlv);
289
290 for (uint32_t i = 0; i < aNetworkDiagnosticTlv.GetLength(); i++)
291 {
292 SuccessOrExit(error = aRequest.Read(offset, type));
293
294 LogInfo("Type %d", type);
295
296 switch (type)
297 {
298 case NetworkDiagnosticTlv::kExtMacAddress:
299 SuccessOrExit(error = Tlv::Append<ExtMacAddressTlv>(aResponse, Get<Mac::Mac>().GetExtAddress()));
300 break;
301
302 case NetworkDiagnosticTlv::kAddress16:
303 SuccessOrExit(error = Tlv::Append<Address16Tlv>(aResponse, Get<Mle::MleRouter>().GetRloc16()));
304 break;
305
306 case NetworkDiagnosticTlv::kMode:
307 SuccessOrExit(error = Tlv::Append<ModeTlv>(aResponse, Get<Mle::MleRouter>().GetDeviceMode().Get()));
308 break;
309
310 case NetworkDiagnosticTlv::kTimeout:
311 if (!Get<Mle::MleRouter>().IsRxOnWhenIdle())
312 {
313 SuccessOrExit(error = Tlv::Append<TimeoutTlv>(aResponse, Get<Mle::MleRouter>().GetTimeout()));
314 }
315
316 break;
317
318 #if OPENTHREAD_FTD
319 case NetworkDiagnosticTlv::kConnectivity:
320 {
321 ConnectivityTlv tlv;
322 tlv.Init();
323 Get<Mle::MleRouter>().FillConnectivityTlv(reinterpret_cast<Mle::ConnectivityTlv &>(tlv));
324 SuccessOrExit(error = tlv.AppendTo(aResponse));
325 break;
326 }
327
328 case NetworkDiagnosticTlv::kRoute:
329 {
330 RouteTlv tlv;
331 tlv.Init();
332 Get<Mle::MleRouter>().FillRouteTlv(reinterpret_cast<Mle::RouteTlv &>(tlv));
333 SuccessOrExit(error = tlv.AppendTo(aResponse));
334 break;
335 }
336 #endif
337
338 case NetworkDiagnosticTlv::kLeaderData:
339 {
340 LeaderDataTlv tlv;
341 const Mle::LeaderData &leaderData = Get<Mle::MleRouter>().GetLeaderData();
342
343 tlv.Init();
344 tlv.SetPartitionId(leaderData.GetPartitionId());
345 tlv.SetWeighting(leaderData.GetWeighting());
346 tlv.SetDataVersion(leaderData.GetDataVersion(NetworkData::kFullSet));
347 tlv.SetStableDataVersion(leaderData.GetDataVersion(NetworkData::kStableSubset));
348 tlv.SetLeaderRouterId(leaderData.GetLeaderRouterId());
349
350 SuccessOrExit(error = tlv.AppendTo(aResponse));
351 break;
352 }
353
354 case NetworkDiagnosticTlv::kNetworkData:
355 {
356 NetworkData::NetworkData &netData = Get<NetworkData::Leader>();
357
358 SuccessOrExit(error = Tlv::Append<NetworkDataTlv>(aResponse, netData.GetBytes(), netData.GetLength()));
359 break;
360 }
361
362 case NetworkDiagnosticTlv::kIp6AddressList:
363 {
364 SuccessOrExit(error = AppendIp6AddressList(aResponse));
365 break;
366 }
367
368 case NetworkDiagnosticTlv::kMacCounters:
369 {
370 MacCountersTlv tlv;
371 memset(&tlv, 0, sizeof(tlv));
372 tlv.Init();
373 FillMacCountersTlv(tlv);
374 SuccessOrExit(error = tlv.AppendTo(aResponse));
375 break;
376 }
377
378 case NetworkDiagnosticTlv::kBatteryLevel:
379 {
380 // Thread 1.1.1 Specification Section 10.11.4.2:
381 // Omitted if the battery level is not measured, is unknown or the device does not
382 // operate on battery power.
383 break;
384 }
385
386 case NetworkDiagnosticTlv::kSupplyVoltage:
387 {
388 // Thread 1.1.1 Specification Section 10.11.4.3:
389 // Omitted if the supply voltage is not measured, is unknown.
390 break;
391 }
392
393 #if OPENTHREAD_FTD
394 case NetworkDiagnosticTlv::kChildTable:
395 {
396 // Thread 1.1.1 Specification Section 10.11.2.2:
397 // If a Thread device is unable to supply a specific Diagnostic TLV, that TLV is omitted.
398 // Here only Leader or Router may have children.
399 if (Get<Mle::MleRouter>().IsRouterOrLeader())
400 {
401 SuccessOrExit(error = AppendChildTable(aResponse));
402 }
403 break;
404 }
405 #endif
406
407 case NetworkDiagnosticTlv::kChannelPages:
408 {
409 uint8_t length = 0;
410 uint32_t pageMask = Radio::kSupportedChannelPages;
411 ChannelPagesTlv tlv;
412
413 tlv.Init();
414 for (uint8_t page = 0; page < sizeof(pageMask) * 8; page++)
415 {
416 if (pageMask & (1 << page))
417 {
418 tlv.GetChannelPages()[length++] = page;
419 }
420 }
421
422 tlv.SetLength(length);
423 SuccessOrExit(error = tlv.AppendTo(aResponse));
424 break;
425 }
426
427 #if OPENTHREAD_FTD
428 case NetworkDiagnosticTlv::kMaxChildTimeout:
429 {
430 uint32_t maxTimeout;
431
432 if (Get<Mle::MleRouter>().GetMaxChildTimeout(maxTimeout) == kErrorNone)
433 {
434 SuccessOrExit(error = Tlv::Append<MaxChildTimeoutTlv>(aResponse, maxTimeout));
435 }
436
437 break;
438 }
439 #endif
440
441 default:
442 // Skip unrecognized TLV type.
443 break;
444 }
445
446 offset += sizeof(type);
447 }
448
449 exit:
450 return error;
451 }
452
HandleDiagnosticGetQuery(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)453 void NetworkDiagnostic::HandleDiagnosticGetQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
454 {
455 static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetQuery(AsCoapMessage(aMessage),
456 AsCoreType(aMessageInfo));
457 }
458
HandleDiagnosticGetQuery(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)459 void NetworkDiagnostic::HandleDiagnosticGetQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
460 {
461 Error error = kErrorNone;
462 Coap::Message * message = nullptr;
463 NetworkDiagnosticTlv networkDiagnosticTlv;
464 Tmf::MessageInfo messageInfo(GetInstance());
465
466 VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
467
468 LogInfo("Received diagnostic get query");
469
470 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv));
471
472 VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse);
473
474 // DIAG_GET.qry may be sent as a confirmable message.
475 if (aMessage.IsConfirmable())
476 {
477 if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
478 {
479 LogInfo("Sent diagnostic get query acknowledgment");
480 }
481 }
482
483 message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticGetAnswer);
484 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
485
486 if (aMessageInfo.GetPeerAddr().IsLinkLocal())
487 {
488 messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
489 }
490 else
491 {
492 messageInfo.SetSockAddrToRloc();
493 }
494
495 messageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
496
497 SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
498
499 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, nullptr, this));
500
501 LogInfo("Sent diagnostic get answer");
502
503 exit:
504 FreeMessageOnError(message, error);
505 }
506
HandleDiagnosticGetRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)507 void NetworkDiagnostic::HandleDiagnosticGetRequest(void * aContext,
508 otMessage * aMessage,
509 const otMessageInfo *aMessageInfo)
510 {
511 static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetRequest(AsCoapMessage(aMessage),
512 AsCoreType(aMessageInfo));
513 }
514
HandleDiagnosticGetRequest(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)515 void NetworkDiagnostic::HandleDiagnosticGetRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
516 {
517 Error error = kErrorNone;
518 Coap::Message * message = nullptr;
519 NetworkDiagnosticTlv networkDiagnosticTlv;
520 Ip6::MessageInfo messageInfo(aMessageInfo);
521
522 VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
523
524 LogInfo("Received diagnostic get request");
525
526 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv));
527
528 VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse);
529
530 message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
531 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
532
533 SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
534
535 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
536
537 LogInfo("Sent diagnostic get response");
538
539 exit:
540 FreeMessageOnError(message, error);
541 }
542
SendDiagnosticReset(const Ip6::Address & aDestination,const uint8_t aTlvTypes[],uint8_t aCount)543 Error NetworkDiagnostic::SendDiagnosticReset(const Ip6::Address &aDestination,
544 const uint8_t aTlvTypes[],
545 uint8_t aCount)
546 {
547 Error error;
548 Coap::Message * message = nullptr;
549 Tmf::MessageInfo messageInfo(GetInstance());
550
551 message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticReset);
552 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
553
554 if (aCount > 0)
555 {
556 SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
557 }
558
559 if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast())
560 {
561 messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
562 }
563 else
564 {
565 messageInfo.SetSockAddrToRloc();
566 }
567
568 messageInfo.SetPeerAddr(aDestination);
569
570 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
571
572 LogInfo("Sent network diagnostic reset");
573
574 exit:
575 FreeMessageOnError(message, error);
576 return error;
577 }
578
HandleDiagnosticReset(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)579 void NetworkDiagnostic::HandleDiagnosticReset(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
580 {
581 static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticReset(AsCoapMessage(aMessage),
582 AsCoreType(aMessageInfo));
583 }
584
HandleDiagnosticReset(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)585 void NetworkDiagnostic::HandleDiagnosticReset(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
586 {
587 uint16_t offset = 0;
588 uint8_t type;
589 NetworkDiagnosticTlv tlv;
590
591 LogInfo("Received diagnostic reset request");
592
593 VerifyOrExit(aMessage.IsConfirmablePostRequest());
594
595 SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv));
596
597 VerifyOrExit(tlv.GetType() == NetworkDiagnosticTlv::kTypeList);
598
599 offset = aMessage.GetOffset() + sizeof(NetworkDiagnosticTlv);
600
601 for (uint8_t i = 0; i < tlv.GetLength(); i++)
602 {
603 SuccessOrExit(aMessage.Read(offset + i, type));
604
605 switch (type)
606 {
607 case NetworkDiagnosticTlv::kMacCounters:
608 Get<Mac::Mac>().ResetCounters();
609 LogInfo("Received diagnostic reset type kMacCounters(9)");
610 break;
611
612 default:
613 LogInfo("Received diagnostic reset other type %d not resetable", type);
614 break;
615 }
616 }
617
618 SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
619
620 LogInfo("Sent diagnostic reset acknowledgment");
621
622 exit:
623 return;
624 }
625
ParseMode(const Mle::DeviceMode & aMode,otLinkModeConfig & aLinkModeConfig)626 static inline void ParseMode(const Mle::DeviceMode &aMode, otLinkModeConfig &aLinkModeConfig)
627 {
628 aLinkModeConfig.mRxOnWhenIdle = aMode.IsRxOnWhenIdle();
629 aLinkModeConfig.mDeviceType = aMode.IsFullThreadDevice();
630 aLinkModeConfig.mNetworkData = (aMode.GetNetworkDataType() == NetworkData::kFullSet);
631 }
632
ParseConnectivity(const ConnectivityTlv & aConnectivityTlv,otNetworkDiagConnectivity & aNetworkDiagConnectivity)633 static inline void ParseConnectivity(const ConnectivityTlv & aConnectivityTlv,
634 otNetworkDiagConnectivity &aNetworkDiagConnectivity)
635 {
636 aNetworkDiagConnectivity.mParentPriority = aConnectivityTlv.GetParentPriority();
637 aNetworkDiagConnectivity.mLinkQuality3 = aConnectivityTlv.GetLinkQuality3();
638 aNetworkDiagConnectivity.mLinkQuality2 = aConnectivityTlv.GetLinkQuality2();
639 aNetworkDiagConnectivity.mLinkQuality1 = aConnectivityTlv.GetLinkQuality1();
640 aNetworkDiagConnectivity.mLeaderCost = aConnectivityTlv.GetLeaderCost();
641 aNetworkDiagConnectivity.mIdSequence = aConnectivityTlv.GetIdSequence();
642 aNetworkDiagConnectivity.mActiveRouters = aConnectivityTlv.GetActiveRouters();
643 aNetworkDiagConnectivity.mSedBufferSize = aConnectivityTlv.GetSedBufferSize();
644 aNetworkDiagConnectivity.mSedDatagramCount = aConnectivityTlv.GetSedDatagramCount();
645 }
646
ParseRoute(const RouteTlv & aRouteTlv,otNetworkDiagRoute & aNetworkDiagRoute)647 static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
648 {
649 uint8_t routeCount = 0;
650
651 for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i)
652 {
653 if (!aRouteTlv.IsRouterIdSet(i))
654 {
655 continue;
656 }
657 aNetworkDiagRoute.mRouteData[routeCount].mRouterId = i;
658 aNetworkDiagRoute.mRouteData[routeCount].mRouteCost = aRouteTlv.GetRouteCost(routeCount);
659 aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn = aRouteTlv.GetLinkQualityIn(routeCount);
660 aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount);
661 ++routeCount;
662 }
663 aNetworkDiagRoute.mRouteCount = routeCount;
664 aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence();
665 }
666
ParseLeaderData(const LeaderDataTlv & aLeaderDataTlv,otLeaderData & aLeaderData)667 static inline void ParseLeaderData(const LeaderDataTlv &aLeaderDataTlv, otLeaderData &aLeaderData)
668 {
669 aLeaderData.mPartitionId = aLeaderDataTlv.GetPartitionId();
670 aLeaderData.mWeighting = aLeaderDataTlv.GetWeighting();
671 aLeaderData.mDataVersion = aLeaderDataTlv.GetDataVersion();
672 aLeaderData.mStableDataVersion = aLeaderDataTlv.GetStableDataVersion();
673 aLeaderData.mLeaderRouterId = aLeaderDataTlv.GetLeaderRouterId();
674 }
675
ParseMacCounters(const MacCountersTlv & aMacCountersTlv,otNetworkDiagMacCounters & aMacCounters)676 static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters)
677 {
678 aMacCounters.mIfInUnknownProtos = aMacCountersTlv.GetIfInUnknownProtos();
679 aMacCounters.mIfInErrors = aMacCountersTlv.GetIfInErrors();
680 aMacCounters.mIfOutErrors = aMacCountersTlv.GetIfOutErrors();
681 aMacCounters.mIfInUcastPkts = aMacCountersTlv.GetIfInUcastPkts();
682 aMacCounters.mIfInBroadcastPkts = aMacCountersTlv.GetIfInBroadcastPkts();
683 aMacCounters.mIfInDiscards = aMacCountersTlv.GetIfInDiscards();
684 aMacCounters.mIfOutUcastPkts = aMacCountersTlv.GetIfOutUcastPkts();
685 aMacCounters.mIfOutBroadcastPkts = aMacCountersTlv.GetIfOutBroadcastPkts();
686 aMacCounters.mIfOutDiscards = aMacCountersTlv.GetIfOutDiscards();
687 }
688
ParseChildEntry(const ChildTableEntry & aChildTableTlvEntry,otNetworkDiagChildEntry & aChildEntry)689 static inline void ParseChildEntry(const ChildTableEntry &aChildTableTlvEntry, otNetworkDiagChildEntry &aChildEntry)
690 {
691 aChildEntry.mTimeout = aChildTableTlvEntry.GetTimeout();
692 aChildEntry.mChildId = aChildTableTlvEntry.GetChildId();
693 ParseMode(aChildTableTlvEntry.GetMode(), aChildEntry.mMode);
694 }
695
GetNextDiagTlv(const Coap::Message & aMessage,Iterator & aIterator,otNetworkDiagTlv & aNetworkDiagTlv)696 Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage,
697 Iterator & aIterator,
698 otNetworkDiagTlv & aNetworkDiagTlv)
699 {
700 Error error = kErrorNone;
701 uint16_t offset = aMessage.GetOffset() + aIterator;
702 NetworkDiagnosticTlv tlv;
703
704 while (true)
705 {
706 uint16_t tlvTotalLength;
707
708 VerifyOrExit(aMessage.Read(offset, tlv) == kErrorNone, error = kErrorNotFound);
709
710 switch (tlv.GetType())
711 {
712 case NetworkDiagnosticTlv::kExtMacAddress:
713 SuccessOrExit(
714 error = Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aNetworkDiagTlv.mData.mExtAddress)));
715 break;
716
717 case NetworkDiagnosticTlv::kAddress16:
718 SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aNetworkDiagTlv.mData.mAddr16));
719 break;
720
721 case NetworkDiagnosticTlv::kMode:
722 {
723 uint8_t mode;
724
725 SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
726 ParseMode(Mle::DeviceMode(mode), aNetworkDiagTlv.mData.mMode);
727 break;
728 }
729
730 case NetworkDiagnosticTlv::kTimeout:
731 SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aNetworkDiagTlv.mData.mTimeout));
732 break;
733
734 case NetworkDiagnosticTlv::kConnectivity:
735 {
736 ConnectivityTlv connectivity;
737
738 SuccessOrExit(error = aMessage.Read(offset, connectivity));
739 VerifyOrExit(connectivity.IsValid(), error = kErrorParse);
740
741 ParseConnectivity(connectivity, aNetworkDiagTlv.mData.mConnectivity);
742 break;
743 }
744
745 case NetworkDiagnosticTlv::kRoute:
746 {
747 RouteTlv route;
748
749 tlvTotalLength = sizeof(tlv) + tlv.GetLength();
750 VerifyOrExit(tlvTotalLength <= sizeof(route), error = kErrorParse);
751 SuccessOrExit(error = aMessage.Read(offset, &route, tlvTotalLength));
752 VerifyOrExit(route.IsValid(), error = kErrorParse);
753
754 ParseRoute(route, aNetworkDiagTlv.mData.mRoute);
755 break;
756 }
757
758 case NetworkDiagnosticTlv::kLeaderData:
759 {
760 LeaderDataTlv leaderData;
761
762 SuccessOrExit(error = aMessage.Read(offset, leaderData));
763 VerifyOrExit(leaderData.IsValid(), error = kErrorParse);
764
765 ParseLeaderData(leaderData, aNetworkDiagTlv.mData.mLeaderData);
766 break;
767 }
768
769 case NetworkDiagnosticTlv::kNetworkData:
770 {
771 NetworkDataTlv networkData;
772
773 tlvTotalLength = sizeof(tlv) + tlv.GetLength();
774 VerifyOrExit(tlvTotalLength <= sizeof(networkData), error = kErrorParse);
775 SuccessOrExit(error = aMessage.Read(offset, &networkData, tlvTotalLength));
776 VerifyOrExit(networkData.IsValid(), error = kErrorParse);
777 VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mNetworkData.m8) >= networkData.GetLength(), error = kErrorParse);
778
779 memcpy(aNetworkDiagTlv.mData.mNetworkData.m8, networkData.GetNetworkData(), networkData.GetLength());
780 aNetworkDiagTlv.mData.mNetworkData.mCount = networkData.GetLength();
781 break;
782 }
783
784 case NetworkDiagnosticTlv::kIp6AddressList:
785 {
786 Ip6AddressListTlv &ip6AddrList = As<Ip6AddressListTlv>(tlv);
787
788 VerifyOrExit(ip6AddrList.IsValid(), error = kErrorParse);
789 VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mIp6AddrList.mList) >= ip6AddrList.GetLength(),
790 error = kErrorParse);
791 SuccessOrExit(error = aMessage.Read(offset + sizeof(ip6AddrList), aNetworkDiagTlv.mData.mIp6AddrList.mList,
792 ip6AddrList.GetLength()));
793 aNetworkDiagTlv.mData.mIp6AddrList.mCount = ip6AddrList.GetLength() / OT_IP6_ADDRESS_SIZE;
794 break;
795 }
796
797 case NetworkDiagnosticTlv::kMacCounters:
798 {
799 MacCountersTlv macCounters;
800
801 SuccessOrExit(error = aMessage.Read(offset, macCounters));
802 VerifyOrExit(macCounters.IsValid(), error = kErrorParse);
803
804 ParseMacCounters(macCounters, aNetworkDiagTlv.mData.mMacCounters);
805 break;
806 }
807
808 case NetworkDiagnosticTlv::kBatteryLevel:
809 SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aNetworkDiagTlv.mData.mBatteryLevel));
810 break;
811
812 case NetworkDiagnosticTlv::kSupplyVoltage:
813 SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aNetworkDiagTlv.mData.mSupplyVoltage));
814 break;
815
816 case NetworkDiagnosticTlv::kChildTable:
817 {
818 ChildTableTlv &childTable = As<ChildTableTlv>(tlv);
819
820 VerifyOrExit(childTable.IsValid(), error = kErrorParse);
821 VerifyOrExit(childTable.GetNumEntries() <= GetArrayLength(aNetworkDiagTlv.mData.mChildTable.mTable),
822 error = kErrorParse);
823
824 for (uint8_t i = 0; i < childTable.GetNumEntries(); ++i)
825 {
826 ChildTableEntry childEntry;
827 VerifyOrExit(childTable.ReadEntry(childEntry, aMessage, offset, i) == kErrorNone, error = kErrorParse);
828 ParseChildEntry(childEntry, aNetworkDiagTlv.mData.mChildTable.mTable[i]);
829 }
830 aNetworkDiagTlv.mData.mChildTable.mCount = childTable.GetNumEntries();
831 break;
832 }
833
834 case NetworkDiagnosticTlv::kChannelPages:
835 {
836 VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mChannelPages.m8) >= tlv.GetLength(), error = kErrorParse);
837 SuccessOrExit(
838 error = aMessage.Read(offset + sizeof(tlv), aNetworkDiagTlv.mData.mChannelPages.m8, tlv.GetLength()));
839 aNetworkDiagTlv.mData.mChannelPages.mCount = tlv.GetLength();
840 break;
841 }
842
843 case NetworkDiagnosticTlv::kMaxChildTimeout:
844 SuccessOrExit(error =
845 Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aNetworkDiagTlv.mData.mMaxChildTimeout));
846 break;
847
848 default:
849 // Ignore unrecognized Network Diagnostic TLV silently and
850 // continue to top of the `while(true)` loop.
851 offset += tlv.GetSize();
852 continue;
853 }
854
855 // Exit if a TLV is recognized and parsed successfully.
856 aNetworkDiagTlv.mType = tlv.GetType();
857 aIterator = static_cast<uint16_t>(offset - aMessage.GetOffset() + tlv.GetSize());
858 ExitNow();
859 }
860
861 exit:
862 return error;
863 }
864
865 } // namespace NetworkDiagnostic
866
867 } // namespace ot
868
869 #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
870