Coverage Report

Created: 2025-05-12 06:47

/src/openthread/src/core/thread/mle_ftd.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (c) 2016, The OpenThread Authors.  All rights reserved.
3
 *
4
 *  Redistribution and use in source and binary forms, with or without
5
 *  modification, are permitted provided that the following conditions are met:
6
 *  1. Redistributions of source code must retain the above copyright
7
 *     notice, this list of conditions and the following disclaimer.
8
 *  2. Redistributions in binary form must reproduce the above copyright
9
 *     notice, this list of conditions and the following disclaimer in the
10
 *     documentation and/or other materials provided with the distribution.
11
 *  3. Neither the name of the copyright holder nor the
12
 *     names of its contributors may be used to endorse or promote products
13
 *     derived from this software without specific prior written permission.
14
 *
15
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
 *  POSSIBILITY OF SUCH DAMAGE.
26
 */
27
28
/**
29
 * @file
30
 *   This file implements MLE functionality required for the Thread Router and Leader roles.
31
 */
32
33
#include "mle.hpp"
34
35
#if OPENTHREAD_FTD
36
37
#include "instance/instance.hpp"
38
39
namespace ot {
40
namespace Mle {
41
42
RegisterLogModule("Mle");
43
44
void Mle::SetAlternateRloc16(uint16_t aRloc16)
45
0
{
46
0
    VerifyOrExit(aRloc16 != Mac::kShortAddrInvalid);
47
48
0
    LogInfo("Setting alternate RLOC16 0x%04x", aRloc16);
49
50
0
    Get<Mac::Mac>().SetAlternateShortAddress(aRloc16);
51
0
    mAlternateRloc16Timeout = kAlternateRloc16Timeout;
52
53
0
exit:
54
0
    return;
55
0
}
56
57
void Mle::ClearAlternateRloc16(void)
58
198k
{
59
198k
    VerifyOrExit(Get<Mac::Mac>().GetAlternateShortAddress() != Mac::kShortAddrInvalid);
60
61
272
    LogInfo("Clearing alternate RLOC16");
62
272
    Get<Mac::Mac>().SetAlternateShortAddress(Mac::kShortAddrInvalid);
63
64
198k
exit:
65
198k
    mAlternateRloc16Timeout = 0;
66
198k
}
67
68
void Mle::HandlePartitionChange(void)
69
0
{
70
0
    mPreviousPartitionId               = mLeaderData.GetPartitionId();
71
0
    mPreviousPartitionRouterIdSequence = mRouterTable.GetRouterIdSequence();
72
0
    mPreviousPartitionIdTimeout        = GetNetworkIdTimeout();
73
74
0
    Get<AddressResolver>().Clear();
75
0
    IgnoreError(Get<Tmf::Agent>().AbortTransaction(&Mle::HandleAddressSolicitResponse, this));
76
0
    mRouterTable.Clear();
77
0
}
78
79
bool Mle::IsRouterEligible(void) const
80
4.76k
{
81
4.76k
    bool                  rval      = false;
82
4.76k
    const SecurityPolicy &secPolicy = Get<KeyManager>().GetSecurityPolicy();
83
84
4.76k
    VerifyOrExit(mRouterEligible && IsFullThreadDevice());
85
86
#if OPENTHREAD_CONFIG_THREAD_VERSION == OT_THREAD_VERSION_1_1
87
    VerifyOrExit(secPolicy.mRoutersEnabled);
88
#else
89
3.72k
    if (secPolicy.mCommercialCommissioningEnabled)
90
936
    {
91
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
92
        VerifyOrExit(mCcmEnabled || secPolicy.mNonCcmRoutersEnabled);
93
#else
94
936
        VerifyOrExit(secPolicy.mNonCcmRoutersEnabled);
95
936
#endif
96
936
    }
97
3.33k
    if (!secPolicy.mRoutersEnabled)
98
920
    {
99
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
100
        VerifyOrExit(!mThreadVersionCheckEnabled ||
101
                     secPolicy.mVersionThresholdForRouting + SecurityPolicy::kVersionThresholdOffsetVersion <=
102
                         kThreadVersion);
103
#else
104
920
        VerifyOrExit(secPolicy.mVersionThresholdForRouting + SecurityPolicy::kVersionThresholdOffsetVersion <=
105
920
                     kThreadVersion);
106
920
#endif
107
920
    }
108
2.80k
#endif
109
110
2.80k
    rval = true;
111
112
4.76k
exit:
113
4.76k
    return rval;
114
2.80k
}
115
116
Error Mle::SetRouterEligible(bool aEligible)
117
988
{
118
988
    Error error = kErrorNone;
119
120
988
    if (!IsFullThreadDevice())
121
428
    {
122
428
        VerifyOrExit(!aEligible, error = kErrorNotCapable);
123
428
    }
124
125
756
    VerifyOrExit(aEligible != mRouterEligible);
126
127
219
    mRouterEligible = aEligible;
128
129
219
    switch (mRole)
130
219
    {
131
35
    case kRoleDisabled:
132
219
    case kRoleDetached:
133
219
        break;
134
135
0
    case kRoleChild:
136
0
        if (mRouterEligible)
137
0
        {
138
0
            mRouterRoleTransition.StartTimeout();
139
0
        }
140
141
0
        Get<Mac::Mac>().SetBeaconEnabled(mRouterEligible);
142
0
        break;
143
144
0
    case kRoleRouter:
145
0
    case kRoleLeader:
146
0
        if (!mRouterEligible)
147
0
        {
148
0
            IgnoreError(BecomeDetached());
149
0
        }
150
151
0
        break;
152
219
    }
153
154
988
exit:
155
988
    return error;
156
219
}
157
158
void Mle::HandleSecurityPolicyChanged(void)
159
0
{
160
    // If we are currently router or leader and no longer eligible to
161
    // be a router (due to security policy change), we start jitter
162
    // timeout to downgrade.
163
164
0
    VerifyOrExit(IsRouterOrLeader() && !IsRouterEligible());
165
166
0
    VerifyOrExit(!mRouterRoleTransition.IsPending());
167
168
0
    mRouterRoleTransition.StartTimeout();
169
170
0
    if (IsLeader())
171
0
    {
172
0
        mRouterRoleTransition.IncreaseTimeout(kLeaderDowngradeExtraDelay);
173
0
    }
174
175
0
exit:
176
0
    return;
177
0
}
178
179
#if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
180
void Mle::SetDeviceProperties(const DeviceProperties &aDeviceProperties)
181
{
182
    mDeviceProperties = aDeviceProperties;
183
    mDeviceProperties.ClampWeightAdjustment();
184
    SetLeaderWeight(mDeviceProperties.CalculateLeaderWeight());
185
}
186
#endif
187
188
Error Mle::BecomeRouter(ThreadStatusTlv::Status aStatus)
189
3.37k
{
190
3.37k
    Error error = kErrorNone;
191
192
3.37k
    VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
193
3.16k
    VerifyOrExit(!IsRouterOrLeader(), error = kErrorNone);
194
3.16k
    VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable);
195
196
2.24k
    LogInfo("Attempt to become router");
197
198
2.24k
    Get<MeshForwarder>().SetRxOnWhenIdle(true);
199
2.24k
    mRouterRoleTransition.StopTimeout();
200
201
2.24k
    switch (mRole)
202
2.24k
    {
203
2.24k
    case kRoleDetached:
204
2.24k
        mRouterRoleRestorer.Start(mLastSavedRole);
205
2.24k
        break;
206
207
0
    case kRoleChild:
208
0
        SuccessOrExit(error = SendAddressSolicit(aStatus));
209
0
        break;
210
211
0
    default:
212
0
        OT_ASSERT(false);
213
2.24k
    }
214
215
3.37k
exit:
216
3.37k
    return error;
217
2.24k
}
218
219
Error Mle::BecomeLeader(bool aCheckWeight)
220
66.1k
{
221
66.1k
    Error    error = kErrorNone;
222
66.1k
    Router  *router;
223
66.1k
    uint32_t partitionId;
224
66.1k
    uint8_t  leaderId;
225
226
#if OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
227
    VerifyOrExit(!Get<MeshCoP::ActiveDatasetManager>().IsPartiallyComplete(), error = kErrorInvalidState);
228
#else
229
66.1k
    VerifyOrExit(Get<MeshCoP::ActiveDatasetManager>().IsComplete(), error = kErrorInvalidState);
230
459
#endif
231
459
    VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
232
0
    VerifyOrExit(!IsLeader(), error = kErrorNone);
233
0
    VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable);
234
235
0
    if (aCheckWeight && IsAttached())
236
0
    {
237
0
        VerifyOrExit(mLeaderWeight > mLeaderData.GetWeighting(), error = kErrorNotCapable);
238
0
    }
239
240
0
    mRouterTable.Clear();
241
242
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
243
    {
244
        uint8_t minId;
245
        uint8_t maxId;
246
247
        mRouterTable.GetRouterIdRange(minId, maxId);
248
        partitionId = mPreferredLeaderPartitionId ? mPreferredLeaderPartitionId : Random::NonCrypto::GetUint32();
249
        leaderId    = (IsRouterIdValid(mPreviousRouterId) && minId <= mPreviousRouterId && mPreviousRouterId <= maxId)
250
                          ? mPreviousRouterId
251
                          : Random::NonCrypto::GetUint8InRange(minId, maxId + 1);
252
    }
253
#else
254
0
    partitionId = Random::NonCrypto::GetUint32();
255
0
    leaderId    = IsRouterIdValid(mPreviousRouterId) ? mPreviousRouterId
256
0
                                                     : Random::NonCrypto::GetUint8InRange(0, kMaxRouterId + 1);
257
0
#endif
258
259
0
    SetLeaderData(partitionId, mLeaderWeight, leaderId);
260
261
0
    router = mRouterTable.Allocate(leaderId);
262
0
    OT_ASSERT(router != nullptr);
263
264
0
    SetRouterId(leaderId);
265
0
    router->SetExtAddress(Get<Mac::Mac>().GetExtAddress());
266
267
0
    Get<NetworkData::Leader>().Reset();
268
0
    Get<MeshCoP::Leader>().SetEmptyCommissionerData();
269
270
0
    SetStateLeader(Rloc16FromRouterId(leaderId), kStartingAsLeader);
271
272
66.1k
exit:
273
66.1k
    return error;
274
0
}
275
276
void Mle::StopLeader(void)
277
112k
{
278
112k
    StopAdvertiseTrickleTimer();
279
112k
    Get<ThreadNetif>().UnsubscribeAllRoutersMulticast();
280
112k
}
281
282
void Mle::HandleDetachStart(void)
283
112k
{
284
112k
    mRouterTable.ClearNeighbors();
285
112k
    StopLeader();
286
112k
    Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMle);
287
112k
}
288
289
void Mle::HandleChildStart(AttachMode aMode)
290
0
{
291
0
    mAddressSolicitRejected = false;
292
293
0
    mRouterRoleTransition.StartTimeout();
294
295
0
    StopLeader();
296
0
    Get<TimeTicker>().RegisterReceiver(TimeTicker::kMle);
297
298
0
    if (mRouterEligible)
299
0
    {
300
0
        Get<Mac::Mac>().SetBeaconEnabled(true);
301
0
    }
302
303
0
    Get<ThreadNetif>().SubscribeAllRoutersMulticast();
304
305
0
    VerifyOrExit(IsRouterIdValid(mPreviousRouterId));
306
307
0
    switch (aMode)
308
0
    {
309
0
    case kDowngradeToReed:
310
0
        SendAddressRelease();
311
312
0
        if (HasChildren())
313
0
        {
314
0
            RemoveChildren();
315
0
        }
316
317
0
        SetRouterId(kInvalidRouterId);
318
0
        break;
319
320
0
    case kSamePartition:
321
0
        if (HasChildren())
322
0
        {
323
0
            IgnoreError(BecomeRouter(ThreadStatusTlv::kHaveChildIdRequest));
324
0
        }
325
326
0
        break;
327
328
0
    case kAnyPartition:
329
0
    case kBetterParent:
330
0
    case kSelectedParent:
331
        // If attach was initiated due to receiving an MLE Announce
332
        // message, all rx-on-when-idle devices will immediately
333
        // attempt to attach as well. This aligns with the Thread 1.1
334
        // specification (Section 4.8.1):
335
        //
336
        // "If the received value is newer and the channel and/or PAN
337
        //  ID in the Announce message differ from those currently in
338
        //  use, the receiving device attempts to attach using the
339
        //  channel and PAN ID received from the Announce message."
340
        //
341
        // Since parent-child relationships are unlikely to persist in
342
        // the new partition, we remove all children here. The
343
        // decision to become router is determined based on the new
344
        // partition's status.
345
346
0
        if (IsAnnounceAttach() && HasChildren())
347
0
        {
348
0
            RemoveChildren();
349
0
        }
350
351
0
        OT_FALL_THROUGH;
352
353
0
    case kBetterPartition:
354
0
        if (HasChildren() && mPreviousPartitionIdRouter != mLeaderData.GetPartitionId())
355
0
        {
356
0
            IgnoreError(BecomeRouter(ThreadStatusTlv::kParentPartitionChange));
357
0
        }
358
359
0
        break;
360
0
    }
361
362
0
exit:
363
364
0
    if (mRouterTable.GetActiveRouterCount() >= mRouterUpgradeThreshold &&
365
0
        (!IsRouterIdValid(mPreviousRouterId) || !HasChildren()))
366
0
    {
367
0
        SetRouterId(kInvalidRouterId);
368
0
    }
369
0
}
370
371
void Mle::SetStateRouter(uint16_t aRloc16)
372
0
{
373
    // The `aStartMode` is ignored when used with `kRoleRouter`
374
0
    SetStateRouterOrLeader(kRoleRouter, aRloc16, /* aStartMode */ kStartingAsLeader);
375
0
}
376
377
void Mle::SetStateLeader(uint16_t aRloc16, LeaderStartMode aStartMode)
378
0
{
379
0
    SetStateRouterOrLeader(kRoleLeader, aRloc16, aStartMode);
380
0
}
381
382
void Mle::SetStateRouterOrLeader(DeviceRole aRole, uint16_t aRloc16, LeaderStartMode aStartMode)
383
0
{
384
0
    if (aRole == kRoleLeader)
385
0
    {
386
0
        IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
387
0
        IgnoreError(Get<MeshCoP::PendingDatasetManager>().Restore());
388
0
    }
389
390
0
    SetRloc16(aRloc16);
391
392
0
    SetRole(aRole);
393
394
0
    SetAttachState(kAttachStateIdle);
395
0
    mAttachCounter = 0;
396
0
    mAttachTimer.Stop();
397
0
    mMessageTransmissionTimer.Stop();
398
0
    StopAdvertiseTrickleTimer();
399
0
    ResetAdvertiseInterval();
400
401
0
    Get<ThreadNetif>().SubscribeAllRoutersMulticast();
402
0
    mPreviousPartitionIdRouter = mLeaderData.GetPartitionId();
403
0
    Get<Mac::Mac>().SetBeaconEnabled(true);
404
0
    Get<TimeTicker>().RegisterReceiver(TimeTicker::kMle);
405
406
0
    if (aRole == kRoleLeader)
407
0
    {
408
0
        GetLeaderAloc(mLeaderAloc.GetAddress());
409
0
        Get<ThreadNetif>().AddUnicastAddress(mLeaderAloc);
410
0
        Get<NetworkData::Leader>().Start(aStartMode);
411
0
        Get<MeshCoP::ActiveDatasetManager>().StartLeader();
412
0
        Get<MeshCoP::PendingDatasetManager>().StartLeader();
413
0
        Get<AddressResolver>().Clear();
414
0
    }
415
416
    // Remove children that do not have a matching RLOC16
417
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
418
0
    {
419
0
        if (RouterIdFromRloc16(child.GetRloc16()) != mRouterId)
420
0
        {
421
0
            RemoveNeighbor(child);
422
0
        }
423
0
    }
424
425
0
    LogNote("Partition ID 0x%lx", ToUlong(mLeaderData.GetPartitionId()));
426
0
}
427
428
0
void Mle::HandleAdvertiseTrickleTimer(TrickleTimer &aTimer) { aTimer.Get<Mle>().HandleAdvertiseTrickleTimer(); }
429
430
void Mle::HandleAdvertiseTrickleTimer(void)
431
0
{
432
0
    VerifyOrExit(IsRouterEligible(), mAdvertiseTrickleTimer.Stop());
433
434
0
    SendMulticastAdvertisement();
435
436
0
exit:
437
0
    return;
438
0
}
439
440
184k
void Mle::StopAdvertiseTrickleTimer(void) { mAdvertiseTrickleTimer.Stop(); }
441
442
uint32_t Mle::DetermineAdvertiseIntervalMax(void) const
443
0
{
444
0
    uint32_t interval;
445
446
#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
447
    interval = kAdvIntervalMaxLogRoutes;
448
#else
449
    // Determine the interval based on the number of router neighbors
450
    // with link quality 2 or higher.
451
452
0
    interval = (Get<RouterTable>().GetNeighborCount(kLinkQuality2) + 1) * kAdvIntervalNeighborMultiplier;
453
0
    interval = Clamp(interval, kAdvIntervalMaxLowerBound, kAdvIntervalMaxUpperBound);
454
0
#endif
455
456
0
    return interval;
457
0
}
458
459
void Mle::UpdateAdvertiseInterval(void)
460
21.3k
{
461
21.3k
    if (IsRouterOrLeader() && mAdvertiseTrickleTimer.IsRunning())
462
0
    {
463
0
        mAdvertiseTrickleTimer.SetIntervalMax(DetermineAdvertiseIntervalMax());
464
0
    }
465
21.3k
}
466
467
void Mle::ResetAdvertiseInterval(void)
468
0
{
469
0
    VerifyOrExit(IsRouterOrLeader());
470
471
0
    if (!mAdvertiseTrickleTimer.IsRunning())
472
0
    {
473
0
        mAdvertiseTrickleTimer.Start(TrickleTimer::kModeTrickle, kAdvIntervalMin, DetermineAdvertiseIntervalMax());
474
0
    }
475
476
0
    mAdvertiseTrickleTimer.IndicateInconsistent();
477
478
0
exit:
479
0
    return;
480
0
}
481
482
void Mle::SendMulticastAdvertisement(void)
483
0
{
484
0
    Ip6::Address destination;
485
486
0
    destination.SetToLinkLocalAllNodesMulticast();
487
0
    SendAdvertisement(destination);
488
0
}
489
490
void Mle::ScheduleUnicastAdvertisementTo(const Router &aRouter)
491
0
{
492
0
    Ip6::Address destination;
493
494
0
    destination.SetToLinkLocalAddress(aRouter.GetExtAddress());
495
0
    mDelayedSender.ScheduleAdvertisement(destination,
496
0
                                         Random::NonCrypto::GetUint32InRange(0, kMaxUnicastAdvertisementDelay));
497
0
}
498
499
void Mle::SendAdvertisement(const Ip6::Address &aDestination)
500
0
{
501
0
    Error      error   = kErrorNone;
502
0
    TxMessage *message = nullptr;
503
504
    // Suppress MLE Advertisements when trying to attach to a better
505
    // partition. Without this, a candidate parent might incorrectly
506
    // interpret this advertisement (Source Address TLV containing an
507
    // RLOC16 indicating device is acting as router) and reject the
508
    // attaching device.
509
510
0
    VerifyOrExit(!IsAttaching());
511
512
    // Suppress MLE Advertisements when attempting to transition to
513
    // router role. Advertisements as a REED while attaching to a new
514
    // partition can cause existing children to detach
515
    // unnecessarily.
516
517
0
    VerifyOrExit(!mAddressSolicitPending);
518
519
0
    VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs);
520
0
    SuccessOrExit(error = message->AppendSourceAddressTlv());
