Coverage Report

Created: 2025-05-12 06:47

/src/openthread/src/core/thread/network_diagnostic.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (c) 2016, The OpenThread Authors.
3
 *  All rights reserved.
4
 *
5
 *  Redistribution and use in source and binary forms, with or without
6
 *  modification, are permitted provided that the following conditions are met:
7
 *  1. Redistributions of source code must retain the above copyright
8
 *     notice, this list of conditions and the following disclaimer.
9
 *  2. Redistributions in binary form must reproduce the above copyright
10
 *     notice, this list of conditions and the following disclaimer in the
11
 *     documentation and/or other materials provided with the distribution.
12
 *  3. Neither the name of the copyright holder nor the
13
 *     names of its contributors may be used to endorse or promote products
14
 *     derived from this software without specific prior written permission.
15
 *
16
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
 *  POSSIBILITY OF SUCH DAMAGE.
27
 */
28
29
/**
30
 * @file
31
 *   This file implements Thread's Network Diagnostic processing.
32
 */
33
34
#include "network_diagnostic.hpp"
35
36
#include "instance/instance.hpp"
37
38
namespace ot {
39
40
RegisterLogModule("NetDiag");
41
42
namespace NetworkDiagnostic {
43
44
const char Server::kVendorName[]      = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME;
45
const char Server::kVendorModel[]     = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL;
46
const char Server::kVendorSwVersion[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION;
47
const char Server::kVendorAppUrl[]    = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL;
48
49
//---------------------------------------------------------------------------------------------------------------------
50
// Server
51
52
Server::Server(Instance &aInstance)
53
21.3k
    : InstanceLocator(aInstance)
54
21.3k
    , mNonPreferredChannels(0)
55
21.3k
{
56
21.3k
    static_assert(sizeof(kVendorName) <= sizeof(VendorNameTlv::StringType), "VENDOR_NAME is too long");
57
21.3k
    static_assert(sizeof(kVendorModel) <= sizeof(VendorModelTlv::StringType), "VENDOR_MODEL is too long");
58
21.3k
    static_assert(sizeof(kVendorSwVersion) <= sizeof(VendorSwVersionTlv::StringType), "VENDOR_SW_VERSION is too long");
59
21.3k
    static_assert(sizeof(kVendorAppUrl) <= sizeof(VendorAppUrlTlv::StringType), "VENDOR_APP_URL is too long");
60
61
#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
62
    memcpy(mVendorName, kVendorName, sizeof(kVendorName));
63
    memcpy(mVendorModel, kVendorModel, sizeof(kVendorModel));
64
    memcpy(mVendorSwVersion, kVendorSwVersion, sizeof(kVendorSwVersion));
65
    memcpy(mVendorAppUrl, kVendorAppUrl, sizeof(kVendorAppUrl));
66
#endif
67
21.3k
}
68
69
#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
70
71
Error Server::SetVendorName(const char *aVendorName)
72
{
73
    return StringCopy(mVendorName, aVendorName, kStringCheckUtf8Encoding);
74
}
75
76
Error Server::SetVendorModel(const char *aVendorModel)
77
{
78
    return StringCopy(mVendorModel, aVendorModel, kStringCheckUtf8Encoding);
79
}
80
81
Error Server::SetVendorSwVersion(const char *aVendorSwVersion)
82
{
83
    return StringCopy(mVendorSwVersion, aVendorSwVersion, kStringCheckUtf8Encoding);
84
}
85
86
Error Server::SetVendorAppUrl(const char *aVendorAppUrl)
87
{
88
    return StringCopy(mVendorAppUrl, aVendorAppUrl, kStringCheckUtf8Encoding);
89
}
90
91
#endif
92
93
void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const
94
6.07k
{
95
6.07k
    if (aDestination.IsMulticast())
96
0
    {
97
0
        aMessageInfo.SetMulticastLoop(true);
98
0
    }
99
100
6.07k
    if (aDestination.IsLinkLocalUnicastOrMulticast())
101
456
    {
102
456
        aMessageInfo.SetSockAddr(Get<Mle::Mle>().GetLinkLocalAddress());
103
456
    }
104
5.62k
    else
105
5.62k
    {
106
5.62k
        aMessageInfo.SetSockAddrToRloc();
107
5.62k
    }
108
109
6.07k
    aMessageInfo.SetPeerAddr(aDestination);
110
6.07k
}
111
112
Error Server::AppendIp6AddressList(Message &aMessage)
113
2.26k
{
114
2.26k
    Error    error = kErrorNone;
115
2.26k
    uint16_t count = 0;
116
117
2.26k
    for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
118
9.46k
    {
119
9.46k
        OT_UNUSED_VARIABLE(addr);
120
9.46k
        count++;
121
9.46k
    }
122
123
2.26k
    if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength)
124
2.26k
    {
125
2.26k
        Tlv tlv;
126
127
2.26k
        tlv.SetType(Tlv::kIp6AddressList);
128
2.26k
        tlv.SetLength(static_cast<uint8_t>(count * Ip6::Address::kSize));
129
2.26k
        SuccessOrExit(error = aMessage.Append(tlv));
130
2.26k
    }
131
0
    else
132
0
    {
133
0
        ExtendedTlv extTlv;
134
135
0
        extTlv.SetType(Tlv::kIp6AddressList);
136
0
        extTlv.SetLength(count * Ip6::Address::kSize);
137
0
        SuccessOrExit(error = aMessage.Append(extTlv));
138
0
    }
139
140
2.26k
    for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
141
9.33k
    {
142
9.33k
        SuccessOrExit(error = aMessage.Append(addr.GetAddress()));
143
9.33k
    }
144
145
2.26k
exit:
146
2.26k
    return error;
147
2.26k
}
148
149
#if OPENTHREAD_FTD
150
Error Server::AppendChildTable(Message &aMessage)
151
211
{
152
211
    Error    error = kErrorNone;
153
211
    uint16_t count;
154
155
211
    VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader());
156
157
0
    count = Min(Get<ChildTable>().GetNumChildren(Child::kInStateValid), kMaxChildEntries);
158
159
0
    if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength)
160
0
    {
161
0
        Tlv tlv;
162
163
0
        tlv.SetType(Tlv::kChildTable);
164
0
        tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
165
0
        SuccessOrExit(error = aMessage.Append(tlv));
166
0
    }
167
0
    else
168
0
    {
169
0
        ExtendedTlv extTlv;
170
171
0
        extTlv.SetType(Tlv::kChildTable);
172
0
        extTlv.SetLength(count * sizeof(ChildTableEntry));
173
0
        SuccessOrExit(error = aMessage.Append(extTlv));
174
0
    }
175
176
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
177
0
    {
178
0
        uint8_t         timeout = 0;
179
0
        ChildTableEntry entry;
180
181
0
        VerifyOrExit(count--);
182
183
0
        while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
184
0
        {
185
0
            timeout++;
186
0
        }
187
188
0
        entry.Clear();
189
0
        entry.SetTimeout(timeout + 4);
190
0
        entry.SetLinkQuality(child.GetLinkQualityIn());
191
0
        entry.SetChildId(Mle::ChildIdFromRloc16(child.GetRloc16()));
192
0
        entry.SetMode(child.GetDeviceMode());
193
194
0
        SuccessOrExit(error = aMessage.Append(entry));
195
0
    }
196
197
211
exit:
198
211
    return error;
199
0
}
200
201
Error Server::AppendEnhancedRoute(Message &aMessage)
202
195
{
203
195
    Error                 error = kErrorNone;
204
195
    Tlv                   tlv;
205
195
    Mle::RouterIdSet      routerIdSet;
206
195
    EnhancedRouteTlvEntry entry;
207
208
195
    VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader());
