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 ×tamp = 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 ×tamp = 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