521
0
    SuccessOrExit(error = message->AppendLeaderDataTlv());
522
523
0
    switch (mRole)
524
0
    {
525
0
    case kRoleChild:
526
0
        break;
527
528
0
    case kRoleRouter:
529
0
    case kRoleLeader:
530
0
        SuccessOrExit(error = message->AppendRouteTlv());
531
0
        break;
532
533
0
    case kRoleDisabled:
534
0
    case kRoleDetached:
535
0
        OT_ASSERT(false);
536
0
    }
537
538
0
    SuccessOrExit(error = message->SendTo(aDestination));
539
540
0
    Log(kMessageSend, kTypeAdvertisement, aDestination);
541
542
0
exit:
543
0
    FreeMessageOnError(message, error);
544
0
    LogSendError(kTypeAdvertisement, error);
545
0
}
546
547
void Mle::SendLinkRequest(Router *aRouter)
548
0
{
549
0
    static const uint8_t kDetachedTlvs[]      = {Tlv::kAddress16, Tlv::kRoute};
550
0
    static const uint8_t kRouterTlvs[]        = {Tlv::kLinkMargin};
551
0
    static const uint8_t kValidNeighborTlvs[] = {Tlv::kLinkMargin, Tlv::kRoute};
552
553
0
    Error        error   = kErrorNone;
554
0
    TxMessage   *message = nullptr;
555
0
    Ip6::Address destination;
556
557
0
    destination.Clear();
558
559
0
    VerifyOrExit((message = NewMleMessage(kCommandLinkRequest)) != nullptr, error = kErrorNoBufs);
560
0
    SuccessOrExit(error = message->AppendVersionTlv());
561
562
0
    switch (mRole)
563
0
    {
564
0
    case kRoleDetached:
565
0
        SuccessOrExit(error = message->AppendTlvRequestTlv(kDetachedTlvs));
566
0
        break;
567
568
0
    case kRoleChild:
569
0
        SuccessOrExit(error = message->AppendSourceAddressTlv());
570
0
        SuccessOrExit(error = message->AppendLeaderDataTlv());
571
0
        break;
572
573
0
    case kRoleRouter:
574
0
    case kRoleLeader:
575
0
        if (aRouter == nullptr || !aRouter->IsStateValid())
576
0
        {
577
0
            SuccessOrExit(error = message->AppendTlvRequestTlv(kRouterTlvs));
578
0
        }
579
0
        else
580
0
        {
581
0
            SuccessOrExit(error = message->AppendTlvRequestTlv(kValidNeighborTlvs));
582
0
        }
583
584
0
        SuccessOrExit(error = message->AppendSourceAddressTlv());
585
0
        SuccessOrExit(error = message->AppendLeaderDataTlv());
586
0
        break;
587
588
0
    case kRoleDisabled:
589
0
        OT_ASSERT(false);
590
0
    }
591
592
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
593
    SuccessOrExit(error = message->AppendTimeRequestTlv());
594
#endif
595
596
0
    if (aRouter == nullptr)
597
0
    {
598
0
        mRouterRoleRestorer.GenerateRandomChallenge();
599
0
        SuccessOrExit(error = message->AppendChallengeTlv(mRouterRoleRestorer.GetChallenge()));
600
0
        destination.SetToLinkLocalAllRoutersMulticast();
601
0
    }
602
0
    else
603
0
    {
604
0
        if (!aRouter->IsStateValid())
605
0
        {
606
0
            aRouter->GenerateChallenge();
607
0
            SuccessOrExit(error = message->AppendChallengeTlv(aRouter->GetChallenge()));
608
0
        }
609
0
        else
610
0
        {
611
0
            TxChallenge challenge;
612
613
0
            challenge.GenerateRandom();
614
0
            SuccessOrExit(error = message->AppendChallengeTlv(challenge));
615
0
        }
616
617
0
        destination.SetToLinkLocalAddress(aRouter->GetExtAddress());
618
0
        aRouter->RestartLinkAcceptTimeout();
619
0
    }
620
621
0
    SuccessOrExit(error = message->SendTo(destination));
622
623
0
    Log(kMessageSend, kTypeLinkRequest, destination);
624
625
0
exit:
626
0
    FreeMessageOnError(message, error);
627
0
}
628
629
void Mle::HandleLinkRequest(RxInfo &aRxInfo)
630
0
{
631
0
    Error          error    = kErrorNone;
632
0
    Neighbor      *neighbor = nullptr;
633
0
    uint16_t       version;
634
0
    LeaderData     leaderData;
635
0
    uint16_t       sourceAddress;
636
0
    LinkAcceptInfo info;
637
638
0
    Log(kMessageReceive, kTypeLinkRequest, aRxInfo.mMessageInfo.GetPeerAddr());
639
640
0
    VerifyOrExit(IsRouterOrLeader(), error = kErrorInvalidState);
641
642
0
    VerifyOrExit(!IsAttaching(), error = kErrorInvalidState);
643
644
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(info.mRxChallenge));
645
646
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version));
647
648
0
    switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
649
0
    {
650
0
    case kErrorNone:
651
0
        VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId(), error = kErrorInvalidState);
652
0
        break;
653
0
    case kErrorNotFound:
654
0
        break;
655
0
    default:
656
0
        ExitNow(error = kErrorParse);
657
0
    }
658
659
0
    info.mExtAddress.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid());
660
661
0
    info.mLinkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
662
663
0
    switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
664
0
    {
665
0
    case kErrorNone:
666
0
        if (IsRouterRloc16(sourceAddress))
667
0
        {
668
0
            Router *router = mRouterTable.FindRouterByRloc16(sourceAddress);
669
670
0
            VerifyOrExit(router != nullptr, error = kErrorParse);
671
672
0
            if (!router->IsStateValid())
673
0
            {
674
0
                InitNeighbor(*router, aRxInfo);
675
0
                router->SetState(Neighbor::kStateLinkRequest);
676
0
                router->ClearLinkAcceptTimeout();
677
0
            }
678
0
            else
679
0
            {
680
0
                VerifyOrExit(router->GetExtAddress() == info.mExtAddress);
681
0
            }
682
683
0
            neighbor = router;
684
0
        }
685
686
0
        break;
687
688
0
    case kErrorNotFound:
689
        // A missing source address indicates that the router was
690
        // recently reset.
691
0
        VerifyOrExit(aRxInfo.IsNeighborStateValid() && IsRouterRloc16(aRxInfo.mNeighbor->GetRloc16()),
692
0
                     error = kErrorDrop);
693
0
        neighbor = aRxInfo.mNeighbor;
694
0
        break;
695
696
0
    default:
697
0
        ExitNow(error = kErrorParse);
698
0
    }
699
700
0
    switch (aRxInfo.mMessage.ReadTlvRequestTlv(info.mRequestedTlvList))
701
0
    {
702
0
    case kErrorNone:
703
0
    case kErrorNotFound:
704
0
        break;
705
0
    default:
706
0
        ExitNow(error = kErrorParse);
707
0
    }
708
709
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
710
    if (neighbor != nullptr)
711
    {
712
        neighbor->SetTimeSyncEnabled(Tlv::Find<TimeRequestTlv>(aRxInfo.mMessage, nullptr, 0) == kErrorNone);
713
    }
714
#endif
715
716
#if OPENTHREAD_CONFIG_MULTI_RADIO
717
    if (neighbor != nullptr)
718
    {
719
        neighbor->ClearLastRxFragmentTag();
720
    }
721
#endif
722
723
0
    aRxInfo.mClass = RxInfo::kPeerMessage;
724
0
    ProcessKeySequence(aRxInfo);
725
726
0
    if (aRxInfo.mMessageInfo.GetSockAddr().IsMulticast())
727
0
    {
728
0
        mDelayedSender.ScheduleLinkAccept(info, 1 + Random::NonCrypto::GetUint16InRange(0, kMaxLinkAcceptDelay));
729
0
    }
730
0
    else
731
0
    {
732
0
        error = SendLinkAccept(info);
733
0
    }
734
735
0
exit:
736
0
    LogProcessError(kTypeLinkRequest, error);
737
0
    OT_UNUSED_VARIABLE(neighbor);
738
0
}
739
740
Error Mle::SendLinkAccept(const LinkAcceptInfo &aInfo)
741
0
{
742
0
    static const uint8_t kRouterTlvs[] = {Tlv::kLinkMargin};
743
744
0
    Error        error   = kErrorNone;
745
0
    TxMessage   *message = nullptr;
746
0
    Command      command = kCommandLinkAccept;
747
0
    Router      *router;
748
0
    Ip6::Address destination;
749
750
0
    VerifyOrExit(IsAttached());
751
752
0
    router = mRouterTable.FindRouter(aInfo.mExtAddress);
753
754
0
    if (router != nullptr)
755
0
    {
756
0
        if (router->IsStateLinkRequest())
757
0
        {
758
0
            command = kCommandLinkAcceptAndRequest;
759
0
            router->SetLastHeard(TimerMilli::GetNow());
760
0
        }
761
0
        else
762
0
        {
763
0
            VerifyOrExit(router->IsStateValid());
764
0
        }
765
0
    }
766
767
0
    VerifyOrExit((message = NewMleMessage(command)) != nullptr, error = kErrorNoBufs);
768
0
    SuccessOrExit(error = message->AppendVersionTlv());
769
0
    SuccessOrExit(error = message->AppendSourceAddressTlv());
770
0
    SuccessOrExit(error = message->AppendResponseTlv(aInfo.mRxChallenge));
771
0
    SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs());
772
773
0
    SuccessOrExit(error = message->AppendLinkMarginTlv(aInfo.mLinkMargin));
774
775
0
    if (router != nullptr)
776
0
    {
777
0
        SuccessOrExit(error = message->AppendLeaderDataTlv());
778
0
    }
779
780
0
    for (uint8_t tlvType : aInfo.mRequestedTlvList)
781
0
    {
782
0
        switch (tlvType)
783
0
        {
784
0
        case Tlv::kRoute:
785
0
            SuccessOrExit(error = message->AppendRouteTlv(router));
786
0
            break;
787
788
0
        case Tlv::kAddress16:
789
0
            VerifyOrExit(router != nullptr, error = kErrorDrop);
790
0
            SuccessOrExit(error = message->AppendAddress16Tlv(router->GetRloc16()));
791
0
            break;
792
793
0
        case Tlv::kLinkMargin:
794
0
            break;
795
796
0
        default:
797
0
            ExitNow(error = kErrorDrop);
798
0
        }
799
0
    }
800
801
0
    if (command == kCommandLinkAcceptAndRequest)
802
0
    {
803
0
        router->GenerateChallenge();
804
805
0
        SuccessOrExit(error = message->AppendChallengeTlv(router->GetChallenge()));
806
0
        SuccessOrExit(error = message->AppendTlvRequestTlv(kRouterTlvs));
807
0
    }
808
809
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
810
    if (router != nullptr && router->IsTimeSyncEnabled())
811
    {
812
        message->SetTimeSync(true);
813
    }
814
#endif
815
816
0
    destination.SetToLinkLocalAddress(aInfo.mExtAddress);
817
818
0
    SuccessOrExit(error = message->SendTo(destination));
819
820
0
    Log(kMessageSend, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest, destination);
821
822
0
exit:
823
0
    FreeMessageOnError(message, error);
824
0
    return error;
825
0
}
826
827
0
void Mle::HandleLinkAccept(RxInfo &aRxInfo) { HandleLinkAcceptVariant(aRxInfo, kTypeLinkAccept); }
828
829
0
void Mle::HandleLinkAcceptAndRequest(RxInfo &aRxInfo) { HandleLinkAcceptVariant(aRxInfo, kTypeLinkAcceptAndRequest); }
830
831
void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType)
832
0
{
833
    // Handles "Link Accept" or "Link Accept And Request".
834
835
0
    Error           error = kErrorNone;
836
0
    Router         *router;
837
0
    Neighbor::State neighborState;
838
0
    uint16_t        version;
839
0
    RxChallenge     response;
840
0
    uint16_t        sourceAddress;
841
0
    uint32_t        linkFrameCounter;
842
0
    uint32_t        mleFrameCounter;
843
0
    uint8_t         routerId;
844
0
    uint16_t        address16;
845
0
    RouteTlv        routeTlv;
846
0
    LeaderData      leaderData;
847
0
    uint8_t         linkMargin;
848
0
    bool            shouldUpdateRoutes = false;
849
850
0
    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
851
852
0
    Log(kMessageReceive, aMessageType, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
853
854
0
    VerifyOrExit(IsRouterRloc16(sourceAddress), error = kErrorParse);
855
856
0
    routerId      = RouterIdFromRloc16(sourceAddress);
857
0
    router        = mRouterTable.FindRouterById(routerId);
858
0
    neighborState = (router != nullptr) ? router->GetState() : Neighbor::kStateInvalid;
859
860
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
861
862
0
    switch (neighborState)
863
0
    {
864
0
    case Neighbor::kStateLinkRequest:
865
0
        VerifyOrExit(response == router->GetChallenge(), error = kErrorSecurity);
866
0
        break;
867
868
0
    case Neighbor::kStateInvalid:
869
0
        VerifyOrExit(mRouterRoleRestorer.IsActive() && (response == mRouterRoleRestorer.GetChallenge()),
870
0
                     error = kErrorSecurity);
871
872
0
        OT_FALL_THROUGH;
873
874
0
    case Neighbor::kStateValid:
875
0
        break;
876
877
0
    default:
878
0
        ExitNow(error = kErrorSecurity);
879
0
    }
880
881
    // Remove stale neighbors
882
0
    if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != sourceAddress)
883
0
    {
884
0
        RemoveNeighbor(*aRxInfo.mNeighbor);
885
0
    }
886
887
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version));
888
889
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
890
891
0
    switch (Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMargin))
892
0
    {
893
0
    case kErrorNone:
894
0
        break;
895
0
    case kErrorNotFound:
896
        // The Link Margin TLV may be omitted after a reset. We wait
897
        // for MLE Advertisements to establish the routing cost to
898
        // the neighbor
899
0
        VerifyOrExit(IsDetached(), error = kErrorNotFound);
900
0
        linkMargin = 0;
901
0
        break;
902
0
    default:
903
0
        ExitNow(error = kErrorParse);
904
0
    }
905
906
0
    switch (mRole)
907
0
    {
908
0
    case kRoleDetached:
909
0
        SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, address16));
910
0
        VerifyOrExit(GetRloc16() == address16, error = kErrorDrop);
911
912
0
        SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
913
0
        SetLeaderData(leaderData);
914
915
0
        mRouterTable.Clear();
916
0
        SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlv));
917
0
        SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
918
0
        router = mRouterTable.FindRouterById(routerId);
919
0
        VerifyOrExit(router != nullptr);
920
921
0
        if (GetLeaderRloc16() == GetRloc16())
922
0
        {
923
0
            SetStateLeader(GetRloc16(), kRestoringLeaderRoleAfterReset);
924
0
        }
925
0
        else
926
0
        {
927
0
            SetStateRouter(GetRloc16());
928
0
        }
929
930
0
        mRouterRoleRestorer.Stop();
931
0
        mRetrieveNewNetworkData = true;
932
0
        IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
933
0
        shouldUpdateRoutes = true;
934
935
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
936
        Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
937
#endif
938
0
        break;
939
940
0
    case kRoleChild:
941
0
        VerifyOrExit(router != nullptr);
942
0
        break;
943
944
0
    case kRoleRouter:
945
0
    case kRoleLeader:
946
0
        VerifyOrExit(router != nullptr);
947
948
0
        SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
949
0
        VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId());
950
951
0
        if (mRetrieveNewNetworkData ||
952
0
            SerialNumber::IsGreater(leaderData.GetDataVersion(NetworkData::kFullSet),
953
0
                                    Get<NetworkData::Leader>().GetVersion(NetworkData::kFullSet)))
954
0
        {
955
0
            IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
956
0
        }
957
958
0
        switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
959
0
        {
960
0
        case kErrorNone:
961
0
            VerifyOrExit(routeTlv.IsRouterIdSet(routerId), error = kErrorParse);
962
963
0
            if (mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv))
964
0
            {
965
0
                SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
966
0
                router = mRouterTable.FindRouterById(routerId);
967
0
                OT_ASSERT(router != nullptr);
968
0
            }
969
0
            shouldUpdateRoutes = true;
970
0
            break;
971
972
0
        case kErrorNotFound:
973
0
            break;
974
975
0
        default:
976
0
            ExitNow(error = kErrorParse);
977
0
        }
978
979
0
        if (routerId != mRouterId && !IsRouterIdValid(router->GetNextHop()))
980
0
        {
981
0
            ResetAdvertiseInterval();
982
0
        }
983
984
0
        break;
985
986
0
    case kRoleDisabled:
987
0
        OT_ASSERT(false);
988
0
    }
989
990
0
    InitNeighbor(*router, aRxInfo);
991
0
    router->SetRloc16(sourceAddress);
992
0
    router->GetLinkFrameCounters().SetAll(linkFrameCounter);
993
0
    router->SetLinkAckFrameCounter(linkFrameCounter);
994
0
    router->SetMleFrameCounter(mleFrameCounter);
995
0
    router->SetVersion(version);
996
0
    router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle |
997
0
                                     DeviceMode::kModeFullNetworkData));
998
0
    router->SetLinkQualityOut(LinkQualityForLinkMargin(linkMargin));
999
0
    router->SetState(Neighbor::kStateValid);
1000
0
    router->SetKeySequence(aRxInfo.mKeySequence);
1001
0
    router->ClearLinkAcceptTimeout();
1002
1003
0
    mNeighborTable.Signal(NeighborTable::kRouterAdded, *router);
1004
1005
0
    mDelayedSender.RemoveScheduledLinkRequest(*router);
1006
1007
0
    if (shouldUpdateRoutes)
1008
0
    {
1009
0
        mRouterTable.UpdateRoutes(routeTlv, routerId);
1010
0
    }
1011
1012
0
    aRxInfo.mClass = RxInfo::kAuthoritativeMessage;
1013
0
    ProcessKeySequence(aRxInfo);
1014
1015
0
    if (aMessageType == kTypeLinkAcceptAndRequest)
1016
0
    {
1017
0
        LinkAcceptInfo info;
1018
1019
0
        SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(info.mRxChallenge));
1020
1021
0
        switch (aRxInfo.mMessage.ReadTlvRequestTlv(info.mRequestedTlvList))
1022
0
        {
1023
0
        case kErrorNone:
1024
0
        case kErrorNotFound:
1025
0
            break;
1026
0
        default:
1027
0
            ExitNow(error = kErrorParse);
1028
0
        }
1029
1030
0
        info.mExtAddress = router->GetExtAddress();
1031
0
        info.mLinkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
1032
1033
0
        SuccessOrExit(error = SendLinkAccept(info));
1034
0
    }
1035
1036
0
exit:
1037
0
    LogProcessError(aMessageType, error);
