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