1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements MLE functionality required for the Thread Child, Router and Leader roles.
32  */
33 
34 #include "mle.hpp"
35 
36 #include "instance/instance.hpp"
37 #include "openthread/platform/toolchain.h"
38 #include "utils/static_counter.hpp"
39 
40 namespace ot {
41 namespace Mle {
42 
43 RegisterLogModule("Mle");
44 
45 const otMeshLocalPrefix Mle::kMeshLocalPrefixInit = {
46     {0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00},
47 };
48 
Mle(Instance & aInstance)49 Mle::Mle(Instance &aInstance)
50     : InstanceLocator(aInstance)
51     , mRetrieveNewNetworkData(false)
52     , mRequestRouteTlv(false)
53     , mHasRestored(false)
54     , mReceivedResponseFromParent(false)
55     , mDetachingGracefully(false)
56     , mInitiallyAttachedAsSleepy(false)
57     , mWaitingForChildUpdateResponse(false)
58     , mWaitingForDataResponse(false)
59     , mRole(kRoleDisabled)
60     , mLastSavedRole(kRoleDisabled)
61     , mDeviceMode(DeviceMode::kModeRxOnWhenIdle)
62     , mAttachState(kAttachStateIdle)
63     , mReattachState(kReattachStop)
64     , mAttachMode(kAnyPartition)
65     , mAddressRegistrationMode(kAppendAllAddresses)
66     , mParentRequestCounter(0)
67     , mChildUpdateAttempts(0)
68     , mDataRequestAttempts(0)
69     , mAnnounceChannel(0)
70     , mAlternateChannel(0)
71     , mRloc16(kInvalidRloc16)
72     , mPreviousParentRloc(kInvalidRloc16)
73     , mAttachCounter(0)
74     , mAnnounceDelay(kAnnounceTimeout)
75     , mAlternatePanId(Mac::kPanIdBroadcast)
76     , mStoreFrameCounterAhead(kDefaultStoreFrameCounterAhead)
77     , mTimeout(kDefaultChildTimeout)
78 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
79     , mCslTimeout(kDefaultCslTimeout)
80 #endif
81     , mAlternateTimestamp(0)
82     , mNeighborTable(aInstance)
83     , mDelayedSender(aInstance)
84     , mSocket(aInstance, *this)
85 #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
86     , mParentSearch(aInstance)
87 #endif
88     , mAttachTimer(aInstance)
89     , mMessageTransmissionTimer(aInstance)
90 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
91     , mWakeupTxScheduler(aInstance)
92     , mWedAttachState(kWedDetached)
93     , mWedAttachTimer(aInstance)
94 #endif
95 {
96     mParent.Init(aInstance);
97     mParentCandidate.Init(aInstance);
98 
99     mLeaderData.Clear();
100     mParent.Clear();
101     mParentCandidate.Clear();
102     ResetCounters();
103 
104     mLinkLocalAddress.InitAsThreadOrigin();
105     mLinkLocalAddress.GetAddress().SetToLinkLocalAddress(Get<Mac::Mac>().GetExtAddress());
106 
107     mMeshLocalEid.InitAsThreadOriginMeshLocal();
108     mMeshLocalEid.GetAddress().GetIid().GenerateRandom();
109 
110     mMeshLocalRloc.InitAsThreadOriginMeshLocal();
111     mMeshLocalRloc.GetAddress().GetIid().SetToLocator(0);
112     mMeshLocalRloc.mRloc = true;
113 
114     mLinkLocalAllThreadNodes.Clear();
115     mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = BigEndian::HostSwap16(0xff32);
116     mLinkLocalAllThreadNodes.GetAddress().mFields.m16[7] = BigEndian::HostSwap16(0x0001);
117 
118     mRealmLocalAllThreadNodes.Clear();
119     mRealmLocalAllThreadNodes.GetAddress().mFields.m16[0] = BigEndian::HostSwap16(0xff33);
120     mRealmLocalAllThreadNodes.GetAddress().mFields.m16[7] = BigEndian::HostSwap16(0x0001);
121 
122     mMeshLocalPrefix.Clear();
123     SetMeshLocalPrefix(AsCoreType(&kMeshLocalPrefixInit));
124 }
125 
Enable(void)126 Error Mle::Enable(void)
127 {
128     Error error = kErrorNone;
129 
130     UpdateLinkLocalAddress();
131     SuccessOrExit(error = mSocket.Open(Ip6::kNetifThreadInternal));
132     SuccessOrExit(error = mSocket.Bind(kUdpPort));
133 
134 #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
135     mParentSearch.SetEnabled(true);
136 #endif
137 exit:
138     return error;
139 }
140 
ScheduleChildUpdateRequest(void)141 void Mle::ScheduleChildUpdateRequest(void)
142 {
143     mDelayedSender.ScheduleChildUpdateRequestToParent(kChildUpdateRequestDelay);
144 }
145 
Disable(void)146 Error Mle::Disable(void)
147 {
148     Error error = kErrorNone;
149 
150     Stop(kKeepNetworkDatasets);
151     SuccessOrExit(error = mSocket.Close());
152     Get<ThreadNetif>().RemoveUnicastAddress(mLinkLocalAddress);
153 
154 exit:
155     return error;
156 }
157 
Start(StartMode aMode)158 Error Mle::Start(StartMode aMode)
159 {
160     Error error = kErrorNone;
161 
162     // cannot bring up the interface if IEEE 802.15.4 promiscuous mode is enabled
163     VerifyOrExit(!Get<Radio>().GetPromiscuous(), error = kErrorInvalidState);
164     VerifyOrExit(Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
165 
166     if (Get<Mac::Mac>().GetPanId() == Mac::kPanIdBroadcast)
167     {
168         Get<Mac::Mac>().SetPanId(Mac::GenerateRandomPanId());
169     }
170 
171     SetStateDetached();
172 
173     Get<ThreadNetif>().AddUnicastAddress(mMeshLocalEid);
174 
175     Get<ThreadNetif>().SubscribeMulticast(mLinkLocalAllThreadNodes);
176     Get<ThreadNetif>().SubscribeMulticast(mRealmLocalAllThreadNodes);
177 
178     SetRloc16(GetRloc16());
179 
180     mAttachCounter = 0;
181 
182     Get<KeyManager>().Start();
183 
184     if (aMode == kNormalAttach)
185     {
186         mReattachState = kReattachStart;
187     }
188 
189     if ((aMode == kAnnounceAttach) || (GetRloc16() == kInvalidRloc16))
190     {
191         Attach(kAnyPartition);
192     }
193 #if OPENTHREAD_FTD
194     else if (IsRouterRloc16(GetRloc16()))
195     {
196         if (Get<MleRouter>().BecomeRouter(ThreadStatusTlv::kTooFewRouters) != kErrorNone)
197         {
198             Attach(kAnyPartition);
199         }
200     }
201 #endif
202     else
203     {
204         mChildUpdateAttempts = 0;
205         IgnoreError(SendChildUpdateRequestToParent());
206     }
207 
208 exit:
209     return error;
210 }
211 
Stop(StopMode aMode)212 void Mle::Stop(StopMode aMode)
213 {
214     if (aMode == kUpdateNetworkDatasets)
215     {
216         IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
217         IgnoreError(Get<MeshCoP::PendingDatasetManager>().Restore());
218     }
219 
220     VerifyOrExit(!IsDisabled());
221 
222     mDelayedSender.Stop();
223     Get<KeyManager>().Stop();
224     SetStateDetached();
225     Get<ThreadNetif>().UnsubscribeMulticast(mRealmLocalAllThreadNodes);
226     Get<ThreadNetif>().UnsubscribeMulticast(mLinkLocalAllThreadNodes);
227     Get<ThreadNetif>().RemoveUnicastAddress(mMeshLocalRloc);
228     Get<ThreadNetif>().RemoveUnicastAddress(mMeshLocalEid);
229 
230 #if OPENTHREAD_FTD
231     Get<MleRouter>().mRouterRoleRestorer.Stop();
232 #endif
233 
234     SetRole(kRoleDisabled);
235 
236 exit:
237     if (mDetachingGracefully)
238     {
239         mDetachingGracefully = false;
240         mDetachGracefullyCallback.InvokeAndClearIfSet();
241     }
242 }
243 
GetCounters(void)244 const Counters &Mle::GetCounters(void)
245 {
246 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
247     UpdateRoleTimeCounters(mRole);
248 #endif
249 
250     return mCounters;
251 }
252 
ResetCounters(void)253 void Mle::ResetCounters(void)
254 {
255     ClearAllBytes(mCounters);
256 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
257     mLastUpdatedTimestamp = Get<Uptime>().GetUptime();
258 #endif
259 }
260 
261 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
262 
GetCurrentAttachDuration(void) const263 uint32_t Mle::GetCurrentAttachDuration(void) const
264 {
265     return IsAttached() ? Uptime::MsecToSec(Get<Uptime>().GetUptime()) - mLastAttachTime : 0;
266 }
267 
UpdateRoleTimeCounters(DeviceRole aRole)268 void Mle::UpdateRoleTimeCounters(DeviceRole aRole)
269 {
270     uint64_t currentUptimeMsec = Get<Uptime>().GetUptime();
271     uint64_t durationMsec      = currentUptimeMsec - mLastUpdatedTimestamp;
272 
273     mLastUpdatedTimestamp = currentUptimeMsec;
274 
275     mCounters.mTrackedTime += durationMsec;
276 
277     switch (aRole)
278     {
279     case kRoleDisabled:
280         mCounters.mDisabledTime += durationMsec;
281         break;
282     case kRoleDetached:
283         mCounters.mDetachedTime += durationMsec;
284         break;
285     case kRoleChild:
286         mCounters.mChildTime += durationMsec;
287         break;
288     case kRoleRouter:
289         mCounters.mRouterTime += durationMsec;
290         break;
291     case kRoleLeader:
292         mCounters.mLeaderTime += durationMsec;
293         break;
294     }
295 }
296 
297 #endif // OPENTHREAD_CONFIG_UPTIME_ENABLE
298 
SetRole(DeviceRole aRole)299 void Mle::SetRole(DeviceRole aRole)
300 {
301     DeviceRole oldRole = mRole;
302 
303     SuccessOrExit(Get<Notifier>().Update(mRole, aRole, kEventThreadRoleChanged));
304 
305     LogNote("Role %s -> %s", RoleToString(oldRole), RoleToString(mRole));
306 
307 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
308     if ((oldRole == kRoleDetached) && IsAttached())
309     {
310         mLastAttachTime = Uptime::MsecToSec(Get<Uptime>().GetUptime());
311     }
312 
313     UpdateRoleTimeCounters(oldRole);
314 #endif
315 
316     switch (mRole)
317     {
318     case kRoleDisabled:
319         mCounters.mDisabledRole++;
320         break;
321     case kRoleDetached:
322         mCounters.mDetachedRole++;
323         break;
324     case kRoleChild:
325         mCounters.mChildRole++;
326         break;
327     case kRoleRouter:
328         mCounters.mRouterRole++;
329         break;
330     case kRoleLeader:
331         mCounters.mLeaderRole++;
332         break;
333     }
334 
335     // If the previous state is disabled, the parent can be in kStateRestored.
336     if (!IsChild() && oldRole != kRoleDisabled)
337     {
338         mParent.SetState(Neighbor::kStateInvalid);
339     }
340 
341     if ((oldRole == kRoleDetached) && IsChild())
342     {
343         // On transition from detached to child, we remember whether we
344         // attached as sleepy or not. This is then used to determine
345         // whether or not we need to re-attach on mode changes between
346         // rx-on and sleepy (rx-off). If we initially attach as sleepy,
347         // then rx-on/off mode changes are allowed without re-attach.
348 
349         mInitiallyAttachedAsSleepy = !GetDeviceMode().IsRxOnWhenIdle();
350     }
351 
352 exit:
353     return;
354 }
355 
SetAttachState(AttachState aState)356 void Mle::SetAttachState(AttachState aState)
357 {
358     VerifyOrExit(aState != mAttachState);
359     LogInfo("AttachState %s -> %s", AttachStateToString(mAttachState), AttachStateToString(aState));
360     mAttachState = aState;
361 
362 exit:
363     return;
364 }
365 
Restore(void)366 void Mle::Restore(void)
367 {
368     Settings::NetworkInfo networkInfo;
369     Settings::ParentInfo  parentInfo;
370 
371     IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
372     IgnoreError(Get<MeshCoP::PendingDatasetManager>().Restore());
373 
374 #if OPENTHREAD_CONFIG_DUA_ENABLE
375     Get<DuaManager>().Restore();
376 #endif
377 
378     SuccessOrExit(Get<Settings>().Read(networkInfo));
379 
380     Get<KeyManager>().SetCurrentKeySequence(networkInfo.GetKeySequence(),
381                                             KeyManager::kForceUpdate | KeyManager::kGuardTimerUnchanged);
382     Get<KeyManager>().SetMleFrameCounter(networkInfo.GetMleFrameCounter());
383     Get<KeyManager>().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter(), /* aSetIfLarger */ false);
384 
385 #if OPENTHREAD_MTD
386     mDeviceMode.Set(networkInfo.GetDeviceMode() & ~DeviceMode::kModeFullThreadDevice);
387 #else
388     mDeviceMode.Set(networkInfo.GetDeviceMode());
389 #endif
390 
391     // force re-attach when version mismatch.
392     VerifyOrExit(networkInfo.GetVersion() == kThreadVersion);
393 
394     mLastSavedRole = static_cast<DeviceRole>(networkInfo.GetRole());
395 
396     switch (mLastSavedRole)
397     {
398     case kRoleChild:
399     case kRoleRouter:
400     case kRoleLeader:
401         break;
402 
403     default:
404         ExitNow();
405     }
406 
407 #if OPENTHREAD_MTD
408     if (IsChildRloc16(networkInfo.GetRloc16()))
409 #endif
410     {
411         Get<Mac::Mac>().SetShortAddress(networkInfo.GetRloc16());
412         mRloc16 = networkInfo.GetRloc16();
413     }
414     Get<Mac::Mac>().SetExtAddress(networkInfo.GetExtAddress());
415 
416     mMeshLocalEid.GetAddress().SetIid(networkInfo.GetMeshLocalIid());
417 
418     if (networkInfo.GetRloc16() == kInvalidRloc16)
419     {
420         ExitNow();
421     }
422 
423     if (IsChildRloc16(networkInfo.GetRloc16()))
424     {
425         if (Get<Settings>().Read(parentInfo) != kErrorNone)
426         {
427             // If the restored RLOC16 corresponds to an end-device, it
428             // is expected that the `ParentInfo` settings to be valid
429             // as well. The device can still recover from such an invalid
430             // setting by skipping the re-attach ("Child Update Request"
431             // exchange) and going through the full attach process.
432 
433             LogWarn("Invalid settings - no saved parent info with valid end-device RLOC16 0x%04x",
434                     networkInfo.GetRloc16());
435             ExitNow();
436         }
437 
438         mParent.Clear();
439         mParent.SetExtAddress(parentInfo.GetExtAddress());
440         mParent.SetVersion(parentInfo.GetVersion());
441         mParent.SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle |
442                                          DeviceMode::kModeFullNetworkData));
443         mParent.SetRloc16(ParentRloc16ForRloc16(networkInfo.GetRloc16()));
444         mParent.SetState(Neighbor::kStateRestored);
445 
446         mPreviousParentRloc = mParent.GetRloc16();
447     }
448 #if OPENTHREAD_FTD
449     else
450     {
451         Get<MleRouter>().SetRouterId(RouterIdFromRloc16(GetRloc16()));
452         Get<MleRouter>().SetPreviousPartitionId(networkInfo.GetPreviousPartitionId());
453         Get<ChildTable>().Restore();
454     }
455 #endif
456 
457     // Successfully restored the network information from
458     // non-volatile settings after boot.
459     mHasRestored = true;
460 
461 exit:
462     return;
463 }
464 
Store(void)465 Error Mle::Store(void)
466 {
467     Error                 error = kErrorNone;
468     Settings::NetworkInfo networkInfo;
469 
470     networkInfo.Init();
471 
472     if (IsAttached())
473     {
474         // Only update network information while we are attached to
475         // avoid losing/overwriting previous information when a reboot
476         // occurs after a message is sent but before attaching.
477 
478         networkInfo.SetRole(mRole);
479         networkInfo.SetRloc16(GetRloc16());
480         networkInfo.SetPreviousPartitionId(mLeaderData.GetPartitionId());
481         networkInfo.SetExtAddress(Get<Mac::Mac>().GetExtAddress());
482         networkInfo.SetMeshLocalIid(mMeshLocalEid.GetAddress().GetIid());
483         networkInfo.SetVersion(kThreadVersion);
484         mLastSavedRole = mRole;
485 
486         if (IsChild())
487         {
488             Settings::ParentInfo parentInfo;
489 
490             parentInfo.Init();
491             parentInfo.SetExtAddress(mParent.GetExtAddress());
492             parentInfo.SetVersion(mParent.GetVersion());
493 
494             SuccessOrExit(error = Get<Settings>().Save(parentInfo));
495         }
496     }
497     else
498     {
499         // When not attached, read out any previous saved `NetworkInfo`.
500         // If there is none, it indicates that device was never attached
501         // before. In that case, no need to save any info (note that on
502         // a device reset the MLE/MAC frame counters would reset but
503         // device also starts with a new randomly generated extended
504         // address. If there is a previously saved `NetworkInfo`, we
505         // just update the key sequence and MAC and MLE frame counters.
506 
507         SuccessOrExit(Get<Settings>().Read(networkInfo));
508     }
509 
510     networkInfo.SetKeySequence(Get<KeyManager>().GetCurrentKeySequence());
511     networkInfo.SetMleFrameCounter(Get<KeyManager>().GetMleFrameCounter() + mStoreFrameCounterAhead);
512     networkInfo.SetMacFrameCounter(Get<KeyManager>().GetMaximumMacFrameCounter() + mStoreFrameCounterAhead);
513     networkInfo.SetDeviceMode(mDeviceMode.Get());
514 
515     SuccessOrExit(error = Get<Settings>().Save(networkInfo));
516 
517     Get<KeyManager>().SetStoredMleFrameCounter(networkInfo.GetMleFrameCounter());
518     Get<KeyManager>().SetStoredMacFrameCounter(networkInfo.GetMacFrameCounter());
519 
520     LogDebg("Store Network Information");
521 
522 exit:
523     return error;
524 }
525 
BecomeDetached(void)526 Error Mle::BecomeDetached(void)
527 {
528     Error error = kErrorNone;
529 
530     VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
531 
532     if (IsDetached() && (mAttachState == kAttachStateStart))
533     {
534         // Already detached and waiting to start an attach attempt, so
535         // there is not need to make any changes.
536         ExitNow();
537     }
538 
539     // Not in reattach stage after reset
540     if (mReattachState == kReattachStop)
541     {
542         IgnoreError(Get<MeshCoP::PendingDatasetManager>().Restore());
543     }
544 
545 #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
546     mParentSearch.SetRecentlyDetached();
547 #endif
548 
549     SetStateDetached();
550     mParent.SetState(Neighbor::kStateInvalid);
551     SetRloc16(kInvalidRloc16);
552     Attach(kAnyPartition);
553 
554 exit:
555     return error;
556 }
557 
BecomeChild(void)558 Error Mle::BecomeChild(void)
559 {
560     Error error = kErrorNone;
561 
562     VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
563     VerifyOrExit(!IsAttaching(), error = kErrorBusy);
564 
565     Attach(kAnyPartition);
566 
567 exit:
568     return error;
569 }
570 
SearchForBetterParent(void)571 Error Mle::SearchForBetterParent(void)
572 {
573     Error error = kErrorNone;
574 
575     VerifyOrExit(IsChild(), error = kErrorInvalidState);
576     Attach(kBetterParent);
577 
578 exit:
579     return error;
580 }
581 
Attach(AttachMode aMode)582 void Mle::Attach(AttachMode aMode)
583 {
584     VerifyOrExit(!IsDisabled() && !IsAttaching());
585 
586     if (!IsDetached())
587     {
588         mAttachCounter = 0;
589     }
590 
591     if (mReattachState == kReattachStart)
592     {
593         if (Get<MeshCoP::ActiveDatasetManager>().Restore() == kErrorNone)
594         {
595             mReattachState = kReattachActive;
596         }
597         else
598         {
599             mReattachState = kReattachStop;
600         }
601     }
602 
603     mParentCandidate.Clear();
604     SetAttachState(kAttachStateStart);
605     mAttachMode = aMode;
606 
607     if (aMode != kBetterPartition)
608     {
609 #if OPENTHREAD_FTD
610         if (IsFullThreadDevice())
611         {
612             Get<MleRouter>().StopAdvertiseTrickleTimer();
613         }
614 #endif
615     }
616     else
617     {
618         mCounters.mBetterPartitionAttachAttempts++;
619     }
620 
621     mAttachTimer.Start(GetAttachStartDelay());
622 
623     if (IsDetached())
624     {
625         mAttachCounter++;
626 
627         if (mAttachCounter == 0)
628         {
629             mAttachCounter--;
630         }
631 
632         mCounters.mAttachAttempts++;
633 
634         if (!IsRxOnWhenIdle())
635         {
636             Get<Mac::Mac>().SetRxOnWhenIdle(false);
637         }
638     }
639 
640 exit:
641     return;
642 }
643 
GetAttachStartDelay(void) const644 uint32_t Mle::GetAttachStartDelay(void) const
645 {
646     uint32_t delay = 1;
647     uint32_t jitter;
648 
649     VerifyOrExit(IsDetached());
650 
651     if (mAttachCounter == 0)
652     {
653         delay = 1 + Random::NonCrypto::GetUint32InRange(0, kParentRequestRouterTimeout);
654         ExitNow();
655     }
656 #if OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_ENABLE
657     else
658     {
659         uint16_t       counter = mAttachCounter - 1;
660         const uint32_t ratio   = kAttachBackoffMaxInterval / kAttachBackoffMinInterval;
661 
662         if ((counter < BitSizeOf(ratio)) && ((1UL << counter) <= ratio))
663         {
664             delay = kAttachBackoffMinInterval;
665             delay <<= counter;
666         }
667         else
668         {
669             delay = Random::NonCrypto::AddJitter(kAttachBackoffMaxInterval, kAttachBackoffJitter);
670         }
671     }
672 #endif // OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_ENABLE
673 
674     jitter = Random::NonCrypto::GetUint32InRange(0, kAttachStartJitter);
675 
676     if (jitter + delay > delay) // check for overflow
677     {
678         delay += jitter;
679     }
680 
681     LogNote("Attach attempt %u unsuccessful, will try again in %lu.%03u seconds", mAttachCounter, ToUlong(delay / 1000),
682             static_cast<uint16_t>(delay % 1000));
683 
684 exit:
685     return delay;
686 }
687 
IsAttached(void) const688 bool Mle::IsAttached(void) const { return (IsChild() || IsRouter() || IsLeader()); }
689 
IsRouterOrLeader(void) const690 bool Mle::IsRouterOrLeader(void) const { return (IsRouter() || IsLeader()); }
691 
SetStateDetached(void)692 void Mle::SetStateDetached(void)
693 {
694 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
695     Get<BackboneRouter::Local>().Reset();
696 #endif
697 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
698     Get<BackboneRouter::Leader>().Reset();
699 #endif
700 
701 #if OPENTHREAD_FTD
702     if (IsLeader())
703     {
704         Get<ThreadNetif>().RemoveUnicastAddress(Get<MleRouter>().mLeaderAloc);
705     }
706 #endif
707 
708     SetRole(kRoleDetached);
709     SetAttachState(kAttachStateIdle);
710     mAttachTimer.Stop();
711     mDelayedSender.RemoveScheduledChildUpdateRequestToParent();
712     mMessageTransmissionTimer.Stop();
713     mWaitingForChildUpdateResponse = false;
714     mChildUpdateAttempts           = 0;
715     mWaitingForDataResponse        = false;
716     mDataRequestAttempts           = 0;
717     mInitiallyAttachedAsSleepy     = false;
718     Get<MeshForwarder>().SetRxOnWhenIdle(true);
719     Get<Mac::Mac>().SetBeaconEnabled(false);
720 #if OPENTHREAD_FTD
721     Get<MleRouter>().ClearAlternateRloc16();
722     Get<MleRouter>().HandleDetachStart();
723 #endif
724 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
725     Get<Mac::Mac>().UpdateCsl();
726 #endif
727 }
728 
SetStateChild(uint16_t aRloc16)729 void Mle::SetStateChild(uint16_t aRloc16)
730 {
731 #if OPENTHREAD_FTD
732     if (IsLeader())
733     {
734         Get<ThreadNetif>().RemoveUnicastAddress(Get<MleRouter>().mLeaderAloc);
735     }
736 #endif
737 
738     SetRloc16(aRloc16);
739     SetRole(kRoleChild);
740     SetAttachState(kAttachStateIdle);
741     mAttachTimer.Start(kAttachBackoffDelayToResetCounter);
742     mReattachState       = kReattachStop;
743     mChildUpdateAttempts = 0;
744     mDataRequestAttempts = 0;
745     Get<Mac::Mac>().SetBeaconEnabled(false);
746     ScheduleMessageTransmissionTimer();
747 
748 #if OPENTHREAD_FTD
749     if (IsFullThreadDevice())
750     {
751         Get<MleRouter>().HandleChildStart(mAttachMode);
752     }
753 #endif
754 
755     // send announce after attached if needed
756     InformPreviousChannel();
757 
758 #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
759     mParentSearch.UpdateState();
760 #endif
761 
762     if ((mPreviousParentRloc != kInvalidRloc16) && (mPreviousParentRloc != mParent.GetRloc16()))
763     {
764         mCounters.mParentChanges++;
765 
766 #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
767         InformPreviousParent();
768 #endif
769     }
770 
771     mPreviousParentRloc = mParent.GetRloc16();
772 
773 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
774     Get<Mac::Mac>().UpdateCsl();
775 #endif
776 }
777 
InformPreviousChannel(void)778 void Mle::InformPreviousChannel(void)
779 {
780     VerifyOrExit(mAlternatePanId != Mac::kPanIdBroadcast);
781     VerifyOrExit(IsChild() || IsRouter());
782 
783 #if OPENTHREAD_FTD
784     VerifyOrExit(!IsFullThreadDevice() || IsRouter() || !Get<MleRouter>().IsRouterRoleTransitionPending());
785 #endif
786 
787     mAlternatePanId = Mac::kPanIdBroadcast;
788     Get<AnnounceBeginServer>().SendAnnounce(1 << mAlternateChannel);
789 
790 exit:
791     return;
792 }
793 
SetTimeout(uint32_t aTimeout)794 void Mle::SetTimeout(uint32_t aTimeout)
795 {
796     // Determine `kMinTimeout` based on other parameters
797     static constexpr uint32_t kMinPollPeriod       = OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD;
798     static constexpr uint32_t kRetxPollPeriod      = OPENTHREAD_CONFIG_MAC_RETX_POLL_PERIOD;
799     static constexpr uint32_t kMinTimeoutDataPoll  = kMinPollPeriod + kFailedChildTransmissions * kRetxPollPeriod;
800     static constexpr uint32_t kMinTimeoutKeepAlive = (kMaxChildKeepAliveAttempts + 1) * kUnicastRetxDelay;
801     static constexpr uint32_t kMinTimeout          = Time::MsecToSec(OT_MAX(kMinTimeoutKeepAlive, kMinTimeoutDataPoll));
802 
803     aTimeout = Max(aTimeout, kMinTimeout);
804 
805     VerifyOrExit(mTimeout != aTimeout);
806 
807     mTimeout = aTimeout;
808 
809     Get<DataPollSender>().RecalculatePollPeriod();
810 
811     if (IsChild())
812     {
813         IgnoreError(SendChildUpdateRequestToParent());
814     }
815 
816 exit:
817     return;
818 }
819 
SetDeviceMode(DeviceMode aDeviceMode)820 Error Mle::SetDeviceMode(DeviceMode aDeviceMode)
821 {
822     Error      error   = kErrorNone;
823     DeviceMode oldMode = mDeviceMode;
824 
825 #if OPENTHREAD_MTD
826     VerifyOrExit(!aDeviceMode.IsFullThreadDevice(), error = kErrorInvalidArgs);
827 #endif
828 
829     VerifyOrExit(aDeviceMode.IsValid(), error = kErrorInvalidArgs);
830     VerifyOrExit(mDeviceMode != aDeviceMode);
831     mDeviceMode = aDeviceMode;
832 
833 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
834     Get<Utils::HistoryTracker>().RecordNetworkInfo();
835 #endif
836 
837 #if OPENTHREAD_CONFIG_OTNS_ENABLE
838     Get<Utils::Otns>().EmitDeviceMode(mDeviceMode);
839 #endif
840 
841     LogNote("Mode 0x%02x -> 0x%02x [%s]", oldMode.Get(), mDeviceMode.Get(), mDeviceMode.ToString().AsCString());
842 
843     IgnoreError(Store());
844 
845 #if OPENTHREAD_FTD
846     if (!aDeviceMode.IsFullThreadDevice())
847     {
848         Get<MleRouter>().ClearAlternateRloc16();
849     }
850 #endif
851 
852     if (IsAttached())
853     {
854         bool shouldReattach = false;
855 
856         // We need to re-attach when switching between MTD/FTD modes.
857 
858         if (oldMode.IsFullThreadDevice() != mDeviceMode.IsFullThreadDevice())
859         {
860             shouldReattach = true;
861         }
862 
863         // If we initially attached as sleepy we allow mode changes
864         // between rx-on/off without a re-attach (we send "Child Update
865         // Request" to update the parent). But if we initially attached
866         // as rx-on, we require a re-attach on switching from rx-on to
867         // sleepy (rx-off) mode.
868 
869         if (!mInitiallyAttachedAsSleepy && oldMode.IsRxOnWhenIdle() && !mDeviceMode.IsRxOnWhenIdle())
870         {
871             shouldReattach = true;
872         }
873 
874         if (shouldReattach)
875         {
876             mAttachCounter = 0;
877             IgnoreError(BecomeDetached());
878             ExitNow();
879         }
880     }
881 
882     if (IsDetached())
883     {
884         mAttachCounter = 0;
885         SetStateDetached();
886         Attach(kAnyPartition);
887     }
888     else if (IsChild())
889     {
890         SetStateChild(GetRloc16());
891         IgnoreError(SendChildUpdateRequestToParent());
892     }
893 
894 exit:
895     return error;
896 }
897 
UpdateLinkLocalAddress(void)898 void Mle::UpdateLinkLocalAddress(void)
899 {
900     Get<ThreadNetif>().RemoveUnicastAddress(mLinkLocalAddress);
901     mLinkLocalAddress.GetAddress().GetIid().SetFromExtAddress(Get<Mac::Mac>().GetExtAddress());
902     Get<ThreadNetif>().AddUnicastAddress(mLinkLocalAddress);
903 
904     Get<Notifier>().Signal(kEventThreadLinkLocalAddrChanged);
905 }
906 
SetMeshLocalPrefix(const Ip6::NetworkPrefix & aMeshLocalPrefix)907 void Mle::SetMeshLocalPrefix(const Ip6::NetworkPrefix &aMeshLocalPrefix)
908 {
909     VerifyOrExit(mMeshLocalPrefix != aMeshLocalPrefix);
910 
911     mMeshLocalPrefix = aMeshLocalPrefix;
912 
913     // We ask `ThreadNetif` to apply the new mesh-local prefix which
914     // will then update all of its assigned unicast addresses that are
915     // marked as mesh-local, as well as all of the subscribed mesh-local
916     // prefix-based multicast addresses (such as link-local or
917     // realm-local All Thread Nodes addresses). It is important to call
918     // `ApplyNewMeshLocalPrefix()` first so that `ThreadNetif` can
919     // correctly signal the updates. It will first signal the removal
920     // of the previous address based on the old prefix, and then the
921     // addition of the new address with the new mesh-local prefix.
922 
923     Get<ThreadNetif>().ApplyNewMeshLocalPrefix();
924 
925     // Some of the addresses may already be updated from the
926     // `ApplyNewMeshLocalPrefix()` call, but we apply the new prefix to
927     // them in case they are not yet added to the `Netif`. This ensures
928     // that addresses are always updated and other modules can retrieve
929     // them using methods such as `GetMeshLocalRloc()`, `GetMeshLocalEid()`
930     // or `GetLinkLocalAllThreadNodesAddress()`, even if they have not
931     // yet been added to the `Netif`.
932 
933     mMeshLocalEid.GetAddress().SetPrefix(mMeshLocalPrefix);
934     mMeshLocalRloc.GetAddress().SetPrefix(mMeshLocalPrefix);
935     mLinkLocalAllThreadNodes.GetAddress().SetMulticastNetworkPrefix(mMeshLocalPrefix);
936     mRealmLocalAllThreadNodes.GetAddress().SetMulticastNetworkPrefix(mMeshLocalPrefix);
937 
938 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
939     Get<BackboneRouter::Local>().ApplyNewMeshLocalPrefix();
940 #endif
941 
942     Get<Notifier>().Signal(kEventThreadMeshLocalAddrChanged);
943 
944 exit:
945     return;
946 }
947 
948 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
SetMeshLocalIid(const Ip6::InterfaceIdentifier & aMlIid)949 Error Mle::SetMeshLocalIid(const Ip6::InterfaceIdentifier &aMlIid)
950 {
951     Error error = kErrorNone;
952 
953     VerifyOrExit(!Get<ThreadNetif>().HasUnicastAddress(mMeshLocalEid), error = kErrorInvalidState);
954 
955     mMeshLocalEid.GetAddress().SetIid(aMlIid);
956 exit:
957     return error;
958 }
959 #endif
960 
SetRloc16(uint16_t aRloc16)961 void Mle::SetRloc16(uint16_t aRloc16)
962 {
963     uint16_t oldRloc16 = GetRloc16();
964 
965     if (aRloc16 != oldRloc16)
966     {
967         LogNote("RLOC16 %04x -> %04x", oldRloc16, aRloc16);
968     }
969 
970     if (Get<ThreadNetif>().HasUnicastAddress(mMeshLocalRloc) &&
971         (mMeshLocalRloc.GetAddress().GetIid().GetLocator() != aRloc16))
972     {
973         Get<ThreadNetif>().RemoveUnicastAddress(mMeshLocalRloc);
974         Get<Tmf::Agent>().ClearRequests(mMeshLocalRloc.GetAddress());
975     }
976 
977     Get<Mac::Mac>().SetShortAddress(aRloc16);
978     mRloc16 = aRloc16;
979 
980     if (aRloc16 != kInvalidRloc16)
981     {
982         // We can always call `AddUnicastAddress(mMeshLocat16)` and if
983         // the address is already added, it will perform no action.
984 
985         mMeshLocalRloc.GetAddress().GetIid().SetLocator(aRloc16);
986         Get<ThreadNetif>().AddUnicastAddress(mMeshLocalRloc);
987 #if OPENTHREAD_FTD
988         Get<AddressResolver>().RestartAddressQueries();
989 #endif
990     }
991     else
992     {
993 #if OPENTHREAD_FTD
994         Get<MleRouter>().ClearAlternateRloc16();
995 #endif
996     }
997 }
998 
SetLeaderData(const LeaderData & aLeaderData)999 void Mle::SetLeaderData(const LeaderData &aLeaderData)
1000 {
1001     SetLeaderData(aLeaderData.GetPartitionId(), aLeaderData.GetWeighting(), aLeaderData.GetLeaderRouterId());
1002 }
1003 
SetLeaderData(uint32_t aPartitionId,uint8_t aWeighting,uint8_t aLeaderRouterId)1004 void Mle::SetLeaderData(uint32_t aPartitionId, uint8_t aWeighting, uint8_t aLeaderRouterId)
1005 {
1006     if (mLeaderData.GetPartitionId() != aPartitionId)
1007     {
1008 #if OPENTHREAD_FTD
1009         Get<MleRouter>().HandlePartitionChange();
1010 #endif
1011         Get<Notifier>().Signal(kEventThreadPartitionIdChanged);
1012         mCounters.mPartitionIdChanges++;
1013     }
1014     else
1015     {
1016         Get<Notifier>().SignalIfFirst(kEventThreadPartitionIdChanged);
1017     }
1018 
1019     mLeaderData.SetPartitionId(aPartitionId);
1020     mLeaderData.SetWeighting(aWeighting);
1021     mLeaderData.SetLeaderRouterId(aLeaderRouterId);
1022 }
1023 
GetLeaderRloc(Ip6::Address & aAddress) const1024 void Mle::GetLeaderRloc(Ip6::Address &aAddress) const
1025 {
1026     aAddress.SetToRoutingLocator(mMeshLocalPrefix, GetLeaderRloc16());
1027 }
1028 
GetLeaderAloc(Ip6::Address & aAddress) const1029 void Mle::GetLeaderAloc(Ip6::Address &aAddress) const { aAddress.SetToAnycastLocator(mMeshLocalPrefix, kAloc16Leader); }
1030 
GetCommissionerAloc(uint16_t aSessionId,Ip6::Address & aAddress) const1031 void Mle::GetCommissionerAloc(uint16_t aSessionId, Ip6::Address &aAddress) const
1032 {
1033     aAddress.SetToAnycastLocator(mMeshLocalPrefix, CommissionerAloc16FromId(aSessionId));
1034 }
1035 
GetServiceAloc(uint8_t aServiceId,Ip6::Address & aAddress) const1036 void Mle::GetServiceAloc(uint8_t aServiceId, Ip6::Address &aAddress) const
1037 {
1038     aAddress.SetToAnycastLocator(mMeshLocalPrefix, ServiceAlocFromId(aServiceId));
1039 }
1040 
GetLeaderData(void)1041 const LeaderData &Mle::GetLeaderData(void)
1042 {
1043     mLeaderData.SetDataVersion(Get<NetworkData::Leader>().GetVersion(NetworkData::kFullSet));
1044     mLeaderData.SetStableDataVersion(Get<NetworkData::Leader>().GetVersion(NetworkData::kStableSubset));
1045 
1046     return mLeaderData;
1047 }
1048 
HasUnregisteredAddress(void)1049 bool Mle::HasUnregisteredAddress(void)
1050 {
1051     bool retval = false;
1052 
1053     // Checks whether there are any addresses in addition to the mesh-local
1054     // address that need to be registered.
1055 
1056     for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
1057     {
1058         if (!addr.GetAddress().IsLinkLocalUnicast() && !IsRoutingLocator(addr.GetAddress()) &&
1059             !IsAnycastLocator(addr.GetAddress()) && addr.GetAddress() != GetMeshLocalEid())
1060         {
1061             ExitNow(retval = true);
1062         }
1063     }
1064 
1065     if (!IsRxOnWhenIdle())
1066     {
1067         // For sleepy end-device, we register any external multicast
1068         // addresses.
1069 
1070         retval = Get<ThreadNetif>().HasAnyExternalMulticastAddress();
1071     }
1072 
1073 exit:
1074     return retval;
1075 }
1076 
1077 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
SetCslTimeout(uint32_t aTimeout)1078 void Mle::SetCslTimeout(uint32_t aTimeout)
1079 {
1080     VerifyOrExit(mCslTimeout != aTimeout);
1081 
1082     mCslTimeout = aTimeout;
1083 
1084     Get<DataPollSender>().RecalculatePollPeriod();
1085 
1086     if (Get<Mac::Mac>().IsCslEnabled())
1087     {
1088         ScheduleChildUpdateRequest();
1089     }
1090 
1091 exit:
1092     return;
1093 }
1094 #endif
1095 
InitNeighbor(Neighbor & aNeighbor,const RxInfo & aRxInfo)1096 void Mle::InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo)
1097 {
1098     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(aNeighbor.GetExtAddress());
1099     aNeighbor.GetLinkInfo().Clear();
1100     aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
1101     aNeighbor.ResetLinkFailures();
1102     aNeighbor.SetLastHeard(TimerMilli::GetNow());
1103 }
1104 
ScheduleChildUpdateRequestIfMtdChild(void)1105 void Mle::ScheduleChildUpdateRequestIfMtdChild(void)
1106 {
1107     if (IsChild() && !IsFullThreadDevice())
1108     {
1109         ScheduleChildUpdateRequest();
1110     }
1111 }
1112 
HandleNotifierEvents(Events aEvents)1113 void Mle::HandleNotifierEvents(Events aEvents)
1114 {
1115     VerifyOrExit(!IsDisabled());
1116 
1117     if (aEvents.Contains(kEventThreadRoleChanged))
1118     {
1119         if (mAddressRegistrationMode == kAppendMeshLocalOnly)
1120         {
1121             // If only mesh-local address was registered in the "Child
1122             // ID Request" message, after device is attached, trigger a
1123             // "Child Update Request" to register the remaining
1124             // addresses.
1125 
1126             mAddressRegistrationMode = kAppendAllAddresses;
1127             ScheduleChildUpdateRequestIfMtdChild();
1128         }
1129     }
1130 
1131     if (aEvents.ContainsAny(kEventIp6AddressAdded | kEventIp6AddressRemoved))
1132     {
1133         if (!Get<ThreadNetif>().HasUnicastAddress(mMeshLocalEid.GetAddress()))
1134         {
1135             mMeshLocalEid.GetAddress().GetIid().GenerateRandom();
1136 
1137             Get<ThreadNetif>().AddUnicastAddress(mMeshLocalEid);
1138             Get<Notifier>().Signal(kEventThreadMeshLocalAddrChanged);
1139         }
1140 
1141         ScheduleChildUpdateRequestIfMtdChild();
1142     }
1143 
1144     if (aEvents.ContainsAny(kEventIp6MulticastSubscribed | kEventIp6MulticastUnsubscribed))
1145     {
1146         // When multicast subscription changes, SED always notifies
1147         // its parent as it depends on its parent for indirect
1148         // transmission. Since Thread 1.2, MED MAY also notify its
1149         // parent of 1.2 or higher version as it could depend on its
1150         // parent to perform Multicast Listener Report.
1151 
1152         if (!IsRxOnWhenIdle()
1153 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
1154             || !GetParent().IsThreadVersion1p1()
1155 #endif
1156         )
1157 
1158         {
1159             ScheduleChildUpdateRequestIfMtdChild();
1160         }
1161     }
1162 
1163     if (aEvents.Contains(kEventThreadNetdataChanged))
1164     {
1165 #if OPENTHREAD_FTD
1166         if (IsFullThreadDevice())
1167         {
1168             Get<MleRouter>().HandleNetworkDataUpdateRouter();
1169         }
1170         else
1171 #endif
1172         {
1173             if (!aEvents.Contains(kEventThreadRoleChanged))
1174             {
1175                 ScheduleChildUpdateRequest();
1176             }
1177         }
1178 
1179 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
1180         Get<BackboneRouter::Leader>().Update();
1181 #endif
1182 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
1183         UpdateServiceAlocs();
1184 #endif
1185 
1186 #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE
1187         IgnoreError(Get<Dhcp6::Server>().UpdateService());
1188 #endif
1189 
1190 #if OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE
1191         Get<NeighborDiscovery::Agent>().UpdateService();
1192 #endif
1193 
1194 #if OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE
1195         Get<Dhcp6::Client>().UpdateAddresses();
1196 #endif
1197     }
1198 
1199     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadKeySeqCounterChanged))
1200     {
1201         // Store the settings on a key seq change, or when role changes and device
1202         // is attached (i.e., skip `Store()` on role change to detached).
1203 
1204         if (aEvents.Contains(kEventThreadKeySeqCounterChanged) || IsAttached())
1205         {
1206             IgnoreError(Store());
1207         }
1208     }
1209 
1210 #if OPENTHREAD_FTD
1211     if (aEvents.Contains(kEventSecurityPolicyChanged))
1212     {
1213         Get<MleRouter>().HandleSecurityPolicyChanged();
1214     }
1215 #endif
1216 
1217     if (aEvents.Contains(kEventSupportedChannelMaskChanged))
1218     {
1219         Mac::ChannelMask channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
1220 
1221         if (!channelMask.ContainsChannel(Get<Mac::Mac>().GetPanChannel()) && (mRole != kRoleDisabled))
1222         {
1223             LogWarn("Channel %u is not in the supported channel mask %s, detach the network gracefully!",
1224                     Get<Mac::Mac>().GetPanChannel(), channelMask.ToString().AsCString());
1225             IgnoreError(DetachGracefully(nullptr, nullptr));
1226         }
1227     }
1228 
1229 exit:
1230     return;
1231 }
1232 
1233 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
1234 
ServiceAloc(void)1235 Mle::ServiceAloc::ServiceAloc(void)
1236 {
1237     InitAsThreadOriginMeshLocal();
1238     GetAddress().GetIid().SetToLocator(kNotInUse);
1239 }
1240 
FindInServiceAlocs(uint16_t aAloc16)1241 Mle::ServiceAloc *Mle::FindInServiceAlocs(uint16_t aAloc16)
1242 {
1243     // Search in `mServiceAlocs` for an entry matching `aAloc16`.
1244     // Can be used with `aAloc16 = ServerAloc::kNotInUse` to find
1245     // an unused entry in the array.
1246 
1247     ServiceAloc *match = nullptr;
1248 
1249     for (ServiceAloc &serviceAloc : mServiceAlocs)
1250     {
1251         if (serviceAloc.GetAloc16() == aAloc16)
1252         {
1253             match = &serviceAloc;
1254             break;
1255         }
1256     }
1257 
1258     return match;
1259 }
1260 
UpdateServiceAlocs(void)1261 void Mle::UpdateServiceAlocs(void)
1262 {
1263     NetworkData::Iterator      iterator;
1264     NetworkData::ServiceConfig service;
1265 
1266     VerifyOrExit(!IsDisabled());
1267 
1268     // First remove all ALOCs which are no longer in the Network
1269     // Data to free up space in `mServiceAlocs` array.
1270 
1271     for (ServiceAloc &serviceAloc : mServiceAlocs)
1272     {
1273         bool found = false;
1274 
1275         if (!serviceAloc.IsInUse())
1276         {
1277             continue;
1278         }
1279 
1280         iterator = NetworkData::kIteratorInit;
1281 
1282         while (Get<NetworkData::Leader>().GetNextService(iterator, GetRloc16(), service) == kErrorNone)
1283         {
1284             if (service.mServiceId == ServiceIdFromAloc(serviceAloc.GetAloc16()))
1285             {
1286                 found = true;
1287                 break;
1288             }
1289         }
1290 
1291         if (!found)
1292         {
1293             Get<ThreadNetif>().RemoveUnicastAddress(serviceAloc);
1294             serviceAloc.MarkAsNotInUse();
1295         }
1296     }
1297 
1298     // Now add any new ALOCs if there is space in `mServiceAlocs`.
1299 
1300     iterator = NetworkData::kIteratorInit;
1301 
1302     while (Get<NetworkData::Leader>().GetNextService(iterator, GetRloc16(), service) == kErrorNone)
1303     {
1304         uint16_t aloc16 = ServiceAlocFromId(service.mServiceId);
1305 
1306         if (FindInServiceAlocs(aloc16) == nullptr)
1307         {
1308             // No matching ALOC in `mServiceAlocs`, so we try to add it.
1309             ServiceAloc *newServiceAloc = FindInServiceAlocs(ServiceAloc::kNotInUse);
1310 
1311             VerifyOrExit(newServiceAloc != nullptr);
1312             newServiceAloc->SetAloc16(aloc16);
1313             Get<ThreadNetif>().AddUnicastAddress(*newServiceAloc);
1314         }
1315     }
1316 
1317 exit:
1318     return;
1319 }
1320 
1321 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
1322 
DetermineParentRequestType(ParentRequestType & aType) const1323 Error Mle::DetermineParentRequestType(ParentRequestType &aType) const
1324 {
1325     // This method determines the Parent Request type to use during an
1326     // attach cycle based on `mAttachMode`, `mAttachCounter` and
1327     // `mParentRequestCounter`. This method MUST be used while in
1328     // `kAttachStateParentRequest` state.
1329     //
1330     // On success it returns `kErrorNone` and sets `aType`. It returns
1331     // `kErrorNotFound` to indicate that device can now transition
1332     // from `kAttachStateParentRequest` state (has already sent the
1333     // required number of Parent Requests for this attach attempt
1334     // cycle).
1335 
1336     Error error = kErrorNone;
1337 
1338     OT_ASSERT(mAttachState == kAttachStateParentRequest);
1339 
1340     if (mAttachMode == kSelectedParent)
1341     {
1342         aType = kToSelectedRouter;
1343         VerifyOrExit(mParentRequestCounter <= 1, error = kErrorNotFound);
1344         ExitNow();
1345     }
1346 
1347     aType = kToRoutersAndReeds;
1348 
1349     // If device is not yet attached, `mAttachCounter` will track the
1350     // number of attach attempt cycles so far, starting from one for
1351     // the first attempt. `mAttachCounter` will be zero if device is
1352     // already attached. Examples of this situation include a leader or
1353     // router trying to attach to a better partition, or a child trying
1354     // to find a better parent.
1355 
1356     if ((mAttachCounter <= 1) && (mAttachMode != kBetterParent))
1357     {
1358         VerifyOrExit(mParentRequestCounter <= kFirstAttachCycleTotalParentRequests, error = kErrorNotFound);
1359 
1360         // During reattach to the same partition all the Parent
1361         // Request are sent to Routers and REEDs.
1362 
1363         if ((mAttachMode != kSamePartition) && (mParentRequestCounter <= kFirstAttachCycleNumParentRequestToRouters))
1364         {
1365             aType = kToRouters;
1366         }
1367     }
1368     else
1369     {
1370         VerifyOrExit(mParentRequestCounter <= kNextAttachCycleTotalParentRequests, error = kErrorNotFound);
1371 
1372         if (mParentRequestCounter <= kNextAttachCycleNumParentRequestToRouters)
1373         {
1374             aType = kToRouters;
1375         }
1376     }
1377 
1378 exit:
1379     return error;
1380 }
1381 
HasAcceptableParentCandidate(void) const1382 bool Mle::HasAcceptableParentCandidate(void) const
1383 {
1384     bool              hasAcceptableParent = false;
1385     ParentRequestType parentReqType;
1386 
1387     VerifyOrExit(mParentCandidate.IsStateParentResponse());
1388 
1389     switch (mAttachState)
1390     {
1391     case kAttachStateAnnounce:
1392         VerifyOrExit(!HasMoreChannelsToAnnounce());
1393         break;
1394 
1395     case kAttachStateParentRequest:
1396         SuccessOrAssert(DetermineParentRequestType(parentReqType));
1397 
1398         if (parentReqType == kToRouters)
1399         {
1400             // If we cannot find a parent with best link quality (3) when
1401             // in Parent Request was sent to routers, we will keep the
1402             // candidate and forward to REED stage to potentially find a
1403             // better parent.
1404             VerifyOrExit(mParentCandidate.GetTwoWayLinkQuality() == kLinkQuality3);
1405         }
1406 
1407         break;
1408 
1409     default:
1410         ExitNow();
1411     }
1412 
1413     if (IsChild())
1414     {
1415         switch (mAttachMode)
1416         {
1417         case kBetterPartition:
1418             break;
1419 
1420         case kAnyPartition:
1421         case kSamePartition:
1422         case kDowngradeToReed:
1423         case kBetterParent:
1424         case kSelectedParent:
1425             // Ensure that a Parent Response was received from the
1426             // current parent to which the device is attached, so
1427             // that the new parent candidate can be compared with the
1428             // current parent and confirmed to be preferred.
1429             VerifyOrExit(mReceivedResponseFromParent);
1430             break;
1431         }
1432     }
1433 
1434     hasAcceptableParent = true;
1435 
1436 exit:
1437     return hasAcceptableParent;
1438 }
1439 
HandleAttachTimer(void)1440 void Mle::HandleAttachTimer(void)
1441 {
1442     uint32_t          delay          = 0;
1443     bool              shouldAnnounce = true;
1444     ParentRequestType type;
1445 
1446     if (mDetachingGracefully)
1447     {
1448         Stop();
1449         ExitNow();
1450     }
1451 
1452 #if OPENTHREAD_FTD
1453     if (IsDetached() && Get<MleRouter>().mRouterRoleRestorer.IsActive())
1454     {
1455         Get<MleRouter>().mRouterRoleRestorer.HandleTimer();
1456         ExitNow();
1457     }
1458 #endif
1459 
1460     // First, check if we are waiting to receive parent responses and
1461     // found an acceptable parent candidate.
1462 
1463     if (HasAcceptableParentCandidate() && (SendChildIdRequest() == kErrorNone))
1464     {
1465         SetAttachState(kAttachStateChildIdRequest);
1466         delay = kChildIdResponseTimeout;
1467         ExitNow();
1468     }
1469 
1470     switch (mAttachState)
1471     {
1472     case kAttachStateIdle:
1473         mAttachCounter = 0;
1474         break;
1475 
1476     case kAttachStateProcessAnnounce:
1477         ProcessAnnounce();
1478         break;
1479 
1480     case kAttachStateStart:
1481         LogNote("Attach attempt %d, %s %s", mAttachCounter, AttachModeToString(mAttachMode),
1482                 ReattachStateToString(mReattachState));
1483 
1484         SetAttachState(kAttachStateParentRequest);
1485         mParentCandidate.SetState(Neighbor::kStateInvalid);
1486         mReceivedResponseFromParent = false;
1487         mParentRequestCounter       = 0;
1488         Get<MeshForwarder>().SetRxOnWhenIdle(true);
1489 
1490         OT_FALL_THROUGH;
1491 
1492     case kAttachStateParentRequest:
1493         mParentRequestCounter++;
1494         if (DetermineParentRequestType(type) == kErrorNone)
1495         {
1496             SendParentRequest(type);
1497 
1498             switch (type)
1499             {
1500             case kToRouters:
1501             case kToSelectedRouter:
1502                 delay = kParentRequestRouterTimeout;
1503                 break;
1504             case kToRoutersAndReeds:
1505                 delay = kParentRequestReedTimeout;
1506                 break;
1507             }
1508 
1509             break;
1510         }
1511 
1512         shouldAnnounce = PrepareAnnounceState();
1513 
1514         if (shouldAnnounce)
1515         {
1516             // We send an extra "Parent Request" as we switch to
1517             // `kAttachStateAnnounce` and start sending Announce on
1518             // all channels. This gives an additional chance to find
1519             // a parent during this phase. Note that we can stay in
1520             // `kAttachStateAnnounce` for multiple iterations, each
1521             // time sending an Announce on a different channel
1522             // (with `mAnnounceDelay` wait between them).
1523 
1524             SetAttachState(kAttachStateAnnounce);
1525             SendParentRequest(kToRoutersAndReeds);
1526             mAnnounceChannel = Mac::ChannelMask::kChannelIteratorFirst;
1527             delay            = mAnnounceDelay;
1528             break;
1529         }
1530 
1531         OT_FALL_THROUGH;
1532 
1533     case kAttachStateAnnounce:
1534         if (shouldAnnounce && (GetNextAnnounceChannel(mAnnounceChannel) == kErrorNone))
1535         {
1536             SendAnnounce(mAnnounceChannel, kOrphanAnnounce);
1537             delay = mAnnounceDelay;
1538             break;
1539         }
1540 
1541         OT_FALL_THROUGH;
1542 
1543     case kAttachStateChildIdRequest:
1544         SetAttachState(kAttachStateIdle);
1545         mParentCandidate.Clear();
1546         delay = Reattach();
1547         break;
1548     }
1549 
1550 exit:
1551 
1552     if (delay != 0)
1553     {
1554         mAttachTimer.Start(delay);
1555     }
1556 }
1557 
PrepareAnnounceState(void)1558 bool Mle::PrepareAnnounceState(void)
1559 {
1560     bool             shouldAnnounce = false;
1561     Mac::ChannelMask channelMask;
1562 
1563     VerifyOrExit(!IsChild() && (mReattachState == kReattachStop) &&
1564                  (Get<MeshCoP::ActiveDatasetManager>().IsPartiallyComplete() || !IsFullThreadDevice()));
1565 
1566     if (Get<MeshCoP::ActiveDatasetManager>().GetChannelMask(channelMask) != kErrorNone)
1567     {
1568         channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
1569     }
1570 
1571     mAnnounceDelay = kAnnounceTimeout / (channelMask.GetNumberOfChannels() + 1);
1572     mAnnounceDelay = Max(mAnnounceDelay, kMinAnnounceDelay);
1573     shouldAnnounce = true;
1574 
1575 exit:
1576     return shouldAnnounce;
1577 }
1578 
Reattach(void)1579 uint32_t Mle::Reattach(void)
1580 {
1581     uint32_t delay = 0;
1582 
1583     if (mReattachState == kReattachActive)
1584     {
1585         if (Get<MeshCoP::PendingDatasetManager>().Restore() == kErrorNone)
1586         {
1587             IgnoreError(Get<MeshCoP::PendingDatasetManager>().ApplyConfiguration());
1588             mReattachState = kReattachPending;
1589             SetAttachState(kAttachStateStart);
1590             delay = 1 + Random::NonCrypto::GetUint32InRange(0, kAttachStartJitter);
1591         }
1592         else
1593         {
1594             mReattachState = kReattachStop;
1595         }
1596     }
1597     else if (mReattachState == kReattachPending)
1598     {
1599         mReattachState = kReattachStop;
1600         IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
1601     }
1602 
1603     VerifyOrExit(mReattachState == kReattachStop);
1604 
1605     switch (mAttachMode)
1606     {
1607     case kAnyPartition:
1608     case kBetterParent:
1609     case kSelectedParent:
1610         if (!IsChild())
1611         {
1612             if (mAlternatePanId != Mac::kPanIdBroadcast)
1613             {
1614                 IgnoreError(Get<Mac::Mac>().SetPanChannel(mAlternateChannel));
1615                 Get<Mac::Mac>().SetPanId(mAlternatePanId);
1616                 mAlternatePanId = Mac::kPanIdBroadcast;
1617                 IgnoreError(BecomeDetached());
1618             }
1619 #if OPENTHREAD_FTD
1620             else if (IsFullThreadDevice() && Get<MleRouter>().BecomeLeader(/* aCheckWeight */ false) == kErrorNone)
1621             {
1622                 // do nothing
1623             }
1624 #endif
1625             else
1626             {
1627                 IgnoreError(BecomeDetached());
1628             }
1629         }
1630         else if (!IsRxOnWhenIdle())
1631         {
1632             // Return to sleepy operation
1633             Get<DataPollSender>().SetAttachMode(false);
1634             Get<MeshForwarder>().SetRxOnWhenIdle(false);
1635         }
1636 
1637         break;
1638 
1639     case kSamePartition:
1640     case kDowngradeToReed:
1641         Attach(kAnyPartition);
1642         break;
1643 
1644     case kBetterPartition:
1645         break;
1646     }
1647 
1648 exit:
1649     return delay;
1650 }
1651 
SendParentRequest(ParentRequestType aType)1652 void Mle::SendParentRequest(ParentRequestType aType)
1653 {
1654     Error        error = kErrorNone;
1655     TxMessage   *message;
1656     uint8_t      scanMask = 0;
1657     Ip6::Address destination;
1658 
1659     mParentRequestChallenge.GenerateRandom();
1660 
1661     switch (aType)
1662     {
1663     case kToRouters:
1664     case kToSelectedRouter:
1665         scanMask = ScanMaskTlv::kRouterFlag;
1666         break;
1667 
1668     case kToRoutersAndReeds:
1669         scanMask = ScanMaskTlv::kRouterFlag | ScanMaskTlv::kEndDeviceFlag;
1670         break;
1671     }
1672 
1673     VerifyOrExit((message = NewMleMessage(kCommandParentRequest)) != nullptr, error = kErrorNoBufs);
1674     SuccessOrExit(error = message->AppendModeTlv(mDeviceMode));
1675     SuccessOrExit(error = message->AppendChallengeTlv(mParentRequestChallenge));
1676     SuccessOrExit(error = message->AppendScanMaskTlv(scanMask));
1677     SuccessOrExit(error = message->AppendVersionTlv());
1678 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1679     SuccessOrExit(error = message->AppendTimeRequestTlv());
1680 #endif
1681 
1682 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
1683     if (aType == kToSelectedRouter)
1684     {
1685         TxMessage *messageToCurParent = static_cast<TxMessage *>(message->Clone());
1686 
1687         VerifyOrExit(messageToCurParent != nullptr, error = kErrorNoBufs);
1688 
1689         destination.SetToLinkLocalAddress(mParent.GetExtAddress());
1690         error = messageToCurParent->SendTo(destination);
1691 
1692         if (error != kErrorNone)
1693         {
1694             messageToCurParent->Free();
1695             ExitNow();
1696         }
1697 
1698         Log(kMessageSend, kTypeParentRequestToRouters, destination);
1699 
1700         destination.SetToLinkLocalAddress(mParentSearch.GetSelectedParent().GetExtAddress());
1701     }
1702     else
1703 #endif
1704     {
1705         destination.SetToLinkLocalAllRoutersMulticast();
1706     }
1707 
1708     SuccessOrExit(error = message->SendTo(destination));
1709 
1710     switch (aType)
1711     {
1712     case kToRouters:
1713     case kToSelectedRouter:
1714         Log(kMessageSend, kTypeParentRequestToRouters, destination);
1715         break;
1716 
1717     case kToRoutersAndReeds:
1718         Log(kMessageSend, kTypeParentRequestToRoutersReeds, destination);
1719         break;
1720     }
1721 
1722 exit:
1723     FreeMessageOnError(message, error);
1724 }
1725 
RequestShorterChildIdRequest(void)1726 void Mle::RequestShorterChildIdRequest(void)
1727 {
1728     if (mAttachState == kAttachStateChildIdRequest)
1729     {
1730         mAddressRegistrationMode = kAppendMeshLocalOnly;
1731         IgnoreError(SendChildIdRequest());
1732     }
1733 }
1734 
HandleChildIdRequestTxDone(Message & aMessage)1735 void Mle::HandleChildIdRequestTxDone(Message &aMessage)
1736 {
1737     if (aMessage.GetTxSuccess() && !IsRxOnWhenIdle())
1738     {
1739         Get<DataPollSender>().SetAttachMode(true);
1740         Get<MeshForwarder>().SetRxOnWhenIdle(false);
1741     }
1742 
1743     if (aMessage.IsLinkSecurityEnabled())
1744     {
1745         // If the Child ID Request requires fragmentation and therefore
1746         // link layer security, the frame transmission will be aborted.
1747         // When the message is being freed, we signal to MLE to prepare a
1748         // shorter Child ID Request message (by only including mesh-local
1749         // address in the Address Registration TLV).
1750 
1751         LogInfo("Requesting shorter `Child ID Request`");
1752         RequestShorterChildIdRequest();
1753     }
1754 }
1755 
SendChildIdRequest(void)1756 Error Mle::SendChildIdRequest(void)
1757 {
1758     static const uint8_t kTlvs[] = {Tlv::kAddress16, Tlv::kNetworkData, Tlv::kRoute};
1759 
1760     Error        error   = kErrorNone;
1761     uint8_t      tlvsLen = sizeof(kTlvs);
1762     TxMessage   *message = nullptr;
1763     Ip6::Address destination;
1764 
1765     if (mParent.GetExtAddress() == mParentCandidate.GetExtAddress())
1766     {
1767         if (IsChild())
1768         {
1769             LogInfo("Already attached to candidate parent");
1770             ExitNow(error = kErrorAlready);
1771         }
1772         else
1773         {
1774             // Invalidate stale parent state.
1775             //
1776             // Parent state is not normally invalidated after becoming
1777             // a Router/Leader (see #1875).  When trying to attach to
1778             // a better partition, invalidating old parent state
1779             // (especially when in `kStateRestored`) ensures that
1780             // `FindNeighbor()` returns `mParentCandidate` when
1781             // processing the Child ID Response.
1782 
1783             mParent.SetState(Neighbor::kStateInvalid);
1784         }
1785     }
1786 
1787     VerifyOrExit((message = NewMleMessage(kCommandChildIdRequest)) != nullptr, error = kErrorNoBufs);
1788     SuccessOrExit(error = message->AppendResponseTlv(mParentCandidate.mRxChallenge));
1789     SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs());
1790     SuccessOrExit(error = message->AppendModeTlv(mDeviceMode));
1791     SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout));
1792     SuccessOrExit(error = message->AppendVersionTlv());
1793     SuccessOrExit(error = message->AppendSupervisionIntervalTlvIfSleepyChild());
1794 
1795     if (!IsFullThreadDevice())
1796     {
1797         SuccessOrExit(error = message->AppendAddressRegistrationTlv(mAddressRegistrationMode));
1798 
1799         // No need to request the last Route64 TLV for MTD
1800         tlvsLen -= 1;
1801     }
1802 
1803     SuccessOrExit(error = message->AppendTlvRequestTlv(kTlvs, tlvsLen));
1804     SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs());
1805 
1806     mParentCandidate.SetState(Neighbor::kStateValid);
1807 
1808     destination.SetToLinkLocalAddress(mParentCandidate.GetExtAddress());
1809     SuccessOrExit(error = message->SendTo(destination));
1810 
1811     Log(kMessageSend,
1812         (mAddressRegistrationMode == kAppendMeshLocalOnly) ? kTypeChildIdRequestShort : kTypeChildIdRequest,
1813         destination);
1814 exit:
1815     FreeMessageOnError(message, error);
1816     return error;
1817 }
1818 
SendDataRequest(const Ip6::Address & aDestination)1819 Error Mle::SendDataRequest(const Ip6::Address &aDestination)
1820 {
1821     static const uint8_t kTlvs[] = {Tlv::kNetworkData, Tlv::kRoute};
1822 
1823     Error error = kErrorNone;
1824 
1825     VerifyOrExit(IsAttached());
1826 
1827     // Based on `mRequestRouteTlv` include both Network Data and Route
1828     // TLVs or only Network Data TLV.
1829 
1830     error = SendDataRequest(aDestination, kTlvs, mRequestRouteTlv ? 2 : 1);
1831 
1832     if (IsChild() && !IsRxOnWhenIdle())
1833     {
1834         mWaitingForDataResponse = true;
1835 
1836         if (!mWaitingForChildUpdateResponse)
1837         {
1838             ScheduleMessageTransmissionTimer();
1839         }
1840     }
1841 
1842 exit:
1843     return error;
1844 }
1845 
1846 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
SendDataRequestForLinkMetricsReport(const Ip6::Address & aDestination,const LinkMetrics::Initiator::QueryInfo & aQueryInfo)1847 Error Mle::SendDataRequestForLinkMetricsReport(const Ip6::Address                      &aDestination,
1848                                                const LinkMetrics::Initiator::QueryInfo &aQueryInfo)
1849 {
1850     static const uint8_t kTlvs[] = {Tlv::kLinkMetricsReport};
1851 
1852     return SendDataRequest(aDestination, kTlvs, sizeof(kTlvs), &aQueryInfo);
1853 }
1854 
SendDataRequest(const Ip6::Address & aDestination,const uint8_t * aTlvs,uint8_t aTlvsLength,const LinkMetrics::Initiator::QueryInfo * aQueryInfo)1855 Error Mle::SendDataRequest(const Ip6::Address                      &aDestination,
1856                            const uint8_t                           *aTlvs,
1857                            uint8_t                                  aTlvsLength,
1858                            const LinkMetrics::Initiator::QueryInfo *aQueryInfo)
1859 #else
1860 Error Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength)
1861 #endif
1862 {
1863     Error      error = kErrorNone;
1864     TxMessage *message;
1865 
1866     VerifyOrExit((message = NewMleMessage(kCommandDataRequest)) != nullptr, error = kErrorNoBufs);
1867     SuccessOrExit(error = message->AppendTlvRequestTlv(aTlvs, aTlvsLength));
1868 
1869 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
1870     if (aQueryInfo != nullptr)
1871     {
1872         SuccessOrExit(error = Get<LinkMetrics::Initiator>().AppendLinkMetricsQueryTlv(*message, *aQueryInfo));
1873     }
1874 #endif
1875 
1876     SuccessOrExit(error = message->AppendActiveAndPendingTimestampTlvs());
1877 
1878     SuccessOrExit(error = message->SendTo(aDestination));
1879     Log(kMessageSend, kTypeDataRequest, aDestination);
1880 
1881     if (!IsRxOnWhenIdle())
1882     {
1883         Get<DataPollSender>().SendFastPolls(DataPollSender::kDefaultFastPolls);
1884     }
1885 
1886 exit:
1887     FreeMessageOnError(message, error);
1888     return error;
1889 }
1890 
ScheduleMessageTransmissionTimer(void)1891 void Mle::ScheduleMessageTransmissionTimer(void)
1892 {
1893     uint32_t interval = 0;
1894 
1895     if (mWaitingForChildUpdateResponse)
1896     {
1897 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1898         if (Get<Mac::Mac>().IsCslEnabled())
1899         {
1900             ExitNow(interval = Get<Mac::Mac>().GetCslPeriodInMsec() + kUnicastRetxDelay);
1901         }
1902         else
1903 #endif
1904         {
1905             ExitNow(interval = kUnicastRetxDelay);
1906         }
1907     }
1908 
1909     if (mWaitingForDataResponse)
1910     {
1911         ExitNow(interval = kUnicastRetxDelay);
1912     }
1913 
1914     if (IsChild() && IsRxOnWhenIdle())
1915     {
1916         interval = Time::SecToMsec(mTimeout) - kUnicastRetxDelay * kMaxChildKeepAliveAttempts;
1917     }
1918 
1919 exit:
1920     if (interval != 0)
1921     {
1922         mMessageTransmissionTimer.Start(interval);
1923     }
1924     else
1925     {
1926         mMessageTransmissionTimer.Stop();
1927     }
1928 }
1929 
HandleMessageTransmissionTimer(void)1930 void Mle::HandleMessageTransmissionTimer(void)
1931 {
1932     // The `mMessageTransmissionTimer` is used for:
1933     //
1934     //  - Retransmission of "Child Update Request",
1935     //  - Retransmission of "Data Request" on a child,
1936     //  - Sending periodic keep-alive "Child Update Request" messages on a non-sleepy (rx-on) child.
1937 
1938     if (!mWaitingForChildUpdateResponse)
1939     {
1940         if (mWaitingForDataResponse)
1941         {
1942             Ip6::Address destination;
1943 
1944             VerifyOrExit(mDataRequestAttempts < kMaxChildKeepAliveAttempts, IgnoreError(BecomeDetached()));
1945 
1946             destination.SetToLinkLocalAddress(mParent.GetExtAddress());
1947 
1948             if (SendDataRequest(destination) == kErrorNone)
1949             {
1950                 mDataRequestAttempts++;
1951             }
1952 
1953             ExitNow();
1954         }
1955 
1956         // Keep-alive "Child Update Request" only on a non-sleepy child
1957         VerifyOrExit(IsChild() && IsRxOnWhenIdle());
1958     }
1959 
1960     VerifyOrExit(mChildUpdateAttempts < kMaxChildKeepAliveAttempts, IgnoreError(BecomeDetached()));
1961 
1962     if (SendChildUpdateRequestToParent() == kErrorNone)
1963     {
1964         mChildUpdateAttempts++;
1965     }
1966 
1967 exit:
1968     return;
1969 }
1970 
SendChildUpdateRequestToParent(void)1971 Error Mle::SendChildUpdateRequestToParent(void) { return SendChildUpdateRequestToParent(kNormalChildUpdateRequest); }
1972 
SendChildUpdateRequestToParent(ChildUpdateRequestMode aMode)1973 Error Mle::SendChildUpdateRequestToParent(ChildUpdateRequestMode aMode)
1974 {
1975     Error                   error = kErrorNone;
1976     Ip6::Address            destination;
1977     TxMessage              *message     = nullptr;
1978     AddressRegistrationMode addrRegMode = kAppendAllAddresses;
1979 
1980     if (!mParent.IsStateValidOrRestoring())
1981     {
1982         LogWarn("No valid parent when sending Child Update Request");
1983         IgnoreError(BecomeDetached());
1984         ExitNow();
1985     }
1986 
1987     if (aMode != kAppendZeroTimeout)
1988     {
1989         // Enable MLE retransmissions on all Child Update Request
1990         // messages, except when actively detaching.
1991         mWaitingForChildUpdateResponse = true;
1992         mDelayedSender.RemoveScheduledChildUpdateRequestToParent();
1993         ScheduleMessageTransmissionTimer();
1994     }
1995 
1996     VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs);
1997     SuccessOrExit(error = message->AppendModeTlv(mDeviceMode));
1998 
1999     if ((aMode == kAppendChallengeTlv) || IsDetached())
2000     {
2001         mParentRequestChallenge.GenerateRandom();
2002         SuccessOrExit(error = message->AppendChallengeTlv(mParentRequestChallenge));
2003     }
2004 
2005     switch (mRole)
2006     {
2007     case kRoleDetached:
2008         addrRegMode = kAppendMeshLocalOnly;
2009         break;
2010 
2011     case kRoleChild:
2012         SuccessOrExit(error = message->AppendSourceAddressTlv());
2013         SuccessOrExit(error = message->AppendLeaderDataTlv());
2014         SuccessOrExit(error = message->AppendTimeoutTlv((aMode == kAppendZeroTimeout) ? 0 : mTimeout));
2015         SuccessOrExit(error = message->AppendSupervisionIntervalTlvIfSleepyChild());
2016 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2017         if (Get<Mac::Mac>().IsCslEnabled())
2018         {
2019             SuccessOrExit(error = message->AppendCslChannelTlv());
2020             SuccessOrExit(error = message->AppendCslTimeoutTlv());
2021         }
2022 #endif
2023         break;
2024 
2025     case kRoleDisabled:
2026     case kRoleRouter:
2027     case kRoleLeader:
2028         OT_ASSERT(false);
2029     }
2030 
2031     if (!IsFullThreadDevice())
2032     {
2033         SuccessOrExit(error = message->AppendAddressRegistrationTlv(addrRegMode));
2034     }
2035 
2036     destination.SetToLinkLocalAddress(mParent.GetExtAddress());
2037     SuccessOrExit(error = message->SendTo(destination));
2038 
2039     Log(kMessageSend, kTypeChildUpdateRequestAsChild, destination);
2040 
2041     if (!IsRxOnWhenIdle())
2042     {
2043         Get<MeshForwarder>().SetRxOnWhenIdle(false);
2044 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2045         Get<DataPollSender>().SetAttachMode(!Get<Mac::Mac>().IsCslEnabled());
2046 #else
2047         Get<DataPollSender>().SetAttachMode(true);
2048 #endif
2049     }
2050     else
2051     {
2052         Get<MeshForwarder>().SetRxOnWhenIdle(true);
2053     }
2054 
2055 exit:
2056     FreeMessageOnError(message, error);
2057     return error;
2058 }
2059 
SendChildUpdateResponse(const TlvList & aTlvList,const RxChallenge & aChallenge,const Ip6::Address & aDestination)2060 Error Mle::SendChildUpdateResponse(const TlvList      &aTlvList,
2061                                    const RxChallenge  &aChallenge,
2062                                    const Ip6::Address &aDestination)
2063 {
2064     Error      error = kErrorNone;
2065     TxMessage *message;
2066     bool       checkAddress = false;
2067 
2068     VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs);
2069     SuccessOrExit(error = message->AppendSourceAddressTlv());
2070     SuccessOrExit(error = message->AppendLeaderDataTlv());
2071 
2072     for (uint8_t tlvType : aTlvList)
2073     {
2074         switch (tlvType)
2075         {
2076         case Tlv::kTimeout:
2077             SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout));
2078             break;
2079 
2080         case Tlv::kStatus:
2081             SuccessOrExit(error = message->AppendStatusTlv(StatusTlv::kError));
2082             break;
2083 
2084         case Tlv::kAddressRegistration:
2085             if (!IsFullThreadDevice())
2086             {
2087                 // We only register the mesh-local address in the "Child
2088                 // Update Response" message and if there are additional
2089                 // addresses to register we follow up with a "Child Update
2090                 // Request".
2091 
2092                 SuccessOrExit(error = message->AppendAddressRegistrationTlv(kAppendMeshLocalOnly));
2093                 checkAddress = true;
2094             }
2095 
2096             break;
2097 
2098         case Tlv::kResponse:
2099             SuccessOrExit(error = message->AppendResponseTlv(aChallenge));
2100             break;
2101 
2102         case Tlv::kLinkFrameCounter:
2103             SuccessOrExit(error = message->AppendLinkFrameCounterTlv());
2104             break;
2105 
2106         case Tlv::kMleFrameCounter:
2107             SuccessOrExit(error = message->AppendMleFrameCounterTlv());
2108             break;
2109 
2110         case Tlv::kSupervisionInterval:
2111             SuccessOrExit(error = message->AppendSupervisionIntervalTlvIfSleepyChild());
2112             break;
2113 
2114 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2115         case Tlv::kCslTimeout:
2116             if (Get<Mac::Mac>().IsCslEnabled())
2117             {
2118                 SuccessOrExit(error = message->AppendCslTimeoutTlv());
2119             }
2120             break;
2121 #endif
2122         }
2123     }
2124 
2125     SuccessOrExit(error = message->SendTo(aDestination));
2126 
2127     Log(kMessageSend, kTypeChildUpdateResponseAsChild, aDestination);
2128 
2129     if (checkAddress && HasUnregisteredAddress())
2130     {
2131         IgnoreError(SendChildUpdateRequestToParent());
2132     }
2133 
2134 exit:
2135     FreeMessageOnError(message, error);
2136     return error;
2137 }
2138 
SendAnnounce(uint8_t aChannel,AnnounceMode aMode)2139 void Mle::SendAnnounce(uint8_t aChannel, AnnounceMode aMode)
2140 {
2141     Ip6::Address destination;
2142 
2143     destination.SetToLinkLocalAllNodesMulticast();
2144 
2145     SendAnnounce(aChannel, destination, aMode);
2146 }
2147 
SendAnnounce(uint8_t aChannel,const Ip6::Address & aDestination,AnnounceMode aMode)2148 void Mle::SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, AnnounceMode aMode)
2149 {
2150     Error              error = kErrorNone;
2151     MeshCoP::Timestamp activeTimestamp;
2152     TxMessage         *message = nullptr;
2153 
2154     VerifyOrExit(Get<Mac::Mac>().GetSupportedChannelMask().ContainsChannel(aChannel), error = kErrorInvalidArgs);
2155     VerifyOrExit((message = NewMleMessage(kCommandAnnounce)) != nullptr, error = kErrorNoBufs);
2156     message->SetLinkSecurityEnabled(true);
2157     message->SetChannel(aChannel);
2158 
2159     SuccessOrExit(error = Tlv::Append<ChannelTlv>(*message, ChannelTlvValue(Get<Mac::Mac>().GetPanChannel())));
2160 
2161     switch (aMode)
2162     {
2163     case kOrphanAnnounce:
2164         activeTimestamp.SetToOrphanAnnounce();
2165         SuccessOrExit(error = Tlv::Append<ActiveTimestampTlv>(*message, activeTimestamp));
2166         break;
2167 
2168     case kNormalAnnounce:
2169         SuccessOrExit(error = message->AppendActiveTimestampTlv());
2170         break;
2171     }
2172 
2173     SuccessOrExit(error = Tlv::Append<PanIdTlv>(*message, Get<Mac::Mac>().GetPanId()));
2174 
2175     SuccessOrExit(error = message->SendTo(aDestination));
2176 
2177     LogInfo("Send Announce on channel %d", aChannel);
2178 
2179 exit:
2180     FreeMessageOnError(message, error);
2181 }
2182 
GetNextAnnounceChannel(uint8_t & aChannel) const2183 Error Mle::GetNextAnnounceChannel(uint8_t &aChannel) const
2184 {
2185     // This method gets the next channel to send announce on after
2186     // `aChannel`. Returns `kErrorNotFound` if no more channel in the
2187     // channel mask after `aChannel`.
2188 
2189     Mac::ChannelMask channelMask;
2190 
2191     if (Get<MeshCoP::ActiveDatasetManager>().GetChannelMask(channelMask) != kErrorNone)
2192     {
2193         channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
2194     }
2195 
2196     return channelMask.GetNextChannel(aChannel);
2197 }
2198 
HasMoreChannelsToAnnounce(void) const2199 bool Mle::HasMoreChannelsToAnnounce(void) const
2200 {
2201     uint8_t channel = mAnnounceChannel;
2202 
2203     return GetNextAnnounceChannel(channel) == kErrorNone;
2204 }
2205 
2206 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
SendLinkMetricsManagementResponse(const Ip6::Address & aDestination,LinkMetrics::Status aStatus)2207 Error Mle::SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, LinkMetrics::Status aStatus)
2208 {
2209     Error      error = kErrorNone;
2210     TxMessage *message;
2211     Tlv        tlv;
2212     ot::Tlv    statusSubTlv;
2213 
2214     VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementResponse)) != nullptr, error = kErrorNoBufs);
2215 
2216     tlv.SetType(Tlv::kLinkMetricsManagement);
2217     statusSubTlv.SetType(LinkMetrics::SubTlv::kStatus);
2218     statusSubTlv.SetLength(sizeof(aStatus));
2219     tlv.SetLength(statusSubTlv.GetSize());
2220 
2221     SuccessOrExit(error = message->Append(tlv));
2222     SuccessOrExit(error = message->Append(statusSubTlv));
2223     SuccessOrExit(error = message->Append(aStatus));
2224 
2225     SuccessOrExit(error = message->SendTo(aDestination));
2226 
2227 exit:
2228     FreeMessageOnError(message, error);
2229     return error;
2230 }
2231 #endif
2232 
2233 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
SendLinkProbe(const Ip6::Address & aDestination,uint8_t aSeriesId,uint8_t * aBuf,uint8_t aLength)2234 Error Mle::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t *aBuf, uint8_t aLength)
2235 {
2236     Error      error = kErrorNone;
2237     TxMessage *message;
2238     Tlv        tlv;
2239 
2240     VerifyOrExit((message = NewMleMessage(kCommandLinkProbe)) != nullptr, error = kErrorNoBufs);
2241 
2242     tlv.SetType(Tlv::kLinkProbe);
2243     tlv.SetLength(sizeof(aSeriesId) + aLength);
2244 
2245     SuccessOrExit(error = message->Append(tlv));
2246     SuccessOrExit(error = message->Append(aSeriesId));
2247     SuccessOrExit(error = message->AppendBytes(aBuf, aLength));
2248 
2249     SuccessOrExit(error = message->SendTo(aDestination));
2250 
2251 exit:
2252     FreeMessageOnError(message, error);
2253     return error;
2254 }
2255 #endif
2256 
ProcessMessageSecurity(Crypto::AesCcm::Mode aMode,Message & aMessage,const Ip6::MessageInfo & aMessageInfo,uint16_t aCmdOffset,const SecurityHeader & aHeader)2257 Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode    aMode,
2258                                   Message                &aMessage,
2259                                   const Ip6::MessageInfo &aMessageInfo,
2260                                   uint16_t                aCmdOffset,
2261                                   const SecurityHeader   &aHeader)
2262 {
2263     // This method performs MLE message security. Based on `aMode` it
2264     // can be used to encrypt and append tag to `aMessage` or to
2265     // decrypt and validate the tag in a received `aMessage` (which is
2266     // then removed from `aMessage`).
2267     //
2268     // `aCmdOffset` in both cases specifies the offset in `aMessage`
2269     // to the start of MLE payload (i.e., the command field).
2270     //
2271     // When decrypting, possible errors are:
2272     // `kErrorNone` decrypted and verified tag, tag is also removed.
2273     // `kErrorParse` message does not contain the tag
2274     // `kErrorSecurity` message tag is invalid.
2275     //
2276     // When encrypting, possible errors are:
2277     // `kErrorNone` message encrypted and tag appended to message.
2278     // `kErrorNoBufs` could not grow the message to append the tag.
2279 
2280     Error               error = kErrorNone;
2281     Crypto::AesCcm      aesCcm;
2282     uint8_t             nonce[Crypto::AesCcm::kNonceSize];
2283     uint8_t             tag[kMleSecurityTagSize];
2284     Mac::ExtAddress     extAddress;
2285     uint32_t            keySequence;
2286     uint16_t            payloadLength   = aMessage.GetLength() - aCmdOffset;
2287     const Ip6::Address *senderAddress   = &aMessageInfo.GetSockAddr();
2288     const Ip6::Address *receiverAddress = &aMessageInfo.GetPeerAddr();
2289 
2290     switch (aMode)
2291     {
2292     case Crypto::AesCcm::kEncrypt:
2293         // Use the initialized values for `senderAddress`,
2294         // `receiverAddress` and `payloadLength`
2295         break;
2296 
2297     case Crypto::AesCcm::kDecrypt:
2298         senderAddress   = &aMessageInfo.GetPeerAddr();
2299         receiverAddress = &aMessageInfo.GetSockAddr();
2300         // Ensure message contains command field (uint8_t) and
2301         // tag. Then exclude the tag from payload to decrypt.
2302         VerifyOrExit(aCmdOffset + sizeof(uint8_t) + kMleSecurityTagSize <= aMessage.GetLength(), error = kErrorParse);
2303         payloadLength -= kMleSecurityTagSize;
2304         break;
2305     }
2306 
2307     senderAddress->GetIid().ConvertToExtAddress(extAddress);
2308     Crypto::AesCcm::GenerateNonce(extAddress, aHeader.GetFrameCounter(), Mac::Frame::kSecurityEncMic32, nonce);
2309 
2310     keySequence = aHeader.GetKeyId();
2311 
2312     aesCcm.SetKey(keySequence == Get<KeyManager>().GetCurrentKeySequence()
2313                       ? Get<KeyManager>().GetCurrentMleKey()
2314                       : Get<KeyManager>().GetTemporaryMleKey(keySequence));
2315 
2316     aesCcm.Init(sizeof(Ip6::Address) + sizeof(Ip6::Address) + sizeof(SecurityHeader), payloadLength,
2317                 kMleSecurityTagSize, nonce, sizeof(nonce));
2318 
2319     aesCcm.Header(*senderAddress);
2320     aesCcm.Header(*receiverAddress);
2321     aesCcm.Header(aHeader);
2322 
2323 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2324     if (aMode == Crypto::AesCcm::kDecrypt)
2325     {
2326         // Skip decrypting the message under fuzz build mode
2327         aMessage.RemoveFooter(kMleSecurityTagSize);
2328         ExitNow();
2329     }
2330 #endif
2331 
2332     aesCcm.Payload(aMessage, aCmdOffset, payloadLength, aMode);
2333     aesCcm.Finalize(tag);
2334 
2335     if (aMode == Crypto::AesCcm::kEncrypt)
2336     {
2337         SuccessOrExit(error = aMessage.Append(tag));
2338     }
2339     else
2340     {
2341         VerifyOrExit(aMessage.Compare(aMessage.GetLength() - kMleSecurityTagSize, tag), error = kErrorSecurity);
2342         aMessage.RemoveFooter(kMleSecurityTagSize);
2343     }
2344 
2345 exit:
2346     return error;
2347 }
2348 
HandleUdpReceive(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)2349 void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
2350 {
2351     Error           error = kErrorNone;
2352     RxInfo          rxInfo(aMessage, aMessageInfo);
2353     uint8_t         securitySuite;
2354     SecurityHeader  header;
2355     uint32_t        keySequence;
2356     uint32_t        frameCounter;
2357     Mac::ExtAddress extAddr;
2358     uint8_t         command;
2359     Neighbor       *neighbor;
2360 #if OPENTHREAD_FTD
2361     bool isNeighborRxOnly = false;
2362 #endif
2363 
2364     LogDebg("Receive MLE message");
2365 
2366     VerifyOrExit(aMessage.GetOrigin() == Message::kOriginThreadNetif);
2367     VerifyOrExit(aMessageInfo.GetHopLimit() == kMleHopLimit, error = kErrorParse);
2368 
2369     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), securitySuite));
2370     aMessage.MoveOffset(sizeof(securitySuite));
2371 
2372     if (securitySuite == kNoSecurity)
2373     {
2374         SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), command));
2375         aMessage.MoveOffset(sizeof(command));
2376 
2377         switch (command)
2378         {
2379 #if OPENTHREAD_FTD
2380         case kCommandDiscoveryRequest:
2381             Get<MleRouter>().HandleDiscoveryRequest(rxInfo);
2382             break;
2383 #endif
2384         case kCommandDiscoveryResponse:
2385             Get<DiscoverScanner>().HandleDiscoveryResponse(rxInfo);
2386             break;
2387 
2388         default:
2389             break;
2390         }
2391 
2392         ExitNow();
2393     }
2394 
2395     VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
2396     VerifyOrExit(securitySuite == k154Security, error = kErrorParse);
2397 
2398     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), header));
2399     aMessage.MoveOffset(sizeof(header));
2400 
2401     VerifyOrExit(header.IsSecurityControlValid(), error = kErrorParse);
2402 
2403     keySequence  = header.GetKeyId();
2404     frameCounter = header.GetFrameCounter();
2405 
2406     SuccessOrExit(
2407         error = ProcessMessageSecurity(Crypto::AesCcm::kDecrypt, aMessage, aMessageInfo, aMessage.GetOffset(), header));
2408 
2409     IgnoreError(aMessage.Read(aMessage.GetOffset(), command));
2410     aMessage.MoveOffset(sizeof(command));
2411 
2412     aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
2413     neighbor = (command == kCommandChildIdResponse) ? mNeighborTable.FindParent(extAddr)
2414                                                     : mNeighborTable.FindNeighbor(extAddr);
2415 
2416 #if OPENTHREAD_FTD
2417     if (neighbor == nullptr)
2418     {
2419         // As an FTD child, we may have rx-only neighbors. We find and set
2420         // `neighbor` to perform security processing (frame counter
2421         // and key sequence checks) for messages from such neighbors.
2422 
2423         neighbor         = mNeighborTable.FindRxOnlyNeighborRouter(extAddr);
2424         isNeighborRxOnly = true;
2425     }
2426 #endif
2427 
2428     if (neighbor != nullptr && neighbor->IsStateValid())
2429     {
2430         if (keySequence == neighbor->GetKeySequence())
2431         {
2432 #if OPENTHREAD_CONFIG_MULTI_RADIO
2433             // Only when counter is exactly one off, we allow it to be
2434             // used for updating radio link info (by `RadioSelector`)
2435             // before message is dropped as a duplicate. This handles
2436             // the common case where a broadcast MLE message (such as
2437             // Link Advertisement) is received over multiple radio
2438             // links.
2439 
2440             if ((frameCounter + 1) == neighbor->GetMleFrameCounter())
2441             {
2442                 OT_ASSERT(aMessage.IsRadioTypeSet());
2443                 Get<RadioSelector>().UpdateOnReceive(*neighbor, aMessage.GetRadioType(), /* IsDuplicate */ true);
2444 
2445 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2446                 CheckTrelPeerAddrOnSecureMleRx(aMessage);
2447 #endif
2448 
2449                 // We intentionally exit without setting the error to
2450                 // skip logging "Failed to process UDP" at the exit
2451                 // label. Note that in multi-radio mode, receiving
2452                 // duplicate MLE message (with one-off counter) would
2453                 // be common and ok for broadcast MLE messages (e.g.
2454                 // MLE Link Advertisements).
2455 
2456                 ExitNow();
2457             }
2458 #endif
2459             VerifyOrExit(frameCounter >= neighbor->GetMleFrameCounter(), error = kErrorDuplicated);
2460         }
2461         else
2462         {
2463             VerifyOrExit(keySequence > neighbor->GetKeySequence(), error = kErrorDuplicated);
2464             neighbor->SetKeySequence(keySequence);
2465             neighbor->GetLinkFrameCounters().Reset();
2466             neighbor->SetLinkAckFrameCounter(0);
2467         }
2468 
2469         neighbor->SetMleFrameCounter(frameCounter + 1);
2470     }
2471 
2472 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2473     CheckTrelPeerAddrOnSecureMleRx(aMessage);
2474 #endif
2475 
2476 #if OPENTHREAD_CONFIG_MULTI_RADIO
2477     if (neighbor != nullptr)
2478     {
2479         OT_ASSERT(aMessage.IsRadioTypeSet());
2480         Get<RadioSelector>().UpdateOnReceive(*neighbor, aMessage.GetRadioType(), /* IsDuplicate */ false);
2481     }
2482 #endif
2483 
2484 #if OPENTHREAD_FTD
2485     if (isNeighborRxOnly)
2486     {
2487         // Clear the `neighbor` if it is a rx-only one before calling
2488         // `Handle{Msg}()`, except for a subset of MLE messages such
2489         // as MLE Advertisement. This ensures that, as an FTD child,
2490         // we are selective about which messages to process from
2491         // rx-only neighbors.
2492 
2493         switch (command)
2494         {
2495         case kCommandAdvertisement:
2496         case kCommandLinkRequest:
2497         case kCommandLinkAccept:
2498         case kCommandLinkAcceptAndRequest:
2499             break;
2500 
2501         default:
2502             neighbor = nullptr;
2503             break;
2504         }
2505     }
2506 #endif
2507 
2508     rxInfo.mKeySequence  = keySequence;
2509     rxInfo.mFrameCounter = frameCounter;
2510     rxInfo.mNeighbor     = neighbor;
2511 
2512     switch (command)
2513     {
2514     case kCommandAdvertisement:
2515         HandleAdvertisement(rxInfo);
2516         break;
2517 
2518     case kCommandDataResponse:
2519         HandleDataResponse(rxInfo);
2520         break;
2521 
2522     case kCommandParentResponse:
2523         HandleParentResponse(rxInfo);
2524         break;
2525 
2526     case kCommandChildIdResponse:
2527         HandleChildIdResponse(rxInfo);
2528         break;
2529 
2530     case kCommandAnnounce:
2531         HandleAnnounce(rxInfo);
2532         break;
2533 
2534     case kCommandChildUpdateRequest:
2535         HandleChildUpdateRequest(rxInfo);
2536         break;
2537 
2538     case kCommandChildUpdateResponse:
2539         HandleChildUpdateResponse(rxInfo);
2540         break;
2541 
2542 #if OPENTHREAD_FTD
2543     case kCommandLinkRequest:
2544         Get<MleRouter>().HandleLinkRequest(rxInfo);
2545         break;
2546 
2547     case kCommandLinkAccept:
2548         Get<MleRouter>().HandleLinkAccept(rxInfo);
2549         break;
2550 
2551     case kCommandLinkAcceptAndRequest:
2552         Get<MleRouter>().HandleLinkAcceptAndRequest(rxInfo);
2553         break;
2554 
2555     case kCommandDataRequest:
2556         Get<MleRouter>().HandleDataRequest(rxInfo);
2557         break;
2558 
2559     case kCommandParentRequest:
2560         Get<MleRouter>().HandleParentRequest(rxInfo);
2561         break;
2562 
2563     case kCommandChildIdRequest:
2564         Get<MleRouter>().HandleChildIdRequest(rxInfo);
2565         break;
2566 #endif // OPENTHREAD_FTD
2567 
2568 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
2569     case kCommandTimeSync:
2570         HandleTimeSync(rxInfo);
2571         break;
2572 #endif
2573 
2574 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
2575     case kCommandLinkMetricsManagementRequest:
2576         HandleLinkMetricsManagementRequest(rxInfo);
2577         break;
2578 #endif
2579 
2580 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
2581     case kCommandLinkMetricsManagementResponse:
2582         HandleLinkMetricsManagementResponse(rxInfo);
2583         break;
2584 #endif
2585 
2586 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
2587     case kCommandLinkProbe:
2588         HandleLinkProbe(rxInfo);
2589         break;
2590 #endif
2591 
2592     default:
2593         ExitNow(error = kErrorDrop);
2594     }
2595 
2596     ProcessKeySequence(rxInfo);
2597 
2598 #if OPENTHREAD_CONFIG_MULTI_RADIO
2599     // If we could not find a neighbor matching the MAC address of the
2600     // received MLE messages, or if the neighbor is now invalid, we
2601     // check again after the message is handled with a relaxed neighbor
2602     // state filer. The processing of the received MLE message may
2603     // create a new neighbor or change the neighbor table (e.g.,
2604     // receiving a "Parent Request" from a new child, or processing a
2605     // "Link Request" from a previous child which is being promoted to a
2606     // router).
2607 
2608     if ((neighbor == nullptr) || neighbor->IsStateInvalid())
2609     {
2610         neighbor = Get<NeighborTable>().FindNeighbor(extAddr, Neighbor::kInStateAnyExceptInvalid);
2611 
2612         if (neighbor != nullptr)
2613         {
2614             Get<RadioSelector>().UpdateOnReceive(*neighbor, aMessage.GetRadioType(), /* aIsDuplicate */ false);
2615         }
2616     }
2617 #endif
2618 
2619 exit:
2620     // We skip logging failures for broadcast MLE messages since it
2621     // can be common to receive such messages from adjacent Thread
2622     // networks.
2623     if (!aMessageInfo.GetSockAddr().IsMulticast() || !aMessage.IsDstPanIdBroadcast())
2624     {
2625         LogProcessError(kTypeGenericUdp, error);
2626     }
2627 }
2628 
ProcessKeySequence(RxInfo & aRxInfo)2629 void Mle::ProcessKeySequence(RxInfo &aRxInfo)
2630 {
2631     // In case key sequence is larger, we determine whether to adopt it
2632     // or not. The `Handle{MleMsg}()` methods set the `rxInfo.mClass`
2633     // based on the message command type and the included TLVs. If
2634     // there is any error during parsing of the message the `mClass`
2635     // remains as its default value of `RxInfo::kUnknown`. Message
2636     // classes are determined based on this:
2637     //
2638     // Authoritative : Larger key seq MUST be adopted.
2639     // Peer          : If from a known neighbor
2640     //                    If difference is 1, adopt
2641     //                    Otherwise don't adopt and try to re-sync with
2642     //                    neighbor.
2643     //                 Otherwise larger key seq MUST NOT be adopted.
2644 
2645     bool                          isNextKeySeq;
2646     KeyManager::KeySeqUpdateFlags flags = 0;
2647 
2648     VerifyOrExit(aRxInfo.mKeySequence > Get<KeyManager>().GetCurrentKeySequence());
2649 
2650     isNextKeySeq = (aRxInfo.mKeySequence - Get<KeyManager>().GetCurrentKeySequence() == 1);
2651 
2652     switch (aRxInfo.mClass)
2653     {
2654     case RxInfo::kAuthoritativeMessage:
2655         flags = KeyManager::kForceUpdate;
2656         break;
2657 
2658     case RxInfo::kPeerMessage:
2659         VerifyOrExit(aRxInfo.IsNeighborStateValid());
2660 
2661         if (!isNextKeySeq)
2662         {
2663             LogInfo("Large key seq jump in peer class msg from 0x%04x ", aRxInfo.mNeighbor->GetRloc16());
2664             ReestablishLinkWithNeighbor(*aRxInfo.mNeighbor);
2665             ExitNow();
2666         }
2667 
2668         flags = KeyManager::kApplySwitchGuard;
2669         break;
2670 
2671     case RxInfo::kUnknown:
2672         ExitNow();
2673     }
2674 
2675     if (isNextKeySeq)
2676     {
2677         flags |= KeyManager::kResetGuardTimer;
2678     }
2679 
2680     Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence, flags);
2681 
2682 exit:
2683     return;
2684 }
2685 
2686 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
CheckTrelPeerAddrOnSecureMleRx(const Message & aMessage)2687 void Mle::CheckTrelPeerAddrOnSecureMleRx(const Message &aMessage)
2688 {
2689     OT_UNUSED_VARIABLE(aMessage);
2690 
2691 #if OPENTHREAD_CONFIG_MULTI_RADIO
2692     if (aMessage.IsRadioTypeSet() && aMessage.GetRadioType() == Mac::kRadioTypeTrel)
2693 #endif
2694     {
2695         Get<Trel::Link>().CheckPeerAddrOnRxSuccess(Trel::Link::kAllowPeerSockAddrUpdate);
2696     }
2697 }
2698 #endif
2699 
ReestablishLinkWithNeighbor(Neighbor & aNeighbor)2700 void Mle::ReestablishLinkWithNeighbor(Neighbor &aNeighbor)
2701 {
2702     VerifyOrExit(IsAttached() && aNeighbor.IsStateValid());
2703 
2704     if (IsChild() && (&aNeighbor == &mParent))
2705     {
2706         IgnoreError(SendChildUpdateRequestToParent(kAppendChallengeTlv));
2707         ExitNow();
2708     }
2709 
2710 #if OPENTHREAD_FTD
2711     VerifyOrExit(IsFullThreadDevice());
2712 
2713     if (IsRouterRloc16(aNeighbor.GetRloc16()))
2714     {
2715         Get<MleRouter>().SendLinkRequest(static_cast<Router *>(&aNeighbor));
2716     }
2717     else if (Get<ChildTable>().Contains(aNeighbor))
2718     {
2719         Child &child = static_cast<Child &>(aNeighbor);
2720 
2721         child.SetState(Child::kStateChildUpdateRequest);
2722         IgnoreError(Get<MleRouter>().SendChildUpdateRequestToChild(child));
2723     }
2724 #endif
2725 
2726 exit:
2727     return;
2728 }
2729 
HandleAdvertisement(RxInfo & aRxInfo)2730 void Mle::HandleAdvertisement(RxInfo &aRxInfo)
2731 {
2732     Error      error = kErrorNone;
2733     uint16_t   sourceAddress;
2734     LeaderData leaderData;
2735     uint16_t   delay;
2736 
2737     VerifyOrExit(IsAttached());
2738 
2739     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
2740 
2741     Log(kMessageReceive, kTypeAdvertisement, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
2742 
2743     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
2744 
2745 #if OPENTHREAD_FTD
2746     if (IsFullThreadDevice())
2747     {
2748         SuccessOrExit(error = Get<MleRouter>().HandleAdvertisementOnFtd(aRxInfo, sourceAddress, leaderData));
2749     }
2750 #endif
2751 
2752     if (IsChild())
2753     {
2754         VerifyOrExit(aRxInfo.mNeighbor == &mParent);
2755 
2756         if (mParent.GetRloc16() != sourceAddress)
2757         {
2758             // Remove stale parent.
2759             IgnoreError(BecomeDetached());
2760             ExitNow();
2761         }
2762 
2763         if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) ||
2764             (leaderData.GetLeaderRouterId() != GetLeaderId()))
2765         {
2766             SetLeaderData(leaderData);
2767 
2768 #if OPENTHREAD_FTD
2769             SuccessOrExit(error = Get<MleRouter>().ReadAndProcessRouteTlvOnFtdChild(aRxInfo, mParent.GetRouterId()));
2770 #endif
2771 
2772             mRetrieveNewNetworkData = true;
2773         }
2774 
2775         mParent.SetLastHeard(TimerMilli::GetNow());
2776     }
2777     else // Device is router or leader
2778     {
2779         VerifyOrExit(aRxInfo.IsNeighborStateValid());
2780     }
2781 
2782     if (mRetrieveNewNetworkData || IsNetworkDataNewer(leaderData))
2783     {
2784         delay = 1 + Random::NonCrypto::GetUint16InRange(0, kMleMaxResponseDelay);
2785         mDelayedSender.ScheduleDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), delay);
2786     }
2787 
2788     aRxInfo.mClass = RxInfo::kPeerMessage;
2789 
2790 exit:
2791     LogProcessError(kTypeAdvertisement, error);
2792 }
2793 
HandleDataResponse(RxInfo & aRxInfo)2794 void Mle::HandleDataResponse(RxInfo &aRxInfo)
2795 {
2796     Error error;
2797 
2798     Log(kMessageReceive, kTypeDataResponse, aRxInfo.mMessageInfo.GetPeerAddr());
2799 
2800     VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorDrop);
2801 
2802 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
2803     {
2804         OffsetRange offsetRange;
2805 
2806         if (Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kLinkMetricsReport, offsetRange) == kErrorNone)
2807         {
2808             Get<LinkMetrics::Initiator>().HandleReport(aRxInfo.mMessage, offsetRange,
2809                                                        aRxInfo.mMessageInfo.GetPeerAddr());
2810         }
2811     }
2812 #endif
2813 
2814 #if OPENTHREAD_FTD
2815     SuccessOrExit(error = Get<MleRouter>().ReadAndProcessRouteTlvOnFtdChild(aRxInfo, mParent.GetRouterId()));
2816 #endif
2817 
2818     error = HandleLeaderData(aRxInfo);
2819 
2820     if (!mWaitingForDataResponse && !IsRxOnWhenIdle())
2821     {
2822         // Stop fast data poll request by MLE since we received
2823         // the response.
2824         Get<DataPollSender>().StopFastPolls();
2825     }
2826 
2827     SuccessOrExit(error);
2828     aRxInfo.mClass = RxInfo::kPeerMessage;
2829 
2830 exit:
2831     LogProcessError(kTypeDataResponse, error);
2832 }
2833 
IsNetworkDataNewer(const LeaderData & aLeaderData)2834 bool Mle::IsNetworkDataNewer(const LeaderData &aLeaderData)
2835 {
2836     return SerialNumber::IsGreater(aLeaderData.GetDataVersion(GetNetworkDataType()),
2837                                    Get<NetworkData::Leader>().GetVersion(GetNetworkDataType()));
2838 }
2839 
HandleLeaderData(RxInfo & aRxInfo)2840 Error Mle::HandleLeaderData(RxInfo &aRxInfo)
2841 {
2842     Error              error = kErrorNone;
2843     LeaderData         leaderData;
2844     MeshCoP::Timestamp activeTimestamp;
2845     MeshCoP::Timestamp pendingTimestamp;
2846     bool               saveActiveDataset  = false;
2847     bool               savePendingDataset = false;
2848     bool               dataRequest        = false;
2849 
2850     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
2851 
2852     if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) ||
2853         (leaderData.GetWeighting() != mLeaderData.GetWeighting()) || (leaderData.GetLeaderRouterId() != GetLeaderId()))
2854     {
2855         if (IsChild())
2856         {
2857             SetLeaderData(leaderData);
2858             mRetrieveNewNetworkData = true;
2859         }
2860         else
2861         {
2862             ExitNow(error = kErrorDrop);
2863         }
2864     }
2865     else if (!mRetrieveNewNetworkData)
2866     {
2867         VerifyOrExit(IsNetworkDataNewer(leaderData));
2868     }
2869 
2870     switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, activeTimestamp))
2871     {
2872     case kErrorNone:
2873 #if OPENTHREAD_FTD
2874         if (IsLeader())
2875         {
2876             break;
2877         }
2878 #endif
2879         if (activeTimestamp != Get<MeshCoP::ActiveDatasetManager>().GetTimestamp())
2880         {
2881             // Send an MLE Data Request if the received timestamp
2882             // mismatches the local value and the message does not
2883             // include the dataset.
2884 
2885             VerifyOrExit(aRxInfo.mMessage.ContainsTlv(Tlv::kActiveDataset), dataRequest = true);
2886             saveActiveDataset = true;
2887         }
2888 
2889         break;
2890 
2891     case kErrorNotFound:
2892         break;
2893 
2894     default:
2895         ExitNow(error = kErrorParse);
2896     }
2897 
2898     switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, pendingTimestamp))
2899     {
2900     case kErrorNone:
2901 #if OPENTHREAD_FTD
2902         if (IsLeader())
2903         {
2904             break;
2905         }
2906 #endif
2907         if (pendingTimestamp != Get<MeshCoP::PendingDatasetManager>().GetTimestamp())
2908         {
2909             VerifyOrExit(aRxInfo.mMessage.ContainsTlv(Tlv::kPendingDataset), dataRequest = true);
2910             savePendingDataset = true;
2911         }
2912 
2913         break;
2914 
2915     case kErrorNotFound:
2916         break;
2917 
2918     default:
2919         ExitNow(error = kErrorParse);
2920     }
2921 
2922     switch (error = aRxInfo.mMessage.ReadAndSetNetworkDataTlv(leaderData))
2923     {
2924     case kErrorNone:
2925         break;
2926     case kErrorNotFound:
2927         dataRequest = true;
2928         OT_FALL_THROUGH;
2929     default:
2930         ExitNow();
2931     }
2932 
2933 #if OPENTHREAD_FTD
2934     if (IsLeader())
2935     {
2936         Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
2937     }
2938     else
2939 #endif
2940     {
2941         // We previously confirmed the message contains an
2942         // Active or a Pending Dataset TLV before setting the
2943         // corresponding `saveDataset` flag.
2944 
2945         if (saveActiveDataset)
2946         {
2947             IgnoreError(aRxInfo.mMessage.ReadAndSaveActiveDataset(activeTimestamp));
2948         }
2949 
2950         if (savePendingDataset)
2951         {
2952             IgnoreError(aRxInfo.mMessage.ReadAndSavePendingDataset(pendingTimestamp));
2953         }
2954     }
2955 
2956     mRetrieveNewNetworkData = false;
2957 
2958 exit:
2959 
2960     if (dataRequest)
2961     {
2962         uint16_t delay;
2963 
2964         if (aRxInfo.mMessageInfo.GetSockAddr().IsMulticast())
2965         {
2966             delay = 1 + Random::NonCrypto::GetUint16InRange(0, kMleMaxResponseDelay);
2967         }
2968         else
2969         {
2970             // This method may have been called from an MLE request
2971             // handler.  We add a minimum delay here so that the MLE
2972             // response is enqueued before the MLE Data Request.
2973             delay = 10;
2974         }
2975 
2976         mDelayedSender.ScheduleDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), delay);
2977     }
2978     else if (error == kErrorNone)
2979     {
2980         mDataRequestAttempts    = 0;
2981         mWaitingForDataResponse = false;
2982 
2983         // Here the `mMessageTransmissionTimer` is intentionally not canceled
2984         // so that when it fires from its callback a "Child Update" is sent
2985         // if the device is a rx-on child. This way, even when the timer is
2986         // reused for retransmission of "Data Request" messages, it is ensured
2987         // that keep-alive "Child Update Request" messages are send within the
2988         // child's timeout.
2989     }
2990 
2991     return error;
2992 }
2993 
IsBetterParent(uint16_t aRloc16,uint8_t aTwoWayLinkMargin,const ConnectivityTlv & aConnectivityTlv,uint16_t aVersion,const Mac::CslAccuracy & aCslAccuracy)2994 bool Mle::IsBetterParent(uint16_t                aRloc16,
2995                          uint8_t                 aTwoWayLinkMargin,
2996                          const ConnectivityTlv  &aConnectivityTlv,
2997                          uint16_t                aVersion,
2998                          const Mac::CslAccuracy &aCslAccuracy)
2999 {
3000     int rval;
3001 
3002     // Mesh Impacting Criteria
3003     rval = ThreeWayCompare(LinkQualityForLinkMargin(aTwoWayLinkMargin), mParentCandidate.GetTwoWayLinkQuality());
3004     VerifyOrExit(rval == 0);
3005 
3006     rval = ThreeWayCompare(IsRouterRloc16(aRloc16), IsRouterRloc16(mParentCandidate.GetRloc16()));
3007     VerifyOrExit(rval == 0);
3008 
3009     rval = ThreeWayCompare(aConnectivityTlv.GetParentPriority(), mParentCandidate.mPriority);
3010     VerifyOrExit(rval == 0);
3011 
3012     // Prefer the parent with highest quality links (Link Quality 3 field in Connectivity TLV) to neighbors
3013     rval = ThreeWayCompare(aConnectivityTlv.GetLinkQuality3(), mParentCandidate.mLinkQuality3);
3014     VerifyOrExit(rval == 0);
3015 
3016     // Thread 1.2 Specification 4.5.2.1.2 Child Impacting Criteria
3017 
3018     rval = ThreeWayCompare(aVersion, mParentCandidate.GetVersion());
3019     VerifyOrExit(rval == 0);
3020 
3021     rval = ThreeWayCompare(aConnectivityTlv.GetSedBufferSize(), mParentCandidate.mSedBufferSize);
3022     VerifyOrExit(rval == 0);
3023 
3024     rval = ThreeWayCompare(aConnectivityTlv.GetSedDatagramCount(), mParentCandidate.mSedDatagramCount);
3025     VerifyOrExit(rval == 0);
3026 
3027     // Extra rules
3028     rval = ThreeWayCompare(aConnectivityTlv.GetLinkQuality2(), mParentCandidate.mLinkQuality2);
3029     VerifyOrExit(rval == 0);
3030 
3031     rval = ThreeWayCompare(aConnectivityTlv.GetLinkQuality1(), mParentCandidate.mLinkQuality1);
3032     VerifyOrExit(rval == 0);
3033 
3034 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
3035     // CSL metric
3036     if (!IsRxOnWhenIdle())
3037     {
3038         uint64_t cslMetric          = CalcParentCslMetric(aCslAccuracy);
3039         uint64_t candidateCslMetric = CalcParentCslMetric(mParentCandidate.GetCslAccuracy());
3040 
3041         // Smaller metric is better.
3042         rval = ThreeWayCompare(candidateCslMetric, cslMetric);
3043         VerifyOrExit(rval == 0);
3044     }
3045 #else
3046     OT_UNUSED_VARIABLE(aCslAccuracy);
3047 #endif
3048 
3049     rval = ThreeWayCompare(aTwoWayLinkMargin, mParentCandidate.mLinkMargin);
3050 
3051 exit:
3052     return (rval > 0);
3053 }
3054 
HandleParentResponse(RxInfo & aRxInfo)3055 void Mle::HandleParentResponse(RxInfo &aRxInfo)
3056 {
3057     Error            error = kErrorNone;
3058     int8_t           rss   = aRxInfo.mMessage.GetAverageRss();
3059     uint16_t         version;
3060     uint16_t         sourceAddress;
3061     LeaderData       leaderData;
3062     uint8_t          linkMarginOut;
3063     uint8_t          twoWayLinkMargin;
3064     ConnectivityTlv  connectivityTlv;
3065     uint32_t         linkFrameCounter;
3066     uint32_t         mleFrameCounter;
3067     Mac::ExtAddress  extAddress;
3068     Mac::CslAccuracy cslAccuracy;
3069 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3070     TimeParameterTlv timeParameterTlv;
3071 #endif
3072 
3073     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
3074 
3075     Log(kMessageReceive, kTypeParentResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
3076 
3077     SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version));
3078 
3079     SuccessOrExit(error = aRxInfo.mMessage.ReadAndMatchResponseTlvWith(mParentRequestChallenge));
3080 
3081     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddress);
3082 
3083     if (IsChild() && mParent.GetExtAddress() == extAddress)
3084     {
3085         mReceivedResponseFromParent = true;
3086     }
3087 
3088     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
3089 
3090     SuccessOrExit(error = Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMarginOut));
3091     twoWayLinkMargin = Min(Get<Mac::Mac>().ComputeLinkMargin(rss), linkMarginOut);
3092 
3093     SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, connectivityTlv));
3094     VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
3095 
3096 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
3097     switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
3098     {
3099     case kErrorNone:
3100         break;
3101     case kErrorNotFound:
3102         cslAccuracy.Init(); // Use worst-case values if TLV is not found
3103         break;
3104     default:
3105         ExitNow(error = kErrorParse);
3106     }
3107 #else
3108     cslAccuracy.Init();
3109 #endif
3110 
3111 #if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE
3112     if (mParentResponseCallback.IsSet())
3113     {
3114         otThreadParentResponseInfo parentinfo;
3115 
3116         parentinfo.mExtAddr      = extAddress;
3117         parentinfo.mRloc16       = sourceAddress;
3118         parentinfo.mRssi         = rss;
3119         parentinfo.mPriority     = connectivityTlv.GetParentPriority();
3120         parentinfo.mLinkQuality3 = connectivityTlv.GetLinkQuality3();
3121         parentinfo.mLinkQuality2 = connectivityTlv.GetLinkQuality2();
3122         parentinfo.mLinkQuality1 = connectivityTlv.GetLinkQuality1();
3123         parentinfo.mIsAttached   = IsAttached();
3124 
3125         mParentResponseCallback.Invoke(&parentinfo);
3126     }
3127 #endif
3128 
3129     aRxInfo.mClass = RxInfo::kAuthoritativeMessage;
3130 
3131 #if OPENTHREAD_FTD
3132     if (IsFullThreadDevice() && !IsDetached())
3133     {
3134         bool isPartitionIdSame = (leaderData.GetPartitionId() == mLeaderData.GetPartitionId());
3135         bool isIdSequenceSame  = (connectivityTlv.GetIdSequence() == Get<RouterTable>().GetRouterIdSequence());
3136         bool isIdSequenceGreater =
3137             SerialNumber::IsGreater(connectivityTlv.GetIdSequence(), Get<RouterTable>().GetRouterIdSequence());
3138 
3139         switch (mAttachMode)
3140         {
3141         case kAnyPartition:
3142             VerifyOrExit(!isPartitionIdSame || isIdSequenceGreater);
3143             break;
3144 
3145         case kSamePartition:
3146             VerifyOrExit(isPartitionIdSame && isIdSequenceGreater);
3147             break;
3148 
3149         case kDowngradeToReed:
3150             VerifyOrExit(isPartitionIdSame && (isIdSequenceSame || isIdSequenceGreater));
3151             break;
3152 
3153         case kBetterPartition:
3154             VerifyOrExit(!isPartitionIdSame);
3155 
3156             VerifyOrExit(MleRouter::ComparePartitions(connectivityTlv.IsSingleton(), leaderData,
3157                                                       Get<MleRouter>().IsSingleton(), mLeaderData) > 0);
3158             break;
3159 
3160         case kBetterParent:
3161         case kSelectedParent:
3162             break;
3163         }
3164     }
3165 #endif
3166 
3167     // Continue to process the "ParentResponse" if it is from current
3168     // parent candidate to update the challenge and frame counters.
3169 
3170     if (mParentCandidate.IsStateParentResponse() && (mParentCandidate.GetExtAddress() != extAddress))
3171     {
3172         // If already have a candidate parent, only seek a better parent
3173 
3174         int compare = 0;
3175 
3176 #if OPENTHREAD_FTD
3177         if (IsFullThreadDevice())
3178         {
3179             compare = MleRouter::ComparePartitions(connectivityTlv.IsSingleton(), leaderData,
3180                                                    mParentCandidate.mIsSingleton, mParentCandidate.mLeaderData);
3181         }
3182 
3183         // Only consider partitions that are the same or better
3184         VerifyOrExit(compare >= 0);
3185 #endif
3186 
3187         // Only consider better parents if the partitions are the same
3188         if (compare == 0)
3189         {
3190             VerifyOrExit(IsBetterParent(sourceAddress, twoWayLinkMargin, connectivityTlv, version, cslAccuracy));
3191         }
3192     }
3193 
3194     SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
3195 
3196 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3197 
3198     if (Tlv::FindTlv(aRxInfo.mMessage, timeParameterTlv) == kErrorNone)
3199     {
3200         VerifyOrExit(timeParameterTlv.IsValid());
3201 
3202         Get<TimeSync>().SetTimeSyncPeriod(timeParameterTlv.GetTimeSyncPeriod());
3203         Get<TimeSync>().SetXtalThreshold(timeParameterTlv.GetXtalThreshold());
3204     }
3205 
3206 #if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
3207     else
3208     {
3209         // If the time sync feature is required, don't choose the
3210         // parent which doesn't support it.
3211         ExitNow();
3212     }
3213 #endif
3214 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3215 
3216     SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(mParentCandidate.mRxChallenge));
3217 
3218     InitNeighbor(mParentCandidate, aRxInfo);
3219     mParentCandidate.SetRloc16(sourceAddress);
3220     mParentCandidate.GetLinkFrameCounters().SetAll(linkFrameCounter);
3221     mParentCandidate.SetLinkAckFrameCounter(linkFrameCounter);
3222     mParentCandidate.SetMleFrameCounter(mleFrameCounter);
3223     mParentCandidate.SetVersion(version);
3224     mParentCandidate.SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle |
3225                                               DeviceMode::kModeFullNetworkData));
3226     mParentCandidate.SetLinkQualityOut(LinkQualityForLinkMargin(linkMarginOut));
3227     mParentCandidate.SetState(Neighbor::kStateParentResponse);
3228     mParentCandidate.SetKeySequence(aRxInfo.mKeySequence);
3229     mParentCandidate.SetLeaderCost(connectivityTlv.GetLeaderCost());
3230 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
3231     mParentCandidate.SetCslAccuracy(cslAccuracy);
3232 #endif
3233 
3234     mParentCandidate.mPriority         = connectivityTlv.GetParentPriority();
3235     mParentCandidate.mLinkQuality3     = connectivityTlv.GetLinkQuality3();
3236     mParentCandidate.mLinkQuality2     = connectivityTlv.GetLinkQuality2();
3237     mParentCandidate.mLinkQuality1     = connectivityTlv.GetLinkQuality1();
3238     mParentCandidate.mSedBufferSize    = connectivityTlv.GetSedBufferSize();
3239     mParentCandidate.mSedDatagramCount = connectivityTlv.GetSedDatagramCount();
3240     mParentCandidate.mLeaderData       = leaderData;
3241     mParentCandidate.mIsSingleton      = connectivityTlv.IsSingleton();
3242     mParentCandidate.mLinkMargin       = twoWayLinkMargin;
3243 
3244 exit:
3245     LogProcessError(kTypeParentResponse, error);
3246 }
3247 
HandleChildIdResponse(RxInfo & aRxInfo)3248 void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
3249 {
3250     Error              error = kErrorNone;
3251     LeaderData         leaderData;
3252     uint16_t           sourceAddress;
3253     uint16_t           shortAddress;
3254     MeshCoP::Timestamp timestamp;
3255 
3256     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
3257 
3258     Log(kMessageReceive, kTypeChildIdResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
3259 
3260     VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorSecurity);
3261 
3262     VerifyOrExit(mAttachState == kAttachStateChildIdRequest);
3263 
3264     SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, shortAddress));
3265     VerifyOrExit(RouterIdMatch(sourceAddress, shortAddress), error = kErrorRejected);
3266 
3267     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
3268 
3269     VerifyOrExit(aRxInfo.mMessage.ContainsTlv(Tlv::kNetworkData));
3270 
3271     switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
3272     {
3273     case kErrorNone:
3274         error = aRxInfo.mMessage.ReadAndSaveActiveDataset(timestamp);
3275         error = (error == kErrorNotFound) ? kErrorNone : error;
3276         SuccessOrExit(error);
3277         break;
3278 
3279     case kErrorNotFound:
3280         break;
3281 
3282     default:
3283         ExitNow(error = kErrorParse);
3284     }
3285 
3286     // Clear Pending Dataset if device succeed to reattach using stored Pending Dataset
3287     if (mReattachState == kReattachPending)
3288     {
3289         Get<MeshCoP::PendingDatasetManager>().Clear();
3290     }
3291 
3292     switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
3293     {
3294     case kErrorNone:
3295         IgnoreError(aRxInfo.mMessage.ReadAndSavePendingDataset(timestamp));
3296         break;
3297 
3298     case kErrorNotFound:
3299         Get<MeshCoP::PendingDatasetManager>().Clear();
3300         break;
3301 
3302     default:
3303         ExitNow(error = kErrorParse);
3304     }
3305 
3306 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3307     if (aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ)
3308     {
3309         Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
3310     }
3311 #endif
3312 
3313     // Parent Attach Success
3314 
3315     SetStateDetached();
3316 
3317     SetLeaderData(leaderData);
3318 
3319 #if OPENTHREAD_FTD
3320     SuccessOrExit(error =
3321                       Get<MleRouter>().ReadAndProcessRouteTlvOnFtdChild(aRxInfo, RouterIdFromRloc16(sourceAddress)));
3322 #endif
3323 
3324     mParentCandidate.CopyTo(mParent);
3325     mParentCandidate.Clear();
3326 
3327 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
3328     Get<Mac::Mac>().SetCslParentAccuracy(mParent.GetCslAccuracy());
3329 #endif
3330 
3331     mParent.SetRloc16(sourceAddress);
3332 
3333     IgnoreError(aRxInfo.mMessage.ReadAndSetNetworkDataTlv(leaderData));
3334 
3335     SetStateChild(shortAddress);
3336 
3337     if (!IsRxOnWhenIdle())
3338     {
3339         Get<DataPollSender>().SetAttachMode(false);
3340         Get<MeshForwarder>().SetRxOnWhenIdle(false);
3341     }
3342     else
3343     {
3344         Get<MeshForwarder>().SetRxOnWhenIdle(true);
3345     }
3346 
3347     aRxInfo.mClass = RxInfo::kPeerMessage;
3348 
3349 exit:
3350     LogProcessError(kTypeChildIdResponse, error);
3351 }
3352 
HandleChildUpdateRequest(RxInfo & aRxInfo)3353 void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
3354 {
3355 #if OPENTHREAD_FTD
3356     if (IsRouterOrLeader())
3357     {
3358         Get<MleRouter>().HandleChildUpdateRequestOnParent(aRxInfo);
3359     }
3360     else
3361 #endif
3362     {
3363         HandleChildUpdateRequestOnChild(aRxInfo);
3364     }
3365 }
3366 
HandleChildUpdateRequestOnChild(RxInfo & aRxInfo)3367 void Mle::HandleChildUpdateRequestOnChild(RxInfo &aRxInfo)
3368 {
3369     Error       error = kErrorNone;
3370     uint16_t    sourceAddress;
3371     RxChallenge challenge;
3372     TlvList     requestedTlvList;
3373     TlvList     tlvList;
3374     uint8_t     linkMarginOut;
3375 
3376     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
3377 
3378     Log(kMessageReceive, kTypeChildUpdateRequestAsChild, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
3379 
3380     switch (aRxInfo.mMessage.ReadChallengeTlv(challenge))
3381     {
3382     case kErrorNone:
3383         tlvList.Add(Tlv::kResponse);
3384         tlvList.Add(Tlv::kMleFrameCounter);
3385         tlvList.Add(Tlv::kLinkFrameCounter);
3386         break;
3387     case kErrorNotFound:
3388         challenge.Clear();
3389         break;
3390     default:
3391         ExitNow(error = kErrorParse);
3392     }
3393 
3394     if (aRxInfo.mNeighbor == &mParent)
3395     {
3396         uint8_t status;
3397 
3398         switch (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status))
3399         {
3400         case kErrorNone:
3401             VerifyOrExit(status != StatusTlv::kError, IgnoreError(BecomeDetached()));
3402             break;
3403         case kErrorNotFound:
3404             break;
3405         default:
3406             ExitNow(error = kErrorParse);
3407         }
3408 
3409         if (mParent.GetRloc16() != sourceAddress)
3410         {
3411             IgnoreError(BecomeDetached());
3412             ExitNow();
3413         }
3414 
3415         SuccessOrExit(error = HandleLeaderData(aRxInfo));
3416 
3417         switch (Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMarginOut))
3418         {
3419         case kErrorNone:
3420             mParent.SetLinkQualityOut(LinkQualityForLinkMargin(linkMarginOut));
3421             break;
3422         case kErrorNotFound:
3423             break;
3424         default:
3425             ExitNow(error = kErrorParse);
3426         }
3427 
3428 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
3429         {
3430             Mac::CslAccuracy cslAccuracy;
3431 
3432             if (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy) == kErrorNone)
3433             {
3434                 // MUST include CSL timeout TLV when request includes
3435                 // CSL accuracy
3436                 tlvList.Add(Tlv::kCslTimeout);
3437             }
3438         }
3439 #endif
3440     }
3441     else
3442     {
3443         // This device is not a child of the Child Update Request source
3444         tlvList.Add(Tlv::kStatus);
3445     }
3446 
3447     switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
3448     {
3449     case kErrorNone:
3450         tlvList.AddElementsFrom(requestedTlvList);
3451         break;
3452     case kErrorNotFound:
3453         break;
3454     default:
3455         ExitNow(error = kErrorParse);
3456     }
3457 
3458     aRxInfo.mClass = RxInfo::kPeerMessage;
3459     ProcessKeySequence(aRxInfo);
3460 
3461 #if OPENTHREAD_CONFIG_MULTI_RADIO
3462     if ((aRxInfo.mNeighbor != nullptr) && !challenge.IsEmpty())
3463     {
3464         aRxInfo.mNeighbor->ClearLastRxFragmentTag();
3465     }
3466 #endif
3467 
3468     // Send the response to the requester, regardless if it's this
3469     // device's parent or not.
3470     SuccessOrExit(error = SendChildUpdateResponse(tlvList, challenge, aRxInfo.mMessageInfo.GetPeerAddr()));
3471 
3472 exit:
3473     LogProcessError(kTypeChildUpdateRequestAsChild, error);
3474 }
3475 
HandleChildUpdateResponse(RxInfo & aRxInfo)3476 void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo)
3477 {
3478 #if OPENTHREAD_FTD
3479     if (IsRouterOrLeader())
3480     {
3481         Get<MleRouter>().HandleChildUpdateResponseOnParent(aRxInfo);
3482     }
3483     else
3484 #endif
3485     {
3486         HandleChildUpdateResponseOnChild(aRxInfo);
3487     }
3488 }
3489 
HandleChildUpdateResponseOnChild(RxInfo & aRxInfo)3490 void Mle::HandleChildUpdateResponseOnChild(RxInfo &aRxInfo)
3491 {
3492     Error       error = kErrorNone;
3493     uint8_t     status;
3494     DeviceMode  mode;
3495     RxChallenge response;
3496     uint32_t    linkFrameCounter;
3497     uint32_t    mleFrameCounter;
3498     uint16_t    sourceAddress;
3499     uint32_t    timeout;
3500     uint8_t     linkMarginOut;
3501 
3502     Log(kMessageReceive, kTypeChildUpdateResponseAsChild, aRxInfo.mMessageInfo.GetPeerAddr());
3503 
3504     switch (aRxInfo.mMessage.ReadResponseTlv(response))
3505     {
3506     case kErrorNone:
3507         break;
3508     case kErrorNotFound:
3509         response.Clear();
3510         break;
3511     default:
3512         ExitNow(error = kErrorParse);
3513     }
3514 
3515     switch (mRole)
3516     {
3517     case kRoleDetached:
3518         VerifyOrExit(response == mParentRequestChallenge, error = kErrorSecurity);
3519         break;
3520 
3521     case kRoleChild:
3522         VerifyOrExit((aRxInfo.mNeighbor == &mParent) && mParent.IsStateValid(), error = kErrorSecurity);
3523         break;
3524 
3525     default:
3526         OT_ASSERT(false);
3527     }
3528 
3529     if (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status) == kErrorNone)
3530     {
3531         IgnoreError(BecomeDetached());
3532         ExitNow();
3533     }
3534 
3535     SuccessOrExit(error = aRxInfo.mMessage.ReadModeTlv(mode));
3536     VerifyOrExit(mode == mDeviceMode, error = kErrorDrop);
3537 
3538     switch (mRole)
3539     {
3540     case kRoleDetached:
3541         SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
3542 
3543         mParent.GetLinkFrameCounters().SetAll(linkFrameCounter);
3544         mParent.SetLinkAckFrameCounter(linkFrameCounter);
3545         mParent.SetMleFrameCounter(mleFrameCounter);
3546 
3547         mParent.SetState(Neighbor::kStateValid);
3548         SetStateChild(GetRloc16());
3549 
3550         mRetrieveNewNetworkData = true;
3551 
3552 #if OPENTHREAD_FTD
3553         if (IsFullThreadDevice())
3554         {
3555             mRequestRouteTlv = true;
3556         }
3557 #endif
3558 
3559         OT_FALL_THROUGH;
3560 
3561     case kRoleChild:
3562         SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
3563 
3564         if (!HasMatchingRouterIdWith(sourceAddress))
3565         {
3566             IgnoreError(BecomeDetached());
3567             ExitNow();
3568         }
3569 
3570         SuccessOrExit(error = HandleLeaderData(aRxInfo));
3571 
3572         switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
3573         {
3574         case kErrorNone:
3575             if (timeout == 0 && mDetachingGracefully)
3576             {
3577                 Stop();
3578             }
3579             else
3580             {
3581                 mTimeout = timeout;
3582             }
3583             break;
3584         case kErrorNotFound:
3585             break;
3586         default:
3587             ExitNow(error = kErrorParse);
3588         }
3589 
3590 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
3591         {
3592             Mac::CslAccuracy cslAccuracy;
3593 
3594             switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
3595             {
3596             case kErrorNone:
3597                 Get<Mac::Mac>().SetCslParentAccuracy(cslAccuracy);
3598                 break;
3599             case kErrorNotFound:
3600                 break;
3601             default:
3602                 ExitNow(error = kErrorParse);
3603             }
3604         }
3605 #endif
3606 
3607         if (!IsRxOnWhenIdle())
3608         {
3609             Get<DataPollSender>().SetAttachMode(false);
3610             Get<MeshForwarder>().SetRxOnWhenIdle(false);
3611         }
3612         else
3613         {
3614             Get<MeshForwarder>().SetRxOnWhenIdle(true);
3615         }
3616 
3617         break;
3618 
3619     default:
3620         OT_ASSERT(false);
3621     }
3622 
3623     switch (Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMarginOut))
3624     {
3625     case kErrorNone:
3626         mParent.SetLinkQualityOut(LinkQualityForLinkMargin(linkMarginOut));
3627         break;
3628     case kErrorNotFound:
3629         break;
3630     default:
3631         ExitNow(error = kErrorParse);
3632     }
3633 
3634     aRxInfo.mClass = response.IsEmpty() ? RxInfo::kPeerMessage : RxInfo::kAuthoritativeMessage;
3635 
3636 exit:
3637 
3638     if (error == kErrorNone)
3639     {
3640         if (mWaitingForChildUpdateResponse)
3641         {
3642             mChildUpdateAttempts           = 0;
3643             mWaitingForChildUpdateResponse = false;
3644             ScheduleMessageTransmissionTimer();
3645         }
3646     }
3647 
3648     LogProcessError(kTypeChildUpdateResponseAsChild, error);
3649 }
3650 
HandleAnnounce(RxInfo & aRxInfo)3651 void Mle::HandleAnnounce(RxInfo &aRxInfo)
3652 {
3653     Error              error = kErrorNone;
3654     ChannelTlvValue    channelTlvValue;
3655     MeshCoP::Timestamp timestamp;
3656     MeshCoP::Timestamp pendingActiveTimestamp;
3657     uint8_t            channel;
3658     uint16_t           panId;
3659     bool               isFromOrphan;
3660     bool               channelAndPanIdMatch;
3661     int                timestampCompare;
3662 
3663     Log(kMessageReceive, kTypeAnnounce, aRxInfo.mMessageInfo.GetPeerAddr());
3664 
3665     SuccessOrExit(error = Tlv::Find<ChannelTlv>(aRxInfo.mMessage, channelTlvValue));
3666     channel = static_cast<uint8_t>(channelTlvValue.GetChannel());
3667 
3668     SuccessOrExit(error = Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp));
3669     SuccessOrExit(error = Tlv::Find<PanIdTlv>(aRxInfo.mMessage, panId));
3670 
3671     aRxInfo.mClass = RxInfo::kPeerMessage;
3672 
3673     isFromOrphan         = timestamp.IsOrphanAnnounce();
3674     timestampCompare     = MeshCoP::Timestamp::Compare(timestamp, Get<MeshCoP::ActiveDatasetManager>().GetTimestamp());
3675     channelAndPanIdMatch = (channel == Get<Mac::Mac>().GetPanChannel()) && (panId == Get<Mac::Mac>().GetPanId());
3676 
3677     if (isFromOrphan || (timestampCompare < 0))
3678     {
3679         if (isFromOrphan)
3680         {
3681             VerifyOrExit(!channelAndPanIdMatch);
3682         }
3683 
3684         SendAnnounce(channel);
3685 
3686 #if OPENTHREAD_CONFIG_MLE_SEND_UNICAST_ANNOUNCE_RESPONSE
3687         SendAnnounce(channel, aRxInfo.mMessageInfo.GetPeerAddr());
3688 #endif
3689     }
3690     else if (timestampCompare > 0)
3691     {
3692         // No action is required if device is detached, and current
3693         // channel and pan-id match the values from the received MLE
3694         // Announce message.
3695 
3696         if (IsDetached())
3697         {
3698             VerifyOrExit(!channelAndPanIdMatch);
3699         }
3700 
3701         if (Get<MeshCoP::PendingDatasetManager>().ReadActiveTimestamp(pendingActiveTimestamp) == kErrorNone)
3702         {
3703             // Ignore the Announce and take no action, if a pending
3704             // dataset exists with an equal or more recent timestamp,
3705             // and it will be applied soon.
3706 
3707             if (pendingActiveTimestamp >= timestamp)
3708             {
3709                 uint32_t remainingDelay;
3710 
3711                 if ((Get<MeshCoP::PendingDatasetManager>().ReadRemainingDelay(remainingDelay) == kErrorNone) &&
3712                     (remainingDelay < kAnnounceBackoffForPendingDataset))
3713                 {
3714                     ExitNow();
3715                 }
3716             }
3717         }
3718 
3719         if (mAttachState == kAttachStateProcessAnnounce)
3720         {
3721             VerifyOrExit(mAlternateTimestamp < timestamp.GetSeconds());
3722         }
3723 
3724         mAlternateTimestamp = timestamp.GetSeconds();
3725         mAlternateChannel   = channel;
3726         mAlternatePanId     = panId;
3727         SetAttachState(kAttachStateProcessAnnounce);
3728         mAttachTimer.Start(kAnnounceProcessTimeout);
3729         mAttachCounter = 0;
3730 
3731         LogNote("Delay processing Announce - channel %d, panid 0x%02x", channel, panId);
3732     }
3733     else
3734     {
3735         // Timestamps are equal.
3736 
3737 #if OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE
3738         // Notify `AnnounceSender` of the received Announce
3739         // message so it can update its state to determine
3740         // whether to send Announce or not.
3741         Get<AnnounceSender>().UpdateOnReceivedAnnounce();
3742 #endif
3743     }
3744 
3745 exit:
3746     LogProcessError(kTypeAnnounce, error);
3747 }
3748 
3749 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
HandleLinkMetricsManagementRequest(RxInfo & aRxInfo)3750 void Mle::HandleLinkMetricsManagementRequest(RxInfo &aRxInfo)
3751 {
3752     Error               error = kErrorNone;
3753     LinkMetrics::Status status;
3754 
3755     Log(kMessageReceive, kTypeLinkMetricsManagementRequest, aRxInfo.mMessageInfo.GetPeerAddr());
3756 
3757     VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorInvalidState);
3758 
3759     SuccessOrExit(
3760         error = Get<LinkMetrics::Subject>().HandleManagementRequest(aRxInfo.mMessage, *aRxInfo.mNeighbor, status));
3761 
3762     error = SendLinkMetricsManagementResponse(aRxInfo.mMessageInfo.GetPeerAddr(), status);
3763 
3764     aRxInfo.mClass = RxInfo::kPeerMessage;
3765 
3766 exit:
3767     LogProcessError(kTypeLinkMetricsManagementRequest, error);
3768 }
3769 #endif
3770 
3771 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
HandleTimeSync(RxInfo & aRxInfo)3772 void Mle::HandleTimeSync(RxInfo &aRxInfo)
3773 {
3774     Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
3775 
3776     VerifyOrExit(aRxInfo.IsNeighborStateValid());
3777 
3778     aRxInfo.mClass = RxInfo::kPeerMessage;
3779 
3780     Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
3781 
3782 exit:
3783     return;
3784 }
3785 #endif
3786 
3787 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
HandleLinkMetricsManagementResponse(RxInfo & aRxInfo)3788 void Mle::HandleLinkMetricsManagementResponse(RxInfo &aRxInfo)
3789 {
3790     Error error = kErrorNone;
3791 
3792     Log(kMessageReceive, kTypeLinkMetricsManagementResponse, aRxInfo.mMessageInfo.GetPeerAddr());
3793 
3794     VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorInvalidState);
3795 
3796     error =
3797         Get<LinkMetrics::Initiator>().HandleManagementResponse(aRxInfo.mMessage, aRxInfo.mMessageInfo.GetPeerAddr());
3798 
3799     aRxInfo.mClass = RxInfo::kPeerMessage;
3800 
3801 exit:
3802     LogProcessError(kTypeLinkMetricsManagementResponse, error);
3803 }
3804 #endif
3805 
3806 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
HandleLinkProbe(RxInfo & aRxInfo)3807 void Mle::HandleLinkProbe(RxInfo &aRxInfo)
3808 {
3809     Error   error = kErrorNone;
3810     uint8_t seriesId;
3811 
3812     Log(kMessageReceive, kTypeLinkProbe, aRxInfo.mMessageInfo.GetPeerAddr());
3813 
3814     VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorInvalidState);
3815 
3816     SuccessOrExit(error = Get<LinkMetrics::Subject>().HandleLinkProbe(aRxInfo.mMessage, seriesId));
3817     aRxInfo.mNeighbor->AggregateLinkMetrics(seriesId, LinkMetrics::SeriesInfo::kSeriesTypeLinkProbe,
3818                                             aRxInfo.mMessage.GetAverageLqi(), aRxInfo.mMessage.GetAverageRss());
3819 
3820     aRxInfo.mClass = RxInfo::kPeerMessage;
3821 
3822 exit:
3823     LogProcessError(kTypeLinkProbe, error);
3824 }
3825 #endif
3826 
ProcessAnnounce(void)3827 void Mle::ProcessAnnounce(void)
3828 {
3829     uint8_t  newChannel = mAlternateChannel;
3830     uint16_t newPanId   = mAlternatePanId;
3831 
3832     OT_ASSERT(mAttachState == kAttachStateProcessAnnounce);
3833 
3834     LogNote("Processing Announce - channel %d, panid 0x%02x", newChannel, newPanId);
3835 
3836     Stop(kKeepNetworkDatasets);
3837 
3838     // Save the current/previous channel and pan-id
3839     mAlternateChannel   = Get<Mac::Mac>().GetPanChannel();
3840     mAlternatePanId     = Get<Mac::Mac>().GetPanId();
3841     mAlternateTimestamp = 0;
3842 
3843     IgnoreError(Get<Mac::Mac>().SetPanChannel(newChannel));
3844     Get<Mac::Mac>().SetPanId(newPanId);
3845 
3846     IgnoreError(Start(kAnnounceAttach));
3847 }
3848 
GetParentRloc16(void) const3849 uint16_t Mle::GetParentRloc16(void) const { return (mParent.IsStateValid() ? mParent.GetRloc16() : kInvalidRloc16); }
3850 
GetParentInfo(Router::Info & aParentInfo) const3851 Error Mle::GetParentInfo(Router::Info &aParentInfo) const
3852 {
3853     Error error = kErrorNone;
3854 
3855     // Skip the check for reference device since it needs to get the
3856     // original parent's info even after role change.
3857 
3858 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
3859     VerifyOrExit(IsChild(), error = kErrorInvalidState);
3860 #endif
3861 
3862     aParentInfo.SetFrom(mParent);
3863     ExitNow();
3864 
3865 exit:
3866     return error;
3867 }
3868 
IsRoutingLocator(const Ip6::Address & aAddress) const3869 bool Mle::IsRoutingLocator(const Ip6::Address &aAddress) const
3870 {
3871     return IsMeshLocalAddress(aAddress) && aAddress.GetIid().IsRoutingLocator();
3872 }
3873 
IsAnycastLocator(const Ip6::Address & aAddress) const3874 bool Mle::IsAnycastLocator(const Ip6::Address &aAddress) const
3875 {
3876     return IsMeshLocalAddress(aAddress) && aAddress.GetIid().IsAnycastLocator();
3877 }
3878 
IsMeshLocalAddress(const Ip6::Address & aAddress) const3879 bool Mle::IsMeshLocalAddress(const Ip6::Address &aAddress) const { return (aAddress.GetPrefix() == mMeshLocalPrefix); }
3880 
3881 #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
InformPreviousParent(void)3882 void Mle::InformPreviousParent(void)
3883 {
3884     Error            error   = kErrorNone;
3885     Message         *message = nullptr;
3886     Ip6::MessageInfo messageInfo;
3887 
3888     VerifyOrExit((message = Get<Ip6::Ip6>().NewMessage(0)) != nullptr, error = kErrorNoBufs);
3889     SuccessOrExit(error = message->SetLength(0));
3890 
3891     messageInfo.SetSockAddr(GetMeshLocalEid());
3892     messageInfo.GetPeerAddr().SetToRoutingLocator(mMeshLocalPrefix, mPreviousParentRloc);
3893 
3894     SuccessOrExit(error = Get<Ip6::Ip6>().SendDatagram(*message, messageInfo, Ip6::kProtoNone));
3895 
3896     LogNote("Sending message to inform previous parent 0x%04x", mPreviousParentRloc);
3897 
3898 exit:
3899     LogWarnOnError(error, "inform previous parent");
3900     FreeMessageOnError(message, error);
3901 }
3902 #endif // OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
3903 
3904 #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
3905 
SetEnabled(bool aEnabled)3906 void Mle::ParentSearch::SetEnabled(bool aEnabled)
3907 {
3908     VerifyOrExit(mEnabled != aEnabled);
3909     mEnabled = aEnabled;
3910     StartTimer();
3911 
3912 exit:
3913     return;
3914 }
3915 
HandleTimer(void)3916 void Mle::ParentSearch::HandleTimer(void)
3917 {
3918     AttachMode attachMode;
3919 
3920     LogInfo("PeriodicParentSearch: %s interval passed", mIsInBackoff ? "Backoff" : "Check");
3921 
3922     if (mBackoffWasCanceled)
3923     {
3924         // Backoff can be canceled if the device switches to a new parent.
3925         // from `UpdateParentSearchState()`. We want to limit this to happen
3926         // only once within a backoff interval.
3927 
3928         if (TimerMilli::GetNow() - mBackoffCancelTime >= kBackoffInterval)
3929         {
3930             mBackoffWasCanceled = false;
3931             LogInfo("PeriodicParentSearch: Backoff cancellation is allowed on parent switch");
3932         }
3933     }
3934 
3935     mIsInBackoff = false;
3936 
3937     VerifyOrExit(Get<Mle>().IsChild());
3938 
3939 #if OPENTHREAD_FTD
3940     if (Get<Mle>().IsFullThreadDevice())
3941     {
3942         SuccessOrExit(SelectBetterParent());
3943         attachMode = kSelectedParent;
3944     }
3945     else
3946 #endif
3947     {
3948         int8_t parentRss;
3949 
3950         parentRss = Get<Mle>().GetParent().GetLinkInfo().GetAverageRss();
3951         LogInfo("PeriodicParentSearch: Parent RSS %d", parentRss);
3952         VerifyOrExit(parentRss != Radio::kInvalidRssi);
3953 
3954         VerifyOrExit(parentRss < kRssThreshold);
3955         LogInfo("PeriodicParentSearch: Parent RSS less than %d, searching for new parents", kRssThreshold);
3956 
3957         mIsInBackoff = true;
3958         attachMode   = kBetterParent;
3959     }
3960 
3961     Get<Mle>().mCounters.mBetterParentAttachAttempts++;
3962     Get<Mle>().Attach(attachMode);
3963 
3964 exit:
3965     StartTimer();
3966 }
3967 
3968 #if OPENTHREAD_FTD
SelectBetterParent(void)3969 Error Mle::ParentSearch::SelectBetterParent(void)
3970 {
3971     Error error = kErrorNone;
3972 
3973     mSelectedParent = nullptr;
3974 
3975     for (Router &router : Get<RouterTable>())
3976     {
3977         CompareAndUpdateSelectedParent(router);
3978     }
3979 
3980     VerifyOrExit(mSelectedParent != nullptr, error = kErrorNotFound);
3981     mSelectedParent->RestartParentReselectTimeout();
3982 
3983     LogInfo("PeriodicParentSearch: Selected router 0x%04x as parent with RSS %d", mSelectedParent->GetRloc16(),
3984             mSelectedParent->GetLinkInfo().GetAverageRss());
3985 
3986 exit:
3987     return error;
3988 }
3989 
CompareAndUpdateSelectedParent(Router & aRouter)3990 void Mle::ParentSearch::CompareAndUpdateSelectedParent(Router &aRouter)
3991 {
3992     int8_t routerRss;
3993 
3994     VerifyOrExit(aRouter.IsSelectableAsParent());
3995     VerifyOrExit(aRouter.GetParentReselectTimeout() == 0);
3996     VerifyOrExit(aRouter.GetRloc16() != Get<Mle>().GetParent().GetRloc16());
3997 
3998     routerRss = aRouter.GetLinkInfo().GetAverageRss();
3999     VerifyOrExit(routerRss != Radio::kInvalidRssi);
4000 
4001     if (mSelectedParent == nullptr)
4002     {
4003         VerifyOrExit(routerRss >= Get<Mle>().GetParent().GetLinkInfo().GetAverageRss() + kRssMarginOverParent);
4004     }
4005     else
4006     {
4007         VerifyOrExit(routerRss > mSelectedParent->GetLinkInfo().GetAverageRss());
4008     }
4009 
4010     mSelectedParent = &aRouter;
4011 
4012 exit:
4013     return;
4014 }
4015 
4016 #endif // OPENTHREAD_FTD
4017 
StartTimer(void)4018 void Mle::ParentSearch::StartTimer(void)
4019 {
4020     uint32_t interval;
4021 
4022     if (!mEnabled)
4023     {
4024         mTimer.Stop();
4025         ExitNow();
4026     }
4027 
4028     interval = Random::NonCrypto::GetUint32InRange(0, kJitterInterval);
4029 
4030     if (mIsInBackoff)
4031     {
4032         interval += kBackoffInterval;
4033     }
4034     else
4035     {
4036         interval += kCheckInterval;
4037     }
4038 
4039     mTimer.Start(interval);
4040 
4041     LogInfo("PeriodicParentSearch: (Re)starting timer for %s interval", mIsInBackoff ? "backoff" : "check");
4042 
4043 exit:
4044     return;
4045 }
4046 
UpdateState(void)4047 void Mle::ParentSearch::UpdateState(void)
4048 {
4049 #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
4050 
4051     // If we are in middle of backoff and backoff was not canceled
4052     // recently and we recently detached from a previous parent,
4053     // then we check if the new parent is different from the previous
4054     // one, and if so, we cancel the backoff mode and also remember
4055     // the backoff cancel time. This way the canceling of backoff
4056     // is allowed only once within a backoff window.
4057     //
4058     // The reason behind the canceling of the backoff is to handle
4059     // the scenario where a previous parent is not available for a
4060     // short duration (e.g., it is going through a software update)
4061     // and the child switches to a less desirable parent. With this
4062     // model the child will check for other parents sooner and have
4063     // the chance to switch back to the original (and possibly
4064     // preferred) parent more quickly.
4065 
4066     if (mIsInBackoff && !mBackoffWasCanceled && mRecentlyDetached)
4067     {
4068         if ((Get<Mle>().mPreviousParentRloc != kInvalidRloc16) &&
4069             (Get<Mle>().mPreviousParentRloc != Get<Mle>().mParent.GetRloc16()))
4070         {
4071             mIsInBackoff        = false;
4072             mBackoffWasCanceled = true;
4073             mBackoffCancelTime  = TimerMilli::GetNow();
4074             LogInfo("PeriodicParentSearch: Canceling backoff on switching to a new parent");
4075         }
4076     }
4077 
4078 #endif // OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
4079 
4080     mRecentlyDetached = false;
4081 
4082     if (!mIsInBackoff)
4083     {
4084         StartTimer();
4085     }
4086 }
4087 #endif // OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
4088 
4089 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
Log(MessageAction aAction,MessageType aType,const Ip6::Address & aAddress)4090 void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress)
4091 {
4092     Log(aAction, aType, aAddress, kInvalidRloc16);
4093 }
4094 
Log(MessageAction aAction,MessageType aType,const Ip6::Address & aAddress,uint16_t aRloc)4095 void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress, uint16_t aRloc)
4096 {
4097     enum : uint8_t
4098     {
4099         kRlocStringSize = 17,
4100     };
4101 
4102     String<kRlocStringSize> rlocString;
4103 
4104     if (aRloc != kInvalidRloc16)
4105     {
4106         rlocString.Append(",0x%04x", aRloc);
4107     }
4108 
4109     LogInfo("%s %s%s (%s%s)", MessageActionToString(aAction), MessageTypeToString(aType),
4110             MessageTypeActionToSuffixString(aType, aAction), aAddress.ToString().AsCString(), rlocString.AsCString());
4111 }
4112 #endif
4113 
4114 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
LogProcessError(MessageType aType,Error aError)4115 void Mle::LogProcessError(MessageType aType, Error aError) { LogError(kMessageReceive, aType, aError); }
4116 
LogSendError(MessageType aType,Error aError)4117 void Mle::LogSendError(MessageType aType, Error aError) { LogError(kMessageSend, aType, aError); }
4118 
LogError(MessageAction aAction,MessageType aType,Error aError)4119 void Mle::LogError(MessageAction aAction, MessageType aType, Error aError)
4120 {
4121     if (aError != kErrorNone)
4122     {
4123         if (aAction == kMessageReceive && (aError == kErrorDrop || aError == kErrorNoRoute))
4124         {
4125             LogInfo("Failed to %s %s%s: %s", "process", MessageTypeToString(aType),
4126                     MessageTypeActionToSuffixString(aType, aAction), ErrorToString(aError));
4127         }
4128         else
4129         {
4130             LogWarn("Failed to %s %s%s: %s", aAction == kMessageSend ? "send" : "process", MessageTypeToString(aType),
4131                     MessageTypeActionToSuffixString(aType, aAction), ErrorToString(aError));
4132         }
4133     }
4134 }
4135 
MessageActionToString(MessageAction aAction)4136 const char *Mle::MessageActionToString(MessageAction aAction)
4137 {
4138     static const char *const kMessageActionStrings[] = {
4139         "Send",           // (0) kMessageSend
4140         "Receive",        // (1) kMessageReceive
4141         "Delay",          // (2) kMessageDelay
4142         "Remove Delayed", // (3) kMessageRemoveDelayed
4143     };
4144 
4145     struct EnumCheck
4146     {
4147         InitEnumValidatorCounter();
4148         ValidateNextEnum(kMessageSend);
4149         ValidateNextEnum(kMessageReceive);
4150         ValidateNextEnum(kMessageDelay);
4151         ValidateNextEnum(kMessageRemoveDelayed);
4152     };
4153 
4154     return kMessageActionStrings[aAction];
4155 }
4156 
MessageTypeToString(MessageType aType)4157 const char *Mle::MessageTypeToString(MessageType aType)
4158 {
4159     static const char *const kMessageTypeStrings[] = {
4160         "Advertisement",         // (0)  kTypeAdvertisement
4161         "Announce",              // (1)  kTypeAnnounce
4162         "Child ID Request",      // (2)  kTypeChildIdRequest
4163         "Child ID Request",      // (3)  kTypeChildIdRequestShort
4164         "Child ID Response",     // (4)  kTypeChildIdResponse
4165         "Child Update Request",  // (5)  kTypeChildUpdateRequestAsChild
4166         "Child Update Response", // (6)  kTypeChildUpdateResponseAsChild
4167         "Data Request",          // (7)  kTypeDataRequest
4168         "Data Response",         // (8)  kTypeDataResponse
4169         "Discovery Request",     // (9)  kTypeDiscoveryRequest
4170         "Discovery Response",    // (10) kTypeDiscoveryResponse
4171         "delayed message",       // (11) kTypeGenericDelayed
4172         "UDP",                   // (12) kTypeGenericUdp
4173         "Parent Request",        // (13) kTypeParentRequestToRouters
4174         "Parent Request",        // (14) kTypeParentRequestToRoutersReeds
4175         "Parent Response",       // (15) kTypeParentResponse
4176 #if OPENTHREAD_FTD
4177         "Address Release",         // (16) kTypeAddressRelease
4178         "Address Release Reply",   // (17) kTypeAddressReleaseReply
4179         "Address Reply",           // (18) kTypeAddressReply
4180         "Address Solicit",         // (19) kTypeAddressSolicit
4181         "Child Update Request",    // (20) kTypeChildUpdateRequestOfChild
4182         "Child Update Response",   // (21) kTypeChildUpdateResponseOfChild
4183         "Child Update Response",   // (22) kTypeChildUpdateResponseOfUnknownChild
4184         "Link Accept",             // (23) kTypeLinkAccept
4185         "Link Accept and Request", // (24) kTypeLinkAcceptAndRequest
4186         "Link Reject",             // (25) kTypeLinkReject
4187         "Link Request",            // (26) kTypeLinkRequest
4188         "Parent Request",          // (27) kTypeParentRequest
4189 #endif
4190 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
4191         "Link Metrics Management Request",  // (28) kTypeLinkMetricsManagementRequest
4192         "Link Metrics Management Response", // (29) kTypeLinkMetricsManagementResponse
4193         "Link Probe",                       // (30) kTypeLinkProbe
4194 #endif
4195 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
4196         "Time Sync", // (31) kTypeTimeSync
4197 #endif
4198     };
4199 
4200     struct EnumCheck
4201     {
4202         InitEnumValidatorCounter();
4203         ValidateNextEnum(kTypeAdvertisement);
4204         ValidateNextEnum(kTypeAnnounce);
4205         ValidateNextEnum(kTypeChildIdRequest);
4206         ValidateNextEnum(kTypeChildIdRequestShort);
4207         ValidateNextEnum(kTypeChildIdResponse);
4208         ValidateNextEnum(kTypeChildUpdateRequestAsChild);
4209         ValidateNextEnum(kTypeChildUpdateResponseAsChild);
4210         ValidateNextEnum(kTypeDataRequest);
4211         ValidateNextEnum(kTypeDataResponse);
4212         ValidateNextEnum(kTypeDiscoveryRequest);
4213         ValidateNextEnum(kTypeDiscoveryResponse);
4214         ValidateNextEnum(kTypeGenericDelayed);
4215         ValidateNextEnum(kTypeGenericUdp);
4216         ValidateNextEnum(kTypeParentRequestToRouters);
4217         ValidateNextEnum(kTypeParentRequestToRoutersReeds);
4218         ValidateNextEnum(kTypeParentResponse);
4219 #if OPENTHREAD_FTD
4220         ValidateNextEnum(kTypeAddressRelease);
4221         ValidateNextEnum(kTypeAddressReleaseReply);
4222         ValidateNextEnum(kTypeAddressReply);
4223         ValidateNextEnum(kTypeAddressSolicit);
4224         ValidateNextEnum(kTypeChildUpdateRequestOfChild);
4225         ValidateNextEnum(kTypeChildUpdateResponseOfChild);
4226         ValidateNextEnum(kTypeChildUpdateResponseOfUnknownChild);
4227         ValidateNextEnum(kTypeLinkAccept);
4228         ValidateNextEnum(kTypeLinkAcceptAndRequest);
4229         ValidateNextEnum(kTypeLinkReject);
4230         ValidateNextEnum(kTypeLinkRequest);
4231         ValidateNextEnum(kTypeParentRequest);
4232 #endif
4233 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
4234         ValidateNextEnum(kTypeLinkMetricsManagementRequest);
4235         ValidateNextEnum(kTypeLinkMetricsManagementResponse);
4236         ValidateNextEnum(kTypeLinkProbe);
4237 #endif
4238 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
4239         ValidateNextEnum(kTypeTimeSync);
4240 #endif
4241     };
4242 
4243     return kMessageTypeStrings[aType];
4244 }
4245 
MessageTypeActionToSuffixString(MessageType aType,MessageAction aAction)4246 const char *Mle::MessageTypeActionToSuffixString(MessageType aType, MessageAction aAction)
4247 {
4248     const char *str = "";
4249 
4250     OT_UNUSED_VARIABLE(aAction); // Not currently used in non-FTD builds
4251 
4252     switch (aType)
4253     {
4254     case kTypeChildIdRequestShort:
4255         str = " - short";
4256         break;
4257     case kTypeChildUpdateRequestAsChild:
4258     case kTypeChildUpdateResponseAsChild:
4259         str = " as child";
4260         break;
4261     case kTypeParentRequestToRouters:
4262         str = " to routers";
4263         break;
4264     case kTypeParentRequestToRoutersReeds:
4265         str = " to routers and REEDs";
4266         break;
4267 #if OPENTHREAD_FTD
4268     case kTypeChildUpdateRequestOfChild:
4269     case kTypeChildUpdateResponseOfChild:
4270         str = (aAction == kMessageReceive) ? " from child" : " to child";
4271         break;
4272     case kTypeChildUpdateResponseOfUnknownChild:
4273         str = (aAction == kMessageReceive) ? " from unknown child" : " to child";
4274         break;
4275 #endif // OPENTHREAD_FTD
4276     default:
4277         break;
4278     }
4279 
4280     return str;
4281 }
4282 
4283 #endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_WARN)
4284 
4285 // LCOV_EXCL_START
4286 
4287 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
4288 
AttachModeToString(AttachMode aMode)4289 const char *Mle::AttachModeToString(AttachMode aMode)
4290 {
4291     static const char *const kAttachModeStrings[] = {
4292         "AnyPartition",    // (0) kAnyPartition
4293         "SamePartition",   // (1) kSamePartition
4294         "BetterPartition", // (2) kBetterPartition
4295         "DowngradeToReed", // (3) kDowngradeToReed
4296         "BetterParent",    // (4) kBetterParent
4297         "SelectedParent",  // (5) kSelectedParent
4298     };
4299 
4300     struct EnumCheck
4301     {
4302         InitEnumValidatorCounter();
4303         ValidateNextEnum(kAnyPartition);
4304         ValidateNextEnum(kSamePartition);
4305         ValidateNextEnum(kBetterPartition);
4306         ValidateNextEnum(kDowngradeToReed);
4307         ValidateNextEnum(kBetterParent);
4308         ValidateNextEnum(kSelectedParent);
4309     };
4310 
4311     return kAttachModeStrings[aMode];
4312 }
4313 
AttachStateToString(AttachState aState)4314 const char *Mle::AttachStateToString(AttachState aState)
4315 {
4316     static const char *const kAttachStateStrings[] = {
4317         "Idle",            // (0) kAttachStateIdle
4318         "ProcessAnnounce", // (1) kAttachStateProcessAnnounce
4319         "Start",           // (2) kAttachStateStart
4320         "ParentReq",       // (3) kAttachStateParent
4321         "Announce",        // (4) kAttachStateAnnounce
4322         "ChildIdReq",      // (5) kAttachStateChildIdRequest
4323     };
4324 
4325     struct EnumCheck
4326     {
4327         InitEnumValidatorCounter();
4328         ValidateNextEnum(kAttachStateIdle);
4329         ValidateNextEnum(kAttachStateProcessAnnounce);
4330         ValidateNextEnum(kAttachStateStart);
4331         ValidateNextEnum(kAttachStateParentRequest);
4332         ValidateNextEnum(kAttachStateAnnounce);
4333         ValidateNextEnum(kAttachStateChildIdRequest);
4334     };
4335 
4336     return kAttachStateStrings[aState];
4337 }
4338 
ReattachStateToString(ReattachState aState)4339 const char *Mle::ReattachStateToString(ReattachState aState)
4340 {
4341     static const char *const kReattachStateStrings[] = {
4342         "",                                 // (0) kReattachStop
4343         "reattaching",                      // (1) kReattachStart
4344         "reattaching with Active Dataset",  // (2) kReattachActive
4345         "reattaching with Pending Dataset", // (3) kReattachPending
4346     };
4347 
4348     struct EnumCheck
4349     {
4350         InitEnumValidatorCounter();
4351         ValidateNextEnum(kReattachStop);
4352         ValidateNextEnum(kReattachStart);
4353         ValidateNextEnum(kReattachActive);
4354         ValidateNextEnum(kReattachPending);
4355     };
4356 
4357     return kReattachStateStrings[aState];
4358 }
4359 
4360 #endif // OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE)
4361 
4362 // LCOV_EXCL_STOP
4363 
4364 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
SendLinkMetricsManagementRequest(const Ip6::Address & aDestination,const ot::Tlv & aSubTlv)4365 Error Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const ot::Tlv &aSubTlv)
4366 {
4367     Error      error   = kErrorNone;
4368     TxMessage *message = NewMleMessage(kCommandLinkMetricsManagementRequest);
4369     Tlv        tlv;
4370 
4371     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
4372 
4373     tlv.SetType(Tlv::kLinkMetricsManagement);
4374     tlv.SetLength(static_cast<uint8_t>(aSubTlv.GetSize()));
4375 
4376     SuccessOrExit(error = message->Append(tlv));
4377     SuccessOrExit(error = aSubTlv.AppendTo(*message));
4378 
4379     error = message->SendTo(aDestination);
4380 
4381 exit:
4382     FreeMessageOnError(message, error);
4383     return error;
4384 }
4385 #endif
4386 
4387 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
CalcParentCslMetric(const Mac::CslAccuracy & aCslAccuracy) const4388 uint64_t Mle::CalcParentCslMetric(const Mac::CslAccuracy &aCslAccuracy) const
4389 {
4390     // This function calculates the overall time that device will operate
4391     // on battery by summing sequence of "ON quants" over a period of time.
4392 
4393     static constexpr uint64_t usInSecond = 1000000;
4394 
4395     uint64_t cslPeriodUs  = kMinCslPeriod * kUsPerTenSymbols;
4396     uint64_t cslTimeoutUs = GetCslTimeout() * usInSecond;
4397     uint64_t k            = cslTimeoutUs / cslPeriodUs;
4398 
4399     return k * (k + 1) * cslPeriodUs / usInSecond * aCslAccuracy.GetClockAccuracy() +
4400            aCslAccuracy.GetUncertaintyInMicrosec() * k;
4401 }
4402 #endif
4403 
4404 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
HandleWedAttachTimer(void)4405 void Mle::HandleWedAttachTimer(void)
4406 {
4407     switch (mWedAttachState)
4408     {
4409     case kWedAttaching:
4410         // Connection timeout
4411         if (!IsRxOnWhenIdle())
4412         {
4413             Get<MeshForwarder>().SetRxOnWhenIdle(false);
4414         }
4415 
4416         LogInfo("Connection window closed");
4417 
4418         mWedAttachState = kWedDetached;
4419         mWakeupCallback.InvokeAndClearIfSet(kErrorFailed);
4420         break;
4421     default:
4422         break;
4423     }
4424 }
4425 
Wakeup(const Mac::ExtAddress & aWedAddress,uint16_t aIntervalUs,uint16_t aDurationMs,WakeupCallback aCallback,void * aCallbackContext)4426 Error Mle::Wakeup(const Mac::ExtAddress &aWedAddress,
4427                   uint16_t               aIntervalUs,
4428                   uint16_t               aDurationMs,
4429                   WakeupCallback         aCallback,
4430                   void                  *aCallbackContext)
4431 {
4432     Error error;
4433 
4434     VerifyOrExit((aIntervalUs > 0) && (aDurationMs > 0), error = kErrorInvalidArgs);
4435     VerifyOrExit(aIntervalUs < aDurationMs * Time::kOneMsecInUsec, error = kErrorInvalidArgs);
4436     VerifyOrExit(mWedAttachState == kWedDetached, error = kErrorInvalidState);
4437 
4438     SuccessOrExit(error = mWakeupTxScheduler.WakeUp(aWedAddress, aIntervalUs, aDurationMs));
4439 
4440     mWedAttachState = kWedAttaching;
4441     mWakeupCallback.Set(aCallback, aCallbackContext);
4442     Get<MeshForwarder>().SetRxOnWhenIdle(true);
4443     mWedAttachTimer.FireAt(mWakeupTxScheduler.GetTxEndTime() + mWakeupTxScheduler.GetConnectionWindowUs());
4444 
4445     LogInfo("Connection window open");
4446 
4447 exit:
4448     return error;
4449 }
4450 #endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
4451 
DetachGracefully(DetachCallback aCallback,void * aContext)4452 Error Mle::DetachGracefully(DetachCallback aCallback, void *aContext)
4453 {
4454     Error    error   = kErrorNone;
4455     uint32_t timeout = kDetachGracefullyTimeout;
4456 
4457     VerifyOrExit(!mDetachingGracefully, error = kErrorBusy);
4458 
4459     mDetachGracefullyCallback.Set(aCallback, aContext);
4460 
4461 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
4462     Get<BorderRouter::RoutingManager>().RequestStop();
4463 #endif
4464 
4465     switch (mRole)
4466     {
4467     case kRoleLeader:
4468         break;
4469 
4470     case kRoleRouter:
4471 #if OPENTHREAD_FTD
4472         Get<MleRouter>().SendAddressRelease();
4473 #endif
4474         break;
4475 
4476     case kRoleChild:
4477         IgnoreError(SendChildUpdateRequestToParent(kAppendZeroTimeout));
4478         break;
4479 
4480     case kRoleDisabled:
4481     case kRoleDetached:
4482         // If device is already detached or disabled, we start the timer
4483         // with zero duration to stop and invoke the callback when the
4484         // timer fires, so the operation finishes immediately and
4485         // asynchronously.
4486         timeout = 0;
4487         break;
4488     }
4489 
4490     mDetachingGracefully = true;
4491     mAttachTimer.Start(timeout);
4492 
4493 exit:
4494     return error;
4495 }
4496 
4497 //---------------------------------------------------------------------------------------------------------------------
4498 // TlvList
4499 
Add(uint8_t aTlvType)4500 void Mle::TlvList::Add(uint8_t aTlvType)
4501 {
4502     VerifyOrExit(!Contains(aTlvType));
4503 
4504     if (PushBack(aTlvType) != kErrorNone)
4505     {
4506         LogWarn("Failed to include TLV %d", aTlvType);
4507     }
4508 
4509 exit:
4510     return;
4511 }
4512 
AddElementsFrom(const TlvList & aTlvList)4513 void Mle::TlvList::AddElementsFrom(const TlvList &aTlvList)
4514 {
4515     for (uint8_t tlvType : aTlvList)
4516     {
4517         Add(tlvType);
4518     }
4519 }
4520 
4521 //---------------------------------------------------------------------------------------------------------------------
4522 // DelayedSender
4523 
DelayedSender(Instance & aInstance)4524 Mle::DelayedSender::DelayedSender(Instance &aInstance)
4525     : InstanceLocator(aInstance)
4526     , mTimer(aInstance)
4527 {
4528 }
4529 
Stop(void)4530 void Mle::DelayedSender::Stop(void)
4531 {
4532     mTimer.Stop();
4533     mSchedules.DequeueAndFreeAll();
4534 }
4535 
ScheduleDataRequest(const Ip6::Address & aDestination,uint16_t aDelay)4536 void Mle::DelayedSender::ScheduleDataRequest(const Ip6::Address &aDestination, uint16_t aDelay)
4537 {
4538     VerifyOrExit(!HasMatchingSchedule(kTypeDataRequest, aDestination));
4539     AddSchedule(kTypeDataRequest, aDestination, aDelay, nullptr, 0);
4540 
4541 exit:
4542     return;
4543 }
4544 
ScheduleChildUpdateRequestToParent(uint16_t aDelay)4545 void Mle::DelayedSender::ScheduleChildUpdateRequestToParent(uint16_t aDelay)
4546 {
4547     Ip6::Address destination;
4548 
4549     destination.SetToLinkLocalAddress(Get<Mle>().mParent.GetExtAddress());
4550     VerifyOrExit(!HasMatchingSchedule(kTypeChildUpdateRequestAsChild, destination));
4551     AddSchedule(kTypeChildUpdateRequestAsChild, destination, aDelay, nullptr, 0);
4552 
4553 exit:
4554     return;
4555 }
4556 
RemoveScheduledChildUpdateRequestToParent(void)4557 void Mle::DelayedSender::RemoveScheduledChildUpdateRequestToParent(void)
4558 {
4559     Ip6::Address destination;
4560 
4561     destination.SetToLinkLocalAddress(Get<Mle>().mParent.GetExtAddress());
4562     RemoveMatchingSchedules(kTypeChildUpdateRequestAsChild, destination);
4563 }
4564 
4565 #if OPENTHREAD_FTD
4566 
ScheduleParentResponse(const ParentResponseInfo & aInfo,uint16_t aDelay)4567 void Mle::DelayedSender::ScheduleParentResponse(const ParentResponseInfo &aInfo, uint16_t aDelay)
4568 {
4569     Ip6::Address destination;
4570 
4571     destination.SetToLinkLocalAddress(aInfo.mChildExtAddress);
4572 
4573     RemoveMatchingSchedules(kTypeParentResponse, destination);
4574     AddSchedule(kTypeParentResponse, destination, aDelay, &aInfo, sizeof(aInfo));
4575 }
4576 
ScheduleAdvertisement(const Ip6::Address & aDestination,uint16_t aDelay)4577 void Mle::DelayedSender::ScheduleAdvertisement(const Ip6::Address &aDestination, uint16_t aDelay)
4578 {
4579     VerifyOrExit(!HasMatchingSchedule(kTypeAdvertisement, aDestination));
4580     AddSchedule(kTypeAdvertisement, aDestination, aDelay, nullptr, 0);
4581 
4582 exit:
4583     return;
4584 }
4585 
ScheduleMulticastDataResponse(uint16_t aDelay)4586 void Mle::DelayedSender::ScheduleMulticastDataResponse(uint16_t aDelay)
4587 {
4588     Ip6::Address destination;
4589 
4590     destination.SetToLinkLocalAllNodesMulticast();
4591 
4592     Get<MeshForwarder>().RemoveDataResponseMessages();
4593     RemoveMatchingSchedules(kTypeDataResponse, destination);
4594     AddSchedule(kTypeDataResponse, destination, aDelay, nullptr, 0);
4595 }
4596 
ScheduleLinkRequest(const Router & aRouter,uint16_t aDelay)4597 void Mle::DelayedSender::ScheduleLinkRequest(const Router &aRouter, uint16_t aDelay)
4598 {
4599     Ip6::Address destination;
4600     uint16_t     routerRloc16;
4601 
4602     destination.SetToLinkLocalAddress(aRouter.GetExtAddress());
4603 
4604     VerifyOrExit(!HasMatchingSchedule(kTypeLinkRequest, destination));
4605     routerRloc16 = aRouter.GetRloc16();
4606     AddSchedule(kTypeLinkRequest, destination, aDelay, &routerRloc16, sizeof(uint16_t));
4607 
4608 exit:
4609     return;
4610 }
4611 
RemoveScheduledLinkRequest(const Router & aRouter)4612 void Mle::DelayedSender::RemoveScheduledLinkRequest(const Router &aRouter)
4613 {
4614     Ip6::Address destination;
4615 
4616     destination.SetToLinkLocalAddress(aRouter.GetExtAddress());
4617     RemoveMatchingSchedules(kTypeLinkRequest, destination);
4618 }
4619 
ScheduleLinkAccept(const LinkAcceptInfo & aInfo,uint16_t aDelay)4620 void Mle::DelayedSender::ScheduleLinkAccept(const LinkAcceptInfo &aInfo, uint16_t aDelay)
4621 {
4622     Ip6::Address destination;
4623 
4624     destination.SetToLinkLocalAddress(aInfo.mExtAddress);
4625 
4626     RemoveMatchingSchedules(kTypeLinkAccept, destination);
4627     AddSchedule(kTypeLinkAccept, destination, aDelay, &aInfo, sizeof(aInfo));
4628 }
4629 
ScheduleDiscoveryResponse(const Ip6::Address & aDestination,const DiscoveryResponseInfo & aInfo,uint16_t aDelay)4630 void Mle::DelayedSender::ScheduleDiscoveryResponse(const Ip6::Address          &aDestination,
4631                                                    const DiscoveryResponseInfo &aInfo,
4632                                                    uint16_t                     aDelay)
4633 {
4634     AddSchedule(kTypeDiscoveryResponse, aDestination, aDelay, &aInfo, sizeof(aInfo));
4635 }
4636 
4637 #endif // OPENTHREAD_FTD
4638 
AddSchedule(MessageType aMessageType,const Ip6::Address & aDestination,uint16_t aDelay,const void * aInfo,uint16_t aInfoSize)4639 void Mle::DelayedSender::AddSchedule(MessageType         aMessageType,
4640                                      const Ip6::Address &aDestination,
4641                                      uint16_t            aDelay,
4642                                      const void         *aInfo,
4643                                      uint16_t            aInfoSize)
4644 {
4645     Schedule *schedule = Get<MessagePool>().Allocate(Message::kTypeOther);
4646     Header    header;
4647 
4648     VerifyOrExit(schedule != nullptr);
4649 
4650     header.mSendTime    = TimerMilli::GetNow() + aDelay;
4651     header.mDestination = aDestination;
4652     header.mMessageType = aMessageType;
4653     SuccessOrExit(schedule->Append(header));
4654 
4655     if (aInfo != nullptr)
4656     {
4657         SuccessOrExit(schedule->AppendBytes(aInfo, aInfoSize));
4658     }
4659 
4660     mTimer.FireAtIfEarlier(header.mSendTime);
4661 
4662     mSchedules.Enqueue(*schedule);
4663     schedule = nullptr;
4664 
4665     Log(kMessageDelay, aMessageType, aDestination);
4666 
4667 exit:
4668     FreeMessage(schedule);
4669 }
4670 
HandleTimer(void)4671 void Mle::DelayedSender::HandleTimer(void)
4672 {
4673     NextFireTime nextSendTime;
4674     MessageQueue schedulesToExecute;
4675 
4676     for (Schedule &schedule : mSchedules)
4677     {
4678         Header header;
4679 
4680         header.ReadFrom(schedule);
4681 
4682         if (nextSendTime.GetNow() < header.mSendTime)
4683         {
4684             nextSendTime.UpdateIfEarlier(header.mSendTime);
4685         }
4686         else
4687         {
4688             mSchedules.Dequeue(schedule);
4689             schedulesToExecute.Enqueue(schedule);
4690         }
4691     }
4692 
4693     mTimer.FireAt(nextSendTime);
4694 
4695     for (Schedule &schedule : schedulesToExecute)
4696     {
4697         Execute(schedule);
4698     }
4699 
4700     schedulesToExecute.DequeueAndFreeAll();
4701 }
4702 
Execute(const Schedule & aSchedule)4703 void Mle::DelayedSender::Execute(const Schedule &aSchedule)
4704 {
4705     Header header;
4706 
4707     header.ReadFrom(aSchedule);
4708 
4709     switch (header.mMessageType)
4710     {
4711     case kTypeDataRequest:
4712         IgnoreError(Get<Mle>().SendDataRequest(header.mDestination));
4713         break;
4714 
4715     case kTypeChildUpdateRequestAsChild:
4716         IgnoreError(Get<Mle>().SendChildUpdateRequestToParent());
4717         break;
4718 
4719 #if OPENTHREAD_FTD
4720     case kTypeParentResponse:
4721     {
4722         ParentResponseInfo info;
4723 
4724         IgnoreError(aSchedule.Read(sizeof(Header), info));
4725         Get<MleRouter>().SendParentResponse(info);
4726         break;
4727     }
4728 
4729     case kTypeAdvertisement:
4730         Get<MleRouter>().SendAdvertisement(header.mDestination);
4731         break;
4732 
4733     case kTypeDataResponse:
4734         Get<MleRouter>().SendMulticastDataResponse();
4735         break;
4736 
4737     case kTypeLinkAccept:
4738     {
4739         LinkAcceptInfo info;
4740 
4741         IgnoreError(aSchedule.Read(sizeof(Header), info));
4742         IgnoreError(Get<MleRouter>().SendLinkAccept(info));
4743         break;
4744     }
4745 
4746     case kTypeLinkRequest:
4747     {
4748         uint16_t rlco16;
4749         Router  *router;
4750 
4751         IgnoreError(aSchedule.Read(sizeof(Header), rlco16));
4752         router = Get<RouterTable>().FindRouterByRloc16(rlco16);
4753 
4754         if (router != nullptr)
4755         {
4756             Get<MleRouter>().SendLinkRequest(router);
4757         }
4758 
4759         break;
4760     }
4761 
4762     case kTypeDiscoveryResponse:
4763     {
4764         DiscoveryResponseInfo info;
4765 
4766         IgnoreError(aSchedule.Read(sizeof(Header), info));
4767         IgnoreError(Get<MleRouter>().SendDiscoveryResponse(header.mDestination, info));
4768         break;
4769     }
4770 #endif // OPENTHREAD_FTD
4771 
4772     default:
4773         break;
4774     }
4775 }
4776 
Match(const Schedule & aSchedule,MessageType aMessageType,const Ip6::Address & aDestination)4777 bool Mle::DelayedSender::Match(const Schedule &aSchedule, MessageType aMessageType, const Ip6::Address &aDestination)
4778 {
4779     Header header;
4780 
4781     header.ReadFrom(aSchedule);
4782 
4783     return (header.mMessageType == aMessageType) && (header.mDestination == aDestination);
4784 }
4785 
HasMatchingSchedule(MessageType aMessageType,const Ip6::Address & aDestination) const4786 bool Mle::DelayedSender::HasMatchingSchedule(MessageType aMessageType, const Ip6::Address &aDestination) const
4787 {
4788     bool hasMatching = false;
4789 
4790     for (const Schedule &schedule : mSchedules)
4791     {
4792         if (Match(schedule, aMessageType, aDestination))
4793         {
4794             hasMatching = true;
4795             break;
4796         }
4797     }
4798 
4799     return hasMatching;
4800 }
4801 
RemoveMatchingSchedules(MessageType aMessageType,const Ip6::Address & aDestination)4802 void Mle::DelayedSender::RemoveMatchingSchedules(MessageType aMessageType, const Ip6::Address &aDestination)
4803 {
4804     for (Schedule &schedule : mSchedules)
4805     {
4806         if (Match(schedule, aMessageType, aDestination))
4807         {
4808             mSchedules.DequeueAndFree(schedule);
4809             Log(kMessageRemoveDelayed, aMessageType, aDestination);
4810         }
4811     }
4812 }
4813 
4814 //---------------------------------------------------------------------------------------------------------------------
4815 // TxMessage
4816 
NewMleMessage(Command aCommand)4817 Mle::TxMessage *Mle::NewMleMessage(Command aCommand)
4818 {
4819     Error             error = kErrorNone;
4820     TxMessage        *message;
4821     Message::Settings settings(kNoLinkSecurity, Message::kPriorityNet);
4822     uint8_t           securitySuite;
4823 
4824     message = static_cast<TxMessage *>(mSocket.NewMessage(0, settings));
4825     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
4826 
4827     securitySuite = k154Security;
4828 
4829     if ((aCommand == kCommandDiscoveryRequest) || (aCommand == kCommandDiscoveryResponse))
4830     {
4831         securitySuite = kNoSecurity;
4832     }
4833 
4834     message->SetSubType(Message::kSubTypeMle);
4835     message->SetMleCommand(aCommand);
4836 
4837     SuccessOrExit(error = message->Append(securitySuite));
4838 
4839     if (securitySuite == k154Security)
4840     {
4841         SecurityHeader securityHeader;
4842 
4843         // The other fields in security header are updated in the
4844         // message in `TxMessage::SendTo()` before message is sent.
4845 
4846         securityHeader.InitSecurityControl();
4847         SuccessOrExit(error = message->Append(securityHeader));
4848     }
4849 
4850     error = message->Append<uint8_t>(aCommand);
4851 
4852 exit:
4853     FreeAndNullMessageOnError(message, error);
4854     return message;
4855 }
4856 
AppendSourceAddressTlv(void)4857 Error Mle::TxMessage::AppendSourceAddressTlv(void)
4858 {
4859     return Tlv::Append<SourceAddressTlv>(*this, Get<Mle>().GetRloc16());
4860 }
4861 
AppendStatusTlv(StatusTlv::Status aStatus)4862 Error Mle::TxMessage::AppendStatusTlv(StatusTlv::Status aStatus) { return Tlv::Append<StatusTlv>(*this, aStatus); }
4863 
AppendModeTlv(DeviceMode aMode)4864 Error Mle::TxMessage::AppendModeTlv(DeviceMode aMode) { return Tlv::Append<ModeTlv>(*this, aMode.Get()); }
4865 
AppendTimeoutTlv(uint32_t aTimeout)4866 Error Mle::TxMessage::AppendTimeoutTlv(uint32_t aTimeout) { return Tlv::Append<TimeoutTlv>(*this, aTimeout); }
4867 
AppendChallengeTlv(const TxChallenge & aChallenge)4868 Error Mle::TxMessage::AppendChallengeTlv(const TxChallenge &aChallenge)
4869 {
4870     return Tlv::Append<ChallengeTlv>(*this, &aChallenge, sizeof(aChallenge));
4871 }
4872 
AppendResponseTlv(const RxChallenge & aResponse)4873 Error Mle::TxMessage::AppendResponseTlv(const RxChallenge &aResponse)
4874 {
4875     return Tlv::Append<ResponseTlv>(*this, aResponse.GetBytes(), aResponse.GetLength());
4876 }
4877 
AppendLinkFrameCounterTlv(void)4878 Error Mle::TxMessage::AppendLinkFrameCounterTlv(void)
4879 {
4880     uint32_t counter;
4881 
4882     // When including Link-layer Frame Counter TLV in an MLE message
4883     // the value is set to the maximum MAC frame counter on all
4884     // supported radio links. All radio links must also start using
4885     // the same counter value as the value included in the TLV.
4886 
4887     counter = Get<KeyManager>().GetMaximumMacFrameCounter();
4888 
4889 #if OPENTHREAD_CONFIG_MULTI_RADIO
4890     Get<KeyManager>().SetAllMacFrameCounters(counter, /* aSetIfLarger */ true);
4891 #endif
4892 
4893     return Tlv::Append<LinkFrameCounterTlv>(*this, counter);
4894 }
4895 
AppendMleFrameCounterTlv(void)4896 Error Mle::TxMessage::AppendMleFrameCounterTlv(void)
4897 {
4898     return Tlv::Append<MleFrameCounterTlv>(*this, Get<KeyManager>().GetMleFrameCounter());
4899 }
4900 
AppendLinkAndMleFrameCounterTlvs(void)4901 Error Mle::TxMessage::AppendLinkAndMleFrameCounterTlvs(void)
4902 {
4903     Error error;
4904 
4905     SuccessOrExit(error = AppendLinkFrameCounterTlv());
4906     error = AppendMleFrameCounterTlv();
4907 
4908 exit:
4909     return error;
4910 }
4911 
AppendAddress16Tlv(uint16_t aRloc16)4912 Error Mle::TxMessage::AppendAddress16Tlv(uint16_t aRloc16) { return Tlv::Append<Address16Tlv>(*this, aRloc16); }
4913 
AppendLeaderDataTlv(void)4914 Error Mle::TxMessage::AppendLeaderDataTlv(void)
4915 {
4916     LeaderDataTlv leaderDataTlv;
4917 
4918     Get<Mle>().mLeaderData.SetDataVersion(Get<NetworkData::Leader>().GetVersion(NetworkData::kFullSet));
4919     Get<Mle>().mLeaderData.SetStableDataVersion(Get<NetworkData::Leader>().GetVersion(NetworkData::kStableSubset));
4920 
4921     leaderDataTlv.Init();
4922     leaderDataTlv.Set(Get<Mle>().mLeaderData);
4923 
4924     return leaderDataTlv.AppendTo(*this);
4925 }
4926 
AppendNetworkDataTlv(NetworkData::Type aType)4927 Error Mle::TxMessage::AppendNetworkDataTlv(NetworkData::Type aType)
4928 {
4929     Error   error = kErrorNone;
4930     uint8_t networkData[NetworkData::NetworkData::kMaxSize];
4931     uint8_t length;
4932 
4933     VerifyOrExit(!Get<Mle>().mRetrieveNewNetworkData, error = kErrorInvalidState);
4934 
4935     length = sizeof(networkData);
4936     IgnoreError(Get<NetworkData::Leader>().CopyNetworkData(aType, networkData, length));
4937 
4938     error = Tlv::Append<NetworkDataTlv>(*this, networkData, length);
4939 
4940 exit:
4941     return error;
4942 }
4943 
AppendTlvRequestTlv(const uint8_t * aTlvs,uint8_t aTlvsLength)4944 Error Mle::TxMessage::AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLength)
4945 {
4946     return Tlv::Append<TlvRequestTlv>(*this, aTlvs, aTlvsLength);
4947 }
4948 
AppendScanMaskTlv(uint8_t aScanMask)4949 Error Mle::TxMessage::AppendScanMaskTlv(uint8_t aScanMask) { return Tlv::Append<ScanMaskTlv>(*this, aScanMask); }
4950 
AppendLinkMarginTlv(uint8_t aLinkMargin)4951 Error Mle::TxMessage::AppendLinkMarginTlv(uint8_t aLinkMargin)
4952 {
4953     return Tlv::Append<LinkMarginTlv>(*this, aLinkMargin);
4954 }
4955 
AppendVersionTlv(void)4956 Error Mle::TxMessage::AppendVersionTlv(void) { return Tlv::Append<VersionTlv>(*this, kThreadVersion); }
4957 
AppendAddressRegistrationTlv(AddressRegistrationMode aMode)4958 Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode)
4959 {
4960     Error           error = kErrorNone;
4961     Tlv             tlv;
4962     Lowpan::Context context;
4963     uint8_t         counter     = 0;
4964     uint16_t        startOffset = GetLength();
4965 
4966     tlv.SetType(Tlv::kAddressRegistration);
4967     SuccessOrExit(error = Append(tlv));
4968 
4969     // Prioritize ML-EID
4970     SuccessOrExit(error = AppendCompressedAddressEntry(kMeshLocalPrefixContextId, Get<Mle>().GetMeshLocalEid()));
4971 
4972     // Continue to append the other addresses if not `kAppendMeshLocalOnly` mode
4973     VerifyOrExit(aMode != kAppendMeshLocalOnly);
4974     counter++;
4975 
4976 #if OPENTHREAD_CONFIG_DUA_ENABLE
4977     if (Get<ThreadNetif>().HasUnicastAddress(Get<DuaManager>().GetDomainUnicastAddress()) &&
4978         (Get<NetworkData::Leader>().GetContext(Get<DuaManager>().GetDomainUnicastAddress(), context) == kErrorNone))
4979     {
4980         // Prioritize DUA, compressed entry
4981         SuccessOrExit(
4982             error = AppendCompressedAddressEntry(context.mContextId, Get<DuaManager>().GetDomainUnicastAddress()));
4983         counter++;
4984     }
4985 #endif
4986 
4987     for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
4988     {
4989         if (addr.GetAddress().IsLoopback() || addr.GetAddress().IsLinkLocalUnicast() ||
4990             Get<Mle>().IsRoutingLocator(addr.GetAddress()) || Get<Mle>().IsAnycastLocator(addr.GetAddress()) ||
4991             addr.GetAddress() == Get<Mle>().GetMeshLocalEid())
4992         {
4993             continue;
4994         }
4995 
4996 #if OPENTHREAD_CONFIG_DUA_ENABLE
4997         if (addr.GetAddress() == Get<DuaManager>().GetDomainUnicastAddress())
4998         {
4999             continue;
5000         }
5001 #endif
5002 
5003         if (Get<NetworkData::Leader>().GetContext(addr.GetAddress(), context) == kErrorNone)
5004         {
5005             SuccessOrExit(error = AppendCompressedAddressEntry(context.mContextId, addr.GetAddress()));
5006         }
5007         else
5008         {
5009             SuccessOrExit(error = AppendAddressEntry(addr.GetAddress()));
5010         }
5011 
5012         counter++;
5013         // only continue to append if there is available entry.
5014         VerifyOrExit(counter < kMaxIpAddressesToRegister);
5015     }
5016 
5017     // Append external multicast addresses.  For sleepy end device,
5018     // register all external multicast addresses with the parent for
5019     // indirect transmission. Since Thread 1.2, non-sleepy MED should
5020     // also register external multicast addresses of scope larger than
5021     // realm with a 1.2 or higher parent.
5022     if (!Get<Mle>().IsRxOnWhenIdle()
5023 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
5024         || !Get<Mle>().GetParent().IsThreadVersion1p1()
5025 #endif
5026     )
5027     {
5028         for (const Ip6::Netif::MulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
5029         {
5030 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
5031             // For Thread 1.2 MED, skip multicast address with scope not
5032             // larger than realm local when registering.
5033             if (Get<Mle>().IsRxOnWhenIdle() && !addr.GetAddress().IsMulticastLargerThanRealmLocal())
5034             {
5035                 continue;
5036             }
5037 #endif
5038 
5039             SuccessOrExit(error = AppendAddressEntry(addr.GetAddress()));
5040             counter++;
5041             // only continue to append if there is available entry.
5042             VerifyOrExit(counter < kMaxIpAddressesToRegister);
5043         }
5044     }
5045 
5046 exit:
5047 
5048     if (error == kErrorNone)
5049     {
5050         tlv.SetLength(static_cast<uint8_t>(GetLength() - startOffset - sizeof(Tlv)));
5051         Write(startOffset, tlv);
5052     }
5053 
5054     return error;
5055 }
5056 
AppendCompressedAddressEntry(uint8_t aContextId,const Ip6::Address & aAddress)5057 Error Mle::TxMessage::AppendCompressedAddressEntry(uint8_t aContextId, const Ip6::Address &aAddress)
5058 {
5059     // Append an IPv6 address entry in an Address Registration TLV
5060     // using compressed format (context ID with IID).
5061 
5062     Error error;
5063 
5064     SuccessOrExit(error = Append<uint8_t>(AddressRegistrationTlv::ControlByteFor(aContextId)));
5065     error = Append(aAddress.GetIid());
5066 
5067 exit:
5068     return error;
5069 }
5070 
AppendAddressEntry(const Ip6::Address & aAddress)5071 Error Mle::TxMessage::AppendAddressEntry(const Ip6::Address &aAddress)
5072 {
5073     // Append an IPv6 address entry in an Address Registration TLV
5074     // using uncompressed format
5075 
5076     Error   error;
5077     uint8_t controlByte = AddressRegistrationTlv::kControlByteUncompressed;
5078 
5079     SuccessOrExit(error = Append(controlByte));
5080     error = Append(aAddress);
5081 
5082 exit:
5083     return error;
5084 }
5085 
AppendSupervisionIntervalTlvIfSleepyChild(void)5086 Error Mle::TxMessage::AppendSupervisionIntervalTlvIfSleepyChild(void)
5087 {
5088     Error error = kErrorNone;
5089 
5090     VerifyOrExit(!Get<Mle>().IsRxOnWhenIdle());
5091     error = AppendSupervisionIntervalTlv(Get<SupervisionListener>().GetInterval());
5092 
5093 exit:
5094     return error;
5095 }
5096 
AppendSupervisionIntervalTlv(uint16_t aInterval)5097 Error Mle::TxMessage::AppendSupervisionIntervalTlv(uint16_t aInterval)
5098 {
5099     return Tlv::Append<SupervisionIntervalTlv>(*this, aInterval);
5100 }
5101 
5102 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
AppendTimeRequestTlv(void)5103 Error Mle::TxMessage::AppendTimeRequestTlv(void)
5104 {
5105     // `TimeRequestTlv` has no value.
5106     return Tlv::Append<TimeRequestTlv>(*this, nullptr, 0);
5107 }
5108 
AppendTimeParameterTlv(void)5109 Error Mle::TxMessage::AppendTimeParameterTlv(void)
5110 {
5111     TimeParameterTlv tlv;
5112 
5113     tlv.Init();
5114     tlv.SetTimeSyncPeriod(Get<TimeSync>().GetTimeSyncPeriod());
5115     tlv.SetXtalThreshold(Get<TimeSync>().GetXtalThreshold());
5116 
5117     return tlv.AppendTo(*this);
5118 }
5119 
AppendXtalAccuracyTlv(void)5120 Error Mle::TxMessage::AppendXtalAccuracyTlv(void)
5121 {
5122     return Tlv::Append<XtalAccuracyTlv>(*this, otPlatTimeGetXtalAccuracy());
5123 }
5124 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
5125 
AppendActiveTimestampTlv(void)5126 Error Mle::TxMessage::AppendActiveTimestampTlv(void)
5127 {
5128     Error                     error     = kErrorNone;
5129     const MeshCoP::Timestamp &timestamp = Get<MeshCoP::ActiveDatasetManager>().GetTimestamp();
5130 
5131     VerifyOrExit(timestamp.IsValid());
5132     error = Tlv::Append<ActiveTimestampTlv>(*this, timestamp);
5133 
5134 exit:
5135     return error;
5136 }
5137 
AppendPendingTimestampTlv(void)5138 Error Mle::TxMessage::AppendPendingTimestampTlv(void)
5139 {
5140     Error                     error     = kErrorNone;
5141     const MeshCoP::Timestamp &timestamp = Get<MeshCoP::PendingDatasetManager>().GetTimestamp();
5142 
5143     VerifyOrExit(timestamp.IsValid());
5144     error = Tlv::Append<PendingTimestampTlv>(*this, timestamp);
5145 
5146 exit:
5147     return error;
5148 }
5149 
AppendActiveAndPendingTimestampTlvs(void)5150 Error Mle::TxMessage::AppendActiveAndPendingTimestampTlvs(void)
5151 {
5152     Error error;
5153 
5154     SuccessOrExit(error = AppendActiveTimestampTlv());
5155     error = AppendPendingTimestampTlv();
5156 
5157 exit:
5158     return error;
5159 }
5160 
5161 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
AppendCslChannelTlv(void)5162 Error Mle::TxMessage::AppendCslChannelTlv(void)
5163 {
5164     // CSL channel value of zero indicates that the CSL channel is not
5165     // specified. We can use this value in the TLV as well.
5166 
5167     return Tlv::Append<CslChannelTlv>(*this, ChannelTlvValue(Get<Mac::Mac>().GetCslChannel()));
5168 }
5169 
AppendCslTimeoutTlv(void)5170 Error Mle::TxMessage::AppendCslTimeoutTlv(void)
5171 {
5172     uint32_t timeout = Get<Mle>().GetCslTimeout();
5173 
5174     if (timeout == 0)
5175     {
5176         timeout = Get<Mle>().GetTimeout();
5177     }
5178 
5179     return Tlv::Append<CslTimeoutTlv>(*this, timeout);
5180 }
5181 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
5182 
5183 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
AppendCslClockAccuracyTlv(void)5184 Error Mle::TxMessage::AppendCslClockAccuracyTlv(void)
5185 {
5186     CslClockAccuracyTlv cslClockAccuracyTlv;
5187 
5188     cslClockAccuracyTlv.Init();
5189     cslClockAccuracyTlv.SetCslClockAccuracy(Get<Radio>().GetCslAccuracy());
5190     cslClockAccuracyTlv.SetCslUncertainty(Get<Radio>().GetCslUncertainty());
5191 
5192     return Append(cslClockAccuracyTlv);
5193 }
5194 #endif
5195 
SendTo(const Ip6::Address & aDestination)5196 Error Mle::TxMessage::SendTo(const Ip6::Address &aDestination)
5197 {
5198     Error            error  = kErrorNone;
5199     uint16_t         offset = 0;
5200     uint8_t          securitySuite;
5201     Ip6::MessageInfo messageInfo;
5202 
5203     messageInfo.SetPeerAddr(aDestination);
5204     messageInfo.SetSockAddr(Get<Mle>().mLinkLocalAddress.GetAddress());
5205     messageInfo.SetPeerPort(kUdpPort);
5206     messageInfo.SetHopLimit(kMleHopLimit);
5207 
5208     IgnoreError(Read(offset, securitySuite));
5209     offset += sizeof(securitySuite);
5210 
5211     if (securitySuite == k154Security)
5212     {
5213         SecurityHeader header;
5214 
5215         // Update the fields in the security header
5216 
5217         IgnoreError(Read(offset, header));
5218         header.SetFrameCounter(Get<KeyManager>().GetMleFrameCounter());
5219         header.SetKeyId(Get<KeyManager>().GetCurrentKeySequence());
5220         Write(offset, header);
5221         offset += sizeof(SecurityHeader);
5222 
5223         SuccessOrExit(
5224             error = Get<Mle>().ProcessMessageSecurity(Crypto::AesCcm::kEncrypt, *this, messageInfo, offset, header));
5225 
5226         Get<KeyManager>().IncrementMleFrameCounter();
5227     }
5228 
5229     SuccessOrExit(error = Get<Mle>().mSocket.SendTo(*this, messageInfo));
5230 
5231 exit:
5232     return error;
5233 }
5234 
5235 #if OPENTHREAD_FTD
5236 
AppendConnectivityTlv(void)5237 Error Mle::TxMessage::AppendConnectivityTlv(void)
5238 {
5239     ConnectivityTlv tlv;
5240 
5241     tlv.Init();
5242     Get<MleRouter>().FillConnectivityTlv(tlv);
5243 
5244     return tlv.AppendTo(*this);
5245 }
5246 
AppendAddressRegistrationTlv(Child & aChild)5247 Error Mle::TxMessage::AppendAddressRegistrationTlv(Child &aChild)
5248 {
5249     Error           error;
5250     Tlv             tlv;
5251     Lowpan::Context context;
5252     uint16_t        startOffset = GetLength();
5253 
5254     tlv.SetType(Tlv::kAddressRegistration);
5255     SuccessOrExit(error = Append(tlv));
5256 
5257     // The parent must echo back all registered IPv6 addresses except
5258     // for the ML-EID, which is excluded by `Child::GetIp6Addresses()`.
5259 
5260     for (const Ip6::Address &address : aChild.GetIp6Addresses())
5261     {
5262         if (address.IsMulticast() || Get<NetworkData::Leader>().GetContext(address, context) != kErrorNone)
5263         {
5264             SuccessOrExit(error = AppendAddressEntry(address));
5265         }
5266         else
5267         {
5268             SuccessOrExit(error = AppendCompressedAddressEntry(context.mContextId, address));
5269         }
5270     }
5271 
5272     tlv.SetLength(static_cast<uint8_t>(GetLength() - startOffset - sizeof(Tlv)));
5273     Write(startOffset, tlv);
5274 
5275 exit:
5276     return error;
5277 }
5278 
AppendRouteTlv(Neighbor * aNeighbor)5279 Error Mle::TxMessage::AppendRouteTlv(Neighbor *aNeighbor)
5280 {
5281     RouteTlv tlv;
5282 
5283     tlv.Init();
5284     Get<RouterTable>().FillRouteTlv(tlv, aNeighbor);
5285 
5286     return tlv.AppendTo(*this);
5287 }
5288 
AppendActiveDatasetTlv(void)5289 Error Mle::TxMessage::AppendActiveDatasetTlv(void) { return AppendDatasetTlv(MeshCoP::Dataset::kActive); }
5290 
AppendPendingDatasetTlv(void)5291 Error Mle::TxMessage::AppendPendingDatasetTlv(void) { return AppendDatasetTlv(MeshCoP::Dataset::kPending); }
5292 
AppendDatasetTlv(MeshCoP::Dataset::Type aDatasetType)5293 Error Mle::TxMessage::AppendDatasetTlv(MeshCoP::Dataset::Type aDatasetType)
5294 {
5295     Error            error = kErrorNotFound;
5296     Tlv::Type        tlvType;
5297     MeshCoP::Dataset dataset;
5298 
5299     switch (aDatasetType)
5300     {
5301     case MeshCoP::Dataset::kActive:
5302         error   = Get<MeshCoP::ActiveDatasetManager>().Read(dataset);
5303         tlvType = Tlv::kActiveDataset;
5304         break;
5305 
5306     case MeshCoP::Dataset::kPending:
5307         error   = Get<MeshCoP::PendingDatasetManager>().Read(dataset);
5308         tlvType = Tlv::kPendingDataset;
5309         break;
5310     default:
5311         OT_ASSERT(false);
5312     }
5313 
5314     if (error != kErrorNone)
5315     {
5316         // If there's no dataset, no need to append TLV. We'll treat it
5317         // as success.
5318 
5319         ExitNow(error = kErrorNone);
5320     }
5321 
5322     // Remove the Timestamp TLV from Dataset before appending to the
5323     // message. The Timestamp is appended as its own MLE TLV to the
5324     // message.
5325 
5326     dataset.RemoveTimestamp(aDatasetType);
5327 
5328     error = Tlv::AppendTlv(*this, tlvType, dataset.GetBytes(), dataset.GetLength());
5329 
5330 exit:
5331     return error;
5332 }
5333 
AppendSteeringDataTlv(void)5334 Error Mle::TxMessage::AppendSteeringDataTlv(void)
5335 {
5336     Error                 error = kErrorNone;
5337     MeshCoP::SteeringData steeringData;
5338 
5339 #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
5340     if (!Get<MleRouter>().mSteeringData.IsEmpty())
5341     {
5342         steeringData = Get<MleRouter>().mSteeringData;
5343     }
5344     else
5345 #endif
5346     {
5347         SuccessOrExit(Get<NetworkData::Leader>().FindSteeringData(steeringData));
5348     }
5349 
5350     error = Tlv::Append<MeshCoP::SteeringDataTlv>(*this, steeringData.GetData(), steeringData.GetLength());
5351 
5352 exit:
5353     return error;
5354 }
5355 
5356 #endif // OPENTHREAD_FTD
5357 
5358 //---------------------------------------------------------------------------------------------------------------------
5359 // RxMessage
5360 
ContainsTlv(Tlv::Type aTlvType) const5361 bool Mle::RxMessage::ContainsTlv(Tlv::Type aTlvType) const
5362 {
5363     OffsetRange offsetRange;
5364 
5365     return Tlv::FindTlvValueOffsetRange(*this, aTlvType, offsetRange) == kErrorNone;
5366 }
5367 
ReadModeTlv(DeviceMode & aMode) const5368 Error Mle::RxMessage::ReadModeTlv(DeviceMode &aMode) const
5369 {
5370     Error   error;
5371     uint8_t modeBitmask;
5372 
5373     SuccessOrExit(error = Tlv::Find<ModeTlv>(*this, modeBitmask));
5374     aMode.Set(modeBitmask);
5375 
5376 exit:
5377     return error;
5378 }
5379 
ReadVersionTlv(uint16_t & aVersion) const5380 Error Mle::RxMessage::ReadVersionTlv(uint16_t &aVersion) const
5381 {
5382     Error error;
5383 
5384     SuccessOrExit(error = Tlv::Find<VersionTlv>(*this, aVersion));
5385     VerifyOrExit(aVersion >= kThreadVersion1p1, error = kErrorParse);
5386 
5387 exit:
5388     return error;
5389 }
5390 
ReadChallengeOrResponse(uint8_t aTlvType,RxChallenge & aRxChallenge) const5391 Error Mle::RxMessage::ReadChallengeOrResponse(uint8_t aTlvType, RxChallenge &aRxChallenge) const
5392 {
5393     Error       error;
5394     OffsetRange offsetRange;
5395 
5396     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, aTlvType, offsetRange));
5397     error = aRxChallenge.ReadFrom(*this, offsetRange);
5398 
5399 exit:
5400     return error;
5401 }
5402 
ReadChallengeTlv(RxChallenge & aChallenge) const5403 Error Mle::RxMessage::ReadChallengeTlv(RxChallenge &aChallenge) const
5404 {
5405     return ReadChallengeOrResponse(Tlv::kChallenge, aChallenge);
5406 }
5407 
ReadResponseTlv(RxChallenge & aResponse) const5408 Error Mle::RxMessage::ReadResponseTlv(RxChallenge &aResponse) const
5409 {
5410     return ReadChallengeOrResponse(Tlv::kResponse, aResponse);
5411 }
5412 
ReadAndMatchResponseTlvWith(const TxChallenge & aChallenge) const5413 Error Mle::RxMessage::ReadAndMatchResponseTlvWith(const TxChallenge &aChallenge) const
5414 {
5415     Error       error;
5416     RxChallenge response;
5417 
5418     SuccessOrExit(error = ReadResponseTlv(response));
5419     VerifyOrExit(response == aChallenge, error = kErrorSecurity);
5420 
5421 exit:
5422     return error;
5423 }
5424 
ReadFrameCounterTlvs(uint32_t & aLinkFrameCounter,uint32_t & aMleFrameCounter) const5425 Error Mle::RxMessage::ReadFrameCounterTlvs(uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const
5426 {
5427     Error error;
5428 
5429     SuccessOrExit(error = Tlv::Find<LinkFrameCounterTlv>(*this, aLinkFrameCounter));
5430 
5431     switch (Tlv::Find<MleFrameCounterTlv>(*this, aMleFrameCounter))
5432     {
5433     case kErrorNone:
5434         break;
5435     case kErrorNotFound:
5436         aMleFrameCounter = aLinkFrameCounter;
5437         break;
5438     default:
5439         error = kErrorParse;
5440         break;
5441     }
5442 
5443 exit:
5444     return error;
5445 }
5446 
ReadLeaderDataTlv(LeaderData & aLeaderData) const5447 Error Mle::RxMessage::ReadLeaderDataTlv(LeaderData &aLeaderData) const
5448 {
5449     Error         error;
5450     LeaderDataTlv leaderDataTlv;
5451 
5452     SuccessOrExit(error = Tlv::FindTlv(*this, leaderDataTlv));
5453     VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse);
5454     leaderDataTlv.Get(aLeaderData);
5455 
5456 exit:
5457     return error;
5458 }
5459 
ReadAndSetNetworkDataTlv(const LeaderData & aLeaderData) const5460 Error Mle::RxMessage::ReadAndSetNetworkDataTlv(const LeaderData &aLeaderData) const
5461 {
5462     Error       error;
5463     OffsetRange offsetRange;
5464 
5465     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, Tlv::kNetworkData, offsetRange));
5466 
5467     error = Get<NetworkData::Leader>().SetNetworkData(aLeaderData.GetDataVersion(NetworkData::kFullSet),
5468                                                       aLeaderData.GetDataVersion(NetworkData::kStableSubset),
5469                                                       Get<Mle>().GetNetworkDataType(), *this, offsetRange);
5470 exit:
5471     return error;
5472 }
5473 
ReadAndSaveActiveDataset(const MeshCoP::Timestamp & aActiveTimestamp) const5474 Error Mle::RxMessage::ReadAndSaveActiveDataset(const MeshCoP::Timestamp &aActiveTimestamp) const
5475 {
5476     return ReadAndSaveDataset(MeshCoP::Dataset::kActive, aActiveTimestamp);
5477 }
5478 
ReadAndSavePendingDataset(const MeshCoP::Timestamp & aPendingTimestamp) const5479 Error Mle::RxMessage::ReadAndSavePendingDataset(const MeshCoP::Timestamp &aPendingTimestamp) const
5480 {
5481     return ReadAndSaveDataset(MeshCoP::Dataset::kPending, aPendingTimestamp);
5482 }
5483 
ReadAndSaveDataset(MeshCoP::Dataset::Type aDatasetType,const MeshCoP::Timestamp & aTimestamp) const5484 Error Mle::RxMessage::ReadAndSaveDataset(MeshCoP::Dataset::Type    aDatasetType,
5485                                          const MeshCoP::Timestamp &aTimestamp) const
5486 {
5487     Error            error   = kErrorNone;
5488     Tlv::Type        tlvType = (aDatasetType == MeshCoP::Dataset::kActive) ? Tlv::kActiveDataset : Tlv::kPendingDataset;
5489     MeshCoP::Dataset dataset;
5490     OffsetRange      offsetRange;
5491 
5492     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, tlvType, offsetRange));
5493 
5494     SuccessOrExit(error = dataset.SetFrom(*this, offsetRange));
5495     SuccessOrExit(error = dataset.ValidateTlvs());
5496     SuccessOrExit(error = dataset.WriteTimestamp(aDatasetType, aTimestamp));
5497 
5498     switch (aDatasetType)
5499     {
5500     case MeshCoP::Dataset::kActive:
5501         error = Get<MeshCoP::ActiveDatasetManager>().Save(dataset);
5502         break;
5503     case MeshCoP::Dataset::kPending:
5504         error = Get<MeshCoP::PendingDatasetManager>().Save(dataset);
5505         break;
5506     }
5507 
5508 exit:
5509     return error;
5510 }
5511 
ReadTlvRequestTlv(TlvList & aTlvList) const5512 Error Mle::RxMessage::ReadTlvRequestTlv(TlvList &aTlvList) const
5513 {
5514     Error       error;
5515     OffsetRange offsetRange;
5516 
5517     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, Tlv::kTlvRequest, offsetRange));
5518 
5519     offsetRange.ShrinkLength(aTlvList.GetMaxSize());
5520 
5521     ReadBytes(offsetRange, aTlvList.GetArrayBuffer());
5522     aTlvList.SetLength(static_cast<uint8_t>(offsetRange.GetLength()));
5523 
5524 exit:
5525     return error;
5526 }
5527 
5528 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
ReadCslClockAccuracyTlv(Mac::CslAccuracy & aCslAccuracy) const5529 Error Mle::RxMessage::ReadCslClockAccuracyTlv(Mac::CslAccuracy &aCslAccuracy) const
5530 {
5531     Error               error;
5532     CslClockAccuracyTlv clockAccuracyTlv;
5533 
5534     SuccessOrExit(error = Tlv::FindTlv(*this, clockAccuracyTlv));
5535     VerifyOrExit(clockAccuracyTlv.IsValid(), error = kErrorParse);
5536     aCslAccuracy.SetClockAccuracy(clockAccuracyTlv.GetCslClockAccuracy());
5537     aCslAccuracy.SetUncertainty(clockAccuracyTlv.GetCslUncertainty());
5538 
5539 exit:
5540     return error;
5541 }
5542 #endif
5543 
5544 #if OPENTHREAD_FTD
ReadRouteTlv(RouteTlv & aRouteTlv) const5545 Error Mle::RxMessage::ReadRouteTlv(RouteTlv &aRouteTlv) const
5546 {
5547     Error error;
5548 
5549     SuccessOrExit(error = Tlv::FindTlv(*this, aRouteTlv));
5550     VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse);
5551 
5552 exit:
5553     return error;
5554 }
5555 #endif
5556 
5557 //---------------------------------------------------------------------------------------------------------------------
5558 // ParentCandidate
5559 
Clear(void)5560 void Mle::ParentCandidate::Clear(void)
5561 {
5562     Instance &instance = GetInstance();
5563 
5564     ClearAllBytes(*this);
5565     Init(instance);
5566 }
5567 
CopyTo(Parent & aParent) const5568 void Mle::ParentCandidate::CopyTo(Parent &aParent) const
5569 {
5570     // We use an intermediate pointer to copy `ParentCandidate`
5571     // to silence code checker's warning about object slicing
5572     // (assigning a sub-class to base class instance).
5573 
5574     const Parent *candidateAsParent = this;
5575 
5576     aParent = *candidateAsParent;
5577 }
5578 
5579 } // namespace Mle
5580 } // namespace ot
5581