1038
0
}
1039
1040
Error Mle::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo)
1041
0
{
1042
    // This method processes `aRouteTlv` read from an MLE message.
1043
    //
1044
    // During processing of Route TLV, the entries in the router table
1045
    // may shuffle. This method ensures that the `aRxInfo.mNeighbor`
1046
    // (which indicates the neighbor from which the MLE message was
1047
    // received) is correctly updated to point to the same neighbor
1048
    // (in case `mNeighbor` was pointing to a router entry from the
1049
    // `RouterTable`).
1050
1051
0
    Error    error          = kErrorNone;
1052
0
    uint16_t neighborRloc16 = kInvalidRloc16;
1053
1054
0
    if ((aRxInfo.mNeighbor != nullptr) && Get<RouterTable>().Contains(*aRxInfo.mNeighbor))
1055
0
    {
1056
0
        neighborRloc16 = aRxInfo.mNeighbor->GetRloc16();
1057
0
    }
1058
1059
0
    mRouterTable.UpdateRouterIdSet(aRouteTlv.GetRouterIdSequence(), aRouteTlv.GetRouterIdMask());
1060
1061
0
    if (IsRouter() && !mRouterTable.IsAllocated(mRouterId))
1062
0
    {
1063
0
        IgnoreError(BecomeDetached());
1064
0
        error = kErrorNoRoute;
1065
0
    }
1066
1067
0
    if (neighborRloc16 != kInvalidRloc16)
1068
0
    {
1069
0
        aRxInfo.mNeighbor = Get<NeighborTable>().FindNeighbor(neighborRloc16);
1070
0
    }
1071
1072
0
    return error;
1073
0
}
1074
1075
Error Mle::ReadAndProcessRouteTlvOnFtdChild(RxInfo &aRxInfo, uint8_t aParentId)
1076
0
{
1077
    // This method reads and processes Route TLV from message on an
1078
    // FTD child if message contains one. It returns `kErrorNone`
1079
    // when successfully processed or if there is no Route TLV in
1080
    // the message.
1081
    //
1082
    // It MUST be used only when device is acting as a child and
1083
    // for a message received from device's current parent.
1084
1085
0
    Error    error = kErrorNone;
1086
0
    RouteTlv routeTlv;
1087
1088
0
    VerifyOrExit(IsFullThreadDevice());
1089
1090
0
    switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
1091
0
    {
1092
0
    case kErrorNone:
1093
0
        SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
1094
0
        mRouterTable.UpdateRouterOnFtdChild(routeTlv, aParentId);
1095
0
        mRequestRouteTlv = false;
1096
0
        break;
1097
0
    case kErrorNotFound:
1098
0
        break;
1099
0
    default:
1100
0
        ExitNow(error = kErrorParse);
1101
0
    }
1102
1103
0
exit:
1104
0
    return error;
1105
0
}
1106
1107
bool Mle::IsSingleton(void) const
1108
0
{
1109
0
    bool isSingleton = true;
1110
1111
0
    VerifyOrExit(IsAttached() && IsRouterEligible());
1112
0
    isSingleton = (mRouterTable.GetActiveRouterCount() <= 1);
1113
1114
0
exit:
1115
0
    return isSingleton;
1116
0
}
1117
1118
int Mle::ComparePartitions(bool              aSingletonA,
1119
                           const LeaderData &aLeaderDataA,
1120
                           bool              aSingletonB,
1121
                           const LeaderData &aLeaderDataB)
1122
0
{
1123
0
    int rval = 0;
1124
1125
0
    rval = ThreeWayCompare(aLeaderDataA.GetWeighting(), aLeaderDataB.GetWeighting());
1126
0
    VerifyOrExit(rval == 0);
1127
1128
    // Not being a singleton is better.
1129
0
    rval = ThreeWayCompare(!aSingletonA, !aSingletonB);
1130
0
    VerifyOrExit(rval == 0);
1131
1132
0
    rval = ThreeWayCompare(aLeaderDataA.GetPartitionId(), aLeaderDataB.GetPartitionId());
1133
1134
0
exit:
1135
0
    return rval;
1136
0
}
1137
1138
Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, const LeaderData &aLeaderData)
1139
0
{
1140
    // This method processes a received MLE Advertisement message on
1141
    // an FTD device. It is called from `Mle::HandleAdvertisement()`
1142
    // only when device is attached (in child, router, or leader roles)
1143
    // and `IsFullThreadDevice()`.
1144
    //
1145
    // - `aSourceAddress` is the read value from `SourceAddressTlv`.
1146
    // - `aLeaderData` is the read value from `LeaderDataTlv`.
1147
1148
0
    Error    error      = kErrorNone;
1149
0
    uint8_t  linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
1150
0
    RouteTlv routeTlv;
1151
0
    Router  *router;
1152
0
    uint8_t  routerId;
1153
0
    uint32_t delay;
1154
1155
0
    switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
1156
0
    {
1157
0
    case kErrorNone:
1158
0
        break;
1159
0
    case kErrorNotFound:
1160
0
        routeTlv.SetLength(0); // Mark that a Route TLV was not included.
1161
0
        break;
1162
0
    default:
1163
0
        ExitNow(error = kErrorParse);
1164
0
    }
1165
1166
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1167
    // Handle Partition ID mismatch
1168
1169
0
    if (aLeaderData.GetPartitionId() != mLeaderData.GetPartitionId())
1170
0
    {
1171
0
        LogNote("Different partition (peer:%lu, local:%lu)", ToUlong(aLeaderData.GetPartitionId()),
1172
0
                ToUlong(mLeaderData.GetPartitionId()));
1173
1174
0
        VerifyOrExit(linkMargin >= kPartitionMergeMinMargin, error = kErrorLinkMarginLow);
1175
1176
0
        if (routeTlv.IsValid() && (mPreviousPartitionIdTimeout > 0) &&
1177
0
            (aLeaderData.GetPartitionId() == mPreviousPartitionId))
1178
0
        {
1179
0
            VerifyOrExit(SerialNumber::IsGreater(routeTlv.GetRouterIdSequence(), mPreviousPartitionRouterIdSequence),
1180
0
                         error = kErrorDrop);
1181
0
        }
1182
1183
0
        if (IsChild() && (aRxInfo.mNeighbor == &mParent))
1184
0
        {
1185
0
            ExitNow();
1186
0
        }
1187
1188
0
        if (ComparePartitions(routeTlv.IsSingleton(), aLeaderData, IsSingleton(), mLeaderData) > 0
1189
#if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
1190
            // Allow a better partition if it also enables time sync.
1191
            && aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ
1192
#endif
1193
0
        )
1194
0
        {
1195
0
            Attach(kBetterPartition);
1196
0
        }
1197
1198
0
        ExitNow(error = kErrorDrop);
1199
0
    }
1200
1201
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1202
    // Handle Leader Router ID mismatch
1203
1204
0
    if (aLeaderData.GetLeaderRouterId() != GetLeaderId())
1205
0
    {
1206
0
        VerifyOrExit(aRxInfo.IsNeighborStateValid());
1207
1208
0
        if (!IsChild())
1209
0
        {
1210
0
            LogInfo("Leader ID mismatch");
1211
0
            IgnoreError(BecomeDetached());
1212
0
            error = kErrorDrop;
1213
0
        }
1214
1215
0
        ExitNow();
1216
0
    }
1217
1218
0
    VerifyOrExit(IsRouterRloc16(aSourceAddress) && routeTlv.IsValid());
1219
0
    routerId = RouterIdFromRloc16(aSourceAddress);
1220
1221
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1222
    Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
1223
#endif
1224
1225
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1226
    // Process `RouteTlv`
1227
1228
0
    if (aRxInfo.IsNeighborStateValid() && mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv))
1229
0
    {
1230
0
        SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
1231
0
    }
1232
1233
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1234
    // Update routers as a child
1235
1236
0
    if (IsChild())
1237
0
    {
1238
0
        if (aRxInfo.mNeighbor == &mParent)
1239
0
        {
1240
            // MLE Advertisement from parent
1241
0
            router = &mParent;
1242
1243
0
            if (mParent.GetRloc16() != aSourceAddress)
1244
0
            {
1245
0
                IgnoreError(BecomeDetached());
1246
0
                ExitNow(error = kErrorDetached);
1247
0
            }
1248
1249
0
            if (!mRouterRoleTransition.IsPending() && (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold))
1250
0
            {
1251
0
                mRouterRoleTransition.StartTimeout();
1252
0
            }
1253
1254
0
            mRouterTable.UpdateRouterOnFtdChild(routeTlv, routerId);
1255
0
        }
1256
0
        else
1257
0
        {
1258
            // MLE Advertisement not from parent, but from some other neighboring router
1259
0
            router = mRouterTable.FindRouterById(routerId);
1260
0
            VerifyOrExit(router != nullptr);
1261
1262
0
            EstablishRouterLinkOnFtdChild(*router, aRxInfo, linkMargin);
1263
0
        }
1264
1265
0
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
1266
0
        router->SetSelectableAsParent(true);
1267
0
#endif
1268
1269
0
        router->SetLastHeard(TimerMilli::GetNow());
1270
1271
0
        ExitNow();
1272
0
    }
1273
1274
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1275
    // Update routers as a router or leader.
1276
1277
0
    if (IsRouter() && ShouldDowngrade(routerId, routeTlv))
1278
0
    {
1279
0
        mRouterRoleTransition.StartTimeout();
1280
0
    }
1281
1282
0
    router = mRouterTable.FindRouterById(routerId);
1283
0
    VerifyOrExit(router != nullptr);
1284
1285
0
    if (!router->IsStateValid() && aRxInfo.IsNeighborStateValid() && Get<ChildTable>().Contains(*aRxInfo.mNeighbor))
1286
0
    {
1287
        // The Adv is from a former child that is now acting as a router,
1288
        // we copy the info from child entry and update the RLOC16.
1289
1290
0
        *static_cast<Neighbor *>(router) = *aRxInfo.mNeighbor;
1291
0
        router->SetRloc16(Rloc16FromRouterId(routerId));
1292
0
        router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle |
1293
0
                                         DeviceMode::kModeFullNetworkData));
1294
1295
0
        mNeighborTable.Signal(NeighborTable::kRouterAdded, *router);
1296
1297
        // Change the cache entries associated with the former child
1298
        // from using the old RLOC16 to its new RLOC16.
1299
0
        Get<AddressResolver>().ReplaceEntriesForRloc16(aRxInfo.mNeighbor->GetRloc16(), router->GetRloc16());
1300
0
    }
1301
1302
    // Send unicast link request if no link to router and no
1303
    // unicast/multicast link request in progress
1304
1305
0
    if (!router->IsStateValid() && !router->IsStateLinkRequest() && (linkMargin >= kLinkRequestMinMargin) &&
1306
0
        routeTlv.IsRouterIdSet(mRouterId))
1307
0
    {
1308
0
        InitNeighbor(*router, aRxInfo);
1309
0
        router->SetState(Neighbor::kStateLinkRequest);
1310
0
        router->ClearLinkAcceptTimeout();
1311
0
        delay = Random::NonCrypto::GetUint32InRange(0, kMaxLinkRequestDelayOnRouter);
1312
0
        mDelayedSender.ScheduleLinkRequest(*router, delay);
1313
0
        ExitNow(error = kErrorNoRoute);
1314
0
    }
1315
1316
0
    router->SetLastHeard(TimerMilli::GetNow());
1317
1318
0
    mRouterTable.UpdateRoutes(routeTlv, routerId);
1319
1320
0
exit:
1321
0
    if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != aSourceAddress)
1322
0
    {
1323
0
        RemoveNeighbor(*aRxInfo.mNeighbor);
1324
0
    }
1325
1326
0
    return error;
1327
0
}
1328
1329
void Mle::EstablishRouterLinkOnFtdChild(Router &aRouter, RxInfo &aRxInfo, uint8_t aLinkMargin)
1330
0
{
1331
    // Decide on an FTD child whether to establish a link with a
1332
    // router upon receiving an advertisement from it.
1333
1334
0
    uint8_t  neighborCount;
1335
0
    uint32_t minDelay;
1336
0
    uint32_t maxDelay;
1337
1338
0
    VerifyOrExit(!aRouter.IsStateValid() && !aRouter.IsStateLinkRequest());
1339
1340
    // The first `mChildRouterLinks` are established quickly. After that,
1341
    // the "gradual router link establishment" mechanism is used, which
1342
    // allows `kExtraChildRouterLinks` additional router links to be
1343
    // established, but it is done slowly and over a longer span of time.
1344
    //
1345
    // Gradual router link establishment conditions:
1346
    // - The maximum `neighborCount` limit is not yet reached.
1347
    // - We see Link Quality 2 or better.
1348
    // - Always skipped in the first 5 minutes
1349
    //   (`kWaitDurationAfterAttach`) after the device attaches.
1350
    // - The child randomly decides whether to perform/skip this (with a 5%
1351
    //   probability, `kProbabilityPercentage`).
1352
    // - If the child decides to send Link Request, a longer random delay
1353
    //   window is used, [1.5-10] seconds.
1354
    //
1355
    // Even in a dense network, if the advertisement is received by 500 FTD
1356
    // children with a 5% selection probability, on average, 25 nodes will
1357
    // try to send a Link Request, which will be randomly spread over a
1358
    // [1.5-10] second window.
1359
    //
1360
    // With a 5% probability, on average, it takes 20 trials (20 advertisement
1361
    // receptions for an FTD child to send a Link Request). Advertisements
1362
    // are, on average, ~32 seconds apart, so, on average, a child will try
1363
    // to establish a link in `20 * 32 = 640` seconds (~10 minutes).
1364
1365
0
    neighborCount = mRouterTable.GetNeighborCount(kLinkQuality1);
1366
1367
0
    if (neighborCount < mChildRouterLinks)
1368
0
    {
1369
0
        minDelay = kMinLinkRequestDelayOnChild;
1370
0
        maxDelay = kMaxLinkRequestDelayOnChild;
1371
0
    }
1372
0
    else
1373
0
    {
1374
0
        VerifyOrExit(neighborCount < mChildRouterLinks + GradualChildRouterLink::kExtraChildRouterLinks);
1375
0
        VerifyOrExit(LinkQualityForLinkMargin(aLinkMargin) >= kLinkQuality2);
1376
0
        VerifyOrExit(GetCurrentAttachDuration() > GradualChildRouterLink::kWaitDurationAfterAttach);
1377
0
        VerifyOrExit(Random::NonCrypto::GetUint8InRange(0, 100) < GradualChildRouterLink::kProbabilityPercentage);
1378
1379
0
        minDelay = GradualChildRouterLink::kMinLinkRequestDelay;
1380
0
        maxDelay = GradualChildRouterLink::kMaxLinkRequestDelay;
1381
0
    }
1382
1383
0
    InitNeighbor(aRouter, aRxInfo);
1384
0
    aRouter.SetState(Neighbor::kStateLinkRequest);
1385
0
    aRouter.ClearLinkAcceptTimeout();
1386
0
    mDelayedSender.ScheduleLinkRequest(aRouter, Random::NonCrypto::GetUint32InRange(minDelay, maxDelay));
1387
1388
0
exit:
1389
0
    return;
1390
0
}
1391
1392
void Mle::HandleParentRequest(RxInfo &aRxInfo)
1393
0
{
1394
0
    Error              error = kErrorNone;
1395
0
    uint16_t           version;
1396
0
    uint8_t            scanMask;
1397
0
    Child             *child;
1398
0
    DeviceMode         mode;
1399
0
    uint16_t           delay;
1400
0
    ParentResponseInfo info;
1401
1402
0
    Log(kMessageReceive, kTypeParentRequest, aRxInfo.mMessageInfo.GetPeerAddr());
1403
1404
0
    VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
1405
1406
    // A Router/REED MUST NOT send an MLE Parent Response if:
1407
1408
    // 0. It is detached or attempting to another partition
1409
0
    VerifyOrExit(!IsDetached() && !IsAttaching(), error = kErrorDrop);
1410
1411
    // 1. It has no available Child capacity (if Max Child Count minus
1412
    // Child Count would be equal to zero)
1413
    // ==> verified below when allocating a child entry
1414
1415
    // 2. It is disconnected from its Partition (that is, it has not
1416
    // received an updated ID sequence number within LEADER_TIMEOUT
1417
    // seconds)
1418
0
    VerifyOrExit(mRouterTable.GetLeaderAge() < mNetworkIdTimeout, error = kErrorDrop);
1419
1420
    // 3. Its current routing path cost to the Leader is infinite.
1421
0
    VerifyOrExit(mRouterTable.GetPathCostToLeader() < kMaxRouteCost, error = kErrorDrop);
1422
1423
    // 4. It is a REED and there are already `kMaxRouters` active routers in
1424
    // the network (because Leader would reject any further address solicit).
1425
    // ==> Verified below when checking the scan mask.
1426
1427
0
    info.mChildExtAddress.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid());
1428
1429
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version));
1430
1431
0
    SuccessOrExit(error = Tlv::Find<ScanMaskTlv>(aRxInfo.mMessage, scanMask));
1432
1433
0
    switch (mRole)
1434
0
    {
1435
0
    case kRoleDisabled:
1436
0
    case kRoleDetached:
1437
0
        ExitNow();
1438
1439
0
    case kRoleChild:
1440
0
        VerifyOrExit(ScanMaskTlv::IsEndDeviceFlagSet(scanMask));
1441
0
        VerifyOrExit(mRouterTable.GetActiveRouterCount() < kMaxRouters, error = kErrorDrop);
1442
0
        break;
1443
1444
0
    case kRoleRouter:
1445
0
    case kRoleLeader:
1446
0
        VerifyOrExit(ScanMaskTlv::IsRouterFlagSet(scanMask));
1447
0
        break;
1448
0
    }
1449
1450
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(info.mRxChallenge));
1451
1452
0
    child = mChildTable.FindChild(info.mChildExtAddress, Child::kInStateAnyExceptInvalid);
1453
1454
0
    if (child == nullptr)
1455
0
    {
1456
0
        VerifyOrExit((child = mChildTable.GetNewChild()) != nullptr, error = kErrorNoBufs);
1457
1458
0
        InitNeighbor(*child, aRxInfo);
1459
0
        child->SetState(Neighbor::kStateParentRequest);
1460
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1461
        child->SetTimeSyncEnabled(Tlv::Find<TimeRequestTlv>(aRxInfo.mMessage, nullptr, 0) == kErrorNone);
1462
#endif
1463
0
        if (aRxInfo.mMessage.ReadModeTlv(mode) == kErrorNone)
1464
0
        {
1465
0
            child->SetDeviceMode(mode);
1466
0
            child->SetVersion(version);
1467
0
        }
1468
0
    }
1469
0
    else if (TimerMilli::GetNow() - child->GetLastHeard() < kParentRequestRouterTimeout - kParentRequestDuplicateMargin)
1470
0
    {
1471
0
        ExitNow(error = kErrorDuplicated);
1472
0
    }
1473
1474
0
    if (!child->IsStateValidOrRestoring())
1475
0
    {
1476
0
        child->SetLastHeard(TimerMilli::GetNow());
1477
0
        child->SetTimeout(Time::MsecToSec(kChildIdRequestTimeout));
1478
0
    }
1479
1480
0
    aRxInfo.mClass = RxInfo::kPeerMessage;
1481
0
    ProcessKeySequence(aRxInfo);
1482
1483
0
    delay = 1 + Random::NonCrypto::GetUint16InRange(0, !ScanMaskTlv::IsEndDeviceFlagSet(scanMask)
1484
0
                                                           ? kParentResponseMaxDelayRouters
1485
0
                                                           : kParentResponseMaxDelayAll);
1486
0
    mDelayedSender.ScheduleParentResponse(info, delay);
1487
1488
0
exit:
1489
0
    LogProcessError(kTypeParentRequest, error);
1490
0
}
1491
1492
bool Mle::HasNeighborWithGoodLinkQuality(void) const
1493
0
{
1494
0
    bool    haveNeighbor = true;
1495
0
    uint8_t linkMargin;
1496
1497
0
    linkMargin = Get<Mac::Mac>().ComputeLinkMargin(mParent.GetLinkInfo().GetLastRss());
1498
1499
0
    if (linkMargin >= kLinkRequestMinMargin)
1500
0
    {
1501
0
        ExitNow();
1502
0
    }
1503
1504
0
    for (const Router &router : Get<RouterTable>())
1505
0
    {
1506
0
        if (!router.IsStateValid())
1507
0
        {
1508
0
            continue;
1509
0
        }
1510
1511
0
        linkMargin = Get<Mac::Mac>().ComputeLinkMargin(router.GetLinkInfo().GetLastRss());
1512
1513
0
        if (linkMargin >= kLinkRequestMinMargin)
1514
0
        {
1515
0
            ExitNow();
1516
0
        }
1517
0
    }
1518
1519
0
    haveNeighbor = false;
1520
1521
0
exit:
1522
0
    return haveNeighbor;
1523
0
}
1524
1525
void Mle::HandleTimeTick(void)
1526
0
{
1527
0
    bool roleTransitionTimeoutExpired = false;
1528
1529
0
    VerifyOrExit(IsFullThreadDevice(), Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMle));
