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