209
210
0
    Get<RouterTable>().GetRouterIdSet(routerIdSet);
211
212
0
    tlv.SetType(Tlv::kEnhancedRoute);
213
0
    tlv.SetLength(sizeof(Mle::RouterIdSet) + routerIdSet.GetNumberOfAllocatedIds() * sizeof(entry));
214
215
0
    SuccessOrExit(error = aMessage.Append(tlv));
216
0
    SuccessOrExit(error = aMessage.Append(routerIdSet));
217
218
0
    for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
219
0
    {
220
0
        if (!routerIdSet.Contains(routerId))
221
0
        {
222
0
            continue;
223
0
        }
224
225
0
        if (Get<Mle::Mle>().MatchesRouterId(routerId))
226
0
        {
227
0
            entry.InitAsSelf();
228
0
        }
229
0
        else
230
0
        {
231
0
            entry.InitFrom(*Get<RouterTable>().FindRouterById(routerId));
232
0
        }
233
234
0
        SuccessOrExit(error = aMessage.Append(entry));
235
0
    }
236
237
195
exit:
238
195
    return error;
239
0
}
240
241
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
242
Error Server::AppendChildTableAsChildTlvs(Message &aMessage)
243
{
244
    Error    error = kErrorNone;
245
    ChildTlv childTlv;
246
247
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
248
    {
249
        childTlv.InitFrom(child);
250
251
        SuccessOrExit(error = childTlv.AppendTo(aMessage));
252
    }
253
254
    // Add empty TLV to indicate end of the list
255
256
    childTlv.InitAsEmpty();
257
    SuccessOrExit(error = childTlv.AppendTo(aMessage));
258
259
exit:
260
    return error;
261
}
262
263
Error Server::AppendRouterNeighborTlvs(Message &aMessage)
264
{
265
    Error             error = kErrorNone;
266
    RouterNeighborTlv neighborTlv;
267
268
    for (Router &router : Get<RouterTable>())
269
    {
270
        if (router.IsStateValid())
271
        {
272
            neighborTlv.InitFrom(router);
273
            SuccessOrExit(error = neighborTlv.AppendTo(aMessage));
274
        }
275
    }
276
277
    // Add empty TLV to indicate end of the list
278
279
    neighborTlv.InitAsEmpty();
280
    SuccessOrExit(error = neighborTlv.AppendTo(aMessage));
281
282
exit:
283
    return error;
284
}
285
286
Error Server::AppendChildTableIp6AddressList(Message &aMessage)
287
{
288
    Error error = kErrorNone;
289
    Tlv   tlv;
290
291
    for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
292
    {
293
        SuccessOrExit(error = AppendChildIp6AddressListTlv(aMessage, child));
294
    }
295
296
    // Add empty TLV to indicate end of the list
297
298
    tlv.SetType(Tlv::kChildIp6AddressList);
299
    tlv.SetLength(0);
300
    SuccessOrExit(error = aMessage.Append(tlv));
301
302
exit:
303
    return error;
304
}
305
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
306
#endif // OPENTHREAD_FTD
307
308
Error Server::AppendMacCounters(Message &aMessage)
309
262
{
310
262
    MacCountersTlv       tlv;
311
262
    const otMacCounters &counters = Get<Mac::Mac>().GetCounters();
312
313
262
    ClearAllBytes(tlv);
314
315
262
    tlv.Init();
316
262
    tlv.SetIfInUnknownProtos(counters.mRxOther);
317
262
    tlv.SetIfInErrors(counters.mRxErrNoFrame + counters.mRxErrUnknownNeighbor + counters.mRxErrInvalidSrcAddr +
318
262
                      counters.mRxErrSec + counters.mRxErrFcs + counters.mRxErrOther);
319
262
    tlv.SetIfOutErrors(counters.mTxErrCca);
320
262
    tlv.SetIfInUcastPkts(counters.mRxUnicast);
321
262
    tlv.SetIfInBroadcastPkts(counters.mRxBroadcast);
322
262
    tlv.SetIfInDiscards(counters.mRxAddressFiltered + counters.mRxDestAddrFiltered + counters.mRxDuplicated);
323
262
    tlv.SetIfOutUcastPkts(counters.mTxUnicast);
324
262
    tlv.SetIfOutBroadcastPkts(counters.mTxBroadcast);
325
262
    tlv.SetIfOutDiscards(counters.mTxErrBusyChannel);
326
327
262
    return tlv.AppendTo(aMessage);
328
262
}
329
330
Error Server::AppendRequestedTlvs(const Message &aRequest, Message &aResponse)
331
835
{
332
835
    Error       error;
333
835
    OffsetRange offsetRange;
334
335
835
    SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kTypeList, offsetRange));
336
337
711
    while (!offsetRange.IsEmpty())
338
550
    {
339
550
        uint8_t tlvType;
340
341
550
        SuccessOrExit(error = aRequest.Read(offsetRange, tlvType));
342
550
        offsetRange.AdvanceOffset(sizeof(tlvType));
343
550
        SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse));
344
550
    }
345
346
835
exit:
347
835
    return error;
348
352
}
349
350
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
351
Error Server::AppendRequestedTlvsForTcat(const Message &aRequest, Message &aResponse, OffsetRange &aOffsetRange)
352
{
353
    Error error = kErrorNone;
354
355
    while (!aOffsetRange.IsEmpty())
356
    {
357
        uint8_t tlvType;
358
359
        SuccessOrExit(error = aRequest.Read(aOffsetRange, tlvType));
360
        aOffsetRange.AdvanceOffset(sizeof(uint8_t));
361
362
#if OPENTHREAD_FTD
363
        switch (tlvType)
364
        {
365
        case ChildTlv::kType:
366
            SuccessOrExit(error = AppendChildTableAsChildTlvs(aResponse));
367
            break;
368
369
        case ChildIp6AddressListTlv::kType:
370
            SuccessOrExit(error = AppendChildTableIp6AddressList(aResponse));
371
            break;
372
373
        case RouterNeighborTlv::kType:
374
            SuccessOrExit(error = AppendRouterNeighborTlvs(aResponse));
375
            break;
376
377
        default:
378
            SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse));
379
            break;
380
        }
381
382
#elif OPENTHREAD_MTD
383
        SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse));
384
#endif
385
    }
386
387
exit:
388
    return error;
