Coverage Report

Created: 2025-05-12 06:47

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