1530
1531
0
    if (mPreviousPartitionIdTimeout > 0)
1532
0
    {
1533
0
        mPreviousPartitionIdTimeout--;
1534
0
    }
1535
1536
0
    if (mAlternateRloc16Timeout > 0)
1537
0
    {
1538
0
        mAlternateRloc16Timeout--;
1539
1540
0
        if (mAlternateRloc16Timeout == 0)
1541
0
        {
1542
0
            ClearAlternateRloc16();
1543
0
        }
1544
0
    }
1545
1546
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1547
    // Role transitions
1548
1549
0
    roleTransitionTimeoutExpired = mRouterRoleTransition.HandleTimeTick();
1550
1551
0
    switch (mRole)
1552
0
    {
1553
0
    case kRoleDetached:
1554
0
        break;
1555
1556
0
    case kRoleChild:
1557
0
        if (roleTransitionTimeoutExpired)
1558
0
        {
1559
0
            if (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold && HasNeighborWithGoodLinkQuality())
1560
0
            {
1561
0
                IgnoreError(BecomeRouter(ThreadStatusTlv::kTooFewRouters));
1562
0
            }
1563
0
            else
1564
0
            {
1565
0
                InformPreviousChannel();
1566
0
            }
1567
1568
0
            if (!mAdvertiseTrickleTimer.IsRunning())
1569
0
            {
1570
0
                SendMulticastAdvertisement();
1571
1572
0
                mAdvertiseTrickleTimer.Start(TrickleTimer::kModePlainTimer, kReedAdvIntervalMin, kReedAdvIntervalMax);
1573
0
            }
1574
1575
0
            ExitNow();
1576
0
        }
1577
1578
0
        OT_FALL_THROUGH;
1579
1580
0
    case kRoleRouter:
1581
0
        LogDebg("Leader age %lu", ToUlong(mRouterTable.GetLeaderAge()));
1582
1583
0
        if ((mRouterTable.GetActiveRouterCount() > 0) && (mRouterTable.GetLeaderAge() >= mNetworkIdTimeout))
1584
0
        {
1585
0
            LogInfo("Leader age timeout");
1586
0
            Attach(kSamePartition);
1587
0
        }
1588
1589
0
        if (roleTransitionTimeoutExpired && mRouterTable.GetActiveRouterCount() > mRouterDowngradeThreshold)
1590
0
        {
1591
0
            LogNote("Downgrade to REED");
1592
0
            Attach(kDowngradeToReed);
1593
0
        }
1594
1595
0
        OT_FALL_THROUGH;
1596
1597
0
    case kRoleLeader:
1598
0
        if (roleTransitionTimeoutExpired && !IsRouterEligible())
1599
0
        {
1600
0
            LogInfo("No longer router eligible");
1601
0
            IgnoreError(BecomeDetached());
1602
0
        }
1603
1604
0
        break;
1605
1606
0
    case kRoleDisabled:
1607
0
        OT_ASSERT(false);
1608
0
    }
1609
1610
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1611
    // Update `ChildTable`
1612
1613
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
1614
0
    {
1615
0
        uint32_t timeout = 0;
1616
1617
0
        switch (child.GetState())
1618
0
        {
1619
0
        case Neighbor::kStateInvalid:
1620
0
        case Neighbor::kStateChildIdRequest:
1621
0
            continue;
1622
1623
0
        case Neighbor::kStateParentRequest:
1624
0
        case Neighbor::kStateValid:
1625
0
        case Neighbor::kStateRestored:
1626
0
        case Neighbor::kStateChildUpdateRequest:
1627
0
            timeout = Time::SecToMsec(child.GetTimeout());
1628
0
            break;
1629
1630
0
        case Neighbor::kStateParentResponse:
1631
0
        case Neighbor::kStateLinkRequest:
1632
0
            OT_ASSERT(false);
1633
0
        }
1634
1635
0
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1636
0
        if (child.IsCslSynchronized() &&
1637
0
            TimerMilli::GetNow() - child.GetCslLastHeard() >= Time::SecToMsec(child.GetCslTimeout()))
1638
0
        {
1639
0
            LogInfo("Child 0x%04x CSL synchronization expired", child.GetRloc16());
1640
0
            child.SetCslSynchronized(false);
1641
0
            Get<CslTxScheduler>().Update();
1642
0
        }
1643
0
#endif
1644
1645
0
        if (TimerMilli::GetNow() - child.GetLastHeard() >= timeout)
1646
0
        {
1647
0
            LogInfo("Child 0x%04x timeout expired", child.GetRloc16());
1648
0
            RemoveNeighbor(child);
1649
0
        }
1650
0
        else if (IsRouterOrLeader() && child.IsStateRestored())
1651
0
        {
1652
0
            IgnoreError(SendChildUpdateRequestToChild(child));
1653
0
        }
1654
0
    }
1655
1656
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1657
    // Update `RouterTable`
1658
1659
0
    for (Router &router : Get<RouterTable>())
1660
0
    {
1661
0
        uint32_t age;
1662
1663
0
        if (router.GetRloc16() == GetRloc16())
1664
0
        {
1665
0
            router.SetLastHeard(TimerMilli::GetNow());
1666
0
            continue;
1667
0
        }
1668
1669
0
        age = TimerMilli::GetNow() - router.GetLastHeard();
1670
1671
0
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
1672
0
        router.DecrementParentReselectTimeout();
1673
1674
0
        if (age >= kMaxNeighborAge)
1675
0
        {
1676
0
            router.SetSelectableAsParent(false);
1677
0
        }
1678
0
#endif
1679
0
        if (router.IsStateValid())
1680
0
        {
1681
            // Neighbor router age and link recovery
1682
            //
1683
            // If the device is an FTD child and has more than
1684
            // `mChildRouterLinks` neighbors, it uses a longer age,
1685
            // `kMaxNeighborAgeOnChild`, and removes the neighboring
1686
            // router upon expiration without trying to re-establish
1687
            // its link with it.
1688
            //
1689
            // Otherwise, if the device itself is a router, or it is an
1690
            // FTD child with `mChildRouterLinks` or fewer neighbors,
1691
            // it uses a shorter `kMaxNeighborAge`. Upon expiration, it
1692
            // tries to re-establish its link with the neighboring router.
1693
1694
0
            if (IsChild() && (mRouterTable.GetNeighborCount(kLinkQuality1) > mChildRouterLinks))
1695
0
            {
1696
0
                if (age >= kMaxNeighborAgeOnChild)
1697
0
                {
1698
0
                    LogInfo("No Adv from router 0x%04x - removing router", router.GetRloc16());
1699
0
                    mDelayedSender.RemoveScheduledLinkRequest(router);
1700
0
                    RemoveNeighbor(router);
1701
0
                    continue;
1702
0
                }
1703
0
            }
1704
0
            else if (age >= kMaxNeighborAge)
1705
0
            {
1706
                // We send a Link Request every time tick (second), up to
1707
                // max attempts. Each transmission is randomly delayed
1708
                // (one-second window). After the last attempt, we wait for
1709
                // the "Link Accept" timeout (~3 seconds) before removing the
1710
                // neighboring router.
1711
1712
0
                if (!mDelayedSender.HasAnyScheduledLinkRequest(router) && !router.IsWaitingForLinkAccept())
1713
0
                {
1714
0
                    LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16());
1715
0
                    router.SetLinkRequestAttemptsToMax();
1716
0
                }
1717
1718
0
                if (router.HasRemainingLinkRequestAttempts())
1719
0
                {
1720
0
                    router.DecrementLinkRequestAttempts();
1721
0
                    mDelayedSender.ScheduleLinkRequest(
1722
0
                        router, Random::NonCrypto::GetUint32InRange(0, kMaxLinkRequestDelayOnRouter));
1723
0
                }
1724
0
            }
1725
0
        }
1726
1727
0
        if (router.IsStateLinkRequest() && !mDelayedSender.HasAnyScheduledLinkRequest(router) &&
1728
0
            !router.IsWaitingForLinkAccept())
1729
0
        {
1730
0
            LogInfo("Router 0x%04x - Failed to schedule/send Link Request", router.GetRloc16());
1731
0
            RemoveNeighbor(router);
1732
0
            continue;
1733
0
        }
1734
1735
0
        if (router.IsWaitingForLinkAccept() && (router.DecrementLinkAcceptTimeout() == 0))
1736
0
        {
1737
0
            LogInfo("Router 0x%04x - Link Accept timeout expired", router.GetRloc16());
1738
0
            RemoveNeighbor(router);
1739
0
            continue;
1740
0
        }
1741
1742
0
        if (IsLeader() && (mRouterTable.FindNextHopOf(router) == nullptr) &&
1743
0
            (mRouterTable.GetLinkCost(router) >= kMaxRouteCost) && (age >= kMaxLeaderToRouterTimeout))
1744
0
        {
1745
0
            LogInfo("Router 0x%04x ID timeout expired (no route)", router.GetRloc16());
1746
0
            IgnoreError(mRouterTable.Release(router.GetRouterId()));
1747
0
        }
1748
0
    }
1749
1750
0
    mRouterTable.HandleTimeTick();
1751
1752
0
    SynchronizeChildNetworkData();
1753
1754
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1755
    if (IsRouterOrLeader())
1756
    {
1757
        Get<TimeSync>().ProcessTimeSync();
1758
    }
1759
#endif
1760
1761
0
exit:
1762
0
    return;
1763
0
}
1764
1765
void Mle::SendParentResponse(const ParentResponseInfo &aInfo)
1766
0
{
1767
0
    Error        error   = kErrorNone;
1768
0
    TxMessage   *message = nullptr;
1769
0
    Child       *child;
1770
0
    Ip6::Address destination;
1771
1772
0
    child = mChildTable.FindChild(aInfo.mChildExtAddress, Child::kInStateAnyExceptInvalid);
1773
0
    VerifyOrExit(child != nullptr);
1774
1775
0
    VerifyOrExit((message = NewMleMessage(kCommandParentResponse)) != nullptr, error = kErrorNoBufs);
1776
0
    message->SetDirectTransmission();
1777
1778
0
    SuccessOrExit(error = message->AppendSourceAddressTlv());
1779
0
    SuccessOrExit(error = message->AppendLeaderDataTlv());
1780
0
    SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs());
1781
0
    SuccessOrExit(error = message->AppendResponseTlv(aInfo.mRxChallenge));
1782
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1783
    if (child->IsTimeSyncEnabled())
1784
    {
1785
        SuccessOrExit(error = message->AppendTimeParameterTlv());
1786
    }
1787
#endif
1788
0
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1789
0
    if (child->IsThreadVersionCslCapable())
1790
0
    {
1791
0
        SuccessOrExit(error = message->AppendCslClockAccuracyTlv());
1792
0
    }
1793
0
#endif
1794
0
    child->GenerateChallenge();
1795
0
    SuccessOrExit(error = message->AppendChallengeTlv(child->GetChallenge()));
1796
0
    SuccessOrExit(error = message->AppendLinkMarginTlv(child->GetLinkInfo().GetLinkMargin()));
1797
0
    SuccessOrExit(error = message->AppendConnectivityTlv());
1798
0
    SuccessOrExit(error = message->AppendVersionTlv());
1799
1800
0
    destination.SetToLinkLocalAddress(aInfo.mChildExtAddress);
1801
1802
0
    SuccessOrExit(error = message->SendTo(destination));
1803
1804
0
    Log(kMessageSend, kTypeParentResponse, destination);
1805
1806
0
exit:
1807
0
    FreeMessageOnError(message, error);
1808
0
    LogSendError(kTypeParentResponse, error);
1809
0
}
1810
1811
uint8_t Mle::GetMaxChildIpAddresses(void) const
1812
0
{
1813
0
    uint8_t num = kMaxChildIpAddresses;
1814
1815
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1816
    if (mMaxChildIpAddresses != 0)
1817
    {
1818
        num = mMaxChildIpAddresses;
1819
    }
1820
#endif
1821
1822
0
    return num;
1823
0
}
1824
1825
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1826
Error Mle::SetMaxChildIpAddresses(uint8_t aMaxIpAddresses)
1827
{
1828
    Error error = kErrorNone;
1829
1830
    VerifyOrExit(aMaxIpAddresses <= kMaxChildIpAddresses, error = kErrorInvalidArgs);
1831
1832
    mMaxChildIpAddresses = aMaxIpAddresses;
1833
1834
exit:
1835
    return error;
1836
}
1837
#endif
1838
1839
Error Mle::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild)
1840
0
{
1841
0
    Error       error;
1842
0
    OffsetRange offsetRange;
1843
0
    uint8_t     count       = 0;
1844
0
    uint8_t     storedCount = 0;
1845
0
#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
1846
0
    Ip6::Address oldDua;
1847
0
#endif
1848
0
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
1849
0
    MlrManager::MlrAddressArray oldMlrRegisteredAddresses;
1850
0
#endif
1851
1852
0
    OT_UNUSED_VARIABLE(storedCount);
1853
1854
0
    SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kAddressRegistration, offsetRange));
1855
1856
0
#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
1857
0
    if (aChild.GetDomainUnicastAddress(oldDua) != kErrorNone)
1858
0
    {
1859
0
        oldDua.Clear();
1860
0
    }
1861
0
#endif
1862
1863
0
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
1864
0
    if (aChild.HasAnyMlrRegisteredAddress())
1865
0
    {
1866
0
        OT_ASSERT(aChild.IsStateValid());
1867
1868
0
        for (const Child::Ip6AddrEntry &addrEntry : aChild.GetIp6Addresses())
1869
0
        {
1870
0
            if (!addrEntry.IsMulticastLargerThanRealmLocal())
1871
0
            {
1872
0
                continue;
1873
0
            }
1874
1875
0
            if (addrEntry.GetMlrState(aChild) == kMlrStateRegistered)
1876
0
            {
1877
0
                IgnoreError(oldMlrRegisteredAddresses.PushBack(addrEntry));
1878
0
            }
1879
0
        }
1880
0
    }
1881
0
#endif
1882
1883
0
    aChild.ClearIp6Addresses();
1884
1885
0
    while (!offsetRange.IsEmpty())
1886
0
    {
1887
0
        uint8_t      controlByte;
1888
0
        Ip6::Address address;
1889
1890
        // Read out the control byte (first byte in entry)
1891
0
        SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, controlByte));
1892
0
        offsetRange.AdvanceOffset(sizeof(uint8_t));
1893
0
        count++;
1894
1895
0
        address.Clear();
1896
1897
0
        if (AddressRegistrationTlv::IsEntryCompressed(controlByte))
1898
0
        {
1899
            // Compressed entry contains IID with the 64-bit prefix
1900
            // determined from 6LoWPAN context identifier (from
1901
            // the control byte).
1902
1903
0
            uint8_t         contextId = AddressRegistrationTlv::GetContextId(controlByte);
1904
0
            Lowpan::Context context;
1905
1906
0
            IgnoreError(aRxInfo.mMessage.Read(offsetRange, address.GetIid()));
1907
0
            offsetRange.AdvanceOffset(sizeof(Ip6::InterfaceIdentifier));
1908
1909
0
            if (Get<NetworkData::Leader>().GetContext(contextId, context) != kErrorNone)
1910
0
            {
1911
0
                LogWarn("Failed to get context %u for compressed address from child 0x%04x", contextId,
1912
0
                        aChild.GetRloc16());
1913
0
                continue;
1914
0
            }
1915
1916
0
            address.SetPrefix(context.mPrefix);
1917
0
        }
1918
0
        else
1919
0
        {
1920
            // Uncompressed entry contains the full IPv6 address.
1921
1922
0
            IgnoreError(aRxInfo.mMessage.Read(offsetRange, address));
1923
0
            offsetRange.AdvanceOffset(sizeof(Ip6::Address));
1924
0
        }
1925
1926
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1927
        if (mMaxChildIpAddresses > 0 && storedCount >= mMaxChildIpAddresses)
1928
        {
1929
            // Skip remaining address registration entries but keep logging
1930
            // skipped addresses.
1931
            error = kErrorNoBufs;
1932
        }
1933
        else
1934
#endif
1935
0
        {
1936
            // We try to accept/add as many IPv6 addresses as possible.
1937
            // "Child ID/Update Response" will indicate the accepted
1938
            // addresses.
1939
0
            error = aChild.AddIp6Address(address);
1940
0
        }
1941
1942
0
        if (error == kErrorNone)
1943
0
        {
1944
0
            storedCount++;
1945
0
            LogInfo("Child 0x%04x IPv6 address[%u]=%s", aChild.GetRloc16(), storedCount,
1946
0
                    address.ToString().AsCString());
1947
0
        }
1948
0
        else
1949
0
        {
1950
0
            LogWarn("Error %s adding IPv6 address %s to child 0x%04x", ErrorToString(error),
1951
0
                    address.ToString().AsCString(), aChild.GetRloc16());
1952
0
        }
1953
1954
0
        if (address.IsMulticast())
1955
0
        {
1956
0
            continue;
1957
0
        }
1958
1959
        // We check if the same address is in-use by another child, if so
1960
        // remove it. This implements "last-in wins" duplicate address
1961
        // resolution policy.
1962
        //
1963
        // Duplicate addresses can occur if a previously attached child
1964
        // attaches to same parent again (after a reset, memory wipe) using
1965
        // a new random extended address before the old entry in the child
1966
        // table is timed out and then trying to register its globally unique
1967
        // IPv6 address as the new child.
1968
1969
0
        for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
1970
0
        {
1971
0
            if (&child == &aChild)
1972
0
            {
1973
0
                continue;
1974
0
            }
1975
1976
0
            IgnoreError(child.RemoveIp6Address(address));
1977
0
        }
1978
1979
        // Clear EID-to-RLOC cache for the unicast address registered by the child.
1980
0
        Get<AddressResolver>().RemoveEntryForAddress(address);
1981
0
    }
1982
0
#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
1983
0
    SignalDuaAddressEvent(aChild, oldDua);
1984
0
#endif
1985
1986
0
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
1987
0
    Get<MlrManager>().UpdateProxiedSubscriptions(aChild, oldMlrRegisteredAddresses);
1988
0
#endif
1989
1990
0
    if (count == 0)
1991
0
    {
1992
0
        LogInfo("Child 0x%04x has no registered IPv6 address", aChild.GetRloc16());
1993
0
    }
1994
0
    else
1995
0
    {
1996
0
        LogInfo("Child 0x%04x has %u registered IPv6 address%s, %u address%s stored", aChild.GetRloc16(), count,
1997
0
                (count == 1) ? "" : "es", storedCount, (storedCount == 1) ? "" : "es");
1998
0
    }
1999
2000
0
    error = kErrorNone;
2001
2002
0
exit:
2003
0
    return error;