389
}
390
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
391
392
Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage)
393
12.9k
{
394
12.9k
    Error error = kErrorNone;
395
396
12.9k
    switch (aTlvType)
397
12.9k
    {
398
219
    case Tlv::kExtMacAddress:
399
219
        error = Tlv::Append<ExtMacAddressTlv>(aMessage, Get<Mac::Mac>().GetExtAddress());
400
219
        break;
401
402
211
    case Tlv::kAddress16:
403
211
        error = Tlv::Append<Address16Tlv>(aMessage, Get<Mle::Mle>().GetRloc16());
404
211
        break;
405
406
208
    case Tlv::kMode:
407
208
        error = Tlv::Append<ModeTlv>(aMessage, Get<Mle::Mle>().GetDeviceMode().Get());
408
208
        break;
409
410
66
    case Tlv::kEui64:
411
66
    {
412
66
        Mac::ExtAddress eui64;
413
414
66
        Get<Radio>().GetIeeeEui64(eui64);
415
66
        error = Tlv::Append<Eui64Tlv>(aMessage, eui64);
416
66
        break;
417
0
    }
418
419
1.15k
    case Tlv::kVersion:
420
1.15k
        error = Tlv::Append<VersionTlv>(aMessage, kThreadVersion);
421
1.15k
        break;
422
423
2.15k
    case Tlv::kTimeout:
424
2.15k
        VerifyOrExit(!Get<Mle::Mle>().IsRxOnWhenIdle());
425
298
        error = Tlv::Append<TimeoutTlv>(aMessage, Get<Mle::Mle>().GetTimeout());
426
298
        break;
427
428
208
    case Tlv::kLeaderData:
429
208
    {
430
208
        LeaderDataTlv tlv;
431
432
208
        tlv.Init();
433
208
        tlv.Set(Get<Mle::Mle>().GetLeaderData());
434
208
        error = tlv.AppendTo(aMessage);
435
208
        break;
436
2.15k
    }
437
438
202
    case Tlv::kNetworkData:
439
202
        error = Tlv::Append<NetworkDataTlv>(aMessage, Get<NetworkData::Leader>().GetBytes(),
440
202
                                            Get<NetworkData::Leader>().GetLength());
441
202
        break;
442
443
2.26k
    case Tlv::kIp6AddressList:
444
2.26k
        error = AppendIp6AddressList(aMessage);
445
2.26k
        break;
446
447
262
    case Tlv::kMacCounters:
448
262
        error = AppendMacCounters(aMessage);
449
262
        break;
450
451
322
    case Tlv::kMleCounters:
452
322
    {
453
322
        MleCountersTlv tlv;
454
455
322
        tlv.Init(Get<Mle::Mle>().GetCounters());
456
322
        error = tlv.AppendTo(aMessage);
457
322
        break;
458
2.15k
    }
459
460
752
    case Tlv::kVendorName:
461
752
        error = Tlv::Append<VendorNameTlv>(aMessage, GetVendorName());
462
752
        break;
463
464
1.37k
    case Tlv::kVendorModel:
465
1.37k
        error = Tlv::Append<VendorModelTlv>(aMessage, GetVendorModel());
466
1.37k
        break;
467
468
67
    case Tlv::kVendorSwVersion:
469
67
        error = Tlv::Append<VendorSwVersionTlv>(aMessage, GetVendorSwVersion());
470
67
        break;
471
472
608
    case Tlv::kVendorAppUrl:
473
608
        error = Tlv::Append<VendorAppUrlTlv>(aMessage, GetVendorAppUrl());
474
608
        break;
475
476
170
    case Tlv::kThreadStackVersion:
477
170
        error = Tlv::Append<ThreadStackVersionTlv>(aMessage, otGetVersionString());
478
170
        break;
479
480
203
    case Tlv::kChannelPages:
481
203
    {
482
203
        ChannelPagesTlv tlv;
483
203
        uint8_t         length = 0;
484
485
203
        tlv.Init();
486
487
203
        for (uint8_t page : Radio::kSupportedChannelPages)
488
203
        {
489
203
            tlv.GetChannelPages()[length++] = page;
490
203
        }
491
492
203
        tlv.SetLength(length);
493
203
        error = tlv.AppendTo(aMessage);
494
495
203
        break;
496
2.15k
    }
497
498
200
    case Tlv::kNonPreferredChannels:
499
200
    {
500
200
        MeshCoP::ChannelMaskTlv::Value value;
501
502
200
        MeshCoP::ChannelMaskTlv::PrepareValue(value, mNonPreferredChannels, /* aIncludeZeroPageMasks */ true);
503
200
        error = Tlv::AppendTlv(aMessage, Tlv::kNonPreferredChannels, value.mData, value.mLength);
504
200
        break;
505
2.15k
    }
506
507
0
#if OPENTHREAD_FTD
508
509
407
    case Tlv::kConnectivity:
510
407
    {
511
407
        ConnectivityTlv tlv;
512
513
407
        tlv.Init();
514
407
        Get<Mle::Mle>().FillConnectivityTlv(tlv);
515
407
        error = tlv.AppendTo(aMessage);
516
407
        break;
517
2.15k
    }
518
519
968
    case Tlv::kRoute:
520
968
    {
521
968
        RouteTlv tlv;
522
523
968
        tlv.Init();
524
968
        Get<RouterTable>().FillRouteTlv(tlv);
525
968
        SuccessOrExit(error = tlv.AppendTo(aMessage));
526
968
        break;
527
968
    }
528
529
968
    case Tlv::kEnhancedRoute:
530
195
        error = AppendEnhancedRoute(aMessage);
531
195
        break;
532
533
211
    case Tlv::kChildTable:
534
211
        error = AppendChildTable(aMessage);
535
211
        break;
536
537
66
    case Tlv::kMaxChildTimeout:
538
66
    {
539
66
        uint32_t maxTimeout;
540
541
66
        SuccessOrExit(Get<Mle::Mle>().GetMaxChildTimeout(maxTimeout));
542
0
        error = Tlv::Append<MaxChildTimeoutTlv>(aMessage, maxTimeout);
543
0
        break;
544
66
    }
545
546
0
#endif // OPENTHREAD_FTD
547
548
436
    default:
549
436
        break;
550
12.9k
    }
551
552
12.9k
exit:
553
12.9k
    return error;
554
12.9k
}
555
556
template <>
557
void Server::HandleTmf<kUriDiagnosticGetQuery>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
558
8.02k
{
559
8.02k
    VerifyOrExit(aMessage.IsPostRequest());
560
561
7.83k
    LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetQuery>(),
562
7.83k
            aMessageInfo.GetPeerAddr().ToString().AsCString());
563
564
    // DIAG_GET.qry may be sent as a confirmable request.
565
7.83k
    if (aMessage.IsConfirmable())
566
270
    {
567
270
        IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
568
270
    }
569
570
#if OPENTHREAD_MTD
571
    SendAnswer(aMessageInfo.GetPeerAddr(), aMessage);
572
#elif OPENTHREAD_FTD
573
    PrepareAndSendAnswers(aMessageInfo.GetPeerAddr(), aMessage);
574
7.83k
#endif
575
576
8.02k
exit:
577
8.02k
    return;
578
7.83k
}
579
580
#if OPENTHREAD_MTD
581
582
void Server::SendAnswer(const Ip6::Address &aDestination, const Message &aRequest)
583
{
584
    Error            error  = kErrorNone;
585
    Coap::Message   *answer = nullptr;
586
    Tmf::MessageInfo messageInfo(GetInstance());
587
    AnswerTlv        answerTlv;
588
    uint16_t         queryId;
589
590
    answer = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
591
    VerifyOrExit(answer != nullptr, error = kErrorNoBufs);
592
593
    IgnoreError(answer->SetPriority(aRequest.GetPriority()));
594
595
    if (Tlv::Find<QueryIdTlv>(aRequest, queryId) == kErrorNone)
596
    {
597
        SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*answer, queryId));
