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