• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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