598
    }
599
600
    SuccessOrExit(error = AppendRequestedTlvs(aRequest, *answer));
601
602
    answerTlv.Init(0, /* aIsLast */ true);
603
    SuccessOrExit(answer->Append(answerTlv));
604
605
    PrepareMessageInfoForDest(aDestination, messageInfo);
606
607
    error = Get<Tmf::Agent>().SendMessage(*answer, messageInfo);
608
609
exit:
610
    FreeMessageOnError(answer, error);
611
}
612
613
#endif // OPENTHREAD_MTD
614
615
#if OPENTHREAD_FTD
616
617
Error Server::AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo)
618
7.83k
{
619
    // Allocate an `Answer` message, adds it in `mAnswerQueue`,
620
    // update the `aInfo.mFirstAnswer` if it is the first allocated
621
    // messages, and appends `QueryIdTlv` to the message (if needed).
622
623
7.83k
    Error error = kErrorNone;
624
625
7.83k
    aAnswer = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
626
7.83k
    VerifyOrExit(aAnswer != nullptr, error = kErrorNoBufs);
627
7.37k
    IgnoreError(aAnswer->SetPriority(aInfo.mPriority));
628
629
7.37k
    mAnswerQueue.Enqueue(*aAnswer);
630
631
7.37k
    if (aInfo.mFirstAnswer == nullptr)
632
7.37k
    {
633
7.37k
        aInfo.mFirstAnswer = aAnswer;
634
7.37k
    }
635
636
7.37k
    if (aInfo.mHasQueryId)
637
194
    {
638
194
        SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*aAnswer, aInfo.mQueryId));
639
194
    }
640
641
7.83k
exit:
642
7.83k
    return error;
643
7.37k
}
644
645
bool Server::IsLastAnswer(const Coap::Message &aAnswer) const
646
7.37k
{
647
    // Indicates whether `aAnswer` is the last one associated with
648
    // the same query.
649
650
7.37k
    bool      isLast = true;
651
7.37k
    AnswerTlv answerTlv;
652
653
    // If there is no Answer TLV, we assume it is the last answer.
654
655
7.37k
    SuccessOrExit(Tlv::FindTlv(aAnswer, answerTlv));
656
6.07k
    isLast = answerTlv.IsLast();
657
658
7.37k
exit:
659
7.37k
    return isLast;
660
6.07k
}
661
662
void Server::FreeAllRelatedAnswers(Coap::Message &aFirstAnswer)
663
1.29k
{
664
    // This method dequeues and frees all answer messages related to
665
    // same query as `aFirstAnswer`. Note that related answers are
666
    // enqueued in order.
667
668
1.29k
    Coap::Message *answer = &aFirstAnswer;
669
670
2.59k
    while (answer != nullptr)
671
1.29k
    {
672
1.29k
        Coap::Message *next = IsLastAnswer(*answer) ? nullptr : answer->GetNextCoapMessage();
673
674
1.29k
        mAnswerQueue.DequeueAndFree(*answer);
675
1.29k
        answer = next;
676
1.29k
    }
677
1.29k
}
678
679
void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest)
680
7.83k
{
681
7.83k
    Coap::Message *answer;
682
7.83k
    Error          error;
683
7.83k
    AnswerInfo     info;
684
7.83k
    OffsetRange    offsetRange;
685
7.83k
    AnswerTlv      answerTlv;
686
687
7.83k
    if (Tlv::Find<QueryIdTlv>(aRequest, info.mQueryId) == kErrorNone)
688
194
    {
689
194
        info.mHasQueryId = true;
690
194
    }
691
692
7.83k
    info.mPriority = aRequest.GetPriority();
693
694
7.83k
    SuccessOrExit(error = AllocateAnswer(answer, info));
695
696
7.37k
    SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRequest, Tlv::kTypeList, offsetRange));
697
698
18.8k
    while (!offsetRange.IsEmpty())
699
12.7k
    {
700
12.7k
        uint8_t tlvType;
701
702
12.7k
        SuccessOrExit(error = aRequest.Read(offsetRange, tlvType));
703
12.7k
        offsetRange.AdvanceOffset(sizeof(tlvType));
704
705
12.7k
        switch (tlvType)
706
12.7k
        {
707
195
        case ChildTlv::kType:
708
195
            SuccessOrExit(error = AppendChildTableAsChildTlvs(answer, info));
709
195
            break;
710
195
        case ChildIp6AddressListTlv::kType:
711
76
            SuccessOrExit(error = AppendChildTableIp6AddressList(answer, info));
712
76
            break;
713
76
        case RouterNeighborTlv::kType:
714
66
            SuccessOrExit(error = AppendRouterNeighborTlvs(answer, info));
715
66
            break;
716
12.3k
        default:
717
12.3k
            SuccessOrExit(error = AppendDiagTlv(tlvType, *answer));
718
12.1k
            break;
719
12.7k
        }
720
721
12.4k
        SuccessOrExit(error = CheckAnswerLength(answer, info));
722
12.4k
    }
723
724
6.13k
    answerTlv.Init(info.mAnswerIndex, /* aIsLast */ true);
725
6.13k
    SuccessOrExit(error = answer->Append(answerTlv));
726
727
6.07k
    SendNextAnswer(*info.mFirstAnswer, aDestination);
728
729
7.83k
exit:
730
7.83k
    if ((error != kErrorNone) && (info.mFirstAnswer != nullptr))
731
1.29k
    {
732
1.29k
        FreeAllRelatedAnswers(*info.mFirstAnswer);
733
1.29k
    }
734
7.83k
}
735
736
Error Server::CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo)
737
12.4k
{
738
    // This method checks the length of the `aAnswer` message and if it
739
    // is above the threshold, it enqueues the message for transmission
740
    // after appending an Answer TLV with the current index to the
741
    // message. In this case, it will also allocate a new answer
742
    // message.
743
744
12.4k
    Error     error = kErrorNone;
745
12.4k
    AnswerTlv answerTlv;
746
747
12.4k
    VerifyOrExit(aAnswer->GetLength() >= kAnswerMessageLengthThreshold);
748
749
0
    answerTlv.Init(aInfo.mAnswerIndex++, /* aIsLast */ false);
750
0
    SuccessOrExit(error = aAnswer->Append(answerTlv));
751
752
0
    error = AllocateAnswer(aAnswer, aInfo);
753
754
12.4k
exit:
755
12.4k
    return error;
756
0
}
757
758
void Server::SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination)
759
6.07k
{
760
    // This method send the given next `aAnswer` associated with
761
    // a query to the  `aDestination`.
762
763
6.07k
    Error            error      = kErrorNone;
764
6.07k
    Coap::Message   *nextAnswer = IsLastAnswer(aAnswer) ? nullptr : aAnswer.GetNextCoapMessage();
765
6.07k
    Tmf::MessageInfo messageInfo(GetInstance());
766
767
6.07k
    mAnswerQueue.Dequeue(aAnswer);
768
769
6.07k
    PrepareMessageInfoForDest(aDestination, messageInfo);
770
771
    // When sending the message, we pass `nextAnswer` as `aContext`
772
    // to be used when invoking callback `HandleAnswerResponse()`.
773
774
6.07k
    error = Get<Tmf::Agent>().SendMessage(aAnswer, messageInfo, HandleAnswerResponse, nextAnswer);
775
776
6.07k
    if (error != kErrorNone)
777
4.13k
    {
778
        // If the `SendMessage()` fails, we `Free` the dequeued
779
        // `aAnswer` and all the related next answers in the queue.
780
781
4.13k
        aAnswer.Free();
782
783
4.13k
        if (nextAnswer != nullptr)
784
0
        {
785
0
            FreeAllRelatedAnswers(*nextAnswer);
786
0
        }
787
4.13k
    }
788
6.07k
}
789
790
void Server::HandleAnswerResponse(void                *aContext,
791
                                  otMessage           *aMessage,
792
                                  const otMessageInfo *aMessageInfo,
793
                                  otError              aResult)