2004
0
}
2005
2006
#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
2007
void Mle::SignalDuaAddressEvent(const Child &aChild, const Ip6::Address &aOldDua) const
2008
0
{
2009
0
    DuaManager::ChildDuaAddressEvent event = DuaManager::kAddressUnchanged;
2010
0
    Ip6::Address                     newDua;
2011
2012
0
    if (aChild.GetDomainUnicastAddress(newDua) == kErrorNone)
2013
0
    {
2014
0
        if (aOldDua.IsUnspecified())
2015
0
        {
2016
0
            event = DuaManager::kAddressAdded;
2017
0
        }
2018
0
        else if (aOldDua != newDua)
2019
0
        {
2020
0
            event = DuaManager::kAddressChanged;
2021
0
        }
2022
0
    }
2023
0
    else
2024
0
    {
2025
        // Child has no DUA address. If there was no old DUA, no need
2026
        // to signal.
2027
2028
0
        VerifyOrExit(!aOldDua.IsUnspecified());
2029
2030
0
        event = DuaManager::kAddressRemoved;
2031
0
    }
2032
2033
0
    Get<DuaManager>().HandleChildDuaAddressEvent(aChild, event);
2034
2035
0
exit:
2036
0
    return;
2037
0
}
2038
#endif // OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
2039
2040
0
bool Mle::IsMessageMleSubType(const Message &aMessage) { return aMessage.IsSubTypeMle(); }
2041
2042
bool Mle::IsMessageChildUpdateRequest(const Message &aMessage)
2043
0
{
2044
0
    return aMessage.IsMleCommand(kCommandChildUpdateRequest);
2045
0
}
2046
2047
void Mle::HandleChildIdRequest(RxInfo &aRxInfo)
2048
0
{
2049
0
    Error              error = kErrorNone;
2050
0
    Mac::ExtAddress    extAddr;
2051
0
    uint16_t           version;
2052
0
    uint32_t           linkFrameCounter;
2053
0
    uint32_t           mleFrameCounter;
2054
0
    DeviceMode         mode;
2055
0
    uint32_t           timeout;
2056
0
    TlvList            tlvList;
2057
0
    MeshCoP::Timestamp timestamp;
2058
0
    Child             *child;
2059
0
    Router            *router;
2060
0
    uint16_t           supervisionInterval;
2061
2062
0
    Log(kMessageReceive, kTypeChildIdRequest, aRxInfo.mMessageInfo.GetPeerAddr());
2063
2064
0
    VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
2065
2066
0
    VerifyOrExit(IsAttached(), error = kErrorInvalidState);
2067
2068
0
    extAddr.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid());
2069
2070
0
    child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
2071
0
    VerifyOrExit(child != nullptr, error = kErrorAlready);
2072
2073
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version));
2074
2075
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadAndMatchResponseTlvWith(child->GetChallenge()));
2076
2077
0
    Get<MeshForwarder>().RemoveMessagesForChild(*child, IsMessageMleSubType);
2078
2079
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
2080
2081
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadModeTlv(mode));
2082
2083
0
    SuccessOrExit(error = Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout));
2084
2085
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList));
2086
2087
0
    switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
2088
0
    {
2089
0
    case kErrorNone:
2090
0
        tlvList.Add(Tlv::kSupervisionInterval);
2091
0
        break;
2092
0
    case kErrorNotFound:
2093
0
        supervisionInterval = (version <= kThreadVersion1p3) ? kChildSupervisionDefaultIntervalForOlderVersion : 0;
2094
0
        break;
2095
0
    default:
2096
0
        ExitNow(error = kErrorParse);
2097
0
    }
2098
2099
0
    switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
2100
0
    {
2101
0
    case kErrorNone:
2102
0
        if (timestamp == Get<MeshCoP::ActiveDatasetManager>().GetTimestamp())
2103
0
        {
2104
0
            break;
2105
0
        }
2106
2107
0
        OT_FALL_THROUGH;
2108
2109
0
    case kErrorNotFound:
2110
0
        tlvList.Add(Tlv::kActiveDataset);
2111
0
        break;
2112
2113
0
    default:
2114
0
        ExitNow(error = kErrorParse);
2115
0
    }
2116
2117
0
    switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
2118
0
    {
2119
0
    case kErrorNone:
2120
0
        if (timestamp == Get<MeshCoP::PendingDatasetManager>().GetTimestamp())
2121
0
        {
2122
0
            break;
2123
0
        }
2124
2125
0
        OT_FALL_THROUGH;
2126
2127
0
    case kErrorNotFound:
2128
0
        tlvList.Add(Tlv::kPendingDataset);
2129
0
        break;
2130
2131
0
    default:
2132
0
        ExitNow(error = kErrorParse);
2133
0
    }
2134
2135
0
    VerifyOrExit(tlvList.GetLength() <= Child::kMaxRequestTlvs, error = kErrorParse);
2136
2137
0
    if (!mode.IsFullThreadDevice())
2138
0
    {
2139
0
        SuccessOrExit(error = ProcessAddressRegistrationTlv(aRxInfo, *child));
2140
0
    }
2141
2142
0
    router = mRouterTable.FindRouter(extAddr);
2143
2144
0
    if (router != nullptr)
2145
0
    {
2146
0
        RemoveNeighbor(*router);
2147
0
    }
2148
2149
0
    if (!child->IsStateValid())
2150
0
    {
2151
0
        child->SetState(Neighbor::kStateChildIdRequest);
2152
0
    }
2153
0
    else
2154
0
    {
2155
0
        RemoveNeighbor(*child);
2156
0
    }
2157
2158
0
    child->SetLastHeard(TimerMilli::GetNow());
2159
0
    child->GetLinkFrameCounters().SetAll(linkFrameCounter);
2160
0
    child->SetLinkAckFrameCounter(linkFrameCounter);
2161
0
    child->SetMleFrameCounter(mleFrameCounter);
2162
0
    child->SetKeySequence(aRxInfo.mKeySequence);
2163
0
    child->SetDeviceMode(mode);
2164
0
    child->SetVersion(version);
2165
0
    child->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
2166
0
    child->SetTimeout(timeout);
2167
0
    child->SetSupervisionInterval(supervisionInterval);
2168
#if OPENTHREAD_CONFIG_MULTI_RADIO
2169
    child->ClearLastRxFragmentTag();
2170
#endif
2171
2172
0
    child->SetNetworkDataVersion(mLeaderData.GetDataVersion(mode.GetNetworkDataType()));
2173
2174
    // We already checked above that `tlvList` will fit in
2175
    // `child` entry (with `Child::kMaxRequestTlvs` TLVs).
2176
2177
0
    child->ClearRequestTlvs();
2178
2179
0
    for (uint8_t index = 0; index < tlvList.GetLength(); index++)
2180
0
    {
2181
0
        child->SetRequestTlv(index, tlvList[index]);
2182
0
    }
2183
2184
0
    aRxInfo.mClass = RxInfo::kAuthoritativeMessage;
2185
0
    ProcessKeySequence(aRxInfo);
2186
2187
0
    switch (mRole)
2188
0
    {
2189
0
    case kRoleChild:
2190
0
        child->SetState(Neighbor::kStateChildIdRequest);
2191
0
        IgnoreError(BecomeRouter(ThreadStatusTlv::kHaveChildIdRequest));
2192
0
        break;
2193
2194
0
    case kRoleRouter:
2195
0
    case kRoleLeader:
2196
0
        SuccessOrExit(error = SendChildIdResponse(*child));
2197
0
        break;
2198
2199
0
    case kRoleDisabled:
2200
0
    case kRoleDetached:
2201
0
        OT_ASSERT(false);
2202
0
    }
2203
2204
0
exit:
2205
0
    LogProcessError(kTypeChildIdRequest, error);
2206
0
}
2207
2208
void Mle::HandleChildUpdateRequestOnParent(RxInfo &aRxInfo)
2209
0
{
2210
0
    Error           error = kErrorNone;
2211
0
    Mac::ExtAddress extAddr;
2212
0
    DeviceMode      mode;
2213
0
    RxChallenge     challenge;
2214
0
    LeaderData      leaderData;
2215
0
    uint32_t        timeout;
2216
0
    uint16_t        supervisionInterval;
2217
0
    Child          *child;
2218
0
    DeviceMode      oldMode;
2219
0
    TlvList         requestedTlvList;
2220
0
    TlvList         tlvList;
2221
0
    bool            childDidChange = false;
2222
2223
0
    Log(kMessageReceive, kTypeChildUpdateRequestOfChild, aRxInfo.mMessageInfo.GetPeerAddr());
2224
2225
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadModeTlv(mode));
2226
2227
0
    switch (aRxInfo.mMessage.ReadChallengeTlv(challenge))
2228
0
    {
2229
0
    case kErrorNone:
2230
0
        tlvList.Add(Tlv::kResponse);
2231
0
        break;
2232
0
    case kErrorNotFound:
2233
0
        challenge.Clear();
2234
0
        break;
2235
0
    default:
2236
0
        ExitNow(error = kErrorParse);
2237
0
    }
2238
2239
0
    tlvList.Add(Tlv::kSourceAddress);
2240
2241
0
    extAddr.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid());
2242
0
    child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
2243
2244
0
    if (child == nullptr)
2245
0
    {
2246
        // For invalid non-sleepy child, send Child Update Response with
2247
        // Status TLV (error).
2248
0
        if (mode.IsRxOnWhenIdle())
2249
0
        {
2250
0
            tlvList.Add(Tlv::kStatus);
2251
0
            SendChildUpdateResponseToChild(nullptr, aRxInfo.mMessageInfo, tlvList, challenge);
2252
0
        }
2253
2254
0
        ExitNow();
2255
0
    }
2256
2257
    // Ignore "Child Update Request" from a child that is present in the
2258
    // child table but it is not yet in valid state. For example, a
2259
    // child which is being restored (due to parent reset) or is in the
2260
    // middle of the attach process (in `kStateParentRequest` or
2261
    // `kStateChildIdRequest`).
2262
2263
0
    VerifyOrExit(child->IsStateValid());
2264
2265
0
    oldMode = child->GetDeviceMode();
2266
0
    child->SetDeviceMode(mode);
2267
2268
0
    tlvList.Add(Tlv::kMode);
2269
0
    tlvList.Add(Tlv::kLinkMargin);
2270
2271
    // Parent MUST include Leader Data TLV in Child Update Response
2272
0
    tlvList.Add(Tlv::kLeaderData);
2273
2274
0
    if (!challenge.IsEmpty())
2275
0
    {
2276
0
        tlvList.Add(Tlv::kMleFrameCounter);
2277
0
        tlvList.Add(Tlv::kLinkFrameCounter);
2278
0
    }
2279
2280
0
    switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
2281
0
    {
2282
0
    case kErrorNone:
2283
0
        tlvList.Add(Tlv::kAddressRegistration);
2284
0
        break;
2285
0
    case kErrorNotFound:
2286
0
        break;
2287
0
    default:
2288
0
        ExitNow(error = kErrorParse);
2289
0
    }
2290
2291
0
    switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
2292
0
    {
2293
0
    case kErrorNone:
2294
0
        child->SetNetworkDataVersion(leaderData.GetDataVersion(child->GetNetworkDataType()));
2295
0
        break;
2296
0
    case kErrorNotFound:
2297
0
        break;
2298
0
    default:
2299
0
        ExitNow(error = kErrorParse);
2300
0
    }
2301
2302
0
    switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
2303
0
    {
2304
0
    case kErrorNone:
2305
0
        if (child->GetTimeout() != timeout)
2306
0
        {
2307
0
            child->SetTimeout(timeout);
2308
0
            childDidChange = true;
2309
0
        }
2310
2311
0
        tlvList.Add(Tlv::kTimeout);
2312
0
        break;
2313
2314
0
    case kErrorNotFound:
2315
0
        break;
2316
2317
0
    default:
2318
0
        ExitNow(error = kErrorParse);
2319
0
    }
2320
2321
0
    switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
2322
0
    {
2323
0
    case kErrorNone:
2324
0
        tlvList.Add(Tlv::kSupervisionInterval);
2325
0
        break;
2326
2327
0
    case kErrorNotFound:
2328
0
        supervisionInterval =
2329
0
            (child->GetVersion() <= kThreadVersion1p3) ? kChildSupervisionDefaultIntervalForOlderVersion : 0;
2330
0
        break;
2331
2332
0
    default:
2333
0
        ExitNow(error = kErrorParse);
2334
0
    }
2335
2336
0
    child->SetSupervisionInterval(supervisionInterval);
2337
2338
0
    switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
2339
0
    {
2340
0
    case kErrorNone:
2341
0
        tlvList.AddElementsFrom(requestedTlvList);
2342
0
        break;
2343
0
    case kErrorNotFound:
2344
0
        break;
2345
0
    default:
2346
0
        ExitNow(error = kErrorParse);
2347
0
    }
2348
2349
0
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2350
0
    if (child->IsCslSynchronized())
2351
0
    {
2352
0
        ChannelTlvValue cslChannelTlvValue;
2353
0
        uint32_t        cslTimeout;
2354
2355
0
        switch (Tlv::Find<CslTimeoutTlv>(aRxInfo.mMessage, cslTimeout))
2356
0
        {
2357
0
        case kErrorNone:
2358
0
            child->SetCslTimeout(cslTimeout);
2359
            // MUST include CSL accuracy TLV when request includes CSL timeout
2360
0
            tlvList.Add(Tlv::kCslClockAccuracy);
2361
0
            break;
2362
0
        case kErrorNotFound:
2363
0
            break;
2364
0
        default:
2365
0
            ExitNow(error = kErrorNone);
2366
0
        }
2367
2368
0
        if (Tlv::Find<CslChannelTlv>(aRxInfo.mMessage, cslChannelTlvValue) == kErrorNone)
2369
0
        {
2370
            // Special value of zero is used to indicate that
2371
            // CSL channel is not specified.
2372
0
            child->SetCslChannel(static_cast<uint8_t>(cslChannelTlvValue.GetChannel()));
2373
0
        }
2374
0
    }
2375
0
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2376
2377
0
    child->SetLastHeard(TimerMilli::GetNow());
2378
2379
0
    if (oldMode != child->GetDeviceMode())
2380
0
    {
2381
0
        LogNote("Child 0x%04x mode change 0x%02x -> 0x%02x [%s]", child->GetRloc16(), oldMode.Get(),
2382
0
                child->GetDeviceMode().Get(), child->GetDeviceMode().ToString().AsCString());
2383
2384
0
        childDidChange = true;
2385
2386
0
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2387
0
        if (child->IsRxOnWhenIdle())
2388
0
        {
2389
            // Clear CSL synchronization state
2390
0
            child->SetCslSynchronized(false);
2391
0
        }
2392
0
#endif
2393
2394
        // The `IndirectSender::HandleChildModeChange()` needs to happen
2395
        // after "Child Update" message is fully parsed to ensure that
2396
        // any registered IPv6 addresses included in the "Child Update"
2397
        // are added to the child.
2398
2399
0
        Get<IndirectSender>().HandleChildModeChange(*child, oldMode);
2400
0
    }
2401
2402
0
    if (childDidChange)
2403
0
    {
2404
0
        IgnoreError(mChildTable.StoreChild(*child));
2405
0
    }
2406
2407
#if OPENTHREAD_CONFIG_MULTI_RADIO
2408
    // We clear the fragment tag only if the "Child Update Request" is
2409
    // from a detached child trying to restore its link with its
2410
    // parent which is indicated by the presence of Challenge TLV in
2411
    // the message.
2412
    if (!challenge.IsEmpty())
2413
    {
2414
        child->ClearLastRxFragmentTag();
2415
    }
2416
#endif
2417
2418
0
    SendChildUpdateResponseToChild(child, aRxInfo.mMessageInfo, tlvList, challenge);
2419
2420
0
    aRxInfo.mClass = RxInfo::kPeerMessage;
2421
2422
0
exit:
2423
0
    LogProcessError(kTypeChildUpdateRequestOfChild, error);
2424
0
}
2425
2426
void Mle::HandleChildUpdateResponseOnParent(RxInfo &aRxInfo)
2427
0
{
2428
0
    Error       error = kErrorNone;
2429
0
    uint16_t    sourceAddress;
2430
0
    uint32_t    timeout;
2431
0
    RxChallenge response;
2432
0
    uint8_t     status;
2433
0
    uint32_t    linkFrameCounter;
2434
0
    uint32_t    mleFrameCounter;
2435
0
    LeaderData  leaderData;
2436
0
    Child      *child;
2437
2438
0
    if ((aRxInfo.mNeighbor == nullptr) || IsRouterRloc16(aRxInfo.mNeighbor->GetRloc16()) ||
2439
0
        !Get<ChildTable>().Contains(*aRxInfo.mNeighbor))
2440
0
    {
2441
0
        Log(kMessageReceive, kTypeChildUpdateResponseOfUnknownChild, aRxInfo.mMessageInfo.GetPeerAddr());
2442
0
        ExitNow(error = kErrorNotFound);
2443
0
    }
2444
2445
0
    child = static_cast<Child *>(aRxInfo.mNeighbor);
2446
2447
0
    switch (aRxInfo.mMessage.ReadResponseTlv(response))
2448
0
    {
2449
0
    case kErrorNone:
2450
0
        VerifyOrExit(response == child->GetChallenge(), error = kErrorSecurity);
2451
0
        break;
2452
0
    case kErrorNotFound:
2453
0
        VerifyOrExit(child->IsStateValid(), error = kErrorSecurity);
2454
0
        response.Clear();
2455
0
        break;
2456
0
    default:
2457
0
        ExitNow(error = kErrorNone);
2458
0
    }
2459
2460
0
    Log(kMessageReceive, kTypeChildUpdateResponseOfChild, aRxInfo.mMessageInfo.GetPeerAddr(), child->GetRloc16());
2461
2462
0
    switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
2463
0
    {
2464
0
    case kErrorNone:
2465
0
        if (child->GetRloc16() != sourceAddress)
2466
0
        {
2467
0
            RemoveNeighbor(*child);
2468
0
            ExitNow();
2469
0
        }
2470
2471
0
        break;
2472
2473
0
    case kErrorNotFound:
2474
0
        break;
2475
2476
0
    default:
2477
0
        ExitNow(error = kErrorParse);
2478
0
    }
2479
2480
0
    switch (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status))
2481
0
    {
2482
0
    case kErrorNone:
2483
0
        VerifyOrExit(status != StatusTlv::kError, RemoveNeighbor(*child));
2484
0
        break;
2485
0
    case kErrorNotFound:
2486
0
        break;
2487
0
    default:
2488
0
        ExitNow(error = kErrorParse);
2489
0
    }
2490
2491
0
    switch (Tlv::Find<LinkFrameCounterTlv>(aRxInfo.mMessage, linkFrameCounter))
2492
0
    {
2493
0
    case kErrorNone:
2494
0
        child->GetLinkFrameCounters().SetAll(linkFrameCounter);
2495
0
        child->SetLinkAckFrameCounter(linkFrameCounter);
2496
0
        break;
2497
0
    case kErrorNotFound:
2498
0
        break;
2499
0
    default:
2500
0
        ExitNow(error = kErrorParse);
2501
0
    }
2502
2503
0
    switch (Tlv::Find<MleFrameCounterTlv>(aRxInfo.mMessage, mleFrameCounter))
2504
0
    {
2505
0
    case kErrorNone:
2506
0
        child->SetMleFrameCounter(mleFrameCounter);
2507
0
        break;
2508
0
    case kErrorNotFound:
2509
0
        break;
2510
0
    default:
2511
0
        ExitNow(error = kErrorNone);
2512
0
    }
2513
2514
0
    switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
2515
0
    {
2516
0
    case kErrorNone:
2517
0
        child->SetTimeout(timeout);
2518
0
        break;
2519
0
    case kErrorNotFound:
2520
0
        break;
2521
0
    default:
2522
0
        ExitNow(error = kErrorParse);
2523
0
    }
2524
2525
0
    {
2526
0
        uint16_t supervisionInterval;
2527
2528
0
        switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
2529
0
        {
2530
0
        case kErrorNone:
2531
0
            child->SetSupervisionInterval(supervisionInterval);
2532
0
            break;
2533
0
        case kErrorNotFound:
2534
0
            break;
2535
0
        default:
2536
0
            ExitNow(error = kErrorParse);
2537
0
        }
2538
0
    }
2539
2540
0
    switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
