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