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