794
1.94k
{
795
1.94k
    Coap::Message *nextAnswer = static_cast<Coap::Message *>(aContext);
796
797
1.94k
    VerifyOrExit(nextAnswer != nullptr);
798
799
0
    nextAnswer->Get<Server>().HandleAnswerResponse(*nextAnswer, AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
800
0
                                                   aResult);
801
802
1.94k
exit:
803
1.94k
    return;
804
0
}
805
806
void Server::HandleAnswerResponse(Coap::Message          &aNextAnswer,
807
                                  Coap::Message          *aResponse,
808
                                  const Ip6::MessageInfo *aMessageInfo,
809
                                  Error                   aResult)
810
0
{
811
0
    Error error = aResult;
812
813
0
    SuccessOrExit(error);
814
0
    VerifyOrExit(aResponse != nullptr && aMessageInfo != nullptr, error = kErrorDrop);
815
0
    VerifyOrExit(aResponse->GetCode() == Coap::kCodeChanged, error = kErrorDrop);
816
817
0
    SendNextAnswer(aNextAnswer, aMessageInfo->GetPeerAddr());
818
819
0
exit:
820
0
    if (error != kErrorNone)
821
0
    {
822
0
        FreeAllRelatedAnswers(aNextAnswer);
823
0
    }
824
0
}
825
826
Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
827
195
{
828
195
    Error    error = kErrorNone;
829
195
    ChildTlv childTlv;
830
831
195
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
832
0
    {
833
0
        childTlv.InitFrom(child);
834
835
0
        SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
836
0
        SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
837
0
    }
838
839
    // Add empty TLV to indicate end of the list
840
841
195
    childTlv.InitAsEmpty();
842
195
    SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
843
844
195
exit:
845
195
    return error;
846
195
}
847
848
Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
849
66
{
850
66
    Error             error = kErrorNone;
851
66
    RouterNeighborTlv neighborTlv;
852
853
66
    for (Router &router : Get<RouterTable>())
854
0
    {
855
0
        if (!router.IsStateValid())
856
0
        {
857
0
            continue;
858
0
        }
859
860
0
        neighborTlv.InitFrom(router);
861
862
0
        SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
863
0
        SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
864
0
    }
865
866
    // Add empty TLV to indicate end of the list
867
868
66
    neighborTlv.InitAsEmpty();
869
66
    SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
870
871
66
exit:
872
66
    return error;
873
66
}
874
875
Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo)
876
76
{
877
76
    Error error = kErrorNone;
878
76
    Tlv   tlv;
879
880
76
    for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
881
0
    {
882
0
        SuccessOrExit(error = AppendChildIp6AddressListTlv(*aAnswer, child));
883
0
        SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
884
0
    }
885
886
    // Add empty TLV to indicate end of the list
887
888
76
    tlv.SetType(Tlv::kChildIp6AddressList);
889
76
    tlv.SetLength(0);
890
76
    SuccessOrExit(error = aAnswer->Append(tlv));
891
892
76
exit:
893
76
    return error;
894
76
}
895
896
Error Server::AppendChildIp6AddressListTlv(Message &aAnswer, const Child &aChild)
897
0
{
898
0
    Error                       error      = kErrorNone;
899
0
    uint16_t                    numIp6Addr = aChild.GetIp6Addresses().GetLength();
900
0
    ChildIp6AddressListTlvValue tlvValue;
901
0
    Ip6::Address                mlEid;
902
903
0
    if (aChild.GetMeshLocalIp6Address(mlEid) == kErrorNone)
904
0
    {
905
0
        numIp6Addr++;
906
0
    }
907
0
    else
908
0
    {
909
0
        mlEid.Clear();
910
0
    }
911
912
0
    VerifyOrExit(numIp6Addr > 0);
913
914
0
    if ((numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)) <= Tlv::kBaseTlvMaxLength)
915
0
    {
916
0
        Tlv tlv;
917
918
0
        tlv.SetType(Tlv::kChildIp6AddressList);
919
0
        tlv.SetLength(static_cast<uint8_t>(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)));
920
0
        SuccessOrExit(error = aAnswer.Append(tlv));
921
0
    }
922
0
    else
923
0
    {
924
0
        ExtendedTlv extTlv;
925
926
0
        extTlv.SetType(Tlv::kChildIp6AddressList);
927
0
        extTlv.SetLength(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue));
928
0
        SuccessOrExit(error = aAnswer.Append(extTlv));
929
0
    }
930
931
0
    tlvValue.SetRloc16(aChild.GetRloc16());
932
933
0
    SuccessOrExit(error = aAnswer.Append(tlvValue));
934
935
0
    if (!mlEid.IsUnspecified())
936
0
    {
937
0
        SuccessOrExit(error = aAnswer.Append(mlEid));
938
0
    }
939
940
0
    for (const Ip6::Address &address : aChild.GetIp6Addresses())
941
0
    {
942
0
        SuccessOrExit(error = aAnswer.Append(address));
943
0
    }
944
945
0
exit:
946
0
    return error;
947
0
}
948
949
#endif // OPENTHREAD_FTD
950
951
template <>
952
void Server::HandleTmf<kUriDiagnosticGetRequest>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
953
1.22k
{
954
1.22k
    Error          error    = kErrorNone;
955
1.22k
    Coap::Message *response = nullptr;
956
957
1.22k
    VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
958
959
1.02k
    LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetRequest>(),
960
1.02k
            aMessageInfo.GetPeerAddr().ToString().AsCString());
961
962
1.02k
    response = Get<Tmf::Agent>().NewResponseMessage(aMessage);
963
1.02k
    VerifyOrExit(response != nullptr, error = kErrorNoBufs);
964
965
835
    IgnoreError(response->SetPriority(aMessage.GetPriority()));
966
835
    SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
967
161
    SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, aMessageInfo));
968
969
1.22k
exit:
970
1.22k
    FreeMessageOnError(response, error);
971
1.22k
}
972
973
template <> void Server::HandleTmf<kUriDiagnosticReset>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
974
1.86k
{
975
1.86k
    uint16_t offset = 0;
976
1.86k
    uint8_t  type;
977
1.86k
    Tlv      tlv;
978
979
1.86k
    VerifyOrExit(aMessage.IsConfirmablePostRequest());
980
981
1.67k
    LogInfo("Received %s from %s", UriToString<kUriDiagnosticReset>(),
982
1.67k
            aMessageInfo.GetPeerAddr().ToString().AsCString());
983
984
1.67k
    SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv));
