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 the subset of IEEE 802.15.4 primitives required for Thread.
32 */
33
34 #include "mac.hpp"
35
36 #include <stdio.h>
37
38 #include "crypto/aes_ccm.hpp"
39 #include "crypto/sha256.hpp"
40 #include "instance/instance.hpp"
41 #include "utils/static_counter.hpp"
42
43 namespace ot {
44 namespace Mac {
45
46 RegisterLogModule("Mac");
47
48 const otExtAddress Mac::sMode2ExtAddress = {
49 {0x35, 0x06, 0xfe, 0xb8, 0x23, 0xd4, 0x87, 0x12},
50 };
51
Mac(Instance & aInstance)52 Mac::Mac(Instance &aInstance)
53 : InstanceLocator(aInstance)
54 , mEnabled(false)
55 , mShouldTxPollBeforeData(false)
56 , mRxOnWhenIdle(false)
57 , mPromiscuous(false)
58 , mBeaconsEnabled(false)
59 , mUsingTemporaryChannel(false)
60 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
61 , mShouldDelaySleep(false)
62 , mDelayingSleep(false)
63 #endif
64 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
65 , mWakeupListenEnabled(false)
66 #endif
67 , mOperation(kOperationIdle)
68 , mPendingOperations(0)
69 , mBeaconSequence(Random::NonCrypto::GetUint8())
70 , mDataSequence(Random::NonCrypto::GetUint8())
71 , mBroadcastTransmitCount(0)
72 , mPanId(kPanIdBroadcast)
73 , mPanChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
74 , mRadioChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
75 , mSupportedChannelMask(Get<Radio>().GetSupportedChannelMask())
76 , mScanChannel(Radio::kChannelMin)
77 , mScanDuration(0)
78 , mMaxFrameRetriesDirect(kDefaultMaxFrameRetriesDirect)
79 #if OPENTHREAD_FTD
80 , mMaxFrameRetriesIndirect(kDefaultMaxFrameRetriesIndirect)
81 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
82 , mCslTxFireTime(TimeMilli::kMaxDuration)
83 #endif
84 #endif
85 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
86 , mCslChannel(0)
87 , mCslPeriod(0)
88 #endif
89 , mWakeupChannel(OPENTHREAD_CONFIG_DEFAULT_WAKEUP_CHANNEL)
90 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
91 , mWakeupListenInterval(kDefaultWedListenInterval)
92 , mWakeupListenDuration(kDefaultWedListenDuration)
93 #endif
94 , mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union
95 , mScanHandlerContext(nullptr)
96 , mLinks(aInstance)
97 , mOperationTask(aInstance)
98 , mTimer(aInstance)
99 , mKeyIdMode2FrameCounter(0)
100 , mCcaSampleCount(0)
101 #if OPENTHREAD_CONFIG_MULTI_RADIO
102 , mTxError(kErrorNone)
103 #endif
104 {
105 ExtAddress randomExtAddress;
106
107 static const otMacKey sMode2Key = {
108 {0x78, 0x58, 0x16, 0x86, 0xfd, 0xb4, 0x58, 0x0f, 0xb0, 0x92, 0x54, 0x6a, 0xec, 0xbd, 0x15, 0x66}};
109
110 randomExtAddress.GenerateRandom();
111
112 mCcaSuccessRateTracker.Clear();
113 ResetCounters();
114
115 SetEnabled(true);
116
117 Get<KeyManager>().UpdateKeyMaterial();
118 SetPanId(mPanId);
119 SetExtAddress(randomExtAddress);
120 SetShortAddress(GetShortAddress());
121 #if OPENTHREAD_FTD
122 SetAlternateShortAddress(kShortAddrInvalid);
123 #endif
124
125 mMode2KeyMaterial.SetFrom(AsCoreType(&sMode2Key));
126 }
127
SetEnabled(bool aEnable)128 void Mac::SetEnabled(bool aEnable)
129 {
130 mEnabled = aEnable;
131
132 if (aEnable)
133 {
134 mLinks.Enable();
135 }
136 else
137 {
138 mLinks.Disable();
139 }
140 }
141
ActiveScan(uint32_t aScanChannels,uint16_t aScanDuration,ActiveScanHandler aHandler,void * aContext)142 Error Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveScanHandler aHandler, void *aContext)
143 {
144 Error error = kErrorNone;
145
146 VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
147 VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = kErrorBusy);
148
149 mActiveScanHandler = aHandler;
150 mScanHandlerContext = aContext;
151
152 if (aScanDuration == 0)
153 {
154 aScanDuration = kScanDurationDefault;
155 }
156
157 Scan(kOperationActiveScan, aScanChannels, aScanDuration);
158
159 exit:
160 return error;
161 }
162
EnergyScan(uint32_t aScanChannels,uint16_t aScanDuration,EnergyScanHandler aHandler,void * aContext)163 Error Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergyScanHandler aHandler, void *aContext)
164 {
165 Error error = kErrorNone;
166
167 VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
168 VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = kErrorBusy);
169
170 mEnergyScanHandler = aHandler;
171 mScanHandlerContext = aContext;
172
173 Scan(kOperationEnergyScan, aScanChannels, aScanDuration);
174
175 exit:
176 return error;
177 }
178
Scan(Operation aScanOperation,uint32_t aScanChannels,uint16_t aScanDuration)179 void Mac::Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanDuration)
180 {
181 mScanDuration = aScanDuration;
182 mScanChannel = ChannelMask::kChannelIteratorFirst;
183
184 if (aScanChannels == 0)
185 {
186 aScanChannels = mSupportedChannelMask.GetMask();
187 }
188
189 mScanChannelMask.SetMask(aScanChannels);
190 mScanChannelMask.Intersect(mSupportedChannelMask);
191 StartOperation(aScanOperation);
192 }
193
IsInTransmitState(void) const194 bool Mac::IsInTransmitState(void) const
195 {
196 bool retval = false;
197
198 switch (mOperation)
199 {
200 case kOperationTransmitDataDirect:
201 #if OPENTHREAD_FTD
202 case kOperationTransmitDataIndirect:
203 #endif
204 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
205 case kOperationTransmitDataCsl:
206 #endif
207 case kOperationTransmitBeacon:
208 case kOperationTransmitPoll:
209 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
210 case kOperationTransmitWakeup:
211 #endif
212 retval = true;
213 break;
214
215 case kOperationIdle:
216 case kOperationActiveScan:
217 case kOperationEnergyScan:
218 case kOperationWaitingForData:
219 retval = false;
220 break;
221 }
222
223 return retval;
224 }
225
ConvertBeaconToActiveScanResult(const RxFrame * aBeaconFrame,ActiveScanResult & aResult)226 Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveScanResult &aResult)
227 {
228 Error error = kErrorNone;
229 Address address;
230 #if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE
231 const BeaconPayload *beaconPayload = nullptr;
232 const Beacon *beacon = nullptr;
233 uint16_t payloadLength;
234 #endif
235
236 ClearAllBytes(aResult);
237
238 VerifyOrExit(aBeaconFrame != nullptr, error = kErrorInvalidArgs);
239
240 VerifyOrExit(aBeaconFrame->GetType() == Frame::kTypeBeacon, error = kErrorParse);
241 SuccessOrExit(error = aBeaconFrame->GetSrcAddr(address));
242 VerifyOrExit(address.IsExtended(), error = kErrorParse);
243 aResult.mExtAddress = address.GetExtended();
244
245 if (kErrorNone != aBeaconFrame->GetSrcPanId(aResult.mPanId))
246 {
247 IgnoreError(aBeaconFrame->GetDstPanId(aResult.mPanId));
248 }
249
250 aResult.mChannel = aBeaconFrame->GetChannel();
251 aResult.mRssi = aBeaconFrame->GetRssi();
252 aResult.mLqi = aBeaconFrame->GetLqi();
253
254 #if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE
255 payloadLength = aBeaconFrame->GetPayloadLength();
256
257 beacon = reinterpret_cast<const Beacon *>(aBeaconFrame->GetPayload());
258 beaconPayload = reinterpret_cast<const BeaconPayload *>(beacon->GetPayload());
259
260 if ((payloadLength >= (sizeof(*beacon) + sizeof(*beaconPayload))) && beacon->IsValid() && beaconPayload->IsValid())
261 {
262 aResult.mVersion = beaconPayload->GetProtocolVersion();
263 aResult.mIsJoinable = beaconPayload->IsJoiningPermitted();
264 aResult.mIsNative = beaconPayload->IsNative();
265 IgnoreError(AsCoreType(&aResult.mNetworkName).Set(beaconPayload->GetNetworkName()));
266 VerifyOrExit(IsValidUtf8String(aResult.mNetworkName.m8), error = kErrorParse);
267 aResult.mExtendedPanId = beaconPayload->GetExtendedPanId();
268 }
269 #endif
270
271 LogBeacon("Received");
272
273 exit:
274 return error;
275 }
276
UpdateScanChannel(void)277 Error Mac::UpdateScanChannel(void)
278 {
279 Error error;
280
281 VerifyOrExit(IsEnabled(), error = kErrorAbort);
282
283 error = mScanChannelMask.GetNextChannel(mScanChannel);
284
285 exit:
286 return error;
287 }
288
PerformActiveScan(void)289 void Mac::PerformActiveScan(void)
290 {
291 if (UpdateScanChannel() == kErrorNone)
292 {
293 // If there are more channels to scan, send the beacon request.
294 BeginTransmit();
295 }
296 else
297 {
298 mLinks.SetPanId(mPanId);
299 FinishOperation();
300 ReportActiveScanResult(nullptr);
301 PerformNextOperation();
302 }
303 }
304
ReportActiveScanResult(const RxFrame * aBeaconFrame)305 void Mac::ReportActiveScanResult(const RxFrame *aBeaconFrame)
306 {
307 VerifyOrExit(mActiveScanHandler != nullptr);
308
309 if (aBeaconFrame == nullptr)
310 {
311 mActiveScanHandler(nullptr, mScanHandlerContext);
312 }
313 else
314 {
315 ActiveScanResult result;
316
317 SuccessOrExit(ConvertBeaconToActiveScanResult(aBeaconFrame, result));
318 mActiveScanHandler(&result, mScanHandlerContext);
319 }
320
321 exit:
322 return;
323 }
324
PerformEnergyScan(void)325 void Mac::PerformEnergyScan(void)
326 {
327 Error error = kErrorNone;
328
329 SuccessOrExit(error = UpdateScanChannel());
330
331 if (mScanDuration == 0)
332 {
333 while (true)
334 {
335 mLinks.Receive(mScanChannel);
336 ReportEnergyScanResult(mLinks.GetRssi());
337 SuccessOrExit(error = UpdateScanChannel());
338 }
339 }
340 else
341 {
342 if (!mRxOnWhenIdle)
343 {
344 mLinks.Receive(mScanChannel);
345 }
346 error = mLinks.EnergyScan(mScanChannel, mScanDuration);
347 }
348
349 exit:
350
351 if (error != kErrorNone)
352 {
353 FinishOperation();
354
355 if (mEnergyScanHandler != nullptr)
356 {
357 mEnergyScanHandler(nullptr, mScanHandlerContext);
358 }
359
360 PerformNextOperation();
361 }
362 }
363
ReportEnergyScanResult(int8_t aRssi)364 void Mac::ReportEnergyScanResult(int8_t aRssi)
365 {
366 EnergyScanResult result;
367
368 VerifyOrExit((mEnergyScanHandler != nullptr) && (aRssi != Radio::kInvalidRssi));
369
370 result.mChannel = mScanChannel;
371 result.mMaxRssi = aRssi;
372
373 mEnergyScanHandler(&result, mScanHandlerContext);
374
375 exit:
376 return;
377 }
378
EnergyScanDone(int8_t aEnergyScanMaxRssi)379 void Mac::EnergyScanDone(int8_t aEnergyScanMaxRssi)
380 {
381 ReportEnergyScanResult(aEnergyScanMaxRssi);
382 PerformEnergyScan();
383 }
384
SetRxOnWhenIdle(bool aRxOnWhenIdle)385 void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
386 {
387 VerifyOrExit(mRxOnWhenIdle != aRxOnWhenIdle);
388
389 mRxOnWhenIdle = aRxOnWhenIdle;
390
391 // If the new value for `mRxOnWhenIdle` is `true` (i.e., radio should
392 // remain in Rx while idle) we stop any ongoing or pending `WaitingForData`
393 // operation (since this operation only applies to sleepy devices).
394
395 if (mRxOnWhenIdle)
396 {
397 if (IsPending(kOperationWaitingForData))
398 {
399 mTimer.Stop();
400 ClearPending(kOperationWaitingForData);
401 }
402
403 if (mOperation == kOperationWaitingForData)
404 {
405 mTimer.Stop();
406 FinishOperation();
407 mOperationTask.Post();
408 }
409
410 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
411 mDelayingSleep = false;
412 mShouldDelaySleep = false;
413 #endif
414 }
415
416 mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
417 UpdateIdleMode();
418
419 exit:
420 return;
421 }
422
SetPanChannel(uint8_t aChannel)423 Error Mac::SetPanChannel(uint8_t aChannel)
424 {
425 Error error = kErrorNone;
426
427 VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs);
428
429 SuccessOrExit(Get<Notifier>().Update(mPanChannel, aChannel, kEventThreadChannelChanged));
430
431 mCcaSuccessRateTracker.Clear();
432
433 VerifyOrExit(!mUsingTemporaryChannel);
434
435 mRadioChannel = mPanChannel;
436
437 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
438 UpdateCsl();
439 #endif
440
441 UpdateIdleMode();
442
443 exit:
444 return error;
445 }
446
SetTemporaryChannel(uint8_t aChannel)447 Error Mac::SetTemporaryChannel(uint8_t aChannel)
448 {
449 Error error = kErrorNone;
450
451 VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs);
452
453 mUsingTemporaryChannel = true;
454 mRadioChannel = aChannel;
455
456 UpdateIdleMode();
457
458 exit:
459 return error;
460 }
461
ClearTemporaryChannel(void)462 void Mac::ClearTemporaryChannel(void)
463 {
464 if (mUsingTemporaryChannel)
465 {
466 mUsingTemporaryChannel = false;
467 mRadioChannel = mPanChannel;
468 UpdateIdleMode();
469 }
470 }
471
SetSupportedChannelMask(const ChannelMask & aMask)472 void Mac::SetSupportedChannelMask(const ChannelMask &aMask)
473 {
474 ChannelMask newMask = aMask;
475
476 newMask.Intersect(mSupportedChannelMask);
477 IgnoreError(Get<Notifier>().Update(mSupportedChannelMask, newMask, kEventSupportedChannelMaskChanged));
478 }
479
SetPanId(PanId aPanId)480 void Mac::SetPanId(PanId aPanId)
481 {
482 SuccessOrExit(Get<Notifier>().Update(mPanId, aPanId, kEventThreadPanIdChanged));
483 mLinks.SetPanId(mPanId);
484
485 exit:
486 return;
487 }
488
RequestDirectFrameTransmission(void)489 void Mac::RequestDirectFrameTransmission(void)
490 {
491 VerifyOrExit(IsEnabled());
492 VerifyOrExit(!IsActiveOrPending(kOperationTransmitDataDirect));
493
494 StartOperation(kOperationTransmitDataDirect);
495
496 exit:
497 return;
498 }
499
500 #if OPENTHREAD_FTD
RequestIndirectFrameTransmission(void)501 void Mac::RequestIndirectFrameTransmission(void)
502 {
503 VerifyOrExit(IsEnabled());
504 VerifyOrExit(!IsActiveOrPending(kOperationTransmitDataIndirect));
505
506 StartOperation(kOperationTransmitDataIndirect);
507
508 exit:
509 return;
510 }
511 #endif
512
513 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
RequestCslFrameTransmission(uint32_t aDelay)514 void Mac::RequestCslFrameTransmission(uint32_t aDelay)
515 {
516 VerifyOrExit(mEnabled);
517
518 mCslTxFireTime = TimerMilli::GetNow() + aDelay;
519
520 StartOperation(kOperationTransmitDataCsl);
521
522 exit:
523 return;
524 }
525 #endif
526
527 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
RequestWakeupFrameTransmission(void)528 void Mac::RequestWakeupFrameTransmission(void)
529 {
530 VerifyOrExit(IsEnabled());
531 StartOperation(kOperationTransmitWakeup);
532
533 exit:
534 return;
535 }
536 #endif
537
RequestDataPollTransmission(void)538 Error Mac::RequestDataPollTransmission(void)
539 {
540 Error error = kErrorNone;
541
542 VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
543 VerifyOrExit(!IsActiveOrPending(kOperationTransmitPoll));
544
545 // We ensure data frame and data poll tx requests are handled in the
546 // order they are requested. So if we have a pending direct data frame
547 // tx request, it should be sent before the poll frame.
548
549 mShouldTxPollBeforeData = !IsPending(kOperationTransmitDataDirect);
550
551 StartOperation(kOperationTransmitPoll);
552
553 exit:
554 return error;
555 }
556
UpdateIdleMode(void)557 void Mac::UpdateIdleMode(void)
558 {
559 bool shouldSleep = !mRxOnWhenIdle && !mPromiscuous;
560
561 VerifyOrExit(mOperation == kOperationIdle);
562
563 if (!mRxOnWhenIdle)
564 {
565 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
566 if (mShouldDelaySleep)
567 {
568 mTimer.Start(kSleepDelay);
569 mShouldDelaySleep = false;
570 mDelayingSleep = true;
571 LogDebg("Idle mode: Sleep delayed");
572 }
573
574 if (mDelayingSleep)
575 {
576 shouldSleep = false;
577 }
578 #endif
579 }
580 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
581 else if (IsPending(kOperationTransmitDataCsl))
582 {
583 mTimer.FireAt(mCslTxFireTime);
584 }
585 #endif
586
587 if (shouldSleep)
588 {
589 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
590 if (IsCslEnabled())
591 {
592 mLinks.CslSample();
593 ExitNow();
594 }
595 #endif
596 mLinks.Sleep();
597 LogDebg("Idle mode: Radio sleeping");
598 }
599 else
600 {
601 mLinks.Receive(mRadioChannel);
602 LogDebg("Idle mode: Radio receiving on channel %u", mRadioChannel);
603 }
604
605 exit:
606 return;
607 }
608
IsActiveOrPending(Operation aOperation) const609 bool Mac::IsActiveOrPending(Operation aOperation) const { return (mOperation == aOperation) || IsPending(aOperation); }
610
StartOperation(Operation aOperation)611 void Mac::StartOperation(Operation aOperation)
612 {
613 if (aOperation != kOperationIdle)
614 {
615 SetPending(aOperation);
616
617 LogDebg("Request to start operation \"%s\"", OperationToString(aOperation));
618
619 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
620 if (mDelayingSleep)
621 {
622 LogDebg("Canceling sleep delay");
623 mTimer.Stop();
624 mDelayingSleep = false;
625 mShouldDelaySleep = false;
626 }
627 #endif
628 }
629
630 if (mOperation == kOperationIdle)
631 {
632 mOperationTask.Post();
633 }
634 }
635
PerformNextOperation(void)636 void Mac::PerformNextOperation(void)
637 {
638 VerifyOrExit(mOperation == kOperationIdle);
639
640 if (!IsEnabled())
641 {
642 mPendingOperations = 0;
643 mTimer.Stop();
644 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
645 mDelayingSleep = false;
646 mShouldDelaySleep = false;
647 #endif
648 ExitNow();
649 }
650
651 // `WaitingForData` should be checked before any other pending
652 // operations since radio should remain in receive mode after
653 // a data poll ack indicating a pending frame from parent.
654 if (IsPending(kOperationWaitingForData))
655 {
656 mOperation = kOperationWaitingForData;
657 }
658 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
659 else if (IsPending(kOperationTransmitWakeup))
660 {
661 mOperation = kOperationTransmitWakeup;
662 }
663 #endif
664 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
665 else if (IsPending(kOperationTransmitDataCsl) && TimerMilli::GetNow() >= mCslTxFireTime)
666 {
667 mOperation = kOperationTransmitDataCsl;
668 }
669 #endif
670 else if (IsPending(kOperationActiveScan))
671 {
672 mOperation = kOperationActiveScan;
673 }
674 else if (IsPending(kOperationEnergyScan))
675 {
676 mOperation = kOperationEnergyScan;
677 }
678 else if (IsPending(kOperationTransmitBeacon))
679 {
680 mOperation = kOperationTransmitBeacon;
681 }
682 #if OPENTHREAD_FTD
683 else if (IsPending(kOperationTransmitDataIndirect))
684 {
685 mOperation = kOperationTransmitDataIndirect;
686 }
687 #endif // OPENTHREAD_FTD
688 else if (IsPending(kOperationTransmitPoll) && (!IsPending(kOperationTransmitDataDirect) || mShouldTxPollBeforeData))
689 {
690 mOperation = kOperationTransmitPoll;
691 }
692 else if (IsPending(kOperationTransmitDataDirect))
693 {
694 mOperation = kOperationTransmitDataDirect;
695
696 if (IsPending(kOperationTransmitPoll))
697 {
698 // Ensure that a pending "transmit poll" operation request
699 // is prioritized over any future "transmit data" requests.
700 mShouldTxPollBeforeData = true;
701 }
702 }
703
704 if (mOperation != kOperationIdle)
705 {
706 ClearPending(mOperation);
707 LogDebg("Starting operation \"%s\"", OperationToString(mOperation));
708 mTimer.Stop(); // Stop the timer before any non-idle operation, have the operation itself be responsible to
709 // start the timer (if it wants to).
710 }
711
712 switch (mOperation)
713 {
714 case kOperationIdle:
715 UpdateIdleMode();
716 break;
717
718 case kOperationActiveScan:
719 PerformActiveScan();
720 break;
721
722 case kOperationEnergyScan:
723 PerformEnergyScan();
724 break;
725
726 case kOperationTransmitBeacon:
727 case kOperationTransmitDataDirect:
728 #if OPENTHREAD_FTD
729 case kOperationTransmitDataIndirect:
730 #endif
731 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
732 case kOperationTransmitDataCsl:
733 #endif
734 case kOperationTransmitPoll:
735 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
736 case kOperationTransmitWakeup:
737 #endif
738 BeginTransmit();
739 break;
740
741 case kOperationWaitingForData:
742 mLinks.Receive(mRadioChannel);
743 mTimer.Start(kDataPollTimeout);
744 break;
745 }
746
747 exit:
748 return;
749 }
750
FinishOperation(void)751 void Mac::FinishOperation(void)
752 {
753 LogDebg("Finishing operation \"%s\"", OperationToString(mOperation));
754 mOperation = kOperationIdle;
755 }
756
PrepareBeaconRequest(void)757 TxFrame *Mac::PrepareBeaconRequest(void)
758 {
759 TxFrame &frame = mLinks.GetTxFrames().GetBroadcastTxFrame();
760 TxFrame::Info frameInfo;
761
762 frameInfo.mAddrs.mSource.SetNone();
763 frameInfo.mAddrs.mDestination.SetShort(kShortAddrBroadcast);
764 frameInfo.mPanIds.SetDestination(kShortAddrBroadcast);
765
766 frameInfo.mType = Frame::kTypeMacCmd;
767 frameInfo.mCommandId = Frame::kMacCmdBeaconRequest;
768 frameInfo.mVersion = Frame::kVersion2003;
769
770 frameInfo.PrepareHeadersIn(frame);
771
772 LogInfo("Sending Beacon Request");
773
774 return &frame;
775 }
776
PrepareBeacon(void)777 TxFrame *Mac::PrepareBeacon(void)
778 {
779 TxFrame *frame;
780 TxFrame::Info frameInfo;
781 Beacon *beacon = nullptr;
782 #if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE
783 uint8_t beaconLength;
784 BeaconPayload *beaconPayload = nullptr;
785 #endif
786
787 #if OPENTHREAD_CONFIG_MULTI_RADIO
788 OT_ASSERT(!mTxBeaconRadioLinks.IsEmpty());
789 frame = &mLinks.GetTxFrames().GetTxFrame(mTxBeaconRadioLinks);
790 mTxBeaconRadioLinks.Clear();
791 #else
792 frame = &mLinks.GetTxFrames().GetBroadcastTxFrame();
793 #endif
794
795 frameInfo.mAddrs.mSource.SetExtended(GetExtAddress());
796 frameInfo.mPanIds.SetSource(mPanId);
797 frameInfo.mAddrs.mDestination.SetNone();
798
799 frameInfo.mType = Frame::kTypeBeacon;
800 frameInfo.mVersion = Frame::kVersion2003;
801
802 frameInfo.PrepareHeadersIn(*frame);
803
804 beacon = reinterpret_cast<Beacon *>(frame->GetPayload());
805 beacon->Init();
806
807 #if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE
808 beaconLength = sizeof(*beacon);
809
810 beaconPayload = reinterpret_cast<BeaconPayload *>(beacon->GetPayload());
811
812 beaconPayload->Init();
813
814 if (IsJoinable())
815 {
816 beaconPayload->SetJoiningPermitted();
817 }
818 else
819 {
820 beaconPayload->ClearJoiningPermitted();
821 }
822
823 beaconPayload->SetNetworkName(Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData());
824 beaconPayload->SetExtendedPanId(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
825
826 beaconLength += sizeof(*beaconPayload);
827
828 frame->SetPayloadLength(beaconLength);
829 #endif
830
831 LogBeacon("Sending");
832
833 return frame;
834 }
835
ShouldSendBeacon(void) const836 bool Mac::ShouldSendBeacon(void) const
837 {
838 bool shouldSend = false;
839
840 VerifyOrExit(IsEnabled());
841
842 shouldSend = IsBeaconEnabled();
843
844 #if OPENTHREAD_CONFIG_MAC_BEACON_RSP_WHEN_JOINABLE_ENABLE
845 if (!shouldSend)
846 {
847 // When `ENABLE_BEACON_RSP_WHEN_JOINABLE` feature is enabled,
848 // the device should transmit IEEE 802.15.4 Beacons in response
849 // to IEEE 802.15.4 Beacon Requests even while the device is not
850 // router capable and detached (i.e., `IsBeaconEnabled()` is
851 // false) but only if it is in joinable state (unsecure port
852 // list is not empty).
853
854 shouldSend = IsJoinable();
855 }
856 #endif
857
858 exit:
859 return shouldSend;
860 }
861
IsJoinable(void) const862 bool Mac::IsJoinable(void) const
863 {
864 uint8_t numUnsecurePorts;
865
866 Get<Ip6::Filter>().GetUnsecurePorts(numUnsecurePorts);
867
868 return (numUnsecurePorts != 0);
869 }
870
ProcessTransmitSecurity(TxFrame & aFrame)871 void Mac::ProcessTransmitSecurity(TxFrame &aFrame)
872 {
873 KeyManager &keyManager = Get<KeyManager>();
874 uint8_t keyIdMode;
875 const ExtAddress *extAddress = nullptr;
876
877 VerifyOrExit(aFrame.GetSecurityEnabled());
878
879 IgnoreError(aFrame.GetKeyIdMode(keyIdMode));
880
881 switch (keyIdMode)
882 {
883 case Frame::kKeyIdMode0:
884 aFrame.SetAesKey(keyManager.GetKek());
885 extAddress = &GetExtAddress();
886
887 if (!aFrame.IsHeaderUpdated())
888 {
889 aFrame.SetFrameCounter(keyManager.GetKekFrameCounter());
890 keyManager.IncrementKekFrameCounter();
891 }
892
893 break;
894
895 case Frame::kKeyIdMode1:
896
897 // For 15.4 radio link, the AES CCM* and frame security counter (under MAC
898 // key ID mode 1) are managed by `SubMac` or `Radio` modules.
899 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
900 #if !OPENTHREAD_CONFIG_MULTI_RADIO
901 ExitNow();
902 #else
903 VerifyOrExit(aFrame.GetRadioType() != kRadioTypeIeee802154);
904 #endif
905 #endif
906
907 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
908 aFrame.SetAesKey(*mLinks.GetCurrentMacKey(aFrame));
909 extAddress = &GetExtAddress();
910
911 // If the frame header is marked as updated, `MeshForwarder` which
912 // prepared the frame should set the frame counter and key id to the
913 // same values used in the earlier transmit attempt. For a new frame (header
914 // not updated), we get a new frame counter and key id from the key
915 // manager.
916
917 if (!aFrame.IsHeaderUpdated())
918 {
919 mLinks.SetMacFrameCounter(aFrame);
920 aFrame.SetKeyId((keyManager.GetCurrentKeySequence() & 0x7f) + 1);
921 }
922 #endif
923 break;
924
925 case Frame::kKeyIdMode2:
926 {
927 uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff};
928
929 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
930 if (aFrame.IsWakeupFrame())
931 {
932 // Just set the key source here, further security processing will happen in SubMac
933 BigEndian::WriteUint32(keyManager.GetCurrentKeySequence(), keySource);
934 aFrame.SetKeySource(keySource);
935 ExitNow();
936 }
937 #endif
938 aFrame.SetAesKey(mMode2KeyMaterial);
939
940 mKeyIdMode2FrameCounter++;
941 aFrame.SetFrameCounter(mKeyIdMode2FrameCounter);
942 aFrame.SetKeySource(keySource);
943 aFrame.SetKeyId(0xff);
944 extAddress = &AsCoreType(&sMode2ExtAddress);
945 break;
946 }
947
948 default:
949 OT_ASSERT(false);
950 }
951
952 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
953 // Transmit security will be processed after time IE content is updated.
954 VerifyOrExit(aFrame.GetTimeIeOffset() == 0);
955 #endif
956
957 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
958 // Transmit security will be processed after time IE content is updated.
959 VerifyOrExit(!aFrame.IsCslIePresent());
960 #endif
961
962 aFrame.ProcessTransmitAesCcm(*extAddress);
963
964 exit:
965 return;
966 }
967
BeginTransmit(void)968 void Mac::BeginTransmit(void)
969 {
970 TxFrame *frame = nullptr;
971 TxFrames &txFrames = mLinks.GetTxFrames();
972 Address dstAddr;
973
974 txFrames.Clear();
975
976 #if OPENTHREAD_CONFIG_MULTI_RADIO
977 mTxPendingRadioLinks.Clear();
978 mTxError = kErrorAbort;
979 #endif
980
981 VerifyOrExit(IsEnabled());
982
983 switch (mOperation)
984 {
985 case kOperationActiveScan:
986 mLinks.SetPanId(kPanIdBroadcast);
987 frame = PrepareBeaconRequest();
988 VerifyOrExit(frame != nullptr);
989 frame->SetChannel(mScanChannel);
990 frame->SetSequence(0);
991 frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
992 frame->SetMaxFrameRetries(mMaxFrameRetriesDirect);
993 break;
994
995 case kOperationTransmitBeacon:
996 frame = PrepareBeacon();
997 VerifyOrExit(frame != nullptr);
998 frame->SetChannel(mRadioChannel);
999 frame->SetSequence(mBeaconSequence++);
1000 frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
1001 frame->SetMaxFrameRetries(mMaxFrameRetriesDirect);
1002 break;
1003
1004 case kOperationTransmitPoll:
1005 txFrames.SetChannel(mRadioChannel);
1006 txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
1007 txFrames.SetMaxFrameRetries(mMaxFrameRetriesDirect);
1008 frame = Get<DataPollSender>().PrepareDataRequest(txFrames);
1009 VerifyOrExit(frame != nullptr);
1010 frame->SetSequence(mDataSequence++);
1011 break;
1012
1013 case kOperationTransmitDataDirect:
1014 // Set channel and retry counts on all TxFrames before asking
1015 // the next layer (`MeshForwarder`) to prepare the frame. This
1016 // allows next layer to possibility change these parameters.
1017 txFrames.SetChannel(mRadioChannel);
1018 txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
1019 txFrames.SetMaxFrameRetries(mMaxFrameRetriesDirect);
1020 frame = Get<MeshForwarder>().HandleFrameRequest(txFrames);
1021 VerifyOrExit(frame != nullptr);
1022 frame->SetSequence(mDataSequence++);
1023 break;
1024
1025 #if OPENTHREAD_FTD
1026 case kOperationTransmitDataIndirect:
1027 txFrames.SetChannel(mRadioChannel);
1028 txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsIndirect);
1029 txFrames.SetMaxFrameRetries(mMaxFrameRetriesIndirect);
1030 frame = Get<DataPollHandler>().HandleFrameRequest(txFrames);
1031 VerifyOrExit(frame != nullptr);
1032
1033 // If the frame is marked as retransmission, then data sequence number is already set.
1034 if (!frame->IsARetransmission())
1035 {
1036 frame->SetSequence(mDataSequence++);
1037 }
1038 break;
1039 #endif
1040
1041 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1042 case kOperationTransmitDataCsl:
1043 txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsCsl);
1044 txFrames.SetMaxFrameRetries(kMaxFrameRetriesCsl);
1045 frame = Get<CslTxScheduler>().HandleFrameRequest(txFrames);
1046 VerifyOrExit(frame != nullptr);
1047
1048 // If the frame is marked as retransmission, then data sequence number is already set.
1049 if (!frame->IsARetransmission())
1050 {
1051 frame->SetSequence(mDataSequence++);
1052 }
1053
1054 break;
1055
1056 #endif
1057
1058 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
1059 case kOperationTransmitWakeup:
1060 frame = Get<WakeupTxScheduler>().PrepareWakeupFrame(txFrames);
1061 VerifyOrExit(frame != nullptr);
1062 frame->SetChannel(mWakeupChannel);
1063 frame->SetRxChannelAfterTxDone(mRadioChannel);
1064 break;
1065 #endif
1066
1067 default:
1068 OT_ASSERT(false);
1069 }
1070
1071 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1072 {
1073 uint8_t timeIeOffset = GetTimeIeOffset(*frame);
1074
1075 frame->SetTimeIeOffset(timeIeOffset);
1076
1077 if (timeIeOffset != 0)
1078 {
1079 frame->SetTimeSyncSeq(Get<TimeSync>().GetTimeSyncSeq());
1080 frame->SetNetworkTimeOffset(Get<TimeSync>().GetNetworkTimeOffset());
1081 }
1082 }
1083 #endif
1084
1085 if (!frame->IsSecurityProcessed())
1086 {
1087 #if OPENTHREAD_CONFIG_MULTI_RADIO
1088 // Go through all selected radio link types for this tx and
1089 // copy the frame into correct `TxFrame` for each radio type
1090 // (if it is not already prepared).
1091
1092 for (RadioType radio : RadioTypes::kAllRadioTypes)
1093 {
1094 if (txFrames.GetSelectedRadioTypes().Contains(radio))
1095 {
1096 TxFrame &txFrame = txFrames.GetTxFrame(radio);
1097
1098 if (txFrame.IsEmpty())
1099 {
1100 txFrame.CopyFrom(*frame);
1101 }
1102 }
1103 }
1104
1105 // Go through all selected radio link types for this tx and
1106 // process security for each radio type separately. This
1107 // allows radio links to handle security differently, e.g.,
1108 // with different keys or link frame counters.
1109 for (RadioType radio : RadioTypes::kAllRadioTypes)
1110 {
1111 if (txFrames.GetSelectedRadioTypes().Contains(radio))
1112 {
1113 ProcessTransmitSecurity(txFrames.GetTxFrame(radio));
1114 }
1115 }
1116 #else
1117 ProcessTransmitSecurity(*frame);
1118 #endif
1119 }
1120
1121 mBroadcastTransmitCount = 0;
1122
1123 #if OPENTHREAD_CONFIG_MULTI_RADIO
1124 mTxPendingRadioLinks = txFrames.GetSelectedRadioTypes();
1125
1126 // If the "required radio type set" is empty,`mTxError` starts as
1127 // `kErrorAbort`. In this case, successful tx over any radio
1128 // link is sufficient for overall tx to be considered successful.
1129 // When the "required radio type set" is not empty, `mTxError`
1130 // starts as `kErrorNone` and we update it if tx over any link
1131 // in the required set fails.
1132
1133 if (!txFrames.GetRequiredRadioTypes().IsEmpty())
1134 {
1135 mTxError = kErrorNone;
1136 }
1137 #endif
1138
1139 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
1140 if (!mRxOnWhenIdle && !mPromiscuous)
1141 {
1142 mShouldDelaySleep = frame->GetFramePending();
1143 LogDebg("Delay sleep for pending tx");
1144 }
1145 #endif
1146
1147 #if OPENTHREAD_CONFIG_MULTI_RADIO
1148 mLinks.Send(*frame, mTxPendingRadioLinks);
1149 #else
1150 mLinks.Send();
1151 #endif
1152
1153 exit:
1154
1155 if (frame == nullptr)
1156 {
1157 // If the frame could not be prepared and the tx is being
1158 // aborted, we set the frame length to zero to mark it as empty.
1159 // The empty frame helps differentiate between an aborted tx due
1160 // to OpenThread itself not being able to prepare the frame, versus
1161 // the radio platform aborting the tx operation.
1162
1163 frame = &txFrames.GetBroadcastTxFrame();
1164 frame->SetLength(0);
1165 HandleTransmitDone(*frame, nullptr, kErrorAbort);
1166 }
1167 }
1168
RecordCcaStatus(bool aCcaSuccess,uint8_t aChannel)1169 void Mac::RecordCcaStatus(bool aCcaSuccess, uint8_t aChannel)
1170 {
1171 if (!aCcaSuccess)
1172 {
1173 mCounters.mTxErrCca++;
1174 }
1175
1176 // Only track the CCA success rate for frame transmissions
1177 // on the PAN channel or the CSL channel.
1178
1179 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1180 if ((aChannel == mPanChannel) || (IsCslEnabled() && (aChannel == mCslChannel)))
1181 #else
1182 if (aChannel == mPanChannel)
1183 #endif
1184 {
1185 if (mCcaSampleCount < kMaxCcaSampleCount)
1186 {
1187 mCcaSampleCount++;
1188 }
1189
1190 mCcaSuccessRateTracker.AddSample(aCcaSuccess, mCcaSampleCount);
1191 }
1192 }
1193
RecordFrameTransmitStatus(const TxFrame & aFrame,Error aError,uint8_t aRetryCount,bool aWillRetx)1194 void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame, Error aError, uint8_t aRetryCount, bool aWillRetx)
1195 {
1196 bool ackRequested = aFrame.GetAckRequest();
1197 Address dstAddr;
1198 Neighbor *neighbor;
1199
1200 VerifyOrExit(!aFrame.IsEmpty());
1201
1202 IgnoreError(aFrame.GetDstAddr(dstAddr));
1203 neighbor = Get<NeighborTable>().FindNeighbor(dstAddr);
1204
1205 // Record frame transmission success/failure state (for a neighbor).
1206
1207 if ((neighbor != nullptr) && ackRequested)
1208 {
1209 bool frameTxSuccess = true;
1210
1211 // CCA or abort errors are excluded from frame tx error
1212 // rate tracking, since when they occur, the frame is
1213 // not actually sent over the air.
1214
1215 switch (aError)
1216 {
1217 case kErrorNoAck:
1218 frameTxSuccess = false;
1219
1220 OT_FALL_THROUGH;
1221
1222 case kErrorNone:
1223 neighbor->GetLinkInfo().AddFrameTxStatus(frameTxSuccess);
1224 break;
1225
1226 default:
1227 break;
1228 }
1229 }
1230
1231 // Log frame transmission failure.
1232
1233 if (aError != kErrorNone)
1234 {
1235 LogFrameTxFailure(aFrame, aError, aRetryCount, aWillRetx);
1236 DumpDebg("TX ERR", aFrame.GetHeader(), 16);
1237
1238 if (aWillRetx)
1239 {
1240 mCounters.mTxRetry++;
1241
1242 // Since this failed transmission will be retried by `SubMac` layer
1243 // there is no need to update any other MAC counter. MAC counters
1244 // are updated on the final transmission attempt.
1245
1246 ExitNow();
1247 }
1248 }
1249
1250 // Update MAC counters.
1251
1252 mCounters.mTxTotal++;
1253
1254 if (aError == kErrorAbort)
1255 {
1256 mCounters.mTxErrAbort++;
1257 }
1258
1259 if (aError == kErrorChannelAccessFailure)
1260 {
1261 mCounters.mTxErrBusyChannel++;
1262 }
1263
1264 if (ackRequested)
1265 {
1266 mCounters.mTxAckRequested++;
1267
1268 if (aError == kErrorNone)
1269 {
1270 mCounters.mTxAcked++;
1271 }
1272 }
1273 else
1274 {
1275 mCounters.mTxNoAckRequested++;
1276 }
1277
1278 if (dstAddr.IsBroadcast())
1279 {
1280 mCounters.mTxBroadcast++;
1281 }
1282 else
1283 {
1284 mCounters.mTxUnicast++;
1285 }
1286
1287 exit:
1288 return;
1289 }
1290
HandleTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)1291 void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
1292 {
1293 bool ackRequested = aFrame.GetAckRequest();
1294
1295 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
1296 if (!aFrame.IsEmpty()
1297 #if OPENTHREAD_CONFIG_MULTI_RADIO
1298 && (aFrame.GetRadioType() == kRadioTypeIeee802154)
1299 #endif
1300 )
1301 {
1302 Address dstAddr;
1303
1304 IgnoreError(aFrame.GetDstAddr(dstAddr));
1305
1306 // Determine whether to re-transmit a broadcast frame.
1307 if (dstAddr.IsBroadcast())
1308 {
1309 mBroadcastTransmitCount++;
1310
1311 if (mBroadcastTransmitCount < kTxNumBcast)
1312 {
1313 #if OPENTHREAD_CONFIG_MULTI_RADIO
1314 {
1315 RadioTypes radioTypes;
1316 radioTypes.Add(kRadioTypeIeee802154);
1317 mLinks.Send(aFrame, radioTypes);
1318 }
1319 #else
1320 mLinks.Send();
1321 #endif
1322 ExitNow();
1323 }
1324
1325 mBroadcastTransmitCount = 0;
1326 }
1327
1328 if (ackRequested && (aAckFrame != nullptr))
1329 {
1330 Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(dstAddr);
1331
1332 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
1333 if ((aError == kErrorNone) && (neighbor != nullptr) &&
1334 (mFilter.ApplyToRxFrame(*aAckFrame, neighbor->GetExtAddress(), neighbor) != kErrorNone))
1335 {
1336 aError = kErrorNoAck;
1337 }
1338 #endif
1339
1340 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1341 // Verify Enh-ACK integrity by checking its MIC
1342 if ((aError == kErrorNone) && (ProcessEnhAckSecurity(aFrame, *aAckFrame) != kErrorNone))
1343 {
1344 aError = kErrorNoAck;
1345 }
1346 #endif
1347
1348 if ((aError == kErrorNone) && (neighbor != nullptr))
1349 {
1350 UpdateNeighborLinkInfo(*neighbor, *aAckFrame);
1351
1352 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
1353 ProcessEnhAckProbing(*aAckFrame, *neighbor);
1354 #endif
1355 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1356 ProcessCsl(*aAckFrame, dstAddr);
1357 #endif
1358 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1359 if (!mRxOnWhenIdle && aFrame.HasCslIe())
1360 {
1361 Get<DataPollSender>().ResetKeepAliveTimer();
1362 }
1363 #endif
1364 }
1365 }
1366 }
1367 #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
1368
1369 #if OPENTHREAD_CONFIG_MULTI_RADIO
1370 if (!aFrame.IsEmpty())
1371 {
1372 RadioType radio = aFrame.GetRadioType();
1373 RadioTypes requiredRadios = mLinks.GetTxFrames().GetRequiredRadioTypes();
1374
1375 Get<RadioSelector>().UpdateOnSendDone(aFrame, aError);
1376
1377 if (requiredRadios.IsEmpty())
1378 {
1379 // If the "required radio type set" is empty, successful
1380 // tx over any radio link is sufficient for overall tx to
1381 // be considered successful. In this case `mTxError`
1382 // starts as `kErrorAbort` and we update it only when
1383 // it is not already `kErrorNone`.
1384
1385 if (mTxError != kErrorNone)
1386 {
1387 mTxError = aError;
1388 }
1389 }
1390 else
1391 {
1392 // When the "required radio type set" is not empty we
1393 // expect the successful frame tx on all links in this set
1394 // to consider the overall tx successful. In this case,
1395 // `mTxError` starts as `kErrorNone` and we update it
1396 // if tx over any link in the set fails.
1397
1398 if (requiredRadios.Contains(radio) && (aError != kErrorNone))
1399 {
1400 LogDebg("Frame tx failed on required radio link %s with error %s", RadioTypeToString(radio),
1401 ErrorToString(aError));
1402
1403 mTxError = aError;
1404 }
1405 }
1406
1407 // Keep track of radio links on which the frame is sent
1408 // and wait for all radio links to finish.
1409 mTxPendingRadioLinks.Remove(radio);
1410
1411 VerifyOrExit(mTxPendingRadioLinks.IsEmpty());
1412
1413 aError = mTxError;
1414 }
1415 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
1416
1417 // Determine next action based on current operation.
1418
1419 switch (mOperation)
1420 {
1421 case kOperationActiveScan:
1422 mCounters.mTxBeaconRequest++;
1423 mTimer.Start(mScanDuration);
1424 break;
1425
1426 case kOperationTransmitBeacon:
1427 mCounters.mTxBeacon++;
1428 FinishOperation();
1429 PerformNextOperation();
1430 break;
1431
1432 case kOperationTransmitPoll:
1433 OT_ASSERT(aFrame.IsEmpty() || ackRequested);
1434
1435 if ((aError == kErrorNone) && (aAckFrame != nullptr))
1436 {
1437 bool framePending = aAckFrame->GetFramePending();
1438
1439 if (IsEnabled() && framePending)
1440 {
1441 StartOperation(kOperationWaitingForData);
1442 }
1443
1444 LogInfo("Sent data poll, fp:%s", ToYesNo(framePending));
1445 }
1446
1447 mCounters.mTxDataPoll++;
1448 FinishOperation();
1449 Get<DataPollSender>().HandlePollSent(aFrame, aError);
1450 PerformNextOperation();
1451 break;
1452
1453 case kOperationTransmitDataDirect:
1454 mCounters.mTxData++;
1455
1456 if (aError != kErrorNone)
1457 {
1458 mCounters.mTxDirectMaxRetryExpiry++;
1459 }
1460 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
1461 else if (mLinks.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT)
1462 {
1463 mRetryHistogram.mTxDirectRetrySuccess[mLinks.GetTransmitRetries()]++;
1464 }
1465 #endif
1466
1467 DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
1468 FinishOperation();
1469 Get<MeshForwarder>().HandleSentFrame(aFrame, aError);
1470 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1471 Get<DataPollSender>().ProcessTxDone(aFrame, aAckFrame, aError);
1472 #endif
1473 PerformNextOperation();
1474 break;
1475
1476 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1477 case kOperationTransmitDataCsl:
1478 mCounters.mTxData++;
1479
1480 DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
1481 FinishOperation();
1482 Get<CslTxScheduler>().HandleSentFrame(aFrame, aError);
1483 PerformNextOperation();
1484
1485 break;
1486 #endif
1487
1488 #if OPENTHREAD_FTD
1489 case kOperationTransmitDataIndirect:
1490 mCounters.mTxData++;
1491
1492 if (aError != kErrorNone)
1493 {
1494 mCounters.mTxIndirectMaxRetryExpiry++;
1495 }
1496 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
1497 else if (mLinks.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT)
1498 {
1499 mRetryHistogram.mTxIndirectRetrySuccess[mLinks.GetTransmitRetries()]++;
1500 }
1501 #endif
1502
1503 DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
1504 FinishOperation();
1505 Get<DataPollHandler>().HandleSentFrame(aFrame, aError);
1506 PerformNextOperation();
1507 break;
1508 #endif // OPENTHREAD_FTD
1509
1510 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
1511 case kOperationTransmitWakeup:
1512 FinishOperation();
1513 PerformNextOperation();
1514 break;
1515 #endif
1516
1517 default:
1518 OT_ASSERT(false);
1519 }
1520
1521 ExitNow(); // Added to suppress "unused label exit" warning (in TREL radio only).
1522
1523 exit:
1524 return;
1525 }
1526
HandleTimer(void)1527 void Mac::HandleTimer(void)
1528 {
1529 switch (mOperation)
1530 {
1531 case kOperationActiveScan:
1532 PerformActiveScan();
1533 break;
1534
1535 case kOperationWaitingForData:
1536 LogDebg("Data poll timeout");
1537 FinishOperation();
1538 Get<DataPollSender>().HandlePollTimeout();
1539 PerformNextOperation();
1540 break;
1541
1542 case kOperationIdle:
1543 if (!mRxOnWhenIdle)
1544 {
1545 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
1546 if (mDelayingSleep)
1547 {
1548 LogDebg("Sleep delay timeout expired");
1549 mDelayingSleep = false;
1550 UpdateIdleMode();
1551 }
1552 #endif
1553 }
1554 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1555 else if (IsPending(kOperationTransmitDataCsl))
1556 {
1557 PerformNextOperation();
1558 }
1559 #endif
1560 break;
1561
1562 default:
1563 OT_ASSERT(false);
1564 }
1565 }
1566
ProcessReceiveSecurity(RxFrame & aFrame,const Address & aSrcAddr,Neighbor * aNeighbor)1567 Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor)
1568 {
1569 KeyManager &keyManager = Get<KeyManager>();
1570 Error error = kErrorSecurity;
1571 uint8_t securityLevel;
1572 uint8_t keyIdMode;
1573 uint32_t frameCounter;
1574 uint8_t keyid;
1575 uint32_t keySequence = 0;
1576 const KeyMaterial *macKey;
1577 const ExtAddress *extAddress;
1578
1579 VerifyOrExit(aFrame.GetSecurityEnabled(), error = kErrorNone);
1580
1581 IgnoreError(aFrame.GetSecurityLevel(securityLevel));
1582 VerifyOrExit(securityLevel == Frame::kSecurityEncMic32);
1583
1584 IgnoreError(aFrame.GetFrameCounter(frameCounter));
1585 LogDebg("Rx security - frame counter %lu", ToUlong(frameCounter));
1586
1587 IgnoreError(aFrame.GetKeyIdMode(keyIdMode));
1588
1589 switch (keyIdMode)
1590 {
1591 case Frame::kKeyIdMode0:
1592 macKey = &keyManager.GetKek();
1593 extAddress = &aSrcAddr.GetExtended();
1594 break;
1595
1596 case Frame::kKeyIdMode1:
1597 VerifyOrExit(aNeighbor != nullptr);
1598
1599 IgnoreError(aFrame.GetKeyId(keyid));
1600 keyid--;
1601
1602 if (keyid == (keyManager.GetCurrentKeySequence() & 0x7f))
1603 {
1604 keySequence = keyManager.GetCurrentKeySequence();
1605 macKey = mLinks.GetCurrentMacKey(aFrame);
1606 }
1607 else if (keyid == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f))
1608 {
1609 keySequence = keyManager.GetCurrentKeySequence() - 1;
1610 macKey = mLinks.GetTemporaryMacKey(aFrame, keySequence);
1611 }
1612 else if (keyid == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f))
1613 {
1614 keySequence = keyManager.GetCurrentKeySequence() + 1;
1615 macKey = mLinks.GetTemporaryMacKey(aFrame, keySequence);
1616 }
1617 else
1618 {
1619 ExitNow();
1620 }
1621
1622 // If the frame is from a neighbor not in valid state (e.g., it is from a child being
1623 // restored), skip the key sequence and frame counter checks but continue to verify
1624 // the tag/MIC. Such a frame is later filtered in `RxDoneTask` which only allows MAC
1625 // Data Request frames from a child being restored.
1626
1627 if (aNeighbor->IsStateValid())
1628 {
1629 VerifyOrExit(keySequence >= aNeighbor->GetKeySequence());
1630
1631 if (keySequence == aNeighbor->GetKeySequence())
1632 {
1633 uint32_t neighborFrameCounter;
1634
1635 #if OPENTHREAD_CONFIG_MULTI_RADIO
1636 neighborFrameCounter = aNeighbor->GetLinkFrameCounters().Get(aFrame.GetRadioType());
1637 #else
1638 neighborFrameCounter = aNeighbor->GetLinkFrameCounters().Get();
1639 #endif
1640
1641 // If frame counter is one off, then frame is a duplicate.
1642 VerifyOrExit((frameCounter + 1) != neighborFrameCounter, error = kErrorDuplicated);
1643
1644 VerifyOrExit(frameCounter >= neighborFrameCounter);
1645 }
1646 }
1647
1648 extAddress = &aSrcAddr.GetExtended();
1649
1650 break;
1651
1652 case Frame::kKeyIdMode2:
1653 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
1654 if (aFrame.IsWakeupFrame())
1655 {
1656 uint32_t sequence;
1657
1658 // TODO: Avoid generating a new key if a wake-up frame was recently received already
1659
1660 IgnoreError(aFrame.GetKeyId(keyid));
1661 sequence = BigEndian::ReadUint32(aFrame.GetKeySource());
1662 VerifyOrExit(((sequence & 0x7f) + 1) == keyid, error = kErrorSecurity);
1663
1664 macKey = (sequence == keyManager.GetCurrentKeySequence()) ? mLinks.GetCurrentMacKey(aFrame)
1665 : &keyManager.GetTemporaryMacKey(sequence);
1666 extAddress = &aSrcAddr.GetExtended();
1667 }
1668 else
1669 #endif
1670 {
1671 macKey = &mMode2KeyMaterial;
1672 extAddress = &AsCoreType(&sMode2ExtAddress);
1673 }
1674 break;
1675
1676 default:
1677 ExitNow();
1678 }
1679
1680 SuccessOrExit(aFrame.ProcessReceiveAesCcm(*extAddress, *macKey));
1681
1682 if ((keyIdMode == Frame::kKeyIdMode1) && aNeighbor->IsStateValid())
1683 {
1684 if (aNeighbor->GetKeySequence() != keySequence)
1685 {
1686 aNeighbor->SetKeySequence(keySequence);
1687 aNeighbor->SetMleFrameCounter(0);
1688 aNeighbor->GetLinkFrameCounters().Reset();
1689 }
1690
1691 #if OPENTHREAD_CONFIG_MULTI_RADIO
1692 aNeighbor->GetLinkFrameCounters().Set(aFrame.GetRadioType(), frameCounter + 1);
1693 #else
1694 aNeighbor->GetLinkFrameCounters().Set(frameCounter + 1);
1695 #endif
1696
1697 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
1698 #if OPENTHREAD_CONFIG_MULTI_RADIO
1699 if (aFrame.GetRadioType() == kRadioTypeIeee802154)
1700 #endif
1701 {
1702 if ((frameCounter + 1) > aNeighbor->GetLinkAckFrameCounter())
1703 {
1704 aNeighbor->SetLinkAckFrameCounter(frameCounter + 1);
1705 }
1706 }
1707 #endif
1708
1709 if (keySequence > keyManager.GetCurrentKeySequence())
1710 {
1711 keyManager.SetCurrentKeySequence(keySequence, KeyManager::kApplySwitchGuard | KeyManager::kResetGuardTimer);
1712 }
1713 }
1714
1715 error = kErrorNone;
1716
1717 exit:
1718 return error;
1719 }
1720
1721 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
ProcessEnhAckSecurity(TxFrame & aTxFrame,RxFrame & aAckFrame)1722 Error Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame)
1723 {
1724 Error error = kErrorSecurity;
1725 uint8_t securityLevel;
1726 uint8_t txKeyId;
1727 uint8_t ackKeyId;
1728 uint8_t keyIdMode;
1729 uint32_t frameCounter;
1730 Address srcAddr;
1731 Address dstAddr;
1732 Neighbor *neighbor = nullptr;
1733 KeyManager &keyManager = Get<KeyManager>();
1734 const KeyMaterial *macKey;
1735
1736 VerifyOrExit(aAckFrame.GetSecurityEnabled(), error = kErrorNone);
1737 VerifyOrExit(aAckFrame.IsVersion2015());
1738
1739 SuccessOrExit(aAckFrame.ValidatePsdu());
1740
1741 IgnoreError(aAckFrame.GetSecurityLevel(securityLevel));
1742 VerifyOrExit(securityLevel == Frame::kSecurityEncMic32);
1743
1744 IgnoreError(aAckFrame.GetKeyIdMode(keyIdMode));
1745 VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
1746
1747 IgnoreError(aTxFrame.GetKeyId(txKeyId));
1748 IgnoreError(aAckFrame.GetKeyId(ackKeyId));
1749
1750 VerifyOrExit(txKeyId == ackKeyId);
1751
1752 IgnoreError(aAckFrame.GetFrameCounter(frameCounter));
1753 LogDebg("Rx security - Ack frame counter %lu", ToUlong(frameCounter));
1754
1755 IgnoreError(aAckFrame.GetSrcAddr(srcAddr));
1756
1757 if (!srcAddr.IsNone())
1758 {
1759 neighbor = Get<NeighborTable>().FindNeighbor(srcAddr);
1760 }
1761 else
1762 {
1763 IgnoreError(aTxFrame.GetDstAddr(dstAddr));
1764
1765 if (!dstAddr.IsNone())
1766 {
1767 // Get neighbor from destination address of transmitted frame
1768 neighbor = Get<NeighborTable>().FindNeighbor(dstAddr);
1769 }
1770 }
1771
1772 if (!srcAddr.IsExtended() && neighbor != nullptr)
1773 {
1774 srcAddr.SetExtended(neighbor->GetExtAddress());
1775 }
1776
1777 VerifyOrExit(srcAddr.IsExtended() && neighbor != nullptr);
1778
1779 ackKeyId--;
1780
1781 if (ackKeyId == (keyManager.GetCurrentKeySequence() & 0x7f))
1782 {
1783 macKey = &mLinks.GetSubMac().GetCurrentMacKey();
1784 }
1785 else if (ackKeyId == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f))
1786 {
1787 macKey = &mLinks.GetSubMac().GetPreviousMacKey();
1788 }
1789 else if (ackKeyId == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f))
1790 {
1791 macKey = &mLinks.GetSubMac().GetNextMacKey();
1792 }
1793 else
1794 {
1795 ExitNow();
1796 }
1797
1798 if (neighbor->IsStateValid())
1799 {
1800 VerifyOrExit(frameCounter >= neighbor->GetLinkAckFrameCounter());
1801 }
1802
1803 error = aAckFrame.ProcessReceiveAesCcm(srcAddr.GetExtended(), *macKey);
1804 SuccessOrExit(error);
1805
1806 if (neighbor->IsStateValid())
1807 {
1808 neighbor->SetLinkAckFrameCounter(frameCounter + 1);
1809 }
1810
1811 exit:
1812 if (error != kErrorNone)
1813 {
1814 LogInfo("Frame tx attempt failed, error: Enh-ACK security check fail");
1815 }
1816
1817 return error;
1818 }
1819 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1820
FilterDestShortAddress(ShortAddress aDestAddress) const1821 Error Mac::FilterDestShortAddress(ShortAddress aDestAddress) const
1822 {
1823 Error error = kErrorNone;
1824
1825 if (aDestAddress == GetShortAddress())
1826 {
1827 ExitNow();
1828 }
1829
1830 #if OPENTHREAD_FTD
1831 if ((GetAlternateShortAddress() != kShortAddrInvalid) && (aDestAddress == GetAlternateShortAddress()))
1832 {
1833 ExitNow();
1834 }
1835 #endif
1836
1837 if (mRxOnWhenIdle && (aDestAddress == kShortAddrBroadcast))
1838 {
1839 ExitNow();
1840 }
1841
1842 error = kErrorDestinationAddressFiltered;
1843
1844 exit:
1845 return error;
1846 }
1847
HandleReceivedFrame(RxFrame * aFrame,Error aError)1848 void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError)
1849 {
1850 Address srcaddr;
1851 Address dstaddr;
1852 PanId panid;
1853 Neighbor *neighbor;
1854 Error error = aError;
1855
1856 mCounters.mRxTotal++;
1857
1858 SuccessOrExit(error);
1859 VerifyOrExit(aFrame != nullptr, error = kErrorNoFrameReceived);
1860 VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
1861
1862 // Ensure we have a valid frame before attempting to read any contents of
1863 // the buffer received from the radio.
1864 SuccessOrExit(error = aFrame->ValidatePsdu());
1865
1866 IgnoreError(aFrame->GetSrcAddr(srcaddr));
1867 IgnoreError(aFrame->GetDstAddr(dstaddr));
1868 neighbor = !srcaddr.IsNone() ? Get<NeighborTable>().FindNeighbor(srcaddr) : nullptr;
1869
1870 // Destination Address Filtering
1871 switch (dstaddr.GetType())
1872 {
1873 case Address::kTypeNone:
1874 break;
1875
1876 case Address::kTypeShort:
1877 SuccessOrExit(error = FilterDestShortAddress(dstaddr.GetShort()));
1878
1879 #if OPENTHREAD_FTD
1880 // Allow multicasts from neighbor routers if FTD
1881 if (neighbor == nullptr && dstaddr.IsBroadcast() && Get<Mle::MleRouter>().IsFullThreadDevice())
1882 {
1883 neighbor = Get<NeighborTable>().FindRxOnlyNeighborRouter(srcaddr);
1884 }
1885 #endif
1886
1887 break;
1888
1889 case Address::kTypeExtended:
1890 VerifyOrExit(dstaddr.GetExtended() == GetExtAddress(), error = kErrorDestinationAddressFiltered);
1891 break;
1892 }
1893
1894 // Verify destination PAN ID if present
1895 if (kErrorNone == aFrame->GetDstPanId(panid))
1896 {
1897 VerifyOrExit(panid == kShortAddrBroadcast || panid == mPanId, error = kErrorDestinationAddressFiltered);
1898 }
1899
1900 // Source Address Filtering
1901 switch (srcaddr.GetType())
1902 {
1903 case Address::kTypeNone:
1904 break;
1905
1906 case Address::kTypeShort:
1907 LogDebg("Received frame from short address 0x%04x", srcaddr.GetShort());
1908
1909 VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
1910
1911 srcaddr.SetExtended(neighbor->GetExtAddress());
1912
1913 OT_FALL_THROUGH;
1914
1915 case Address::kTypeExtended:
1916
1917 // Duplicate Address Protection
1918 VerifyOrExit(srcaddr.GetExtended() != GetExtAddress(), error = kErrorInvalidSourceAddress);
1919
1920 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
1921 SuccessOrExit(error = mFilter.ApplyToRxFrame(*aFrame, srcaddr.GetExtended(), neighbor));
1922 #endif
1923
1924 break;
1925 }
1926
1927 if (dstaddr.IsBroadcast())
1928 {
1929 mCounters.mRxBroadcast++;
1930 }
1931 else
1932 {
1933 mCounters.mRxUnicast++;
1934 }
1935
1936 error = ProcessReceiveSecurity(*aFrame, srcaddr, neighbor);
1937
1938 switch (error)
1939 {
1940 case kErrorDuplicated:
1941
1942 // Allow a duplicate received frame pass, only if the
1943 // current operation is `kOperationWaitingForData` (i.e.,
1944 // the sleepy device is waiting to receive a frame after
1945 // a data poll ack from parent indicating there is a
1946 // pending frame for it). This ensures that the sleepy
1947 // device goes to sleep faster and avoids a data poll
1948 // timeout.
1949 //
1950 // Note that `error` is checked again later after the
1951 // operation `kOperationWaitingForData` is processed
1952 // so the duplicate frame will not be passed to next
1953 // layer (`MeshForwarder`).
1954
1955 VerifyOrExit(mOperation == kOperationWaitingForData);
1956
1957 OT_FALL_THROUGH;
1958
1959 case kErrorNone:
1960 break;
1961
1962 default:
1963 ExitNow();
1964 }
1965
1966 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1967 ProcessCsl(*aFrame, srcaddr);
1968 #endif
1969
1970 Get<DataPollSender>().ProcessRxFrame(*aFrame);
1971
1972 if (neighbor != nullptr)
1973 {
1974 UpdateNeighborLinkInfo(*neighbor, *aFrame);
1975
1976 if (aFrame->GetSecurityEnabled())
1977 {
1978 uint8_t keyIdMode;
1979
1980 IgnoreError(aFrame->GetKeyIdMode(keyIdMode));
1981
1982 if (keyIdMode == Frame::kKeyIdMode1)
1983 {
1984 switch (neighbor->GetState())
1985 {
1986 case Neighbor::kStateValid:
1987 break;
1988
1989 case Neighbor::kStateRestored:
1990 case Neighbor::kStateChildUpdateRequest:
1991
1992 // Only accept a "MAC Data Request" frame from a child being restored.
1993 VerifyOrExit(aFrame->IsDataRequestCommand(), error = kErrorDrop);
1994 break;
1995
1996 default:
1997 ExitNow(error = kErrorUnknownNeighbor);
1998 }
1999
2000 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 && OPENTHREAD_FTD
2001 // From Thread 1.2, MAC Data Frame can also act as keep-alive message if child supports
2002 if (aFrame->GetType() == Frame::kTypeData && !neighbor->IsRxOnWhenIdle() &&
2003 neighbor->IsEnhancedKeepAliveSupported())
2004 {
2005 neighbor->SetLastHeard(TimerMilli::GetNow());
2006 }
2007 #endif
2008 }
2009
2010 #if OPENTHREAD_CONFIG_MULTI_RADIO
2011 Get<RadioSelector>().UpdateOnReceive(*neighbor, aFrame->GetRadioType(), /* aIsDuplicate */ false);
2012 #endif
2013 }
2014 }
2015
2016 switch (mOperation)
2017 {
2018 case kOperationActiveScan:
2019
2020 if (aFrame->GetType() == Frame::kTypeBeacon)
2021 {
2022 mCounters.mRxBeacon++;
2023 ReportActiveScanResult(aFrame);
2024 ExitNow();
2025 }
2026
2027 OT_FALL_THROUGH;
2028
2029 case kOperationEnergyScan:
2030
2031 // We can possibly receive a data frame while either active or
2032 // energy scan is ongoing. We continue to process the frame only
2033 // if the current scan channel matches `mPanChannel`.
2034
2035 VerifyOrExit(mScanChannel == mPanChannel, mCounters.mRxOther++);
2036 break;
2037
2038 case kOperationWaitingForData:
2039
2040 if (!dstaddr.IsNone())
2041 {
2042 mTimer.Stop();
2043
2044 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
2045 if (!mRxOnWhenIdle && !mPromiscuous && aFrame->GetFramePending())
2046 {
2047 mShouldDelaySleep = true;
2048 LogDebg("Delay sleep for pending rx");
2049 }
2050 #endif
2051 FinishOperation();
2052 PerformNextOperation();
2053 }
2054
2055 SuccessOrExit(error);
2056
2057 break;
2058
2059 default:
2060 break;
2061 }
2062
2063 switch (aFrame->GetType())
2064 {
2065 case Frame::kTypeMacCmd:
2066 if (HandleMacCommand(*aFrame)) // returns `true` when handled
2067 {
2068 ExitNow(error = kErrorNone);
2069 }
2070
2071 break;
2072
2073 case Frame::kTypeBeacon:
2074 mCounters.mRxBeacon++;
2075 break;
2076
2077 case Frame::kTypeData:
2078 mCounters.mRxData++;
2079 break;
2080
2081 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2082 case Frame::kTypeMultipurpose:
2083 SuccessOrExit(error = HandleWakeupFrame(*aFrame));
2084 OT_FALL_THROUGH;
2085 #endif
2086
2087 default:
2088 mCounters.mRxOther++;
2089 ExitNow();
2090 }
2091
2092 DumpDebg("RX", aFrame->GetHeader(), aFrame->GetLength());
2093 Get<MeshForwarder>().HandleReceivedFrame(*aFrame);
2094
2095 UpdateIdleMode();
2096
2097 exit:
2098
2099 if (error != kErrorNone)
2100 {
2101 LogFrameRxFailure(aFrame, error);
2102
2103 switch (error)
2104 {
2105 case kErrorSecurity:
2106 mCounters.mRxErrSec++;
2107 break;
2108
2109 case kErrorFcs:
2110 mCounters.mRxErrFcs++;
2111 break;
2112
2113 case kErrorNoFrameReceived:
2114 mCounters.mRxErrNoFrame++;
2115 break;
2116
2117 case kErrorUnknownNeighbor:
2118 mCounters.mRxErrUnknownNeighbor++;
2119 break;
2120
2121 case kErrorInvalidSourceAddress:
2122 mCounters.mRxErrInvalidSrcAddr++;
2123 break;
2124
2125 case kErrorAddressFiltered:
2126 mCounters.mRxAddressFiltered++;
2127 break;
2128
2129 case kErrorDestinationAddressFiltered:
2130 mCounters.mRxDestAddrFiltered++;
2131 break;
2132
2133 case kErrorDuplicated:
2134 mCounters.mRxDuplicated++;
2135 break;
2136
2137 default:
2138 mCounters.mRxErrOther++;
2139 break;
2140 }
2141 }
2142
2143 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2144 #if OPENTHREAD_CONFIG_MULTI_RADIO
2145 if (aFrame->GetRadioType() == kRadioTypeTrel)
2146 #endif
2147 {
2148 if (error == kErrorNone)
2149 {
2150 // If the received frame is using TREL and is successfully
2151 // processed, check for any discrepancy between the socket
2152 // address of the received TREL packet and the information
2153 // saved in the corresponding TREL peer, and signal this to
2154 // the platform layer.
2155 //
2156 // If the frame used link security and was successfully
2157 // processed, we allow the `Peer` entry socket information
2158 // to be updated directly.
2159
2160 Get<Trel::Link>().CheckPeerAddrOnRxSuccess(aFrame->GetSecurityEnabled()
2161 ? Trel::Link::kAllowPeerSockAddrUpdate
2162 : Trel::Link::kDisallowPeerSockAddrUpdate);
2163 }
2164 }
2165 #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2166 }
2167
UpdateNeighborLinkInfo(Neighbor & aNeighbor,const RxFrame & aRxFrame)2168 void Mac::UpdateNeighborLinkInfo(Neighbor &aNeighbor, const RxFrame &aRxFrame)
2169 {
2170 LinkQuality oldLinkQuality = aNeighbor.GetLinkInfo().GetLinkQuality();
2171
2172 aNeighbor.GetLinkInfo().AddRss(aRxFrame.GetRssi());
2173
2174 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
2175 aNeighbor.AggregateLinkMetrics(/* aSeriesId */ 0, aRxFrame.GetType(), aRxFrame.GetLqi(), aRxFrame.GetRssi());
2176 #endif
2177
2178 // Signal when `aNeighbor` is the current parent and its link
2179 // quality gets changed.
2180
2181 VerifyOrExit(Get<Mle::Mle>().IsChild() && (&aNeighbor == &Get<Mle::Mle>().GetParent()));
2182 VerifyOrExit(aNeighbor.GetLinkInfo().GetLinkQuality() != oldLinkQuality);
2183 Get<Notifier>().Signal(kEventParentLinkQualityChanged);
2184
2185 exit:
2186 return;
2187 }
2188
HandleMacCommand(RxFrame & aFrame)2189 bool Mac::HandleMacCommand(RxFrame &aFrame)
2190 {
2191 bool didHandle = false;
2192 uint8_t commandId;
2193
2194 IgnoreError(aFrame.GetCommandId(commandId));
2195
2196 switch (commandId)
2197 {
2198 case Frame::kMacCmdBeaconRequest:
2199 mCounters.mRxBeaconRequest++;
2200 LogInfo("Received Beacon Request");
2201
2202 if (ShouldSendBeacon())
2203 {
2204 #if OPENTHREAD_CONFIG_MULTI_RADIO
2205 mTxBeaconRadioLinks.Add(aFrame.GetRadioType());
2206 #endif
2207 StartOperation(kOperationTransmitBeacon);
2208 }
2209
2210 didHandle = true;
2211 break;
2212
2213 case Frame::kMacCmdDataRequest:
2214 mCounters.mRxDataPoll++;
2215 #if OPENTHREAD_FTD
2216 Get<DataPollHandler>().HandleDataPoll(aFrame);
2217 didHandle = true;
2218 #endif
2219 break;
2220
2221 default:
2222 mCounters.mRxOther++;
2223 break;
2224 }
2225
2226 return didHandle;
2227 }
2228
SetPromiscuous(bool aPromiscuous)2229 void Mac::SetPromiscuous(bool aPromiscuous)
2230 {
2231 mPromiscuous = aPromiscuous;
2232 Get<Radio>().SetPromiscuous(aPromiscuous);
2233
2234 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
2235 mDelayingSleep = false;
2236 mShouldDelaySleep = false;
2237 #endif
2238
2239 mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
2240 UpdateIdleMode();
2241 }
2242
SetRegion(uint16_t aRegionCode)2243 Error Mac::SetRegion(uint16_t aRegionCode)
2244 {
2245 Error error;
2246 ChannelMask oldMask = mSupportedChannelMask;
2247
2248 SuccessOrExit(error = Get<Radio>().SetRegion(aRegionCode));
2249 mSupportedChannelMask.SetMask(Get<Radio>().GetSupportedChannelMask());
2250 IgnoreError(Get<Notifier>().Update(oldMask, mSupportedChannelMask, kEventSupportedChannelMaskChanged));
2251
2252 exit:
2253 return error;
2254 }
2255
GetRegion(uint16_t & aRegionCode) const2256 Error Mac::GetRegion(uint16_t &aRegionCode) const { return Get<Radio>().GetRegion(aRegionCode); }
2257
2258 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
GetDirectRetrySuccessHistogram(uint8_t & aNumberOfEntries)2259 const uint32_t *Mac::GetDirectRetrySuccessHistogram(uint8_t &aNumberOfEntries)
2260 {
2261 if (mMaxFrameRetriesDirect >= OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT)
2262 {
2263 aNumberOfEntries = OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT;
2264 }
2265 else
2266 {
2267 aNumberOfEntries = mMaxFrameRetriesDirect + 1;
2268 }
2269
2270 return mRetryHistogram.mTxDirectRetrySuccess;
2271 }
2272
2273 #if OPENTHREAD_FTD
GetIndirectRetrySuccessHistogram(uint8_t & aNumberOfEntries)2274 const uint32_t *Mac::GetIndirectRetrySuccessHistogram(uint8_t &aNumberOfEntries)
2275 {
2276 if (mMaxFrameRetriesIndirect >= OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT)
2277 {
2278 aNumberOfEntries = OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT;
2279 }
2280 else
2281 {
2282 aNumberOfEntries = mMaxFrameRetriesIndirect + 1;
2283 }
2284
2285 return mRetryHistogram.mTxIndirectRetrySuccess;
2286 }
2287 #endif
2288
ResetRetrySuccessHistogram()2289 void Mac::ResetRetrySuccessHistogram() { ClearAllBytes(mRetryHistogram); }
2290 #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
2291
ComputeLinkMargin(int8_t aRss) const2292 uint8_t Mac::ComputeLinkMargin(int8_t aRss) const { return ot::ComputeLinkMargin(GetNoiseFloor(), aRss); }
2293
2294 // LCOV_EXCL_START
2295
2296 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2297
OperationToString(Operation aOperation)2298 const char *Mac::OperationToString(Operation aOperation)
2299 {
2300 static const char *const kOperationStrings[] = {
2301 "Idle", // (0) kOperationIdle
2302 "ActiveScan", // (1) kOperationActiveScan
2303 "EnergyScan", // (2) kOperationEnergyScan
2304 "TransmitBeacon", // (3) kOperationTransmitBeacon
2305 "TransmitDataDirect", // (4) kOperationTransmitDataDirect
2306 "TransmitPoll", // (5) kOperationTransmitPoll
2307 "WaitingForData", // (6) kOperationWaitingForData
2308 #if OPENTHREAD_FTD
2309 "TransmitDataIndirect", // (7) kOperationTransmitDataIndirect
2310 #endif
2311 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2312 "TransmitDataCsl", // (8) kOperationTransmitDataCsl
2313 #endif
2314 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
2315 "TransmitWakeup", // kOperationTransmitWakeup
2316 #endif
2317 };
2318
2319 struct OperationChecker
2320 {
2321 InitEnumValidatorCounter();
2322
2323 ValidateNextEnum(kOperationIdle);
2324 ValidateNextEnum(kOperationActiveScan);
2325 ValidateNextEnum(kOperationEnergyScan);
2326 ValidateNextEnum(kOperationTransmitBeacon);
2327 ValidateNextEnum(kOperationTransmitDataDirect);
2328 ValidateNextEnum(kOperationTransmitPoll);
2329 ValidateNextEnum(kOperationWaitingForData);
2330 #if OPENTHREAD_FTD
2331 ValidateNextEnum(kOperationTransmitDataIndirect);
2332 #endif
2333 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2334 ValidateNextEnum(kOperationTransmitDataCsl);
2335 #endif
2336 };
2337
2338 return kOperationStrings[aOperation];
2339 }
2340
LogFrameRxFailure(const RxFrame * aFrame,Error aError) const2341 void Mac::LogFrameRxFailure(const RxFrame *aFrame, Error aError) const
2342 {
2343 LogLevel logLevel;
2344
2345 switch (aError)
2346 {
2347 case kErrorAbort:
2348 case kErrorNoFrameReceived:
2349 case kErrorAddressFiltered:
2350 case kErrorDestinationAddressFiltered:
2351 logLevel = kLogLevelDebg;
2352 break;
2353
2354 default:
2355 logLevel = kLogLevelInfo;
2356 break;
2357 }
2358
2359 if (aFrame == nullptr)
2360 {
2361 LogAt(logLevel, "Frame rx failed, error:%s", ErrorToString(aError));
2362 }
2363 else
2364 {
2365 LogAt(logLevel, "Frame rx failed, error:%s, %s", ErrorToString(aError), aFrame->ToInfoString().AsCString());
2366 }
2367 }
2368
LogFrameTxFailure(const TxFrame & aFrame,Error aError,uint8_t aRetryCount,bool aWillRetx) const2369 void Mac::LogFrameTxFailure(const TxFrame &aFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) const
2370 {
2371 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
2372 #if OPENTHREAD_CONFIG_MULTI_RADIO
2373 if (aFrame.GetRadioType() == kRadioTypeIeee802154)
2374 #endif
2375 {
2376 uint8_t maxAttempts = aFrame.GetMaxFrameRetries() + 1;
2377 uint8_t curAttempt = aWillRetx ? (aRetryCount + 1) : maxAttempts;
2378
2379 LogInfo("Frame tx attempt %u/%u failed, error:%s, %s", curAttempt, maxAttempts, ErrorToString(aError),
2380 aFrame.ToInfoString().AsCString());
2381 }
2382 #else
2383 OT_UNUSED_VARIABLE(aRetryCount);
2384 OT_UNUSED_VARIABLE(aWillRetx);
2385 #endif
2386
2387 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2388 #if OPENTHREAD_CONFIG_MULTI_RADIO
2389 if (aFrame.GetRadioType() == kRadioTypeTrel)
2390 #endif
2391 {
2392 if (Get<Trel::Interface>().IsEnabled())
2393 {
2394 LogInfo("Frame tx failed, error:%s, %s", ErrorToString(aError), aFrame.ToInfoString().AsCString());
2395 }
2396 }
2397 #endif
2398 }
2399
LogBeacon(const char * aActionText) const2400 void Mac::LogBeacon(const char *aActionText) const { LogInfo("%s Beacon", aActionText); }
2401
2402 #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2403
LogFrameRxFailure(const RxFrame *,Error) const2404 void Mac::LogFrameRxFailure(const RxFrame *, Error) const {}
2405
LogBeacon(const char *) const2406 void Mac::LogBeacon(const char *) const {}
2407
LogFrameTxFailure(const TxFrame &,Error,uint8_t,bool) const2408 void Mac::LogFrameTxFailure(const TxFrame &, Error, uint8_t, bool) const {}
2409
2410 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2411
2412 // LCOV_EXCL_STOP
2413
2414 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
GetTimeIeOffset(const Frame & aFrame)2415 uint8_t Mac::GetTimeIeOffset(const Frame &aFrame)
2416 {
2417 uint8_t offset = 0;
2418 const uint8_t *base = aFrame.GetPsdu();
2419 const uint8_t *cur = nullptr;
2420
2421 cur = reinterpret_cast<const uint8_t *>(aFrame.GetTimeIe());
2422 VerifyOrExit(cur != nullptr);
2423
2424 cur += sizeof(VendorIeHeader);
2425 offset = static_cast<uint8_t>(cur - base);
2426
2427 exit:
2428 return offset;
2429 }
2430 #endif
2431
2432 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
UpdateCsl(void)2433 void Mac::UpdateCsl(void)
2434 {
2435 uint16_t period = IsCslEnabled() ? GetCslPeriod() : 0;
2436 uint8_t channel = GetCslChannel() ? GetCslChannel() : mRadioChannel;
2437
2438 if (mLinks.UpdateCsl(period, channel, Get<Mle::Mle>().GetParent().GetRloc16(),
2439 &Get<Mle::Mle>().GetParent().GetExtAddress()))
2440 {
2441 if (Get<Mle::Mle>().IsChild())
2442 {
2443 Get<DataPollSender>().RecalculatePollPeriod();
2444
2445 if (period != 0)
2446 {
2447 Get<Mle::Mle>().ScheduleChildUpdateRequest();
2448 }
2449 }
2450
2451 UpdateIdleMode();
2452 }
2453 }
2454
SetCslChannel(uint8_t aChannel)2455 void Mac::SetCslChannel(uint8_t aChannel)
2456 {
2457 mCslChannel = aChannel;
2458 UpdateCsl();
2459 }
2460
SetCslPeriod(uint16_t aPeriod)2461 void Mac::SetCslPeriod(uint16_t aPeriod)
2462 {
2463 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2464 if (IsWakeupListenEnabled() && aPeriod != 0)
2465 {
2466 IgnoreError(SetWakeupListenEnabled(false));
2467 LogWarn("Disabling wake-up frame listening due to CSL period change");
2468 }
2469 #endif
2470
2471 mCslPeriod = aPeriod;
2472 UpdateCsl();
2473 }
2474
GetCslPeriodInMsec(void) const2475 uint32_t Mac::GetCslPeriodInMsec(void) const
2476 {
2477 return DivideAndRoundToClosest<uint32_t>(CslPeriodToUsec(GetCslPeriod()), 1000u);
2478 }
2479
CslPeriodToUsec(uint16_t aPeriodInTenSymbols)2480 uint32_t Mac::CslPeriodToUsec(uint16_t aPeriodInTenSymbols)
2481 {
2482 return static_cast<uint32_t>(aPeriodInTenSymbols) * kUsPerTenSymbols;
2483 }
2484
IsCslEnabled(void) const2485 bool Mac::IsCslEnabled(void) const { return !Get<Mle::Mle>().IsRxOnWhenIdle() && IsCslCapable(); }
2486
IsCslCapable(void) const2487 bool Mac::IsCslCapable(void) const { return (GetCslPeriod() > 0) && IsCslSupported(); }
2488
IsCslSupported(void) const2489 bool Mac::IsCslSupported(void) const
2490 {
2491 return Get<Mle::MleRouter>().IsChild() && Get<Mle::Mle>().GetParent().IsEnhancedKeepAliveSupported();
2492 }
2493 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2494
2495 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2496
ProcessCsl(const RxFrame & aFrame,const Address & aSrcAddr)2497 void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr)
2498 {
2499 CslNeighbor *neighbor = nullptr;
2500 const CslIe *csl;
2501
2502 VerifyOrExit(aFrame.IsVersion2015() && aFrame.GetSecurityEnabled());
2503
2504 csl = aFrame.GetCslIe();
2505 VerifyOrExit(csl != nullptr);
2506
2507 #if OPENTHREAD_FTD
2508 neighbor = Get<ChildTable>().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid);
2509 #else
2510 OT_UNUSED_VARIABLE(aSrcAddr);
2511 #endif
2512
2513 VerifyOrExit(neighbor != nullptr);
2514
2515 VerifyOrExit(csl->GetPeriod() >= kMinCslIePeriod);
2516
2517 neighbor->SetCslPeriod(csl->GetPeriod());
2518 neighbor->SetCslPhase(csl->GetPhase());
2519 neighbor->SetCslSynchronized(true);
2520 neighbor->SetCslLastHeard(TimerMilli::GetNow());
2521 neighbor->SetLastRxTimestamp(aFrame.GetTimestamp());
2522 LogDebg("Timestamp=%lu Sequence=%u CslPeriod=%u CslPhase=%u TransmitPhase=%u",
2523 ToUlong(static_cast<uint32_t>(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(),
2524 csl->GetPhase(), neighbor->GetCslPhase());
2525
2526 #if OPENTHREAD_FTD
2527 Get<CslTxScheduler>().Update();
2528 #endif
2529
2530 exit:
2531 return;
2532 }
2533 #endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2534
2535 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
ProcessEnhAckProbing(const RxFrame & aFrame,const Neighbor & aNeighbor)2536 void Mac::ProcessEnhAckProbing(const RxFrame &aFrame, const Neighbor &aNeighbor)
2537 {
2538 constexpr uint8_t kEnhAckProbingIeMaxLen = 2;
2539
2540 const HeaderIe *enhAckProbingIe =
2541 reinterpret_cast<const HeaderIe *>(aFrame.GetThreadIe(ThreadIe::kEnhAckProbingIe));
2542 const uint8_t *data =
2543 reinterpret_cast<const uint8_t *>(enhAckProbingIe) + sizeof(HeaderIe) + sizeof(VendorIeHeader);
2544 uint8_t dataLen = 0;
2545
2546 VerifyOrExit(enhAckProbingIe != nullptr);
2547
2548 dataLen = enhAckProbingIe->GetLength() - sizeof(VendorIeHeader);
2549 VerifyOrExit(dataLen <= kEnhAckProbingIeMaxLen);
2550
2551 Get<LinkMetrics::Initiator>().ProcessEnhAckIeData(data, dataLen, aNeighbor);
2552 exit:
2553 return;
2554 }
2555 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
2556
2557 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
SetRadioFilterEnabled(bool aFilterEnabled)2558 void Mac::SetRadioFilterEnabled(bool aFilterEnabled)
2559 {
2560 mLinks.GetSubMac().SetRadioFilterEnabled(aFilterEnabled);
2561 UpdateIdleMode();
2562 }
2563 #endif
2564
2565 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
SetWakeupChannel(uint8_t aChannel)2566 Error Mac::SetWakeupChannel(uint8_t aChannel)
2567 {
2568 Error error = kErrorNone;
2569
2570 if (aChannel == 0)
2571 {
2572 mWakeupChannel = GetPanChannel();
2573 ExitNow();
2574 }
2575
2576 VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs);
2577 mWakeupChannel = aChannel;
2578
2579 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2580 UpdateWakeupListening();
2581 #endif
2582
2583 exit:
2584 return error;
2585 }
2586 #endif
2587
2588 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
GetWakeupListenParameters(uint32_t & aInterval,uint32_t & aDuration) const2589 void Mac::GetWakeupListenParameters(uint32_t &aInterval, uint32_t &aDuration) const
2590 {
2591 aInterval = mWakeupListenInterval;
2592 aDuration = mWakeupListenDuration;
2593 }
2594
SetWakeupListenParameters(uint32_t aInterval,uint32_t aDuration)2595 Error Mac::SetWakeupListenParameters(uint32_t aInterval, uint32_t aDuration)
2596 {
2597 Error error = kErrorNone;
2598
2599 VerifyOrExit(aDuration >= kMinWakeupListenDuration, error = kErrorInvalidArgs);
2600 VerifyOrExit(aInterval > aDuration, error = kErrorInvalidArgs);
2601
2602 mWakeupListenInterval = aInterval;
2603 mWakeupListenDuration = aDuration;
2604 UpdateWakeupListening();
2605
2606 exit:
2607 return error;
2608 }
2609
SetWakeupListenEnabled(bool aEnable)2610 Error Mac::SetWakeupListenEnabled(bool aEnable)
2611 {
2612 Error error = kErrorNone;
2613
2614 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2615 if (aEnable && GetCslPeriod() > 0)
2616 {
2617 LogWarn("Cannot enable wake-up frame listening while CSL is enabled");
2618 ExitNow(error = kErrorInvalidState);
2619 }
2620 #endif
2621
2622 if (aEnable == mWakeupListenEnabled)
2623 {
2624 LogInfo("Listening for wake up frames was already %s", aEnable ? "started" : "stopped");
2625 ExitNow();
2626 }
2627
2628 mWakeupListenEnabled = aEnable;
2629 UpdateWakeupListening();
2630
2631 LogInfo("Listening for wake up frames %s: chan:%u, addr:%s", aEnable ? "started" : "stopped", mWakeupChannel,
2632 GetExtAddress().ToString().AsCString());
2633
2634 exit:
2635 return error;
2636 }
2637
UpdateWakeupListening(void)2638 void Mac::UpdateWakeupListening(void)
2639 {
2640 uint8_t channel = mWakeupChannel ? mWakeupChannel : mPanChannel;
2641
2642 mLinks.UpdateWakeupListening(mWakeupListenEnabled, mWakeupListenInterval, mWakeupListenDuration, channel);
2643 }
2644
HandleWakeupFrame(const RxFrame & aFrame)2645 Error Mac::HandleWakeupFrame(const RxFrame &aFrame)
2646 {
2647 Error error = kErrorNone;
2648 const ConnectionIe *connectionIe;
2649 uint32_t rvTimeUs;
2650 uint64_t rvTimestampUs;
2651 uint32_t attachDelayMs;
2652 uint64_t radioNowUs;
2653 uint8_t retryInterval;
2654 uint8_t retryCount;
2655
2656 VerifyOrExit(mWakeupListenEnabled && aFrame.IsWakeupFrame());
2657 connectionIe = aFrame.GetConnectionIe();
2658 retryInterval = connectionIe->GetRetryInterval();
2659 retryCount = connectionIe->GetRetryCount();
2660 VerifyOrExit(retryInterval > 0 && retryCount > 0, error = kErrorInvalidArgs);
2661
2662 radioNowUs = otPlatRadioGetNow(&GetInstance());
2663 rvTimeUs = aFrame.GetRendezvousTimeIe()->GetRendezvousTime() * kUsPerTenSymbols;
2664 rvTimestampUs = aFrame.GetTimestamp() + kRadioHeaderPhrDuration + aFrame.GetLength() * kOctetDuration + rvTimeUs;
2665
2666 if (rvTimestampUs > radioNowUs + kCslRequestAhead)
2667 {
2668 attachDelayMs = static_cast<uint32_t>(rvTimestampUs - radioNowUs - kCslRequestAhead);
2669 attachDelayMs = attachDelayMs / 1000;
2670 }
2671 else
2672 {
2673 attachDelayMs = 0;
2674 }
2675
2676 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2677 {
2678 uint32_t frameCounter;
2679
2680 IgnoreError(aFrame.GetFrameCounter(frameCounter));
2681 LogInfo("Received wake-up frame, fc:%lu, rendezvous:%luus, retries:%u/%u", ToUlong(frameCounter),
2682 ToUlong(rvTimeUs), retryCount, retryInterval);
2683 }
2684 #endif
2685
2686 // Stop receiving more wake up frames
2687 IgnoreError(SetWakeupListenEnabled(false));
2688
2689 // TODO: start MLE attach process with the WC
2690 OT_UNUSED_VARIABLE(attachDelayMs);
2691
2692 exit:
2693 return error;
2694 }
2695 #endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2696
CalculateRadioBusTransferTime(uint16_t aFrameSize) const2697 uint32_t Mac::CalculateRadioBusTransferTime(uint16_t aFrameSize) const
2698 {
2699 uint32_t busSpeed = Get<Radio>().GetBusSpeed();
2700 uint32_t trasnferTime = 0;
2701
2702 if (busSpeed != 0)
2703 {
2704 trasnferTime = DivideAndRoundUp<uint32_t>(aFrameSize * kBitsPerByte * Time::kOneSecondInUsec, busSpeed);
2705 }
2706
2707 trasnferTime += Get<Radio>().GetBusLatency();
2708
2709 return trasnferTime;
2710 }
2711
2712 } // namespace Mac
2713 } // namespace ot
2714