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