985
986
1.47k
    VerifyOrExit(tlv.GetType() == Tlv::kTypeList);
987
988
1.28k
    offset = aMessage.GetOffset() + sizeof(Tlv);
989
990
2.67k
    for (uint8_t i = 0; i < tlv.GetLength(); i++)
991
2.28k
    {
992
2.28k
        SuccessOrExit(aMessage.Read(offset + i, type));
993
994
1.39k
        switch (type)
995
1.39k
        {
996
195
        case Tlv::kMacCounters:
997
195
            Get<Mac::Mac>().ResetCounters();
998
195
            break;
999
1000
194
        case Tlv::kMleCounters:
1001
194
            Get<Mle::Mle>().ResetCounters();
1002
194
            break;
1003
1004
225
        case Tlv::kNonPreferredChannels:
1005
225
            mNonPreferredChannelsResetCallback.InvokeIfSet();
1006
225
            break;
1007
1008
783
        default:
1009
783
            break;
1010
1.39k
        }
1011
1.39k
    }
1012
1013
390
    IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
1014
1015
1.86k
exit:
1016
1.86k
    return;
1017
390
}
1018
1019
#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
1020
1021
//---------------------------------------------------------------------------------------------------------------------
1022
// Client
1023
1024
Client::Client(Instance &aInstance)
1025
21.3k
    : InstanceLocator(aInstance)
1026
21.3k
    , mQueryId(Random::NonCrypto::GetUint16())
1027
21.3k
{
1028
21.3k
}
1029
1030
Error Client::SendDiagnosticGet(const Ip6::Address &aDestination,
1031
                                const uint8_t       aTlvTypes[],
1032
                                uint8_t             aCount,
1033
                                GetCallback         aCallback,
1034
                                void               *aContext)
1035
0
{
1036
0
    Error error;
1037
1038
0
    if (aDestination.IsMulticast())
1039
0
    {
1040
0
        error = SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
1041
0
    }
1042
0
    else
1043
0
    {
1044
0
        error = SendCommand(kUriDiagnosticGetRequest, Message::kPriorityNormal, aDestination, aTlvTypes, aCount,
1045
0
                            &HandleGetResponse, this);
1046
0
    }
1047
1048
0
    SuccessOrExit(error);
1049
1050
0
    mGetCallback.Set(aCallback, aContext);
1051
1052
0
exit:
1053
0
    return error;
1054
0
}
1055
1056
Error Client::SendCommand(Uri                   aUri,
1057
                          Message::Priority     aPriority,
1058
                          const Ip6::Address   &aDestination,
1059
                          const uint8_t         aTlvTypes[],
1060
                          uint8_t               aCount,
1061
                          Coap::ResponseHandler aHandler,
1062
                          void                 *aContext)
1063
0
{
1064
0
    Error            error;
1065
0
    Coap::Message   *message = nullptr;
1066
0
    Tmf::MessageInfo messageInfo(GetInstance());
1067
1068
0
    switch (aUri)
1069
0
    {
1070
0
    case kUriDiagnosticGetQuery:
1071
0
        message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(aUri);
1072
0
        break;
1073
1074
0
    case kUriDiagnosticGetRequest:
1075
0
    case kUriDiagnosticReset:
1076
0
        message = Get<Tmf::Agent>().NewConfirmablePostMessage(aUri);
1077
0
        break;
1078
1079
0
    default:
1080
0
        OT_ASSERT(false);
1081
0
    }
1082
1083
0
    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1084
0
    IgnoreError(message->SetPriority(aPriority));
1085
1086
0
    if (aCount > 0)
1087
0
    {
1088
0
        SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
1089
0
    }
1090
1091
0
    if (aUri == kUriDiagnosticGetQuery)
1092
0
    {
1093
0
        SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*message, ++mQueryId));
1094
0
    }
1095
1096
0
    Get<Server>().PrepareMessageInfoForDest(aDestination, messageInfo);
1097
1098
0
    SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aHandler, aContext));
1099
1100
0
    LogInfo("Sent %s to %s", UriToString(aUri), aDestination.ToString().AsCString());
1101
1102
0
exit:
1103
0
    FreeMessageOnError(message, error);
1104
0
    return error;
1105
0
}
1106
1107
void Client::HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult)
1108
0
{
1109
0
    static_cast<Client *>(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
1110
0
                                                       aResult);
1111
0
}
1112
1113
void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
1114
0
{
1115
0
    SuccessOrExit(aResult);
1116
0
    VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed);
1117
1118
0
exit:
1119
0
    mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo);
1120
0
}
1121
1122
template <>
1123
void Client::HandleTmf<kUriDiagnosticGetAnswer>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1124
398
{
1125
398
    VerifyOrExit(aMessage.IsConfirmablePostRequest());
1126
1127
195
    LogInfo("Received %s from %s", ot::UriToString<kUriDiagnosticGetAnswer>(),
1128
195
            aMessageInfo.GetPeerAddr().ToString().AsCString());
1129
1130
195
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
1131
    // Let the `MeshDiag` process the message first.
1132
195
    if (!Get<Utils::MeshDiag>().HandleDiagnosticGetAnswer(aMessage, aMessageInfo))
1133
195
#endif
1134
195
    {
1135
195
        mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo);
1136
195
    }
1137
1138
195
    IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
1139
1140
398
exit:
1141
398
    return;
1142
195
}
1143
1144
Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount)
1145
0
{
1146
0
    return SendCommand(kUriDiagnosticReset, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
1147
0
}
1148
1149
static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
1150
0
{
1151
0
    uint8_t routeCount = 0;
1152
1153
0
    for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i)
1154
0
    {
1155
0
        if (!aRouteTlv.IsRouterIdSet(i))
1156
0
        {
1157
0
            continue;
1158
0
        }
1159
0
        aNetworkDiagRoute.mRouteData[routeCount].mRouterId       = i;
1160
0
        aNetworkDiagRoute.mRouteData[routeCount].mRouteCost      = aRouteTlv.GetRouteCost(routeCount);
1161
0
        aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn  = aRouteTlv.GetLinkQualityIn(routeCount);
1162
0
        aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount);
1163
0
        ++routeCount;
1164
0
    }
1165
0
    aNetworkDiagRoute.mRouteCount = routeCount;
1166
0
    aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence();