2541
0
    {
2542
0
    case kErrorNone:
2543
0
    case kErrorNotFound:
2544
0
        break;
2545
0
    default:
2546
0
        ExitNow(error = kErrorParse);
2547
0
    }
2548
2549
0
    switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
2550
0
    {
2551
0
    case kErrorNone:
2552
0
        child->SetNetworkDataVersion(leaderData.GetDataVersion(child->GetNetworkDataType()));
2553
0
        break;
2554
0
    case kErrorNotFound:
2555
0
        break;
2556
0
    default:
2557
0
        ExitNow(error = kErrorParse);
2558
0
    }
2559
2560
0
    SetChildStateToValid(*child);
2561
0
    child->SetLastHeard(TimerMilli::GetNow());
2562
0
    child->SetKeySequence(aRxInfo.mKeySequence);
2563
0
    child->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
2564
2565
0
    aRxInfo.mClass = response.IsEmpty() ? RxInfo::kPeerMessage : RxInfo::kAuthoritativeMessage;
2566
2567
0
exit:
2568
0
    LogProcessError(kTypeChildUpdateResponseOfChild, error);
2569
0
}
2570
2571
void Mle::HandleDataRequest(RxInfo &aRxInfo)
2572
0
{
2573
0
    Error              error = kErrorNone;
2574
0
    TlvList            tlvList;
2575
0
    MeshCoP::Timestamp timestamp;
2576
2577
0
    Log(kMessageReceive, kTypeDataRequest, aRxInfo.mMessageInfo.GetPeerAddr());
2578
2579
0
    VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorSecurity);
2580
2581
0
    SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList));
2582
2583
0
    switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
2584
0
    {
2585
0
    case kErrorNone:
2586
0
        if (timestamp == Get<MeshCoP::ActiveDatasetManager>().GetTimestamp())
2587
0
        {
2588
0
            break;
2589
0
        }
2590
2591
0
        OT_FALL_THROUGH;
2592
2593
0
    case kErrorNotFound:
2594
0
        tlvList.Add(Tlv::kActiveDataset);
2595
0
        break;
2596
2597
0
    default:
2598
0
        ExitNow(error = kErrorParse);
2599
0
    }
2600
2601
0
    switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
2602
0
    {
2603
0
    case kErrorNone:
2604
0
        if (timestamp == Get<MeshCoP::PendingDatasetManager>().GetTimestamp())
2605
0
        {
2606
0
            break;
2607
0
        }
2608
2609
0
        OT_FALL_THROUGH;
2610
2611
0
    case kErrorNotFound:
2612
0
        tlvList.Add(Tlv::kPendingDataset);
2613
0
        break;
2614
2615
0
    default:
2616
0
        ExitNow(error = kErrorParse);
2617
0
    }
2618
2619
0
    aRxInfo.mClass = RxInfo::kPeerMessage;
2620
0
    ProcessKeySequence(aRxInfo);
2621
2622
0
    SendDataResponse(aRxInfo.mMessageInfo.GetPeerAddr(), tlvList, &aRxInfo.mMessage);
2623
2624
0
exit:
2625
0
    LogProcessError(kTypeDataRequest, error);
2626
0
}
2627
2628
void Mle::HandleNetworkDataUpdateRouter(void)
2629
21.3k
{
2630
21.3k
    uint16_t delay;
2631
2632
21.3k
    VerifyOrExit(IsRouterOrLeader());
2633
2634
0
    if (IsLeader())
2635
0
    {
2636
0
        SendMulticastDataResponse();
2637
0
    }
2638
0
    else
2639
0
    {
2640
0
        delay = 1 + Random::NonCrypto::GetUint16InRange(0, kUnsolicitedDataResponseJitter);
2641
0
        mDelayedSender.ScheduleMulticastDataResponse(delay);
2642
0
    }
2643
2644
0
    SynchronizeChildNetworkData();
2645
2646
21.3k
exit:
2647
21.3k
    return;
2648
0
}
2649
2650
void Mle::SynchronizeChildNetworkData(void)
2651
0
{
2652
0
    VerifyOrExit(IsRouterOrLeader());
2653
2654
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
2655
0
    {
2656
0
        if (child.IsRxOnWhenIdle())
2657
0
        {
2658
0
            continue;
2659
0
        }
2660
2661
0
        if (child.GetNetworkDataVersion() == Get<NetworkData::Leader>().GetVersion(child.GetNetworkDataType()))
2662
0
        {
2663
0
            continue;
2664
0
        }
2665
2666
0
        SuccessOrExit(SendChildUpdateRequestToChild(child));
2667
0
    }
2668
2669
0
exit:
2670
0
    return;
2671
0
}
2672
2673
#if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
2674
void Mle::SetSteeringData(const Mac::ExtAddress *aExtAddress)
2675
{
2676
    Mac::ExtAddress nullExtAddr;
2677
    Mac::ExtAddress allowAnyExtAddr;
2678
2679
    nullExtAddr.Clear();
2680
    allowAnyExtAddr.Fill(0xff);
2681
2682
    if ((aExtAddress == nullptr) || (*aExtAddress == nullExtAddr))
2683
    {
2684
        mSteeringData.Clear();
2685
    }
2686
    else if (*aExtAddress == allowAnyExtAddr)
2687
    {
2688
        mSteeringData.SetToPermitAllJoiners();
2689
    }
2690
    else
2691
    {
2692
        Mac::ExtAddress joinerId;
2693
2694
        mSteeringData.Init();
2695
        MeshCoP::ComputeJoinerId(*aExtAddress, joinerId);
2696
        mSteeringData.UpdateBloomFilter(joinerId);
2697
    }
2698
}
2699
#endif // OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
2700
2701
void Mle::HandleDiscoveryRequest(RxInfo &aRxInfo)
2702
0
{
2703
0
    Error                        error = kErrorNone;
2704
0
    Tlv::ParsedInfo              tlvInfo;
2705
0
    MeshCoP::DiscoveryRequestTlv discoveryRequestTlv;
2706
0
    MeshCoP::ExtendedPanId       extPanId;
2707
0
    OffsetRange                  offsetRange;
2708
0
    DiscoveryResponseInfo        responseInfo;
2709
2710
0
    Log(kMessageReceive, kTypeDiscoveryRequest, aRxInfo.mMessageInfo.GetPeerAddr());
2711
2712
0
    discoveryRequestTlv.SetLength(0);
2713
2714
0
    VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
2715
2716
0
    SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange));
2717
2718
0
    for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize()))
2719
0
    {
2720
0
        SuccessOrExit(error = tlvInfo.ParseFrom(aRxInfo.mMessage, offsetRange));
2721
2722
0
        if (tlvInfo.mIsExtended)
2723
0
        {
2724
0
            continue;
2725
0
        }
2726
2727
0
        switch (tlvInfo.mType)
2728
0
        {
2729
0
        case MeshCoP::Tlv::kDiscoveryRequest:
2730
0
            SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryRequestTlv));
2731
0
            VerifyOrExit(discoveryRequestTlv.IsValid(), error = kErrorParse);
2732
2733
0
            break;
2734
2735
0
        case MeshCoP::Tlv::kExtendedPanId:
2736
0
            SuccessOrExit(
2737
0
                error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offsetRange.GetOffset(), extPanId));
2738
0
            VerifyOrExit(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId() != extPanId, error = kErrorDrop);
2739
2740
0
            break;
2741
2742
0
        default:
2743
0
            break;
2744
0
        }
2745
0
    }
2746
2747
0
    if (discoveryRequestTlv.IsValid())
2748
0
    {
2749
0
        if (mDiscoveryRequestCallback.IsSet())
2750
0
        {
2751
0
            otThreadDiscoveryRequestInfo info;
2752
2753
0
            AsCoreType(&info.mExtAddress).SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid());
2754
0
            info.mVersion  = discoveryRequestTlv.GetVersion();
2755
0
            info.mIsJoiner = discoveryRequestTlv.IsJoiner();
2756
2757
0
            mDiscoveryRequestCallback.Invoke(&info);
2758
0
        }
2759
2760
0
        if (discoveryRequestTlv.IsJoiner())
2761
0
        {
2762
#if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
2763
            if (!mSteeringData.IsEmpty())
2764
            {
2765
            }
2766
            else // if steering data is not set out of band, fall back to network data
2767
#endif
2768
0
            {
2769
0
                VerifyOrExit(Get<NetworkData::Leader>().IsJoiningAllowed(), error = kErrorSecurity);
2770
0
            }
2771
0
        }
2772
0
    }
2773
2774
0
    responseInfo.mPanId = aRxInfo.mMessage.GetPanId();
2775
2776
#if OPENTHREAD_CONFIG_MULTI_RADIO
2777
    // Send the MLE Discovery Response message on same radio link
2778
    // from which the "MLE Discover Request" message was received.
2779
    responseInfo.mRadioType = aRxInfo.mMessage.GetRadioType();
2780
#endif
2781
2782
0
    mDelayedSender.ScheduleDiscoveryResponse(aRxInfo.mMessageInfo.GetPeerAddr(), responseInfo,
2783
0
                                             1 + Random::NonCrypto::GetUint16InRange(0, kDiscoveryMaxJitter));
2784
2785
0
exit:
2786
0
    LogProcessError(kTypeDiscoveryRequest, error);
2787
0
}
2788
2789
Error Mle::SendDiscoveryResponse(const Ip6::Address &aDestination, const DiscoveryResponseInfo &aInfo)
2790
0
{
2791
0
    Error                         error = kErrorNone;
2792
0
    TxMessage                    *message;
2793
0
    uint16_t                      startOffset;
2794
0
    Tlv                           tlv;
2795
0
    MeshCoP::DiscoveryResponseTlv discoveryResponseTlv;
2796
2797
0
    VerifyOrExit((message = NewMleMessage(kCommandDiscoveryResponse)) != nullptr, error = kErrorNoBufs);
2798
0
    message->SetDirectTransmission();
2799
0
    message->SetPanId(aInfo.mPanId);
2800
#if OPENTHREAD_CONFIG_MULTI_RADIO
2801
    message->SetRadioType(aInfo.mRadioType);
2802
#endif
2803
2804
0
    tlv.SetType(Tlv::kDiscovery);
2805
0
    SuccessOrExit(error = message->Append(tlv));
2806
2807
0
    startOffset = message->GetLength();
2808
2809
0
    discoveryResponseTlv.Init();
2810
0
    discoveryResponseTlv.SetVersion(kThreadVersion);
2811
2812
0
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
2813
0
    if (Get<KeyManager>().GetSecurityPolicy().mNativeCommissioningEnabled)
2814
0
    {
2815
0
        SuccessOrExit(
2816
0
            error = Tlv::Append<MeshCoP::CommissionerUdpPortTlv>(*message, Get<MeshCoP::BorderAgent>().GetUdpPort()));
2817
2818
0
        discoveryResponseTlv.SetNativeCommissioner(true);
2819
0
    }
2820
0
    else
2821
0
#endif
2822
0
    {
2823
0
        discoveryResponseTlv.SetNativeCommissioner(false);
2824
0
    }
2825
2826
0
    if (Get<KeyManager>().GetSecurityPolicy().mCommercialCommissioningEnabled)
2827
0
    {
2828
0
        discoveryResponseTlv.SetCommercialCommissioningMode(true);
2829
0
    }
2830
2831
0
    SuccessOrExit(error = discoveryResponseTlv.AppendTo(*message));
2832
2833
0
    SuccessOrExit(
2834
0
        error = Tlv::Append<MeshCoP::ExtendedPanIdTlv>(*message, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()));
2835
2836
0
    SuccessOrExit(error = Tlv::Append<MeshCoP::NetworkNameTlv>(
2837
0
                      *message, Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsCString()));
2838
2839
0
    SuccessOrExit(error = message->AppendSteeringDataTlv());
2840
2841
0
    SuccessOrExit(
2842
0
        error = Tlv::Append<MeshCoP::JoinerUdpPortTlv>(*message, Get<MeshCoP::JoinerRouter>().GetJoinerUdpPort()));
2843
2844
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4)
2845
    if (!Get<MeshCoP::NetworkNameManager>().IsDefaultDomainNameSet())
2846
    {
2847
        SuccessOrExit(error = Tlv::Append<MeshCoP::ThreadDomainNameTlv>(
2848
                          *message, Get<MeshCoP::NetworkNameManager>().GetDomainName().GetAsCString()));
2849
    }
2850
#endif
2851
2852
0
    tlv.SetLength(static_cast<uint8_t>(message->GetLength() - startOffset));
2853
0
    message->Write(startOffset - sizeof(tlv), tlv);
2854
2855
0
    SuccessOrExit(error = message->SendTo(aDestination));
2856
2857
0
    Log(kMessageSend, kTypeDiscoveryResponse, aDestination);
2858
2859
0
exit:
2860
0
    FreeMessageOnError(message, error);
2861
0
    LogProcessError(kTypeDiscoveryResponse, error);
2862
0
    return error;
2863
0
}
2864
2865
Error Mle::SendChildIdResponse(Child &aChild)
2866
0
{
2867
0
    Error        error = kErrorNone;
2868
0
    Ip6::Address destination;
2869
0
    TxMessage   *message;
2870
2871
0
    VerifyOrExit((message = NewMleMessage(kCommandChildIdResponse)) != nullptr, error = kErrorNoBufs);
2872
0
    SuccessOrExit(error = message->AppendSourceAddressTlv());
2873
0
    SuccessOrExit(error = message->AppendLeaderDataTlv());
2874
0
    SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs());
2875
2876
0
    if ((aChild.GetRloc16() == 0) || !HasMatchingRouterIdWith(aChild.GetRloc16()))
2877
0
    {
2878
0
        uint16_t rloc16;
2879
2880
        // Pick next Child ID that is not being used
2881
0
        do
2882
0
        {
2883
0
            mNextChildId++;
2884
2885
0
            if (mNextChildId > kMaxChildId)
2886
0
            {
2887
0
                mNextChildId = kMinChildId;
2888
0
            }
2889
2890
0
            rloc16 = Get<Mac::Mac>().GetShortAddress() | mNextChildId;
2891
2892
0
        } while (mChildTable.FindChild(rloc16, Child::kInStateAnyExceptInvalid) != nullptr);
2893
2894
0
        aChild.SetRloc16(rloc16);
2895
0
    }
2896
2897
0
    SuccessOrExit(error = message->AppendAddress16Tlv(aChild.GetRloc16()));
2898
2899
0
    for (uint8_t i = 0; i < Child::kMaxRequestTlvs; i++)
2900
0
    {
2901
0
        switch (aChild.GetRequestTlv(i))
2902
0
        {
2903
0
        case Tlv::kNetworkData:
2904
0
            SuccessOrExit(error = message->AppendNetworkDataTlv(aChild.GetNetworkDataType()));
2905
0
            break;
2906
2907
0
        case Tlv::kRoute:
2908
0
            SuccessOrExit(error = message->AppendRouteTlv());
2909
0
            break;
2910
2911
0
        case Tlv::kActiveDataset:
2912
0
            SuccessOrExit(error = message->AppendActiveDatasetTlv());
2913
0
            break;
2914
2915
0
        case Tlv::kPendingDataset:
2916
0
            SuccessOrExit(error = message->AppendPendingDatasetTlv());
2917
0
            break;
2918
2919
0
        case Tlv::kSupervisionInterval:
2920
0
            SuccessOrExit(error = message->AppendSupervisionIntervalTlv(aChild.GetSupervisionInterval()));
2921
0
            break;
2922
2923
0
        default:
2924
0
            break;
2925
0
        }
2926
0
    }
2927
2928
0
    if (!aChild.IsFullThreadDevice())
2929
0
    {
2930
0
        SuccessOrExit(error = message->AppendAddressRegistrationTlv(aChild));
2931
0
    }
2932
2933
0
    SetChildStateToValid(aChild);
2934
2935
0
    if (!aChild.IsRxOnWhenIdle())
2936
0
    {
2937
0
        Get<IndirectSender>().SetChildUseShortAddress(aChild, false);
2938
0
    }
2939
2940
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
2941
    if (aChild.IsTimeSyncEnabled())
2942
    {
2943
        message->SetTimeSync(true);
2944
    }
2945
#endif
2946
2947
0
    destination.SetToLinkLocalAddress(aChild.GetExtAddress());
2948
0
    SuccessOrExit(error = message->SendTo(destination));
2949
2950
0
    Log(kMessageSend, kTypeChildIdResponse, destination, aChild.GetRloc16());
2951
2952
0
exit:
2953
0
    FreeMessageOnError(message, error);
2954
0
    return error;
2955
0
}
2956
2957
Error Mle::SendChildUpdateRequestToChild(Child &aChild)
2958
0
{
2959
0
    static const uint8_t kTlvs[] = {Tlv::kTimeout, Tlv::kAddressRegistration};
2960
2961
0
    Error        error = kErrorNone;
2962
0
    Ip6::Address destination;
2963
0
    TxMessage   *message = nullptr;
2964
2965
0
    if (!aChild.IsRxOnWhenIdle() && aChild.IsStateRestoring())
2966
0
    {
2967
        // No need to send the resync "Child Update Request"
2968
        // to the sleepy child if there is one already
2969
        // queued.
2970
2971
0
        VerifyOrExit(!Get<IndirectSender>().HasQueuedMessageForSleepyChild(aChild, IsMessageChildUpdateRequest));
2972
0
    }
2973
2974
0
    Get<MeshForwarder>().RemoveMessagesForChild(aChild, IsMessageChildUpdateRequest);
2975
2976
0
    VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs);
2977
0
    SuccessOrExit(error = message->AppendSourceAddressTlv());
2978
0
    SuccessOrExit(error = message->AppendLeaderDataTlv());
2979
0
    SuccessOrExit(error = message->AppendNetworkDataTlv(aChild.GetNetworkDataType()));
2980
0
    SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs());
2981
2982
0
    if (aChild.IsStateValid())
2983
0
    {
2984
0
        SuccessOrExit(error = message->AppendLinkMarginTlv(aChild.GetLinkInfo().GetLinkMargin()));
2985
0
    }
2986
0
    else
2987
0
    {
2988
0
        SuccessOrExit(error = message->AppendTlvRequestTlv(kTlvs));
2989
2990
0
        if (!aChild.IsStateRestored())
2991
0
        {
2992
            // A random challenge is generated and saved when `aChild`
2993
            // is first initialized in `kStateRestored`. We will use
2994
            // the saved challenge here. This prevents overwriting
2995
            // the saved challenge when the child is also detached
2996
            // and happens to send a "Parent Request" in the window
2997
            // where the parent transitions to the router/leader role
2998
            // and before the parent sends the "Child Update Request".
2999
            // This ensures that the same random challenge is
3000
            // included in both "Parent Response" and "Child Update
3001
            // Response," guaranteeing proper acceptance of the
3002
            // child's "Child ID request".
3003
3004
0
            aChild.GenerateChallenge();
3005
0
        }
3006
3007
0
        SuccessOrExit(error = message->AppendChallengeTlv(aChild.GetChallenge()));
3008
0
    }
3009
3010
0
    destination.SetToLinkLocalAddress(aChild.GetExtAddress());
3011
0
    SuccessOrExit(error = message->SendTo(destination));
3012
3013
0
    if (aChild.IsRxOnWhenIdle())
3014
0
    {
3015
        // only try to send a single Child Update Request message to an rx-on-when-idle child
3016
0
        aChild.SetState(Child::kStateChildUpdateRequest);
3017
0
    }
3018
3019
0
    Log(kMessageSend, kTypeChildUpdateRequestOfChild, destination, aChild.GetRloc16());
3020
3021
0
exit:
3022
0
    FreeMessageOnError(message, error);
3023
0
    return error;
