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