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