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