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