3024
0
}
3025
3026
void Mle::SendChildUpdateResponseToChild(Child                  *aChild,
3027
                                         const Ip6::MessageInfo &aMessageInfo,
3028
                                         const TlvList          &aTlvList,
3029
                                         const RxChallenge      &aChallenge)
3030
0
{
3031
0
    Error      error = kErrorNone;
3032
0
    TxMessage *message;
3033
3034
0
    VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs);
3035
3036
0
    for (uint8_t tlvType : aTlvList)
3037
0
    {
3038
        // Add all TLV types that do not depend on `child`
3039
3040
0
        switch (tlvType)
3041
0
        {
3042
0
        case Tlv::kStatus:
3043
0
            SuccessOrExit(error = message->AppendStatusTlv(StatusTlv::kError));
3044
0
            break;
3045
3046
0
        case Tlv::kLeaderData:
3047
0
            SuccessOrExit(error = message->AppendLeaderDataTlv());
3048
0
            break;
3049
3050
0
        case Tlv::kResponse:
3051
0
            SuccessOrExit(error = message->AppendResponseTlv(aChallenge));
3052
0
            break;
3053
3054
0
        case Tlv::kSourceAddress:
3055
0
            SuccessOrExit(error = message->AppendSourceAddressTlv());
3056
0
            break;
3057
3058
0
        case Tlv::kMleFrameCounter:
3059
0
            SuccessOrExit(error = message->AppendMleFrameCounterTlv());
3060
0
            break;
3061
3062
0
        case Tlv::kLinkFrameCounter:
3063
0
            SuccessOrExit(error = message->AppendLinkFrameCounterTlv());
3064
0
            break;
3065
0
        }
3066
3067
        // Make sure `child` is not null before adding TLV types
3068
        // that can depend on it.
3069
3070
0
        if (aChild == nullptr)
3071
0
        {
3072
0
            continue;
3073
0
        }
3074
3075
0
        switch (tlvType)
3076
0
        {
3077
0
        case Tlv::kAddressRegistration:
3078
0
            SuccessOrExit(error = message->AppendAddressRegistrationTlv(*aChild));
3079
0
            break;
3080
3081
0
        case Tlv::kMode:
3082
0
            SuccessOrExit(error = message->AppendModeTlv(aChild->GetDeviceMode()));
3083
0
            break;
3084
3085
0
        case Tlv::kNetworkData:
3086
0
            SuccessOrExit(error = message->AppendNetworkDataTlv(aChild->GetNetworkDataType()));
3087
0
            SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs());
3088
0
            break;
3089
3090
0
        case Tlv::kTimeout:
3091
0
            SuccessOrExit(error = message->AppendTimeoutTlv(aChild->GetTimeout()));
3092
0
            break;
3093
3094
0
        case Tlv::kLinkMargin:
3095
0
            SuccessOrExit(error = message->AppendLinkMarginTlv(aChild->GetLinkInfo().GetLinkMargin()));
3096
0
            break;
3097
3098
0
        case Tlv::kSupervisionInterval:
3099
0
            SuccessOrExit(error = message->AppendSupervisionIntervalTlv(aChild->GetSupervisionInterval()));
3100
0
            break;
3101
3102
0
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
3103
0
        case Tlv::kCslClockAccuracy:
3104
0
            if (!aChild->IsRxOnWhenIdle())
3105
0
            {
3106
0
                SuccessOrExit(error = message->AppendCslClockAccuracyTlv());
3107
0
            }
3108
0
            break;
3109
0
#endif
3110
0
        }
3111
0
    }
3112
3113
0
    SuccessOrExit(error = message->SendTo(aMessageInfo.GetPeerAddr()));
3114
3115
0
    if (aChild == nullptr)
3116
0
    {
3117
0
        Log(kMessageSend, kTypeChildUpdateResponseOfChild, aMessageInfo.GetPeerAddr());
3118
0
    }
3119
0
    else
3120
0
    {
3121
0
        Log(kMessageSend, kTypeChildUpdateResponseOfChild, aMessageInfo.GetPeerAddr(), aChild->GetRloc16());
3122
0
    }
3123
3124
0
exit:
3125
0
    FreeMessageOnError(message, error);
3126
0
}
3127
3128
void Mle::SendMulticastDataResponse(void)
3129
0
{
3130
0
    Ip6::Address destination;
3131
0
    TlvList      tlvList;
3132
3133
0
    destination.SetToLinkLocalAllNodesMulticast();
3134
0
    tlvList.Add(Tlv::kNetworkData);
3135
0
    SendDataResponse(destination, tlvList);
3136
0
}
3137
3138
void Mle::SendDataResponse(const Ip6::Address &aDestination, const TlvList &aTlvList, const Message *aRequestMessage)
3139
0
{
3140
0
    OT_UNUSED_VARIABLE(aRequestMessage);
3141
3142
0
    Error      error   = kErrorNone;
3143
0
    TxMessage *message = nullptr;
3144
0
    Neighbor  *neighbor;
3145
3146
0
    VerifyOrExit(IsAttached());
3147
3148
0
    if (mRetrieveNewNetworkData)
3149
0
    {
3150
0
        LogInfo("Suppressing Data Response - waiting for new network data");
3151
0
        ExitNow();
3152
0
    }
3153
3154
0
    VerifyOrExit((message = NewMleMessage(kCommandDataResponse)) != nullptr, error = kErrorNoBufs);
3155
0
    SuccessOrExit(error = message->AppendSourceAddressTlv());
3156
0
    SuccessOrExit(error = message->AppendLeaderDataTlv());
3157
0
    SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs());
3158
3159
0
    for (uint8_t tlvType : aTlvList)
3160
0
    {
3161
0
        switch (tlvType)
3162
0
        {
3163
0
        case Tlv::kNetworkData:
3164
0
            neighbor = mNeighborTable.FindNeighbor(aDestination);
3165
0
            SuccessOrExit(error = message->AppendNetworkDataTlv((neighbor != nullptr) ? neighbor->GetNetworkDataType()
3166
0
                                                                                      : NetworkData::kFullSet));
3167
0
            break;
3168
3169
0
        case Tlv::kActiveDataset:
3170
0
            SuccessOrExit(error = message->AppendActiveDatasetTlv());
3171
0
            break;
3172
3173
0
        case Tlv::kPendingDataset:
3174
0
            SuccessOrExit(error = message->AppendPendingDatasetTlv());
3175
0
            break;
3176
3177
0
        case Tlv::kRoute:
3178
0
            SuccessOrExit(error = message->AppendRouteTlv());
3179
0
            break;
3180
3181
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
3182
        case Tlv::kLinkMetricsReport:
3183
            OT_ASSERT(aRequestMessage != nullptr);
3184
            neighbor = mNeighborTable.FindNeighbor(aDestination);
3185
            VerifyOrExit(neighbor != nullptr, error = kErrorInvalidState);
3186
            SuccessOrExit(error = Get<LinkMetrics::Subject>().AppendReport(*message, *aRequestMessage, *neighbor));
3187
            break;
3188
#endif
3189
0
        }
3190
0
    }
3191
3192
0
    SuccessOrExit(error = message->SendTo(aDestination));
3193
0
    Log(kMessageSend, kTypeDataResponse, aDestination);
3194
3195
0
exit:
3196
0
    FreeMessageOnError(message, error);
3197
0
    LogSendError(kTypeDataResponse, error);
3198
0
}
3199
3200
void Mle::RemoveRouterLink(Router &aRouter)
3201
0
{
3202
0
    switch (mRole)
3203
0
    {
3204
0
    case kRoleChild:
3205
0
        if (&aRouter == &mParent)
3206
0
        {
3207
0
            IgnoreError(BecomeDetached());
3208
0
        }
3209
0
        break;
3210
3211
0
    case kRoleRouter:
3212
0
    case kRoleLeader:
3213
0
        mRouterTable.RemoveRouterLink(aRouter);
3214
0
        break;
3215
3216
0
    default:
3217
0
        break;
3218
0
    }
3219
0
}
3220
3221
void Mle::RemoveNeighbor(Neighbor &aNeighbor)
3222
0
{
3223
0
    VerifyOrExit(!aNeighbor.IsStateInvalid());
3224
3225
0
    if (&aNeighbor == &mParent)
3226
0
    {
3227
0
        if (IsChild())
3228
0
        {
3229
0
            IgnoreError(BecomeDetached());
3230
0
        }
3231
0
    }
3232
0
    else if (&aNeighbor == &GetParentCandidate())
3233
0
    {
3234
0
        ClearParentCandidate();
3235
0
    }
3236
0
    else if (IsChildRloc16(aNeighbor.GetRloc16()))
3237
0
    {
3238
0
        OT_ASSERT(mChildTable.Contains(aNeighbor));
3239
3240
0
        if (aNeighbor.IsStateValidOrRestoring())
3241
0
        {
3242
0
            mNeighborTable.Signal(NeighborTable::kChildRemoved, aNeighbor);
3243
0
        }
3244
3245
0
        Get<IndirectSender>().ClearAllMessagesForSleepyChild(static_cast<Child &>(aNeighbor));
3246
3247
0
        if (aNeighbor.IsFullThreadDevice())
3248
0
        {
3249
0
            Get<AddressResolver>().RemoveEntriesForRloc16(aNeighbor.GetRloc16());
3250
0
        }
3251
3252
0
        mChildTable.RemoveStoredChild(static_cast<Child &>(aNeighbor));
3253
0
    }
3254
0
    else if (aNeighbor.IsStateValid())
3255
0
    {
3256
0
        OT_ASSERT(mRouterTable.Contains(aNeighbor));
3257
3258
0
        mNeighborTable.Signal(NeighborTable::kRouterRemoved, aNeighbor);
3259
0
        mRouterTable.RemoveRouterLink(static_cast<Router &>(aNeighbor));
3260
0
    }
3261
3262
0
    aNeighbor.GetLinkInfo().Clear();
3263
0
    aNeighbor.SetState(Neighbor::kStateInvalid);
3264
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
3265
    aNeighbor.RemoveAllForwardTrackingSeriesInfo();
3266
#endif
3267
3268
0
exit:
3269
0
    return;
3270
0
}
3271
3272
Error Mle::SetPreferredRouterId(uint8_t aRouterId)
3273
403
{
3274
403
    Error error = kErrorNone;
3275
3276
403
    VerifyOrExit(IsDetached() || IsDisabled(), error = kErrorInvalidState);
3277
3278
403
    mPreviousRouterId = aRouterId;
3279
3280
403
exit:
3281
403
    return error;
3282
403
}
3283
3284
void Mle::SetRouterId(uint8_t aRouterId)
3285
21.3k
{
3286
21.3k
    mRouterId         = aRouterId;
3287
21.3k
    mPreviousRouterId = mRouterId;
3288
21.3k
}
3289
3290
Error Mle::SendAddressSolicit(ThreadStatusTlv::Status aStatus)
3291
0
{
3292
0
    Error            error = kErrorNone;
3293
0
    Tmf::MessageInfo messageInfo(GetInstance());
3294
0
    Coap::Message   *message = nullptr;
3295
3296
0
    VerifyOrExit(!mAddressSolicitPending);
3297
3298
0
    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriAddressSolicit);
3299
0
    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
3300
3301
0
    SuccessOrExit(error = Tlv::Append<ThreadExtMacAddressTlv>(*message, Get<Mac::Mac>().GetExtAddress()));
3302
3303
0
    if (IsRouterIdValid(mPreviousRouterId))
3304
0
    {
3305
0
        SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, Rloc16FromRouterId(mPreviousRouterId)));
3306
0
    }
3307
3308
0
    SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(*message, aStatus));
3309
3310
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3311
    SuccessOrExit(error = Tlv::Append<XtalAccuracyTlv>(*message, otPlatTimeGetXtalAccuracy()));
3312
#endif
3313
3314
0
    messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc();
3315
3316
0
    SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &HandleAddressSolicitResponse, this));
3317
0
    mAddressSolicitPending = true;
3318
3319
0
    Log(kMessageSend, kTypeAddressSolicit, messageInfo.GetPeerAddr());
3320
3321
0
exit:
3322
0
    FreeMessageOnError(message, error);
3323
0
    return error;
3324
0
}
3325
3326
void Mle::SendAddressRelease(void)
3327
0
{
3328
0
    Error            error = kErrorNone;
3329
0
    Tmf::MessageInfo messageInfo(GetInstance());
3330
0
    Coap::Message   *message;
3331
3332
0
    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriAddressRelease);
3333
0
    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
3334
3335
0
    SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, Rloc16FromRouterId(mRouterId)));
3336
0
    SuccessOrExit(error = Tlv::Append<ThreadExtMacAddressTlv>(*message, Get<Mac::Mac>().GetExtAddress()));
3337
3338
0
    messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc();
3339
3340
0
    SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
3341
3342
0
    Log(kMessageSend, kTypeAddressRelease, messageInfo.GetPeerAddr());
3343
3344
0
exit:
3345
0
    FreeMessageOnError(message, error);
3346
0
    LogSendError(kTypeAddressRelease, error);
3347
0
}
3348
3349
void Mle::HandleAddressSolicitResponse(void                *aContext,
3350
                                       otMessage           *aMessage,
3351
                                       const otMessageInfo *aMessageInfo,
3352
                                       otError              aResult)
3353
0
{
3354
0
    static_cast<Mle *>(aContext)->HandleAddressSolicitResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
3355
0
                                                               aResult);
3356
0
}
3357
3358
void Mle::HandleAddressSolicitResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
3359
0
{
3360
0
    uint8_t             status;
3361
0
    uint16_t            rloc16;
3362
0
    ThreadRouterMaskTlv routerMaskTlv;
3363
0
    uint8_t             routerId;
3364
0
    Router             *router;
3365
3366
0
    mAddressSolicitPending = false;
3367
3368
0
    VerifyOrExit(aResult == kErrorNone && aMessage != nullptr && aMessageInfo != nullptr);
3369
3370
0
    VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged);
3371
3372
0
    Log(kMessageReceive, kTypeAddressReply, aMessageInfo->GetPeerAddr());
3373
3374
0
    SuccessOrExit(Tlv::Find<ThreadStatusTlv>(*aMessage, status));
3375
3376
0
    if (status != ThreadStatusTlv::kSuccess)
3377
0
    {
3378
0
        mAddressSolicitRejected = true;
3379
3380
0
        if (IsRouterIdValid(mPreviousRouterId))
3381
0
        {
3382
0
            if (HasChildren())
3383
0
            {
3384
0
                RemoveChildren();
3385
0
            }
3386
3387
0
            SetRouterId(kInvalidRouterId);
3388
0
        }
3389
3390
0
        ExitNow();
3391
0
    }
3392
3393
0
    SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(*aMessage, rloc16));
3394
0
    routerId = RouterIdFromRloc16(rloc16);
3395
3396
0
    SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv));
3397
0
    VerifyOrExit(routerMaskTlv.IsValid());
3398
3399
0
    SetAlternateRloc16(GetRloc16());
3400
3401
0
    SetRouterId(routerId);
3402
3403
0
    SetStateRouter(Rloc16FromRouterId(mRouterId));
3404
3405
    // We keep the router table next hop and cost as what we had as a
3406
    // REED, i.e., our parent was the next hop towards all other
3407
    // routers and we tracked its cost towards them. As an FTD child,
3408
    // we may have established links with a subset of neighboring routers.
3409
    // We ensure to clear these links to avoid using them (since will
3410
    // be rejected by the neighbor).
3411
3412
0
    mRouterTable.ClearNeighbors();
3413
3414
0
    mRouterTable.UpdateRouterIdSet(routerMaskTlv.GetIdSequence(), routerMaskTlv.GetAssignedRouterIdMask());
3415
3416
0
    router = mRouterTable.FindRouterById(routerId);
3417
0
    VerifyOrExit(router != nullptr);
3418
0
    router->SetExtAddress(Get<Mac::Mac>().GetExtAddress());
3419
0
    router->SetNextHopToInvalid();
3420
3421
    // Ensure we have our parent as a neighboring router, copying the
3422
    // `mParent` entry.
3423
3424
0
    router = mRouterTable.FindRouterById(mParent.GetRouterId());
3425
0
    VerifyOrExit(router != nullptr);
3426
0
    router->SetFrom(mParent);
3427
0
    router->SetState(Neighbor::kStateValid);
3428
0
    router->SetNextHopToInvalid();
3429
3430
    // Ensure we have a next hop and cost towards leader.
3431
0
    if (mRouterTable.GetPathCostToLeader() >= kMaxRouteCost)
3432
0
    {
3433
0
        Router *leader = mRouterTable.GetLeader();
3434
3435
0
        OT_ASSERT(leader != nullptr);
3436
0
        leader->SetNextHopAndCost(RouterIdFromRloc16(mParent.GetRloc16()), mParent.GetLeaderCost());
3437
0
    }
3438
3439
    // We send a unicast Link Request to our former parent if its
3440
    // version is earlier than 1.3. This is to address a potential
3441
    // compatibility issue with some non-OpenThread stacks which may
3442
    // ignore MLE Advertisements from a former/existing child.
3443
3444
0
    if (mParent.GetVersion() < kThreadVersion1p3)
3445
0
    {
3446
0
        SendLinkRequest(&mParent);
3447
0
    }
3448
3449
    // We send an Advertisement to inform our former parent of our
3450
    // newly allocated Router ID. This will cause the parent to
3451
    // reset its advertisement trickle timer which can help speed
3452
    // up the dissemination of the new Router ID to other routers.
3453
    // This can also help with quicker link establishment with our
3454
    // former parent and other routers.
3455
0
    SendMulticastAdvertisement();
3456
3457
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateChildIdRequest))
3458
0
    {
3459
0
        IgnoreError(SendChildIdResponse(child));
3460
0
    }
3461
3462
0
exit:
3463
0
    InformPreviousChannel();
3464
0
}
3465
3466
Error Mle::SetChildRouterLinks(uint8_t aChildRouterLinks)
3467
0
{
3468
0
    Error error = kErrorNone;
3469
3470
0
    VerifyOrExit(IsDisabled(), error = kErrorInvalidState);
3471
0
    mChildRouterLinks = aChildRouterLinks;
3472
0
exit:
3473
0
    return error;
3474
0
}
3475
3476
bool Mle::IsExpectedToBecomeRouterSoon(void) const
3477
0
{
3478
0
    static constexpr uint8_t kMaxDelay = 10;
3479
3480
0
    return IsRouterEligible() && IsChild() && !mAddressSolicitRejected &&
3481
0
           ((mRouterRoleTransition.IsPending() && mRouterRoleTransition.GetTimeout() <= kMaxDelay) ||
3482
0
            mAddressSolicitPending);
3483
0
}
3484
3485
template <> void Mle::HandleTmf<kUriAddressSolicit>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
3486
194
{
3487
194
    Error                   error          = kErrorNone;
3488
194
    ThreadStatusTlv::Status responseStatus = ThreadStatusTlv::kNoAddressAvailable;
3489
194
    Router                 *router         = nullptr;
3490
194
    Mac::ExtAddress         extAddress;
3491
194
    uint16_t                rloc16;
3492
194
    uint8_t                 status;
3493
3494
194
    VerifyOrExit(mRole == kRoleLeader, error = kErrorInvalidState);
3495
3496
0
    VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse);
3497
3498
0
    Log(kMessageReceive, kTypeAddressSolicit, aMessageInfo.GetPeerAddr());
3499
3500
0
    SuccessOrExit(error = Tlv::Find<ThreadExtMacAddressTlv>(aMessage, extAddress));
3501
0
    SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(aMessage, status));
3502
3503
0
    switch (Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16))
3504
0
    {
3505
0
    case kErrorNone:
3506
0
        break;
3507
0
    case kErrorNotFound:
3508
0
        rloc16 = kInvalidRloc16;
3509
0
        break;
3510
0
    default:
3511
0
        ExitNow(error = kErrorParse);
3512
0
    }