1167
0
}
1168
1169
static Error ParseEnhancedRoute(const Message &aMessage, uint16_t aOffset, otNetworkDiagEnhRoute &aNetworkDiagEnhRoute)
1170
0
{
1171
0
    Error            error;
1172
0
    OffsetRange      offsetRange;
1173
0
    Tlv              tlv;
1174
0
    Mle::RouterIdSet routerIdSet;
1175
0
    uint8_t          index;
1176
1177
0
    SuccessOrExit(error = aMessage.Read(aOffset, tlv));
1178
1179
0
    VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1180
0
    VerifyOrExit(tlv.GetType() == Tlv::kEnhancedRoute, error = kErrorParse);
1181
1182
0
    aOffset += sizeof(tlv);
1183
0
    offsetRange.Init(aOffset, tlv.GetLength());
1184
1185
0
    SuccessOrExit(error = aMessage.Read(offsetRange, routerIdSet));
1186
0
    offsetRange.AdvanceOffset(sizeof(routerIdSet));
1187
1188
0
    index = 0;
1189
1190
0
    for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
1191
0
    {
1192
0
        EnhancedRouteTlvEntry entry;
1193
1194
0
        if (!routerIdSet.Contains(routerId))
1195
0
        {
1196
0
            continue;
1197
0
        }
1198
1199
0
        SuccessOrExit(error = aMessage.Read(offsetRange, entry));
1200
0
        offsetRange.AdvanceOffset(sizeof(entry));
1201
1202
0
        aNetworkDiagEnhRoute.mRouteData[index].mRouterId = routerId;
1203
0
        entry.Parse(aNetworkDiagEnhRoute.mRouteData[index]);
1204
1205
0
        index++;
1206
0
    }
1207
1208
0
    aNetworkDiagEnhRoute.mRouteCount = index;
1209
1210
0
exit:
1211
0
    return error;
1212
0
}
1213
1214
static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters)
1215
0
{
1216
0
    aMacCounters.mIfInUnknownProtos  = aMacCountersTlv.GetIfInUnknownProtos();
1217
0
    aMacCounters.mIfInErrors         = aMacCountersTlv.GetIfInErrors();
1218
0
    aMacCounters.mIfOutErrors        = aMacCountersTlv.GetIfOutErrors();
1219
0
    aMacCounters.mIfInUcastPkts      = aMacCountersTlv.GetIfInUcastPkts();
1220
0
    aMacCounters.mIfInBroadcastPkts  = aMacCountersTlv.GetIfInBroadcastPkts();
1221
0
    aMacCounters.mIfInDiscards       = aMacCountersTlv.GetIfInDiscards();
1222
0
    aMacCounters.mIfOutUcastPkts     = aMacCountersTlv.GetIfOutUcastPkts();
1223
0
    aMacCounters.mIfOutBroadcastPkts = aMacCountersTlv.GetIfOutBroadcastPkts();
1224
0
    aMacCounters.mIfOutDiscards      = aMacCountersTlv.GetIfOutDiscards();
1225
0
}
1226
1227
Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo)
1228
0
{
1229
0
    Error    error;
1230
0
    uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator;
1231
1232
0
    while (offset < aMessage.GetLength())
1233
0
    {
1234
0
        bool        skipTlv = false;
1235
0
        OffsetRange valueOffsetRange;
1236
0
        union
1237
0
        {
1238
0
            Tlv         tlv;
1239
0
            ExtendedTlv extTlv;
1240
0
        };
1241
1242
0
        SuccessOrExit(error = aMessage.Read(offset, tlv));
1243
1244
0
        if (tlv.IsExtended())
1245
0
        {
1246
0
            SuccessOrExit(error = aMessage.Read(offset, extTlv));
1247
0
            valueOffsetRange.Init(offset + sizeof(ExtendedTlv), extTlv.GetLength());
1248
0
        }
1249
0
        else
1250
0
        {
1251
0
            valueOffsetRange.Init(offset + sizeof(Tlv), tlv.GetLength());
1252
0
        }
1253
1254
0
        VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse);
1255
1256
0
        switch (tlv.GetType())
1257
0
        {
1258
0
        case Tlv::kExtMacAddress:
1259
0
            SuccessOrExit(error =
1260
0
                              Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mExtAddress)));
1261
0
            break;
1262
1263
0
        case Tlv::kAddress16:
1264
0
            SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aTlvInfo.mData.mAddr16));
1265
0
            break;
1266
1267
0
        case Tlv::kMode:
1268
0
        {
1269
0
            uint8_t mode;
1270
1271
0
            SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
1272
0
            Mle::DeviceMode(mode).Get(aTlvInfo.mData.mMode);
1273
0
            break;
1274
0
        }
1275
1276
0
        case Tlv::kTimeout:
1277
0
            SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aTlvInfo.mData.mTimeout));
1278
0
            break;
1279
1280
0
        case Tlv::kConnectivity:
1281
0
        {
1282
0
            ConnectivityTlv connectivityTlv;
1283
1284
0
            VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1285
0
            SuccessOrExit(error = aMessage.Read(offset, connectivityTlv));
1286
0
            VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
1287
0
            connectivityTlv.GetConnectivity(aTlvInfo.mData.mConnectivity);
1288
0
            break;
1289
0
        }
1290
1291
0
        case Tlv::kRoute:
1292
0
        {
1293
0
            RouteTlv routeTlv;
1294
0
            uint16_t bytesToRead = static_cast<uint16_t>(Min(tlv.GetSize(), static_cast<uint32_t>(sizeof(routeTlv))));
1295
1296
0
            VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1297
0
            SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead));
1298
0
            VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
1299
0
            ParseRoute(routeTlv, aTlvInfo.mData.mRoute);
1300
0
            break;
1301
0
        }
1302
1303
0
        case Tlv::kEnhancedRoute:
1304
0
            SuccessOrExit(error = ParseEnhancedRoute(aMessage, offset, aTlvInfo.mData.mEnhRoute));
1305
0
            break;
1306
1307
0
        case Tlv::kLeaderData:
1308
0
        {
1309
0
            LeaderDataTlv leaderDataTlv;
1310
1311
0
            VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
1312
0
            SuccessOrExit(error = aMessage.Read(offset, leaderDataTlv));
1313
0
            VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse);
1314
0
            leaderDataTlv.Get(AsCoreType(&aTlvInfo.mData.mLeaderData));
1315
0
            break;
1316
0
        }
1317
1318
0
        case Tlv::kNetworkData:
1319
0
            static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize,
1320
0
                          "NetworkData array in `otNetworkDiagTlv` is too small");
1321
1322
0
            VerifyOrExit(valueOffsetRange.GetLength() <= NetworkData::NetworkData::kMaxSize, error = kErrorParse);
1323
0
            aTlvInfo.mData.mNetworkData.mCount = static_cast<uint8_t>(valueOffsetRange.GetLength());
1324
0
            aMessage.ReadBytes(valueOffsetRange, aTlvInfo.mData.mNetworkData.m8);
1325
0
            break;
1326
1327
0
        case Tlv::kIp6AddressList:
1328
0
        {
1329
0
            uint16_t      addrListLength = GetArrayLength(aTlvInfo.mData.mIp6AddrList.mList);
1330
0
            Ip6::Address *addrEntry      = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]);
1331
0
            uint8_t      &addrCount      = aTlvInfo.mData.mIp6AddrList.mCount;
1332
1333
0
            VerifyOrExit((valueOffsetRange.GetLength() % Ip6::Address::kSize) == 0, error = kErrorParse);
1334
1335
            // `TlvInfo` has a fixed array for IPv6 addresses. If there
1336
            // are more addresses in the message, we read and return as
1337
            // many as can fit in array and ignore the rest.
1338
1339
0
            addrCount = 0;
1340
1341
0
            while (!valueOffsetRange.IsEmpty() && (addrCount < addrListLength))
