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