3513
3514
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3515
    {
3516
        uint16_t xtalAccuracy;
3517
3518
        SuccessOrExit(Tlv::Find<XtalAccuracyTlv>(aMessage, xtalAccuracy));
3519
        VerifyOrExit(xtalAccuracy <= Get<TimeSync>().GetXtalThreshold());
3520
    }
3521
#endif
3522
3523
0
    router = mRouterTable.FindRouter(extAddress);
3524
3525
0
    if (router != nullptr)
3526
0
    {
3527
0
        responseStatus = ThreadStatusTlv::kSuccess;
3528
0
        ExitNow();
3529
0
    }
3530
3531
0
    switch (status)
3532
0
    {
3533
0
    case ThreadStatusTlv::kTooFewRouters:
3534
0
        VerifyOrExit(mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold);
3535
0
        break;
3536
3537
0
    case ThreadStatusTlv::kHaveChildIdRequest:
3538
0
    case ThreadStatusTlv::kParentPartitionChange:
3539
0
        break;
3540
3541
0
    case ThreadStatusTlv::kBorderRouterRequest:
3542
0
        if ((mRouterTable.GetActiveRouterCount() >= mRouterUpgradeThreshold) &&
3543
0
            (Get<NetworkData::Leader>().CountBorderRouters(NetworkData::kRouterRoleOnly) >=
3544
0
             kRouterUpgradeBorderRouterRequestThreshold))
3545
0
        {
3546
0
            LogInfo("Rejecting BR %s router role req - have %u BR routers", extAddress.ToString().AsCString(),
3547
0
                    kRouterUpgradeBorderRouterRequestThreshold);
3548
0
            ExitNow();
3549
0
        }
3550
0
        break;
3551
3552
0
    default:
3553
0
        responseStatus = ThreadStatusTlv::kUnrecognizedStatus;
3554
0
        ExitNow();
3555
0
    }
3556
3557
0
    if (rloc16 != kInvalidRloc16)
3558
0
    {
3559
0
        router = mRouterTable.Allocate(RouterIdFromRloc16(rloc16));
3560
3561
0
        if (router != nullptr)
3562
0
        {
3563
0
            LogInfo("Router id %u requested and provided!", RouterIdFromRloc16(rloc16));
3564
0
        }
3565
0
    }
3566
3567
0
    if (router == nullptr)
3568
0
    {
3569
0
        router = mRouterTable.Allocate();
3570
0
        VerifyOrExit(router != nullptr);
3571
0
    }
3572
3573
0
    router->SetExtAddress(extAddress);
3574
0
    responseStatus = ThreadStatusTlv::kSuccess;
3575
3576
194
exit:
3577
194
    if (error == kErrorNone)
3578
0
    {
3579
0
        SendAddressSolicitResponse(aMessage, responseStatus, router, aMessageInfo);
3580
0
    }
3581
194
}
3582
3583
void Mle::SendAddressSolicitResponse(const Coap::Message    &aRequest,
3584
                                     ThreadStatusTlv::Status aResponseStatus,
3585
                                     const Router           *aRouter,
3586
                                     const Ip6::MessageInfo &aMessageInfo)
3587
0
{
3588
0
    Coap::Message *message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
3589
3590
0
    VerifyOrExit(message != nullptr);
3591
3592
0
    SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aResponseStatus));
3593
3594
0
    if (aRouter != nullptr)
3595
0
    {
3596
0
        ThreadRouterMaskTlv routerMaskTlv;
3597
3598
0
        SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, aRouter->GetRloc16()));
3599
3600
0
        routerMaskTlv.Init();
3601
0
        routerMaskTlv.SetIdSequence(mRouterTable.GetRouterIdSequence());
3602
0
        mRouterTable.GetRouterIdSet(routerMaskTlv.GetAssignedRouterIdMask());
3603
3604
0
        SuccessOrExit(routerMaskTlv.AppendTo(*message));
3605
0
    }
3606
3607
0
    SuccessOrExit(Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
3608
0
    message = nullptr;
3609
3610
0
    Log(kMessageSend, kTypeAddressReply, aMessageInfo.GetPeerAddr());
3611
3612
    // If assigning a new RLOC16 (e.g., on promotion of a child to
3613
    // router role) we clear any address cache entries associated
3614
    // with the old RLOC16 unless the sender is a direct child. For
3615
    // direct children, we retain the cache entries to allow
3616
    // association with the promoted router's new RLOC16 upon
3617
    // receiving its Link Advertisement.
3618
3619
0
    if ((aResponseStatus == ThreadStatusTlv::kSuccess) && (aRouter != nullptr))
3620
0
    {
3621
0
        uint16_t oldRloc16;
3622
3623
0
        VerifyOrExit(IsRoutingLocator(aMessageInfo.GetPeerAddr()));
3624
0
        oldRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
3625
3626
0
        VerifyOrExit(oldRloc16 != aRouter->GetRloc16());
3627
0
        VerifyOrExit(!RouterIdMatch(oldRloc16, GetRloc16()));
3628
0
        Get<AddressResolver>().RemoveEntriesForRloc16(oldRloc16);
3629
0
    }
3630
3631
0
exit:
3632
0
    FreeMessage(message);
3633
0
}
3634
3635
template <> void Mle::HandleTmf<kUriAddressRelease>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
3636
194
{
3637
194
    uint16_t        rloc16;
3638
194
    Mac::ExtAddress extAddress;
3639
194
    uint8_t         routerId;
3640
194
    Router         *router;
3641
3642
194
    VerifyOrExit(mRole == kRoleLeader);
3643
3644
0
    VerifyOrExit(aMessage.IsConfirmablePostRequest());
3645
3646
0
    Log(kMessageReceive, kTypeAddressRelease, aMessageInfo.GetPeerAddr());
3647
3648
0
    SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16));
3649
0
    SuccessOrExit(Tlv::Find<ThreadExtMacAddressTlv>(aMessage, extAddress));
3650
3651
0
    routerId = RouterIdFromRloc16(rloc16);
3652
0
    router   = mRouterTable.FindRouterById(routerId);
3653
3654
0
    VerifyOrExit((router != nullptr) && (router->GetExtAddress() == extAddress));
3655
3656
0
    IgnoreError(mRouterTable.Release(routerId));
3657
3658
0
    SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
3659
3660
0
    Log(kMessageSend, kTypeAddressReleaseReply, aMessageInfo.GetPeerAddr());
3661
3662
194
exit:
3663
194
    return;
3664
0
}
3665
3666
void Mle::FillConnectivityTlv(ConnectivityTlv &aTlv)
3667
407
{
3668
407
    int8_t parentPriority = kParentPriorityMedium;
3669
3670
407
    if (mParentPriority != kParentPriorityUnspecified)
3671
0
    {
3672
0
        parentPriority = mParentPriority;
3673
0
    }
3674
407
    else
3675
407
    {
3676
407
        uint16_t numChildren = mChildTable.GetNumChildren(Child::kInStateValid);
3677
407
        uint16_t maxAllowed  = mChildTable.GetMaxChildrenAllowed();
3678
3679
407
        if ((maxAllowed - numChildren) < (maxAllowed / 3))
3680
0
        {
3681
0
            parentPriority = kParentPriorityLow;
3682
0
        }
3683
407
        else
3684
407
        {
3685
407
            parentPriority = kParentPriorityMedium;
3686
407
        }
3687
407
    }
3688
3689
407
    aTlv.SetParentPriority(parentPriority);
3690
3691
407
    aTlv.SetLinkQuality1(0);
3692
407
    aTlv.SetLinkQuality2(0);
3693
407
    aTlv.SetLinkQuality3(0);
3694
3695
407
    if (IsChild())
3696
0
    {
3697
0
        aTlv.IncrementLinkQuality(mParent.GetLinkQualityIn());
3698
0
    }
3699
3700
407
    for (const Router &router : Get<RouterTable>())
3701
0
    {
3702
0
        if (router.GetRloc16() == GetRloc16())
3703
0
        {
3704
0
            continue;
3705
0
        }
3706
3707
0
        if (!router.IsStateValid())
3708
0
        {
3709
0
            continue;
3710
0
        }
3711
3712
0
        aTlv.IncrementLinkQuality(router.GetTwoWayLinkQuality());
3713
0
    }
3714
3715
407
    aTlv.SetActiveRouters(mRouterTable.GetActiveRouterCount());
3716
407
    aTlv.SetLeaderCost(Min(mRouterTable.GetPathCostToLeader(), kMaxRouteCost));
3717
407
    aTlv.SetIdSequence(mRouterTable.GetRouterIdSequence());
3718
407
    aTlv.SetSedBufferSize(OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE);
3719
407
    aTlv.SetSedDatagramCount(OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT);
3720
407
}
3721
3722
bool Mle::ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const
3723
0
{
3724
    // Determine whether all conditions are satisfied for the router
3725
    // to downgrade after receiving info for a neighboring router
3726
    // with Router ID `aNeighborId` along with its `aRouteTlv`.
3727
3728
0
    bool    shouldDowngrade   = false;
3729
0
    uint8_t activeRouterCount = mRouterTable.GetActiveRouterCount();
3730
0
    uint8_t count;
3731
3732
0
    VerifyOrExit(IsRouter());
3733
0
    VerifyOrExit(mRouterTable.IsAllocated(aNeighborId));
3734
3735
0
    VerifyOrExit(!mRouterRoleTransition.IsPending());
3736
3737
0
    VerifyOrExit(activeRouterCount > mRouterDowngradeThreshold);
3738
3739
    // Check that we have at least `kMinDowngradeNeighbors`
3740
    // neighboring routers with two-way link quality of 2 or better.
3741
3742
0
    count = 0;
3743
3744
0
    for (const Router &router : mRouterTable)
3745
0
    {
3746
0
        if (!router.IsStateValid() || (router.GetTwoWayLinkQuality() < kLinkQuality2))
3747
0
        {
3748
0
            continue;
3749
0
        }
3750
3751
0
        count++;
3752
3753
0
        if (count >= kMinDowngradeNeighbors)
3754
0
        {
3755
0
            break;
3756
0
        }
3757
0
    }
3758
3759
0
    VerifyOrExit(count >= kMinDowngradeNeighbors);
3760
3761
    // Check that we have fewer children than three times the number
3762
    // of excess routers (defined as the difference between number of
3763
    // active routers and `mRouterDowngradeThreshold`).
3764
3765
0
    count = activeRouterCount - mRouterDowngradeThreshold;
3766
0
    VerifyOrExit(mChildTable.GetNumChildren(Child::kInStateValid) < count * 3);
3767
3768
    // Check that the neighbor has as good or better-quality links to
3769
    // same routers.
3770
3771
0
    VerifyOrExit(NeighborHasComparableConnectivity(aRouteTlv, aNeighborId));
3772
3773
0
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
3774
    // Check if we are eligible to be router due to being a BR.
3775
0
    VerifyOrExit(!Get<NetworkData::Notifier>().IsEligibleForRouterRoleUpgradeAsBorderRouter());
3776
0
#endif
3777
3778
0
    shouldDowngrade = true;
3779
3780
0
exit:
3781
0
    return shouldDowngrade;
3782
0
}
3783
3784
bool Mle::NeighborHasComparableConnectivity(const RouteTlv &aRouteTlv, uint8_t aNeighborId) const
3785
0
{
3786
    // Check whether the neighboring router with Router ID `aNeighborId`
3787
    // (along with its `aRouteTlv`) has as good or better-quality links
3788
    // to all our neighboring routers which have a two-way link quality
3789
    // of two or better.
3790
3791
0
    bool isComparable = true;
3792
3793
0
    for (uint8_t routerId = 0, index = 0; routerId <= kMaxRouterId;
3794
0
         index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
3795
0
    {
3796
0
        const Router *router;
3797
0
        LinkQuality   localLinkQuality;
3798
0
        LinkQuality   peerLinkQuality;
3799
3800
0
        if ((routerId == mRouterId) || (routerId == aNeighborId))
3801
0
        {
3802
0
            continue;
3803
0
        }
3804
3805
0
        router = mRouterTable.FindRouterById(routerId);
3806
3807
0
        if ((router == nullptr) || !router->IsStateValid())
3808
0
        {
3809
0
            continue;
3810
0
        }
3811
3812
0
        localLinkQuality = router->GetTwoWayLinkQuality();
3813
3814
0
        if (localLinkQuality < kLinkQuality2)
3815
0
        {
3816
0
            continue;
3817
0
        }
3818
3819
        // `router` is our neighbor with two-way link quality of
3820
        // at least two. Check that `aRouteTlv` has as good or
3821
        // better-quality link to it as well.
3822
3823
0
        if (!aRouteTlv.IsRouterIdSet(routerId))
3824
0
        {
3825
0
            ExitNow(isComparable = false);
3826
0
        }
3827
3828
0
        peerLinkQuality = Min(aRouteTlv.GetLinkQualityIn(index), aRouteTlv.GetLinkQualityOut(index));
3829
3830
0
        if (peerLinkQuality < localLinkQuality)
3831
0
        {
3832
0
            ExitNow(isComparable = false);
3833
0
        }
3834
0
    }
3835
3836
0
exit:
3837
0
    return isComparable;
3838
0
}
3839
3840
void Mle::SetChildStateToValid(Child &aChild)
3841
0
{
3842
0
    VerifyOrExit(!aChild.IsStateValid());
3843
3844
0
    aChild.SetState(Neighbor::kStateValid);
3845
0
    IgnoreError(mChildTable.StoreChild(aChild));
3846
3847
0
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
3848
0
    Get<MlrManager>().UpdateProxiedSubscriptions(aChild, MlrManager::MlrAddressArray());
3849
0
#endif
3850
3851
0
    mNeighborTable.Signal(NeighborTable::kChildAdded, aChild);
3852
3853
0
exit:
3854
0
    return;
3855
0
}
3856
3857
0
bool Mle::HasChildren(void) { return mChildTable.HasChildren(Child::kInStateValidOrAttaching); }
3858
3859
void Mle::RemoveChildren(void)
3860
0
{
3861
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
3862
0
    {
3863
0
        RemoveNeighbor(child);
3864
0
    }
3865
0
}
3866
3867
Error Mle::SetAssignParentPriority(int8_t aParentPriority)
3868
0
{
3869
0
    Error error = kErrorNone;
3870
3871
0
    VerifyOrExit(aParentPriority <= kParentPriorityHigh && aParentPriority >= kParentPriorityUnspecified,
3872
0
                 error = kErrorInvalidArgs);
3873
3874
0
    mParentPriority = aParentPriority;
3875
3876
0
exit:
3877
0
    return error;
3878
0
}
3879
3880
Error Mle::GetMaxChildTimeout(uint32_t &aTimeout) const
3881
66
{
3882
66
    Error error = kErrorNotFound;
3883
3884
66
    aTimeout = 0;
3885
3886
66
    VerifyOrExit(IsRouterOrLeader(), error = kErrorInvalidState);
3887
3888
0
    for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
3889
0
    {
3890
0
        if (child.IsFullThreadDevice())
3891
0
        {
3892
0
            continue;
3893
0
        }
3894
3895
0
        if (child.GetTimeout() > aTimeout)
3896
0
        {
3897
0
            aTimeout = child.GetTimeout();
3898
0
        }
3899
3900
0
        error = kErrorNone;
3901
0
    }
3902
3903
66
exit:
3904
66
    return error;
3905
0
}
3906
3907
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3908
Error Mle::SendTimeSync(void)
3909
{
3910
    Error        error = kErrorNone;
3911
    Ip6::Address destination;
3912
    TxMessage   *message = nullptr;
3913
3914
    VerifyOrExit((message = NewMleMessage(kCommandTimeSync)) != nullptr, error = kErrorNoBufs);
3915
3916
    message->SetTimeSync(true);
3917
3918
    destination.SetToLinkLocalAllNodesMulticast();
3919
    SuccessOrExit(error = message->SendTo(destination));
3920
3921
    Log(kMessageSend, kTypeTimeSync, destination);
3922
3923
exit:
3924
    FreeMessageOnError(message, error);
3925
    return error;
3926
}
3927
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3928
3929
//----------------------------------------------------------------------------------------------------------------------
3930
// RouterRoleTransition
3931
3932
Mle::RouterRoleTransition::RouterRoleTransition(void)
3933
21.3k
    : mTimeout(0)
3934
21.3k
    , mJitter(kRouterSelectionJitter)
3935
21.3k
{
3936
21.3k
}
3937
3938
0
void Mle::RouterRoleTransition::StartTimeout(void) { mTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mJitter); }
3939
3940
bool Mle::RouterRoleTransition::HandleTimeTick(void)
3941
0
{
3942
0
    bool expired = false;
3943
3944
0
    VerifyOrExit(mTimeout > 0);
3945
0
    mTimeout--;
3946
0
    expired = (mTimeout == 0);
3947
3948
0
exit:
3949
0
    return expired;
3950
0
}
3951
3952
//----------------------------------------------------------------------------------------------------------------------
3953
// RouterRoleRestorer
3954
3955
Mle::RouterRoleRestorer::RouterRoleRestorer(Instance &aInstance)
3956
21.3k
    : InstanceLocator(aInstance)
3957
21.3k
    , mAttempts(0)
3958
21.3k
{
3959
21.3k
}
3960
3961
void Mle::RouterRoleRestorer::Start(DeviceRole aPreviousRole)
3962
2.24k
{
3963
    // If the device was previously the leader or had more than
3964
    // `kMinCriticalChildrenCount` children, we use more link
3965
    // request attempts.
3966
3967
2.24k
    mAttempts = 0;
3968
3969
2.24k
    switch (aPreviousRole)
3970
2.24k
    {
3971
0
    case kRoleRouter:
3972
0
        if (Get<Mle>().mChildTable.GetNumChildren(Child::kInStateValidOrRestoring) < kMinCriticalChildrenCount)
3973
0
        {
3974
0
            mAttempts = kMaxTxCount;
3975
0
            break;
3976
0
        }
3977
3978
0
        OT_FALL_THROUGH;
3979
3980
0
    case kRoleLeader:
3981
0
        mAttempts = kMaxCriticalTxCount;
3982
0
        break;
3983
3984
0
    case kRoleChild:
3985
0
    case kRoleDetached:
3986
2.24k
    case kRoleDisabled:
3987
2.24k
        break;
3988
2.24k
    }
3989
3990
2.24k
    SendMulticastLinkRequest();
3991
2.24k
}
3992
3993
void Mle::RouterRoleRestorer::HandleTimer(void)
3994
0
{
3995
0
    if (mAttempts > 0)
3996
0
    {
3997
0
        mAttempts--;
3998
0
    }
3999
4000
0
    SendMulticastLinkRequest();
4001
0
}
4002
4003
void Mle::RouterRoleRestorer::SendMulticastLinkRequest(void)
4004
2.24k
{
4005
2.24k
    uint32_t delay;
4006
4007
2.24k
    VerifyOrExit(Get<Mle>().IsDetached(), mAttempts = 0);
4008
4009
2.24k
    if (mAttempts == 0)
4010
2.24k
    {
4011
2.24k
        IgnoreError(Get<Mle>().BecomeDetached());
4012
2.24k
        ExitNow();
4013
2.24k
    }
4014
4015
0
    Get<Mle>().SendLinkRequest(nullptr);
4016
4017
0
    delay = (mAttempts == 1) ? kLinkRequestTimeout
4018
0
                             : Random::NonCrypto::GetUint32InRange(kMulticastRetxDelayMin, kMulticastRetxDelayMax);
4019
4020
0
    Get<Mle>().mAttachTimer.Start(delay);
4021
4022
2.24k
exit:
4023
2.24k
    return;
4024
0
}
4025
4026
} // namespace Mle
4027
} // namespace ot
4028
4029
#endif // OPENTHREAD_FTD