1342
0
            {
1343
0
                SuccessOrExit(error = aMessage.Read(valueOffsetRange, *addrEntry));
1344
0
                addrCount++;
1345
0
                addrEntry++;
1346
0
                valueOffsetRange.AdvanceOffset(Ip6::Address::kSize);
1347
0
            }
1348
1349
0
            break;
1350
0
        }
1351
1352
0
        case Tlv::kMacCounters:
1353
0
        {
1354
0
            MacCountersTlv macCountersTlv;
1355
1356
0
            SuccessOrExit(error = aMessage.Read(offset, macCountersTlv));
1357
0
            VerifyOrExit(macCountersTlv.IsValid(), error = kErrorParse);
1358
0
            ParseMacCounters(macCountersTlv, aTlvInfo.mData.mMacCounters);
1359
0
            break;
1360
0
        }
1361
1362
0
        case Tlv::kMleCounters:
1363
0
        {
1364
0
            MleCountersTlv mleCoutersTlv;
1365
1366
0
            SuccessOrExit(error = aMessage.Read(offset, mleCoutersTlv));
1367
0
            VerifyOrExit(mleCoutersTlv.IsValid(), error = kErrorParse);
1368
0
            mleCoutersTlv.Read(aTlvInfo.mData.mMleCounters);
1369
0
            break;
1370
0
        }
1371
1372
0
        case Tlv::kBatteryLevel:
1373
0
            SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aTlvInfo.mData.mBatteryLevel));
1374
0
            break;
1375
1376
0
        case Tlv::kSupplyVoltage:
1377
0
            SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aTlvInfo.mData.mSupplyVoltage));
1378
0
            break;
1379
1380
0
        case Tlv::kChildTable:
1381
0
        {
1382
0
            uint16_t   childInfoLength = GetArrayLength(aTlvInfo.mData.mChildTable.mTable);
1383
0
            ChildInfo *childInfo       = &aTlvInfo.mData.mChildTable.mTable[0];
1384
0
            uint8_t   &childCount      = aTlvInfo.mData.mChildTable.mCount;
1385
1386
0
            VerifyOrExit((valueOffsetRange.GetLength() % sizeof(ChildTableEntry)) == 0, error = kErrorParse);
1387
1388
            // `TlvInfo` has a fixed array Child Table entries. If there
1389
            // are more entries in the message, we read and return as
1390
            // many as can fit in array and ignore the rest.
1391
1392
0
            childCount = 0;
1393
1394
0
            while (!valueOffsetRange.IsEmpty() && (childCount < childInfoLength))
1395
0
            {
1396
0
                ChildTableEntry entry;
1397
1398
0
                SuccessOrExit(error = aMessage.Read(valueOffsetRange, entry));
1399
1400
0
                childInfo->mTimeout     = entry.GetTimeout();
1401
0
                childInfo->mLinkQuality = entry.GetLinkQuality();
1402
0
                childInfo->mChildId     = entry.GetChildId();
1403
0
                entry.GetMode().Get(childInfo->mMode);
1404
1405
0
                childCount++;
1406
0
                childInfo++;
1407
0
                valueOffsetRange.AdvanceOffset(sizeof(ChildTableEntry));
1408
0
            }
1409
1410
0
            break;
1411
0
        }
1412
1413
0
        case Tlv::kChannelPages:
1414
0
            aTlvInfo.mData.mChannelPages.mCount = static_cast<uint8_t>(
1415
0
                Min(valueOffsetRange.GetLength(), GetArrayLength(aTlvInfo.mData.mChannelPages.m8)));
1416
0
            aMessage.ReadBytes(valueOffsetRange.GetOffset(), aTlvInfo.mData.mChannelPages.m8,
1417
0
                               aTlvInfo.mData.mChannelPages.mCount);
1418
0
            break;
1419
1420
0
        case Tlv::kMaxChildTimeout:
1421
0
            SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout));
1422
0
            break;
1423
1424
0
        case Tlv::kEui64:
1425
0
            SuccessOrExit(error = Tlv::Read<Eui64Tlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mEui64)));
1426
0
            break;
1427
1428
0
        case Tlv::kVersion:
1429
0
            SuccessOrExit(error = Tlv::Read<VersionTlv>(aMessage, offset, aTlvInfo.mData.mVersion));
1430
0
            break;
1431
1432
0
        case Tlv::kVendorName:
1433
0
            SuccessOrExit(error = Tlv::Read<VendorNameTlv>(aMessage, offset, aTlvInfo.mData.mVendorName));
1434
0
            break;
1435
1436
0
        case Tlv::kVendorModel:
1437
0
            SuccessOrExit(error = Tlv::Read<VendorModelTlv>(aMessage, offset, aTlvInfo.mData.mVendorModel));
1438
0
            break;
1439
1440
0
        case Tlv::kVendorSwVersion:
1441
0
            SuccessOrExit(error = Tlv::Read<VendorSwVersionTlv>(aMessage, offset, aTlvInfo.mData.mVendorSwVersion));
1442
0
            break;
1443
1444
0
        case Tlv::kVendorAppUrl:
1445
0
            SuccessOrExit(error = Tlv::Read<VendorAppUrlTlv>(aMessage, offset, aTlvInfo.mData.mVendorAppUrl));
1446
0
            break;
1447
1448
0
        case Tlv::kThreadStackVersion:
1449
0
            SuccessOrExit(error =
1450
0
                              Tlv::Read<ThreadStackVersionTlv>(aMessage, offset, aTlvInfo.mData.mThreadStackVersion));
1451
0
            break;
1452
1453
0
        case Tlv::kNonPreferredChannels:
1454
0
            SuccessOrExit(error = MeshCoP::ChannelMaskTlv::ParseValue(aMessage, valueOffsetRange,
1455
0
                                                                      aTlvInfo.mData.mNonPreferredChannels));
1456
0
            break;
1457
1458
0
        default:
1459
            // Skip unrecognized TLVs.
1460
0
            skipTlv = true;
1461
0
            break;
1462
0
        }
1463
1464
0
        offset += tlv.GetSize();
1465
1466
0
        if (!skipTlv)
1467
0
        {
1468
            // Exit if a TLV is recognized and parsed successfully.
1469
0
            aTlvInfo.mType = tlv.GetType();
1470
0
            aIterator      = offset;
1471
0
            error          = kErrorNone;
1472
0
            ExitNow();
1473
0
        }
1474
0
    }
1475
1476
0
    error = kErrorNotFound;
1477
1478
0
exit:
1479
0
    return error;
1480
0
}
1481
1482
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1483
1484
const char *Client::UriToString(Uri aUri)
1485
{
1486
    const char *str = "";
1487
1488
    switch (aUri)
1489
    {
1490
    case kUriDiagnosticGetQuery:
1491
        str = ot::UriToString<kUriDiagnosticGetQuery>();
1492
        break;
1493
    case kUriDiagnosticGetRequest:
1494
        str = ot::UriToString<kUriDiagnosticGetRequest>();
1495
        break;
1496
    case kUriDiagnosticReset:
1497
        str = ot::UriToString<kUriDiagnosticReset>();
1498
        break;
1499
    default:
1500
        break;
1501
    }
1502
1503
    return str;
1504
}
1505
1506
#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1507
1508
#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
1509
1510
} // namespace NetworkDiagnostic
1511
1512
} // namespace ot