Coverage Report

Created: 2025-05-12 06:47

/src/openthread/src/core/thread/mesh_forwarder.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 mesh forwarding of IPv6/6LoWPAN messages.
32
 */
33
34
#include "mesh_forwarder.hpp"
35
36
#include "instance/instance.hpp"
37
#include "utils/static_counter.hpp"
38
39
namespace ot {
40
41
RegisterLogModule("MeshForwarder");
42
43
void MeshForwarder::Counters::UpdateOnTxDone(const Message &aMessage, bool aTxSuccess)
44
21.5k
{
45
21.5k
    if (aMessage.GetType() == Message::kTypeIp6)
46
21.5k
    {
47
21.5k
        aTxSuccess ? mTxSuccess++ : mTxFailure++;
48
21.5k
    }
49
21.5k
}
50
51
void MeshForwarder::Counters::UpdateOnRx(const Message &aMessage)
52
0
{
53
0
    if (aMessage.GetType() == Message::kTypeIp6)
54
0
    {
55
0
        mRxSuccess++;
56
0
    }
57
0
}
58
59
void MeshForwarder::Counters::UpdateOnDrop(const Message &aMessage)
60
0
{
61
0
    if (aMessage.GetType() == Message::kTypeIp6)
62
0
    {
63
0
        mRxFailure++;
64
0
    }
65
0
}
66
67
MeshForwarder::MeshForwarder(Instance &aInstance)
68
1.47k
    : InstanceLocator(aInstance)
69
1.47k
    , mMessageNextOffset(0)
70
1.47k
    , mSendMessage(nullptr)
71
    , mMeshSource()
72
    , mMeshDest()
73
1.47k
    , mAddMeshHeader(false)
74
1.47k
    , mEnabled(false)
75
1.47k
    , mTxPaused(false)
76
1.47k
    , mSendBusy(false)
77
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
78
1.47k
    , mDelayNextTx(false)
79
1.47k
    , mTxDelayTimer(aInstance)
80
#endif
81
1.47k
    , mScheduleTransmissionTask(aInstance)
82
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
83
1.47k
    , mIndirectSender(aInstance)
84
#endif
85
1.47k
    , mDataPollSender(aInstance)
86
1.47k
{
87
1.47k
    mFragTag = Random::NonCrypto::GetUint16();
88
89
1.47k
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
90
1.47k
    mTxQueueStats.Clear();
91
1.47k
#endif
92
1.47k
}
93
94
void MeshForwarder::Start(void)
95
1.47k
{
96
1.47k
    if (!mEnabled)
97
1.47k
    {
98
1.47k
        Get<Mac::Mac>().SetRxOnWhenIdle(true);
99
1.47k
#if OPENTHREAD_FTD
100
1.47k
        mIndirectSender.Start();
101
1.47k
#endif
102
103
1.47k
        mEnabled = true;
104
1.47k
    }
105
1.47k
}
106
107
void MeshForwarder::Stop(void)
108
1.47k
{
109
1.47k
    VerifyOrExit(mEnabled);
110
111
1.47k
    mDataPollSender.StopPolling();
112
1.47k
    Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMeshForwarder);
113
1.47k
    Get<Mle::DiscoverScanner>().Stop();
114
115
1.47k
    mSendQueue.DequeueAndFreeAll();
116
1.47k
    mReassemblyList.DequeueAndFreeAll();
117
118
1.47k
#if OPENTHREAD_FTD
119
1.47k
    mIndirectSender.Stop();
120
1.47k
    mFwdFrameInfoArray.Clear();
121
1.47k
#endif
122
123
1.47k
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
124
1.47k
    mTxDelayTimer.Stop();
125
1.47k
    mDelayNextTx = false;
126
1.47k
#endif
127
128
1.47k
    mEnabled     = false;
129
1.47k
    mSendMessage = nullptr;
130
1.47k
    Get<Mac::Mac>().SetRxOnWhenIdle(false);
131
132
1.47k
exit:
133
1.47k
    return;
134
1.47k
}
135
136
void MeshForwarder::PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aMacDest, bool aAckRequest)
137
0
{
138
0
    Mac::TxFrame::Info frameInfo;
139
140
0
    frameInfo.mAddrs.mSource.SetShort(Get<Mac::Mac>().GetShortAddress());
141
142
0
    if (frameInfo.mAddrs.mSource.IsShortAddrInvalid() || aMacDest.IsExtended())
143
0
    {
144
0
        frameInfo.mAddrs.mSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
145
0
    }
146
147
0
    frameInfo.mAddrs.mDestination = aMacDest;
148
0
    frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
149
150
0
    frameInfo.mType          = Mac::Frame::kTypeData;
151
0
    frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32;
152
0
    frameInfo.mKeyIdMode     = Mac::Frame::kKeyIdMode1;
153
154
0
    PrepareMacHeaders(aFrame, frameInfo, nullptr);
155
156
0
    aFrame.SetAckRequest(aAckRequest);
157
0
    aFrame.SetPayloadLength(0);
158
0
}
159
160
void MeshForwarder::ResumeMessageTransmissions(void)
161
0
{
162
0
    if (mTxPaused)
163
0
    {
164
0
        mTxPaused = false;
165
0
        mScheduleTransmissionTask.Post();
166
0
    }
167
0
}
168
169
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
170
void MeshForwarder::HandleTxDelayTimer(void)
171
0
{
172
0
    mDelayNextTx = false;
173
0
    mScheduleTransmissionTask.Post();
174
0
    LogDebg("Tx delay timer expired");
175
0
}
176
#endif
177
178
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
179
180
Error MeshForwarder::UpdateEcnOrDrop(Message &aMessage, bool aPreparingToSend)
181
22.1k
{
182
    // This method performs delay-aware active queue management for
183
    // direct message transmission. It parses the IPv6 header from
184
    // `aMessage` to determine if message is  ECN-capable. This is
185
    // then used along with the message's time-in-queue to decide
186
    // whether to keep the message as is, change the ECN field to
187
    // mark congestion, or drop the message. If the message is to be
188
    // dropped, this method clears the direct tx flag on `aMessage`
189
    // and removes it from the send queue (if no pending indirect tx)
190
    // and returns `kErrorDrop`. This method returns `kErrorNone`
191
    // when the message is kept as is or ECN field is updated.
192
193
22.1k
    Error    error         = kErrorNone;
194
22.1k
    uint32_t timeInQueue   = TimerMilli::GetNow() - aMessage.GetTimestamp();
195
22.1k
    bool     shouldMarkEcn = (timeInQueue >= kTimeInQueueMarkEcn);
196
22.1k
    bool     isEcnCapable  = false;
197
198
22.1k
    VerifyOrExit(aMessage.IsDirectTransmission() && (aMessage.GetOffset() == 0));
199
200
21.5k
    if (aMessage.GetType() == Message::kTypeIp6)
201
21.5k
    {
202
21.5k
        Ip6::Header ip6Header;
203
204
21.5k
        IgnoreError(aMessage.Read(0, ip6Header));
205
206
21.5k
        VerifyOrExit(!Get<ThreadNetif>().HasUnicastAddress(ip6Header.GetSource()));
207
208
882
        isEcnCapable = (ip6Header.GetEcn() != Ip6::kEcnNotCapable);
209
210
882
        if ((shouldMarkEcn && !isEcnCapable) || (timeInQueue >= kTimeInQueueDropMsg))
211
0
        {
212
0
            ExitNow(error = kErrorDrop);
213
0
        }
214
215
882
        if (shouldMarkEcn)
216
0
        {
217
0
            switch (ip6Header.GetEcn())
218
0
            {
219
0
            case Ip6::kEcnCapable0:
220
0
            case Ip6::kEcnCapable1:
221
0
                ip6Header.SetEcn(Ip6::kEcnMarked);
222
0
                aMessage.Write(0, ip6Header);
223
0
                LogMessage(kMessageMarkEcn, aMessage);
224
0
                break;
225
226
0
            case Ip6::kEcnMarked:
227
0
            case Ip6::kEcnNotCapable:
228
0
                break;
229
0
            }
230
0
        }
231
882
    }
232
0
#if OPENTHREAD_FTD
233
0
    else if (aMessage.GetType() == Message::kType6lowpan)
234
0
    {
235
0
        uint16_t               headerLength = 0;
236
0
        uint16_t               offset;
237
0
        bool                   hasFragmentHeader = false;
238
0
        Lowpan::FragmentHeader fragmentHeader;
239
0
        Lowpan::MeshHeader     meshHeader;
240
241
0
        IgnoreError(meshHeader.ParseFrom(aMessage, headerLength));
242
243
0
        offset = headerLength;
244
245
0
        if (fragmentHeader.ParseFrom(aMessage, offset, headerLength) == kErrorNone)
246
0
        {
247
0
            hasFragmentHeader = true;
248
0
            offset += headerLength;
249
0
        }
250
251
0
        if (!hasFragmentHeader || (fragmentHeader.GetDatagramOffset() == 0))
252
0
        {
253
0
            Ip6::Ecn ecn = Get<Lowpan::Lowpan>().DecompressEcn(aMessage, offset);
254
255
0
            isEcnCapable = (ecn != Ip6::kEcnNotCapable);
256
257
0
            if ((shouldMarkEcn && !isEcnCapable) || (timeInQueue >= kTimeInQueueDropMsg))
258
0
            {
259
0
                FwdFrameInfo *entry = FindFwdFrameInfoEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag());
260
261
0
                if (entry != nullptr)
262
0
                {
263
0
                    entry->MarkToDrop();
264
0
                    entry->ResetLifetime();
265
0
                }
266
267
0
                ExitNow(error = kErrorDrop);
268
0
            }
269
270
0
            if (shouldMarkEcn)
271
0
            {
272
0
                switch (ecn)
273
0
                {
274
0
                case Ip6::kEcnCapable0:
275
0
                case Ip6::kEcnCapable1:
276
0
                    Get<Lowpan::Lowpan>().MarkCompressedEcn(aMessage, offset);
277
0
                    LogMessage(kMessageMarkEcn, aMessage);
278
0
                    break;
279
280
0
                case Ip6::kEcnMarked:
281
0
                case Ip6::kEcnNotCapable:
282
0
                    break;
283
0
                }
284
0
            }
285
0
        }
286
0
        else if (hasFragmentHeader)
287
0
        {
288
0
            FwdFrameInfo *entry = FindFwdFrameInfoEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag());
289
290
0
            VerifyOrExit(entry != nullptr);
291
292
0
            if (entry->ShouldDrop())
293
0
            {
294
0
                error = kErrorDrop;
295
0
            }
296
297
            // We can remove the entry if it is the last fragment and
298
            // only if the message is being prepared to be sent out.
299
0
            if (aPreparingToSend && (fragmentHeader.GetDatagramOffset() + aMessage.GetLength() - offset >=
300
0
                                     fragmentHeader.GetDatagramSize()))
301
0
            {
302
0
                mFwdFrameInfoArray.Remove(*entry);
303
0
            }
304
0
        }
305
0
    }
306
#else
307
    OT_UNUSED_VARIABLE(aPreparingToSend);
308
#endif // OPENTHREAD_FTD
309
310
22.1k
exit:
311
22.1k
    if (error == kErrorDrop)
312
0
    {
313
0
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
314
0
        mTxQueueStats.UpdateFor(aMessage);
315
0
#endif
316
0
        LogMessage(kMessageQueueMgmtDrop, aMessage);
317
0
        FinalizeMessageDirectTx(aMessage, kErrorDrop);
318
0
        RemoveMessageIfNoPendingTx(aMessage);
319
0
    }
320
321
22.1k
    return error;
322
21.5k
}
323
324
Error MeshForwarder::RemoveAgedMessages(void)
325
10
{
326
    // This method goes through all messages in the send queue and
327
    // removes all aged messages determined based on the delay-aware
328
    // active queue management rules. It may also mark ECN on some
329
    // messages. It returns `kErrorNone` if at least one message was
330
    // removed, or `kErrorNotFound` if none was removed.
331
332
10
    Error    error = kErrorNotFound;
333
10
    Message *nextMessage;
334
335
10
    for (Message *message = mSendQueue.GetHead(); message != nullptr; message = nextMessage)
336
0
    {
337
0
        nextMessage = message->GetNext();
338
339
        // Exclude the current message being sent `mSendMessage`.
340
0
        if ((message == mSendMessage) || !message->IsDirectTransmission() || message->GetDoNotEvict())
341
0
        {
342
0
            continue;
343
0
        }
344
345
0
        if (UpdateEcnOrDrop(*message, /* aPreparingToSend */ false) == kErrorDrop)
346
0
        {
347
0
            error = kErrorNone;
348
0
        }
349
0
    }
350
351
10
    return error;
352
10
}
353
354
#endif // OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
355
356
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
357
358
bool MeshForwarder::IsDirectTxQueueOverMaxFrameThreshold(void) const
359
21.5k
{
360
21.5k
    uint16_t frameCount = 0;
361
362
21.5k
    for (const Message &message : mSendQueue)
363
22.4k
    {
364
22.4k
        if (!message.IsDirectTransmission() || (&message == mSendMessage))
365
735
        {
366
735
            continue;
367
735
        }
368
369
21.6k
        switch (message.GetType())
370
21.6k
        {
371
21.6k
        case Message::kTypeIp6:
372
21.6k
        {
373
            // If it is an IPv6 message, we estimate the number of
374
            // fragment frames assuming typical header sizes and lowpan
375
            // compression. Since this estimate is only used for queue
376
            // management, we lean towards an under estimate in sense
377
            // that we may allow few more frames in the tx queue over
378
            // threshold in some rare cases.
379
            //
380
            // The constants below are derived as follows: Typical MAC
381
            // header (15 bytes) and MAC footer (6 bytes) leave 106
382
            // bytes for MAC payload. Next fragment header is 5 bytes
383
            // leaving 96 for next fragment payload. Lowpan compression
384
            // on average compresses 40 bytes IPv6 header into about 19
385
            // bytes leaving 87 bytes for the IPv6 payload, so the first
386
            // fragment can fit 87 + 40 = 127 bytes.
387
388
21.6k
            static constexpr uint16_t kFirstFragmentMaxLength = 127;
389
21.6k
            static constexpr uint16_t kNextFragmentSize       = 96;
390
391
21.6k
            uint16_t length = message.GetLength();
392
393
21.6k
            frameCount++;
394
395
21.6k
            if (length > kFirstFragmentMaxLength)
396
363
            {
397
363
                frameCount += (length - kFirstFragmentMaxLength) / kNextFragmentSize;
398
363
            }
399
400
21.6k
            break;
401
0
        }
402
403
0
        case Message::kType6lowpan:
404
0
        case Message::kTypeMacEmptyData:
405
0
            frameCount++;
406
0
            break;
407
408
0
        case Message::kTypeSupervision:
409
0
        default:
410
0
            break;
411
21.6k
        }
412
21.6k
    }
413
414
21.5k
    return (frameCount > OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE);
415
21.5k
}
416
417
void MeshForwarder::ApplyDirectTxQueueLimit(Message &aMessage)
418
21.5k
{
419
21.5k
    VerifyOrExit(aMessage.IsDirectTransmission());
420
21.5k
    VerifyOrExit(IsDirectTxQueueOverMaxFrameThreshold());
421
422
0
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
423
0
    {
424
0
        bool  originalEvictFlag = aMessage.GetDoNotEvict();
425
0
        Error error;
426
427
        // We mark the "do not evict" flag on the new `aMessage` so
428
        // that it will not be removed from `RemoveAgedMessages()`.
429
        // This protects against the unlikely case where the newly
430
        // queued `aMessage` may already be aged due to execution
431
        // being interrupted for a long time between the queuing of
432
        // the message and the `ApplyDirectTxQueueLimit()` call. We
433
        // do not want the message to be potentially removed and
434
        // freed twice.
435
436
0
        aMessage.SetDoNotEvict(true);
437
0
        error = RemoveAgedMessages();
438
0
        aMessage.SetDoNotEvict(originalEvictFlag);
439
440
0
        if (error == kErrorNone)
441
0
        {
442
0
            VerifyOrExit(IsDirectTxQueueOverMaxFrameThreshold());
443
0
        }
444
0
    }
445
0
#endif
446
447
0
    LogMessage(kMessageFullQueueDrop, aMessage);
448
0
    FinalizeMessageDirectTx(aMessage, kErrorDrop);
449
0
    RemoveMessageIfNoPendingTx(aMessage);
450
451
21.5k
exit:
452
21.5k
    return;
453
0
}
454
455
#endif // (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
456
457
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
458
const uint32_t *MeshForwarder::TxQueueStats::GetHistogram(uint16_t &aNumBins, uint32_t &aBinInterval) const
459
0
{
460
0
    aNumBins     = kNumHistBins;
461
0
    aBinInterval = kHistBinInterval;
462
0
    return mHistogram;
463
0
}
464
465
void MeshForwarder::TxQueueStats::UpdateFor(const Message &aMessage)
466
22.1k
{
467
22.1k
    uint32_t timeInQueue = TimerMilli::GetNow() - aMessage.GetTimestamp();
468
469
22.1k
    mHistogram[Min<uint32_t>(timeInQueue / kHistBinInterval, kNumHistBins - 1)]++;
470
22.1k
    mMaxInterval = Max(mMaxInterval, timeInQueue);
471
22.1k
}
472
#endif
473
474
void MeshForwarder::ScheduleTransmissionTask(void)
475
42.5k
{
476
42.5k
    VerifyOrExit(!mSendBusy && !mTxPaused);
477
478
41.7k
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
479
41.7k
    VerifyOrExit(!mDelayNextTx);
480
41.7k
#endif
481
482
41.7k
    mSendMessage = PrepareNextDirectTransmission();
483
41.7k
    VerifyOrExit(mSendMessage != nullptr);
484
485
21.9k
    if (mSendMessage->GetOffset() == 0)
486
21.3k
    {
487
21.3k
        mSendMessage->SetTxSuccess(true);
488
21.3k
    }
489
490
21.9k
    Get<Mac::Mac>().RequestDirectFrameTransmission();
491
492
42.5k
exit:
493
42.5k
    return;
494
21.9k
}
495
496
Message *MeshForwarder::PrepareNextDirectTransmission(void)
497
41.7k
{
498
41.7k
    Message *curMessage, *nextMessage;
499
41.7k
    Error    error = kErrorNone;
500
501
41.9k
    for (curMessage = mSendQueue.GetHead(); curMessage; curMessage = nextMessage)
502
22.1k
    {
503
        // We set the `nextMessage` here but it can be updated again
504
        // after the `switch(message.GetType())` since it may be
505
        // evicted during message processing (e.g., from the call to
506
        // `UpdateIp6Route()` due to Address Solicit).
507
508
22.1k
        nextMessage = curMessage->GetNext();
509
510
22.1k
        if (!curMessage->IsDirectTransmission() || curMessage->IsResolvingAddress())
511
0
        {
512
0
            continue;
513
0
        }
514
515
22.1k
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
516
22.1k
        if (UpdateEcnOrDrop(*curMessage, /* aPreparingToSend */ true) == kErrorDrop)
517
0
        {
518
0
            continue;
519
0
        }
520
22.1k
#endif
521
22.1k
        curMessage->SetDoNotEvict(true);
522
523
22.1k
        switch (curMessage->GetType())
524
22.1k
        {
525
22.1k
        case Message::kTypeIp6:
526
22.1k
            error = UpdateIp6Route(*curMessage);
527
22.1k
            break;
528
529
0
#if OPENTHREAD_FTD
530
531
0
        case Message::kType6lowpan:
532
0
            error = UpdateMeshRoute(*curMessage);
533
0
            break;
534
535
0
#endif
536
537
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
538
        case Message::kTypeMacEmptyData:
539
            error = kErrorNone;
540
            break;
541
#endif
542
543
0
        default:
544
0
            error = kErrorDrop;
545
0
            break;
546
22.1k
        }
547
548
22.1k
        curMessage->SetDoNotEvict(false);
549
550
        // the next message may have been evicted during processing (e.g. due to Address Solicit)
551
22.1k
        nextMessage = curMessage->GetNext();
552
553
22.1k
        switch (error)
554
22.1k
        {
555
21.9k
        case kErrorNone:
556
21.9k
            if (!curMessage->IsDirectTransmission())
557
0
            {
558
                // Skip if message is no longer marked for direct transmission.
559
                // For example, `UpdateIp6Route()` may determine the destination
560
                // is an ALOC associated with an SED child of this device and
561
                // mark it for indirect tx to the SED child.
562
0
                continue;
563
0
            }
564
565
21.9k
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
566
21.9k
            mTxQueueStats.UpdateFor(*curMessage);
567
21.9k
#endif
568
21.9k
            ExitNow();
569
570
0
#if OPENTHREAD_FTD
571
0
        case kErrorAddressQuery:
572
0
            curMessage->SetResolvingAddress(true);
573
0
            continue;
574
0
#endif
575
576
174
        default:
577
174
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
578
174
            mTxQueueStats.UpdateFor(*curMessage);
579
174
#endif
580
174
            LogMessage(kMessageDrop, *curMessage, error);
581
174
            FinalizeMessageDirectTx(*curMessage, error);
582
174
            RemoveMessageIfNoPendingTx(*curMessage);
583
174
            continue;
584
22.1k
        }
585
22.1k
    }
586
587
41.7k
exit:
588
41.7k
    return curMessage;
589
41.7k
}
590
591
Error MeshForwarder::UpdateIp6Route(Message &aMessage)
592
22.1k
{
593
22.1k
    Mle::Mle   &mle   = Get<Mle::Mle>();
594
22.1k
    Error       error = kErrorNone;
595
22.1k
    Ip6::Header ip6Header;
596
597
22.1k
    mAddMeshHeader = false;
598
599
22.1k
    IgnoreError(aMessage.Read(0, ip6Header));
600
601
22.1k
    VerifyOrExit(!ip6Header.GetSource().IsMulticast(), error = kErrorDrop);
602
603
22.1k
    GetMacSourceAddress(ip6Header.GetSource(), mMacAddrs.mSource);
604
605
22.1k
    if (mle.IsDisabled() || mle.IsDetached())
606
22.1k
    {
607
22.1k
        if (ip6Header.GetDestination().IsLinkLocalMulticast())
608
21.4k
        {
609
21.4k
            mMacAddrs.mDestination.SetShort(Mac::kShortAddrBroadcast);
610
21.4k
        }
611
746
        else if (ip6Header.GetDestination().IsLinkLocalUnicast())
612
572
        {
613
572
            mMacAddrs.mDestination.SetExtendedFromIid(ip6Header.GetDestination().GetIid());
614
572
        }
615
174
        else
616
174
        {
617
174
            error = kErrorDrop;
618
174
        }
619
620
22.1k
        ExitNow();
621
22.1k
    }
622
623
0
    if (ip6Header.GetDestination().IsMulticast())
624
0
    {
625
        // With the exception of MLE multicasts and any other message
626
        // with link security disabled, an End Device transmits
627
        // multicasts, as IEEE 802.15.4 unicasts to its parent.
628
629
0
        if (mle.IsChild() && aMessage.IsLinkSecurityEnabled() && !aMessage.IsSubTypeMle())
630
0
        {
631
0
            mMacAddrs.mDestination.SetShort(mle.GetParentRloc16());
632
0
        }
633
0
        else
634
0
        {
635
0
            mMacAddrs.mDestination.SetShort(Mac::kShortAddrBroadcast);
636
0
        }
637
0
    }
638
0
    else if (ip6Header.GetDestination().IsLinkLocalUnicast())
639
0
    {
640
0
        mMacAddrs.mDestination.SetExtendedFromIid(ip6Header.GetDestination().GetIid());
641
0
    }
642
0
    else if (mle.IsMinimalEndDevice())
643
0
    {
644
0
        mMacAddrs.mDestination.SetShort(mle.GetParentRloc16());
645
0
    }
646
0
    else
647
0
    {
648
0
#if OPENTHREAD_FTD
649
0
        error = UpdateIp6RouteFtd(ip6Header, aMessage);
650
#else
651
        OT_ASSERT(false);
652
#endif
653
0
    }
654
655
22.1k
exit:
656
22.1k
    return error;
657
0
}
658
659
0
bool MeshForwarder::GetRxOnWhenIdle(void) const { return Get<Mac::Mac>().GetRxOnWhenIdle(); }
660
661
void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle)
662
18.6k
{
663
18.6k
    Get<Mac::Mac>().SetRxOnWhenIdle(aRxOnWhenIdle);
664
665
18.6k
    if (aRxOnWhenIdle)
666
18.6k
    {
667
18.6k
        mDataPollSender.StopPolling();
668
18.6k
        Get<SupervisionListener>().Stop();
669
18.6k
    }
670
0
    else
671
0
    {
672
0
        mDataPollSender.StartPolling();
673
0
        Get<SupervisionListener>().Start();
674
0
    }
675
18.6k
}
676
677
void MeshForwarder::GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr)
678
22.1k
{
679
22.1k
    aMacAddr.SetExtendedFromIid(aIp6Addr.GetIid());
680
681
22.1k
    if (aMacAddr.GetExtended() != Get<Mac::Mac>().GetExtAddress())
682
1.56k
    {
683
1.56k
        aMacAddr.SetShort(Get<Mac::Mac>().GetShortAddress());
684
1.56k
    }
685
22.1k
}
686
687
Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames)
688
21.9k
{
689
21.9k
    Mac::TxFrame *frame         = nullptr;
690
21.9k
    bool          addFragHeader = false;
691
692
21.9k
    VerifyOrExit(mEnabled && (mSendMessage != nullptr));
693
694
#if OPENTHREAD_CONFIG_MULTI_RADIO
695
    frame = &Get<RadioSelector>().SelectRadio(*mSendMessage, mMacAddrs.mDestination, aTxFrames);
696
697
    // If multi-radio link is supported, when sending frame with link
698
    // security enabled, Fragment Header is always included (even if
699
    // the message is small and does not require 6LoWPAN fragmentation).
700
    // This allows the Fragment Header's tag to be used to detect and
701
    // suppress duplicate received frames over different radio links.
702
703
    if (mSendMessage->IsLinkSecurityEnabled())
704
    {
705
        addFragHeader = true;
706
    }
707
#else
708
21.9k
    frame = &aTxFrames.GetTxFrame();
709
21.9k
#endif
710
711
21.9k
    mSendBusy = true;
712
713
21.9k
    switch (mSendMessage->GetType())
714
21.9k
    {
715
21.9k
    case Message::kTypeIp6:
716
21.9k
        if (mSendMessage->IsMleCommand(Mle::kCommandDiscoveryRequest))
717
0
        {
718
0
            frame = Get<Mle::DiscoverScanner>().PrepareDiscoveryRequestFrame(*frame);
719
0
            VerifyOrExit(frame != nullptr);
720
0
        }
721
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
722
        else if (Get<Mac::Mac>().IsCslEnabled() && mSendMessage->IsSubTypeMle())
723
        {
724
            mSendMessage->SetLinkSecurityEnabled(true);
725
        }
726
#endif
727
21.9k
        mMessageNextOffset =
728
21.9k
            PrepareDataFrame(*frame, *mSendMessage, mMacAddrs, mAddMeshHeader, mMeshSource, mMeshDest, addFragHeader);
729
730
21.9k
        if (mSendMessage->IsMleCommand(Mle::kCommandChildIdRequest) && mSendMessage->IsLinkSecurityEnabled())
731
0
        {
732
0
            LogNote("Child ID Request requires fragmentation, aborting tx");
733
0
            mMessageNextOffset = mSendMessage->GetLength();
734
0
            ExitNow(frame = nullptr);
735
0
        }
736
21.9k
        break;
737
738
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
739
    case Message::kTypeMacEmptyData:
740
    {
741
        Mac::Address macDestAddr;
742
743
        macDestAddr.SetShort(Get<Mle::Mle>().GetParent().GetRloc16());
744
        PrepareEmptyFrame(*frame, macDestAddr, /* aAckRequest */ true);
745
    }
746
    break;
747
#endif
748
749
21.9k
#if OPENTHREAD_FTD
750
751
21.9k
    case Message::kType6lowpan:
752
0
        SendMesh(*mSendMessage, *frame);
753
0
        break;
754
755
0
    case Message::kTypeSupervision:
756
        // A direct supervision message is possible in the case where
757
        // a sleepy child switches its mode (becomes non-sleepy) while
758
        // there is a pending indirect supervision message in the send
759
        // queue for it. The message would be then converted to a
760
        // direct tx.
761
762
0
        OT_FALL_THROUGH;
763
0
#endif
764
765
0
    default:
766
0
        mMessageNextOffset = mSendMessage->GetLength();
767
0
        ExitNow(frame = nullptr);
768
21.9k
    }
769
770
21.9k
    frame->SetIsARetransmission(false);
771
772
21.9k
exit:
773
21.9k
    return frame;
774
21.9k
}
775
776
void MeshForwarder::PrepareMacHeaders(Mac::TxFrame &aTxFrame, Mac::TxFrame::Info &aTxFrameInfo, const Message *aMessage)
777
21.9k
{
778
21.9k
    const Neighbor *neighbor;
779
780
21.9k
    aTxFrameInfo.mVersion = Mac::Frame::kVersion2006;
781
782
21.9k
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
783
784
21.9k
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || \
785
21.9k
    OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
786
787
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
788
    // Determine frame version and Header IE entries
789
790
21.9k
    neighbor = Get<NeighborTable>().FindNeighbor(aTxFrameInfo.mAddrs.mDestination);
791
792
21.9k
    if (neighbor == nullptr)
793
21.9k
    {
794
21.9k
    }
795
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
796
    else if (Get<Mac::Mac>().IsCslEnabled())
797
    {
798
        aTxFrameInfo.mAppendCslIe = true;
799
        aTxFrameInfo.mVersion     = Mac::Frame::kVersion2015;
800
    }
801
#endif
802
0
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
803
0
    else if ((Get<ChildTable>().Contains(*neighbor) && static_cast<const Child *>(neighbor)->IsCslSynchronized()))
804
0
    {
805
0
        aTxFrameInfo.mVersion = Mac::Frame::kVersion2015;
806
0
    }
807
21.9k
#endif
808
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
809
    else if (neighbor->IsEnhAckProbingActive())
810
    {
811
        aTxFrameInfo.mVersion = Mac::Frame::kVersion2015;
812
    }
813
#endif
814
815
21.9k
#endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
816
       // || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
817
818
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
819
    if ((aMessage != nullptr) && aMessage->IsTimeSync())
820
    {
821
        aTxFrameInfo.mAppendTimeIe = true;
822
        aTxFrameInfo.mVersion      = Mac::Frame::kVersion2015;
823
    }
824
#endif
825
826
21.9k
    aTxFrameInfo.mEmptyPayload = (aMessage == nullptr) || (aMessage->GetLength() == 0);
827
828
21.9k
#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
829
830
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
831
    // Prepare MAC headers
832
833
21.9k
    aTxFrameInfo.PrepareHeadersIn(aTxFrame);
834
835
21.9k
    OT_UNUSED_VARIABLE(aMessage);
836
21.9k
    OT_UNUSED_VARIABLE(neighbor);
837
21.9k
}
838
839
// This method constructs a MAC data from from a given IPv6 message.
840
//
841
// This method handles generation of MAC header, mesh header (if
842
// requested), lowpan compression of IPv6 header, lowpan fragmentation
843
// header (if message requires fragmentation or if it is explicitly
844
// requested by setting `aAddFragHeader` to `true`) It uses the
845
// message offset to construct next fragments. This method enables
846
// link security when message is MLE type and requires fragmentation.
847
// It returns the next offset into the message after the prepared
848
// frame.
849
//
850
uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame         &aFrame,
851
                                         Message              &aMessage,
852
                                         const Mac::Addresses &aMacAddrs,
853
                                         bool                  aAddMeshHeader,
854
                                         uint16_t              aMeshSource,
855
                                         uint16_t              aMeshDest,
856
                                         bool                  aAddFragHeader)
857
21.9k
{
858
21.9k
    Mac::TxFrame::Info frameInfo;
859
21.9k
    uint16_t           payloadLength;
860
21.9k
    uint16_t           origMsgOffset;
861
21.9k
    uint16_t           nextOffset;
862
21.9k
    FrameBuilder       frameBuilder;
863
864
21.9k
start:
865
21.9k
    frameInfo.Clear();
866
867
21.9k
    if (aMessage.IsLinkSecurityEnabled())
868
953
    {
869
953
        frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32;
870
871
953
        if (aMessage.GetSubType() == Message::kSubTypeJoinerEntrust)
872
0
        {
873
0
            frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode0;
874
0
        }
875
953
        else if (aMessage.IsMleCommand(Mle::kCommandAnnounce))
876
0
        {
877
0
            frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode2;
878
0
        }
879
953
        else
880
953
        {
881
953
            frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1;
882
953
        }
883
953
    }
884
885
21.9k
    frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
886
887
21.9k
    if (aMessage.IsSubTypeMle())
888
20.5k
    {
889
20.5k
        switch (aMessage.GetMleCommand())
890
20.5k
        {
891
0
        case Mle::kCommandAnnounce:
892
0
            aFrame.SetChannel(aMessage.GetChannel());
893
0
            aFrame.SetRxChannelAfterTxDone(Get<Mac::Mac>().GetPanChannel());
894
0
            frameInfo.mPanIds.SetDestination(Mac::kPanIdBroadcast);
895
0
            break;
896
897
0
        case Mle::kCommandDiscoveryRequest:
898
0
        case Mle::kCommandDiscoveryResponse:
899
0
            frameInfo.mPanIds.SetDestination(aMessage.GetPanId());
900
0
            break;
901
902
20.5k
        default:
903
20.5k
            break;
904
20.5k
        }
905
20.5k
    }
906
907
21.9k
    frameInfo.mType  = Mac::Frame::kTypeData;
908
21.9k
    frameInfo.mAddrs = aMacAddrs;
909
910
21.9k
    PrepareMacHeaders(aFrame, frameInfo, &aMessage);
911
912
21.9k
    frameBuilder.Init(aFrame.GetPayload(), aFrame.GetMaxPayloadLength());
913
914
21.9k
#if OPENTHREAD_FTD
915
916
    // Initialize Mesh header
917
21.9k
    if (aAddMeshHeader)
918
0
    {
919
0
        Lowpan::MeshHeader meshHeader;
920
0
        uint16_t           maxPayloadLength;
921
922
        // Mesh Header frames are forwarded by routers over multiple
923
        // hops to reach a final destination. The forwarding path can
924
        // have routers supporting different radio links with varying
925
        // MTU sizes. Since the originator of the frame does not know the
926
        // path and the MTU sizes of supported radio links by the routers
927
        // in the path, we limit the max payload length of a Mesh Header
928
        // frame to a fixed minimum value (derived from 15.4 radio)
929
        // ensuring it can be handled by any radio link.
930
        //
931
        // Maximum payload length is calculated by subtracting the frame
932
        // header and footer lengths from the MTU size. The footer
933
        // length is derived by removing the `aFrame.GetFcsSize()` and
934
        // then adding the fixed `kMeshHeaderFrameFcsSize` instead
935
        // (updating the FCS size in the calculation of footer length).
936
937
0
        maxPayloadLength = kMeshHeaderFrameMtu - aFrame.GetHeaderLength() -
938
0
                           (aFrame.GetFooterLength() - aFrame.GetFcsSize() + kMeshHeaderFrameFcsSize);
939
940
0
        frameBuilder.Init(aFrame.GetPayload(), maxPayloadLength);
941
942
0
        meshHeader.Init(aMeshSource, aMeshDest, kMeshHeaderHopsLeft);
943
944
0
        IgnoreError(meshHeader.AppendTo(frameBuilder));
945
0
    }
946
947
21.9k
#endif // OPENTHREAD_FTD
948
949
    // While performing lowpan compression, the message offset may be
950
    // changed to skip over the compressed IPv6 headers, we save the
951
    // original offset and set it back on `aMessage` at the end
952
    // before returning.
953
954
21.9k
    origMsgOffset = aMessage.GetOffset();
955
956
    // Compress IPv6 Header
957
21.9k
    if (aMessage.GetOffset() == 0)
958
21.3k
    {
959
21.3k
        uint16_t       fragHeaderOffset;
960
21.3k
        uint16_t       maxFrameLength;
961
21.3k
        Mac::Addresses macAddrs;
962
963
        // Before performing lowpan header compression, we reduce the
964
        // max length on `frameBuilder` to reserve bytes for first
965
        // fragment header. This ensures that lowpan compression will
966
        // leave room for a first fragment header. After the lowpan
967
        // header compression is done, we reclaim the reserved bytes
968
        // by setting the max length back to its original value.
969
970
21.3k
        fragHeaderOffset = frameBuilder.GetLength();
971
21.3k
        maxFrameLength   = frameBuilder.GetMaxLength();
972
21.3k
        frameBuilder.SetMaxLength(maxFrameLength - sizeof(Lowpan::FragmentHeader::FirstFrag));
973
974
21.3k
        if (aAddMeshHeader)
975
0
        {
976
0
            macAddrs.mSource.SetShort(aMeshSource);
977
0
            macAddrs.mDestination.SetShort(aMeshDest);
978
0
        }
979
21.3k
        else
980
21.3k
        {
981
21.3k
            macAddrs = aMacAddrs;
982
21.3k
        }
983
984
21.3k
        SuccessOrAssert(Get<Lowpan::Lowpan>().Compress(aMessage, macAddrs, frameBuilder));
985
986
21.3k
        frameBuilder.SetMaxLength(maxFrameLength);
987
988
21.3k
        payloadLength = aMessage.GetLength() - aMessage.GetOffset();
989
990
21.3k
        if (aAddFragHeader || (payloadLength > frameBuilder.GetRemainingLength()))
991
272
        {
992
272
            Lowpan::FragmentHeader::FirstFrag firstFragHeader;
993
994
272
            if ((!aMessage.IsLinkSecurityEnabled()) && aMessage.IsSubTypeMle())
995
0
            {
996
                // MLE messages that require fragmentation MUST use
997
                // link-layer security. We enable security and try
998
                // constructing the frame again.
999
1000
0
                aMessage.SetOffset(0);
1001
0
                aMessage.SetLinkSecurityEnabled(true);
1002
0
                goto start;
1003
0
            }
1004
1005
            // Insert Fragment header
1006
272
            if (aMessage.GetDatagramTag() == 0)
1007
272
            {
1008
                // Avoid using datagram tag value 0, which indicates the tag has not been set
1009
272
                if (mFragTag == 0)
1010
0
                {
1011
0
                    mFragTag++;
1012
0
                }
1013
1014
272
                aMessage.SetDatagramTag(mFragTag++);
1015
272
            }
1016
1017
272
            firstFragHeader.Init(aMessage.GetLength(), static_cast<uint16_t>(aMessage.GetDatagramTag()));
1018
272
            SuccessOrAssert(frameBuilder.Insert(fragHeaderOffset, firstFragHeader));
1019
272
        }
1020
21.3k
    }
1021
622
    else
1022
622
    {
1023
622
        Lowpan::FragmentHeader::NextFrag nextFragHeader;
1024
1025
622
        nextFragHeader.Init(aMessage.GetLength(), static_cast<uint16_t>(aMessage.GetDatagramTag()),
1026
622
                            aMessage.GetOffset());
1027
622
        SuccessOrAssert(frameBuilder.Append(nextFragHeader));
1028
1029
622
        payloadLength = aMessage.GetLength() - aMessage.GetOffset();
1030
622
    }
1031
1032
21.9k
    if (payloadLength > frameBuilder.GetRemainingLength())
1033
649
    {
1034
649
        payloadLength = (frameBuilder.GetRemainingLength() & ~0x7);
1035
649
    }
1036
1037
    // Copy IPv6 Payload
1038
21.9k
    SuccessOrAssert(frameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), payloadLength));
1039
21.9k
    aFrame.SetPayloadLength(frameBuilder.GetLength());
1040
1041
21.9k
    nextOffset = aMessage.GetOffset() + payloadLength;
1042
1043
21.9k
    if (nextOffset < aMessage.GetLength())
1044
649
    {
1045
649
        aFrame.SetFramePending(true);
1046
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1047
        aMessage.SetTimeSync(false);
1048
#endif
1049
649
    }
1050
1051
21.9k
    aMessage.SetOffset(origMsgOffset);
1052
1053
21.9k
    return nextOffset;
1054
21.9k
}
1055
1056
uint16_t MeshForwarder::PrepareDataFrameWithNoMeshHeader(Mac::TxFrame         &aFrame,
1057
                                                         Message              &aMessage,
1058
                                                         const Mac::Addresses &aMacAddrs)
1059
0
{
1060
0
    return PrepareDataFrame(aFrame, aMessage, aMacAddrs, /* aAddMeshHeader */ false, /* aMeshSource */ 0xffff,
1061
0
                            /* aMeshDest */ 0xffff, /* aAddFragHeader */ false);
1062
0
}
1063
1064
Neighbor *MeshForwarder::UpdateNeighborOnSentFrame(Mac::TxFrame       &aFrame,
1065
                                                   Error               aError,
1066
                                                   const Mac::Address &aMacDest,
1067
                                                   bool                aIsDataPoll)
1068
21.9k
{
1069
21.9k
    OT_UNUSED_VARIABLE(aIsDataPoll);
1070
1071
21.9k
    Neighbor *neighbor  = nullptr;
1072
21.9k
    uint8_t   failLimit = kFailedRouterTransmissions;
1073
1074
21.9k
    VerifyOrExit(mEnabled);
1075
1076
21.9k
    neighbor = Get<NeighborTable>().FindNeighbor(aMacDest);
1077
21.9k
    VerifyOrExit(neighbor != nullptr);
1078
1079
0
    VerifyOrExit(aFrame.GetAckRequest());
1080
1081
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
1082
    // TREL radio link uses deferred ack model. We ignore
1083
    // `SendDone` event from `Mac` layer with success status and
1084
    // wait for deferred ack callback instead.
1085
#if OPENTHREAD_CONFIG_MULTI_RADIO
1086
    if (aFrame.GetRadioType() == Mac::kRadioTypeTrel)
1087
#endif
1088
    {
1089
        VerifyOrExit(aError != kErrorNone);
1090
    }
1091
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
1092
1093
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1094
    if (aFrame.HasCslIe() && aIsDataPoll)
1095
    {
1096
        failLimit = kFailedCslDataPollTransmissions;
1097
    }
1098
#endif
1099
1100
0
    UpdateNeighborLinkFailures(*neighbor, aError, /* aAllowNeighborRemove */ true, failLimit);
1101
1102
21.9k
exit:
1103
21.9k
    return neighbor;
1104
0
}
1105
1106
void MeshForwarder::UpdateNeighborLinkFailures(Neighbor &aNeighbor,
1107
                                               Error     aError,
1108
                                               bool      aAllowNeighborRemove,
1109
                                               uint8_t   aFailLimit)
1110
0
{
1111
    // Update neighbor `LinkFailures` counter on ack error.
1112
1113
0
    if (aError == kErrorNone)
1114
0
    {
1115
0
        aNeighbor.ResetLinkFailures();
1116
0
    }
1117
0
    else if (aError == kErrorNoAck)
1118
0
    {
1119
0
        aNeighbor.IncrementLinkFailures();
1120
1121
0
        if (aAllowNeighborRemove && (Mle::IsRouterRloc16(aNeighbor.GetRloc16())) &&
1122
0
            (aNeighbor.GetLinkFailures() >= aFailLimit))
1123
0
        {
1124
0
#if OPENTHREAD_FTD
1125
0
            Get<Mle::Mle>().RemoveRouterLink(static_cast<Router &>(aNeighbor));
1126
#else
1127
            IgnoreError(Get<Mle::Mle>().BecomeDetached());
1128
#endif
1129
0
        }
1130
0
    }
1131
0
}
1132
1133
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
1134
void MeshForwarder::HandleDeferredAck(Neighbor &aNeighbor, Error aError)
1135
{
1136
    bool allowNeighborRemove = true;
1137
1138
    VerifyOrExit(mEnabled);
1139
1140
    if (aError == kErrorNoAck)
1141
    {
1142
        LogInfo("Deferred ack timeout on trel for neighbor %s rloc16:0x%04x",
1143
                aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16());
1144
    }
1145
1146
#if OPENTHREAD_CONFIG_MULTI_RADIO
1147
    // In multi radio mode, `RadioSelector` will update the neighbor's
1148
    // link failure counter and removes the neighbor if required.
1149
    Get<RadioSelector>().UpdateOnDeferredAck(aNeighbor, aError, allowNeighborRemove);
1150
#else
1151
    UpdateNeighborLinkFailures(aNeighbor, aError, allowNeighborRemove, kFailedRouterTransmissions);
1152
#endif
1153
1154
exit:
1155
    return;
1156
}
1157
#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
1158
1159
void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, Error aError)
1160
21.9k
{
1161
21.9k
    Neighbor    *neighbor = nullptr;
1162
21.9k
    Mac::Address macDest;
1163
1164
21.9k
    OT_ASSERT((aError == kErrorNone) || (aError == kErrorChannelAccessFailure) || (aError == kErrorAbort) ||
1165
21.9k
              (aError == kErrorNoAck));
1166
1167
21.9k
    mSendBusy = false;
1168
1169
21.9k
    VerifyOrExit(mEnabled);
1170
1171
21.9k
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
1172
21.9k
    if (mDelayNextTx && (aError == kErrorNone))
1173
0
    {
1174
0
        mTxDelayTimer.Start(kTxDelayInterval);
1175
0
        LogDebg("Start tx delay timer for %lu msec", ToUlong(kTxDelayInterval));
1176
0
    }
1177
21.9k
    else
1178
21.9k
    {
1179
21.9k
        mDelayNextTx = false;
1180
21.9k
    }
1181
21.9k
#endif
1182
1183
21.9k
    if (!aFrame.IsEmpty())
1184
21.9k
    {
1185
21.9k
        IgnoreError(aFrame.GetDstAddr(macDest));
1186
21.9k
        neighbor = UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ false);
1187
21.9k
    }
1188
1189
21.9k
    UpdateSendMessage(aError, macDest, neighbor);
1190
1191
21.9k
exit:
1192
21.9k
    return;
1193
21.9k
}
1194
1195
void MeshForwarder::UpdateSendMessage(Error aFrameTxError, Mac::Address &aMacDest, Neighbor *aNeighbor)
1196
21.9k
{
1197
21.9k
    Error txError = aFrameTxError;
1198
1199
21.9k
    VerifyOrExit(mSendMessage != nullptr);
1200
1201
21.9k
    OT_ASSERT(mSendMessage->IsDirectTransmission());
1202
1203
21.9k
    if (aFrameTxError != kErrorNone)
1204
0
    {
1205
        // If the transmission of any fragment frame fails,
1206
        // the overall message transmission is considered
1207
        // as failed
1208
1209
0
        mSendMessage->SetTxSuccess(false);
1210
1211
0
#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
1212
1213
        // We set the NextOffset to end of message to avoid sending
1214
        // any remaining fragments in the message.
1215
1216
0
        mMessageNextOffset = mSendMessage->GetLength();
1217
0
#endif
1218
0
    }
1219
1220
21.9k
    if (mMessageNextOffset < mSendMessage->GetLength())
1221
649
    {
1222
649
        mSendMessage->SetOffset(mMessageNextOffset);
1223
649
        ExitNow();
1224
649
    }
1225
1226
21.3k
    txError = aFrameTxError;
1227
1228
21.3k
    if (aNeighbor != nullptr)
1229
0
    {
1230
0
        aNeighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess());
1231
0
    }
1232
1233
#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
1234
1235
    // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
1236
    // disabled, all fragment frames of a larger message are
1237
    // sent even if the transmission of an earlier fragment fail.
1238
    // Note that `GetTxSuccess() tracks the tx success of the
1239
    // entire message, while `aFrameTxError` represents the error
1240
    // status of the last fragment frame transmission.
1241
1242
    if (!mSendMessage->GetTxSuccess() && (txError == kErrorNone))
1243
    {
1244
        txError = kErrorFailed;
1245
    }
1246
#endif
1247
1248
21.3k
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
1249
21.3k
    Get<Utils::HistoryTracker>().RecordTxMessage(*mSendMessage, aMacDest);
1250
21.3k
#endif
1251
1252
21.3k
    LogMessage(kMessageTransmit, *mSendMessage, txError, &aMacDest);
1253
21.3k
    FinalizeMessageDirectTx(*mSendMessage, txError);
1254
21.3k
    RemoveMessageIfNoPendingTx(*mSendMessage);
1255
1256
21.9k
exit:
1257
21.9k
    mScheduleTransmissionTask.Post();
1258
21.9k
}
1259
1260
void MeshForwarder::FinalizeMessageDirectTx(Message &aMessage, Error aError)
1261
21.5k
{
1262
    // Finalizes the direct transmission of `aMessage`. This can be
1263
    // triggered by successful delivery (all fragments reaching the
1264
    // destination), failure of any fragment, queue management
1265
    // dropping the message, or eviction of message to accommodate
1266
    // higher priority messages.
1267
1268
21.5k
    VerifyOrExit(aMessage.IsDirectTransmission());
1269
1270
21.5k
    aMessage.ClearDirectTransmission();
1271
21.5k
    aMessage.SetOffset(0);
1272
1273
21.5k
    if (aError != kErrorNone)
1274
174
    {
1275
174
        aMessage.SetTxSuccess(false);
1276
174
    }
1277
1278
21.5k
    mCounters.UpdateOnTxDone(aMessage, aMessage.GetTxSuccess());
1279
1280
21.5k
    if (aMessage.IsMleCommand(Mle::kCommandDiscoveryRequest))
1281
0
    {
1282
        // Note that `HandleDiscoveryRequestFrameTxDone()` may update
1283
        // `aMessage` and mark it again for direct transmission.
1284
0
        Get<Mle::DiscoverScanner>().HandleDiscoveryRequestFrameTxDone(aMessage, aError);
1285
0
    }
1286
21.5k
    else if (aMessage.IsMleCommand(Mle::kCommandChildIdRequest))
1287
0
    {
1288
0
        Get<Mle::Mle>().HandleChildIdRequestTxDone(aMessage);
1289
0
    }
1290
1291
21.5k
exit:
1292
21.5k
    return;
1293
21.5k
}
1294
1295
void MeshForwarder::FinalizeAndRemoveMessage(Message &aMessage, Error aError, MessageAction aAction)
1296
0
{
1297
0
    LogMessage(aAction, aMessage, aError);
1298
1299
0
#if OPENTHREAD_FTD
1300
0
    FinalizeMessageIndirectTxs(aMessage);
1301
0
#endif
1302
1303
0
    FinalizeMessageDirectTx(aMessage, aError);
1304
0
    RemoveMessageIfNoPendingTx(aMessage);
1305
0
}
1306
1307
bool MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage)
1308
43.0k
{
1309
43.0k
    bool didRemove = false;
1310
1311
43.0k
#if OPENTHREAD_FTD
1312
43.0k
    VerifyOrExit(!aMessage.IsDirectTransmission() && aMessage.GetIndirectTxChildMask().IsEmpty());
1313
#else
1314
    VerifyOrExit(!aMessage.IsDirectTransmission());
1315
#endif
1316
1317
21.5k
    if (mSendMessage == &aMessage)
1318
21.3k
    {
1319
21.3k
        mSendMessage       = nullptr;
1320
21.3k
        mMessageNextOffset = 0;
1321
21.3k
    }
1322
1323
21.5k
    mSendQueue.DequeueAndFree(aMessage);
1324
21.5k
    didRemove = true;
1325
1326
43.0k
exit:
1327
43.0k
    return didRemove;
1328
21.5k
}
1329
1330
Error MeshForwarder::RxInfo::ParseIp6Headers(void)
1331
0
{
1332
0
    Error error = kErrorNone;
1333
1334
0
    VerifyOrExit(!mParsedIp6Headers);
1335
0
    SuccessOrExit(error = mIp6Headers.DecompressFrom(mFrameData, mMacAddrs, GetInstance()));
1336
0
    mParsedIp6Headers = true;
1337
1338
0
exit:
1339
0
    return error;
1340
0
}
1341
1342
void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame)
1343
0
{
1344
0
    Error  error = kErrorNone;
1345
0
    RxInfo rxInfo(GetInstance());
1346
1347
0
    VerifyOrExit(mEnabled, error = kErrorInvalidState);
1348
1349
0
    rxInfo.mFrameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength());
1350
1351
0
    SuccessOrExit(error = aFrame.GetSrcAddr(rxInfo.mMacAddrs.mSource));
1352
0
    SuccessOrExit(error = aFrame.GetDstAddr(rxInfo.mMacAddrs.mDestination));
1353
1354
0
    rxInfo.mLinkInfo.SetFrom(aFrame);
1355
1356
0
    Get<SupervisionListener>().UpdateOnReceive(rxInfo.mMacAddrs.mSource, rxInfo.IsLinkSecurityEnabled());
1357
1358
0
    switch (aFrame.GetType())
1359
0
    {
1360
0
    case Mac::Frame::kTypeData:
1361
0
        if (Lowpan::MeshHeader::IsMeshHeader(rxInfo.mFrameData))
1362
0
        {
1363
0
#if OPENTHREAD_FTD
1364
0
            HandleMesh(rxInfo);
1365
0
#endif
1366
0
        }
1367
0
        else if (Lowpan::FragmentHeader::IsFragmentHeader(rxInfo.mFrameData))
1368
0
        {
1369
0
            HandleFragment(rxInfo);
1370
0
        }
1371
0
        else if (Lowpan::Lowpan::IsLowpanHc(rxInfo.mFrameData))
1372
0
        {
1373
0
            HandleLowpanHc(rxInfo);
1374
0
        }
1375
0
        else
1376
0
        {
1377
0
            VerifyOrExit(rxInfo.mFrameData.GetLength() == 0, error = kErrorNotLowpanDataFrame);
1378
1379
0
            LogFrame("Received empty payload frame", aFrame, kErrorNone);
1380
0
        }
1381
1382
0
        break;
1383
1384
0
    case Mac::Frame::kTypeBeacon:
1385
0
        break;
1386
1387
0
    default:
1388
0
        error = kErrorDrop;
1389
0
        break;
1390
0
    }
1391
1392
0
exit:
1393
1394
0
    if (error != kErrorNone)
1395
0
    {
1396
0
        LogFrame("Dropping rx frame", aFrame, error);
1397
0
    }
1398
0
}
1399
1400
void MeshForwarder::HandleFragment(RxInfo &aRxInfo)
1401
0
{
1402
0
    Error                  error = kErrorNone;
1403
0
    Lowpan::FragmentHeader fragmentHeader;
1404
0
    Message               *message = nullptr;
1405
1406
0
    SuccessOrExit(error = fragmentHeader.ParseFrom(aRxInfo.mFrameData));
1407
1408
#if OPENTHREAD_CONFIG_MULTI_RADIO
1409
1410
    if (aRxInfo.IsLinkSecurityEnabled())
1411
    {
1412
        Neighbor *neighbor =
1413
            Get<NeighborTable>().FindNeighbor(aRxInfo.GetSrcAddr(), Neighbor::kInStateAnyExceptInvalid);
1414
1415
        if ((neighbor != nullptr) && (fragmentHeader.GetDatagramOffset() == 0))
1416
        {
1417
            uint16_t tag = fragmentHeader.GetDatagramTag();
1418
1419
            if (neighbor->IsLastRxFragmentTagSet())
1420
            {
1421
                VerifyOrExit(!neighbor->IsLastRxFragmentTagAfter(tag), error = kErrorDuplicated);
1422
            }
1423
1424
            neighbor->SetLastRxFragmentTag(tag);
1425
        }
1426
1427
        // Duplication suppression for a "next fragment" is handled
1428
        // by the code below where the the datagram offset is
1429
        // checked against the offset of the corresponding message
1430
        // (same datagram tag and size) in Reassembly List. Note
1431
        // that if there is no matching message in the Reassembly
1432
        // List (e.g., in case the message is already fully
1433
        // assembled) the received "next fragment" frame would be
1434
        // dropped.
1435
    }
1436
1437
#endif // OPENTHREAD_CONFIG_MULTI_RADIO
1438
1439
0
    if (fragmentHeader.GetDatagramOffset() == 0)
1440
0
    {
1441
0
        uint16_t datagramSize = fragmentHeader.GetDatagramSize();
1442
1443
0
#if OPENTHREAD_FTD
1444
0
        UpdateEidRlocCacheAndStaleChild(aRxInfo);
1445
0
#endif
1446
1447
0
        SuccessOrExit(error = FrameToMessage(aRxInfo, datagramSize, message));
1448
1449
0
        VerifyOrExit(datagramSize >= message->GetLength(), error = kErrorParse);
1450
0
        SuccessOrExit(error = message->SetLength(datagramSize));
1451
1452
0
        message->SetDatagramTag(fragmentHeader.GetDatagramTag());
1453
0
        message->SetTimestampToNow();
1454
0
        message->UpdateLinkInfoFrom(aRxInfo.mLinkInfo);
1455
1456
0
        VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
1457
1458
0
#if OPENTHREAD_FTD
1459
0
        CheckReachabilityToSendIcmpError(*message, aRxInfo.mMacAddrs);
1460
0
#endif
1461
1462
        // Allow re-assembly of only one message at a time on a SED by clearing
1463
        // any remaining fragments in reassembly list upon receiving of a new
1464
        // (secure) first fragment.
1465
1466
0
        if (!GetRxOnWhenIdle() && message->IsLinkSecurityEnabled())
1467
0
        {
1468
0
            ClearReassemblyList();
1469
0
        }
1470
1471
0
        mReassemblyList.Enqueue(*message);
1472
1473
0
        Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
1474
0
    }
1475
0
    else // Received frame is a "next fragment".
1476
0
    {
1477
0
        for (Message &msg : mReassemblyList)
1478
0
        {
1479
            // Security Check: only consider reassembly buffers that had the same Security Enabled setting.
1480
0
            if (msg.GetLength() == fragmentHeader.GetDatagramSize() &&
1481
0
                msg.GetDatagramTag() == fragmentHeader.GetDatagramTag() &&
1482
0
                msg.GetOffset() == fragmentHeader.GetDatagramOffset() &&
1483
0
                msg.GetOffset() + aRxInfo.mFrameData.GetLength() <= fragmentHeader.GetDatagramSize() &&
1484
0
                msg.IsLinkSecurityEnabled() == aRxInfo.IsLinkSecurityEnabled())
1485
0
            {
1486
0
                message = &msg;
1487
0
                break;
1488
0
            }
1489
0
        }
1490
1491
        // For a sleepy-end-device, if we receive a new (secure) next fragment
1492
        // with a non-matching fragmentation offset or tag, it indicates that
1493
        // we have either missed a fragment, or the parent has moved to a new
1494
        // message with a new tag. In either case, we can safely clear any
1495
        // remaining fragments stored in the reassembly list.
1496
1497
0
        if (!GetRxOnWhenIdle() && (message == nullptr) && aRxInfo.IsLinkSecurityEnabled())
1498
0
        {
1499
0
            ClearReassemblyList();
1500
0
        }
1501
1502
0
        VerifyOrExit(message != nullptr, error = kErrorDrop);
1503
1504
0
        message->WriteData(message->GetOffset(), aRxInfo.mFrameData);
1505
0
        message->MoveOffset(aRxInfo.mFrameData.GetLength());
1506
0
        message->AddRss(aRxInfo.mLinkInfo.GetRss());
1507
0
        message->AddLqi(aRxInfo.mLinkInfo.GetLqi());
1508
0
        message->SetTimestampToNow();
1509
0
    }
1510
1511
0
exit:
1512
1513
0
    if (error == kErrorNone)
1514
0
    {
1515
0
        if (message->GetOffset() >= message->GetLength())
1516
0
        {
1517
0
            mReassemblyList.Dequeue(*message);
1518
0
            IgnoreError(HandleDatagram(*message, aRxInfo.GetSrcAddr()));
1519
0
        }
1520
0
    }
1521
0
    else
1522
0
    {
1523
0
        LogFragmentFrameDrop(error, aRxInfo, fragmentHeader);
1524
0
        FreeMessage(message);
1525
0
    }
1526
0
}
1527
1528
void MeshForwarder::ClearReassemblyList(void)
1529
0
{
1530
0
    for (Message &message : mReassemblyList)
1531
0
    {
1532
0
        LogMessage(kMessageReassemblyDrop, message, kErrorNoFrameReceived);
1533
0
        mCounters.UpdateOnDrop(message);
1534
0
        mReassemblyList.DequeueAndFree(message);
1535
0
    }
1536
0
}
1537
1538
void MeshForwarder::HandleTimeTick(void)
1539
0
{
1540
0
    bool continueRxingTicks = false;
1541
1542
0
#if OPENTHREAD_FTD
1543
0
    continueRxingTicks = UpdateFwdFrameInfoArrayOnTimeTick();
1544
0
#endif
1545
1546
0
    continueRxingTicks = UpdateReassemblyList() || continueRxingTicks;
1547
1548
0
    if (!continueRxingTicks)
1549
0
    {
1550
0
        Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMeshForwarder);
1551
0
    }
1552
0
}
1553
1554
bool MeshForwarder::UpdateReassemblyList(void)
1555
0
{
1556
0
    TimeMilli now = TimerMilli::GetNow();
1557
1558
0
    for (Message &message : mReassemblyList)
1559
0
    {
1560
0
        if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kReassemblyTimeout))
1561
0
        {
1562
0
            LogMessage(kMessageReassemblyDrop, message, kErrorReassemblyTimeout);
1563
0
            mCounters.UpdateOnDrop(message);
1564
0
            mReassemblyList.DequeueAndFree(message);
1565
0
        }
1566
0
    }
1567
1568
0
    return mReassemblyList.GetHead() != nullptr;
1569
0
}
1570
1571
Error MeshForwarder::FrameToMessage(RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage)
1572
0
{
1573
0
    Error             error     = kErrorNone;
1574
0
    FrameData         frameData = aRxInfo.mFrameData;
1575
0
    Message::Priority priority;
1576
1577
0
    SuccessOrExit(error = GetFramePriority(aRxInfo, priority));
1578
1579
0
    aMessage = Get<MessagePool>().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, Message::Settings(priority));
1580
0
    VerifyOrExit(aMessage, error = kErrorNoBufs);
1581
1582
0
    SuccessOrExit(error = Get<Lowpan::Lowpan>().Decompress(*aMessage, aRxInfo.mMacAddrs, frameData, aDatagramSize));
1583
1584
0
    SuccessOrExit(error = aMessage->AppendData(frameData));
1585
0
    aMessage->MoveOffset(frameData.GetLength());
1586
1587
0
exit:
1588
0
    return error;
1589
0
}
1590
1591
void MeshForwarder::HandleLowpanHc(RxInfo &aRxInfo)
1592
0
{
1593
0
    Error    error   = kErrorNone;
1594
0
    Message *message = nullptr;
1595
1596
0
#if OPENTHREAD_FTD
1597
0
    UpdateEidRlocCacheAndStaleChild(aRxInfo);
1598
0
#endif
1599
1600
0
    SuccessOrExit(error = FrameToMessage(aRxInfo, 0, message));
1601
1602
0
    message->UpdateLinkInfoFrom(aRxInfo.mLinkInfo);
1603
1604
0
    VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
1605
1606
0
#if OPENTHREAD_FTD
1607
0
    CheckReachabilityToSendIcmpError(*message, aRxInfo.mMacAddrs);
1608
0
#endif
1609
1610
0
exit:
1611
1612
0
    if (error == kErrorNone)
1613
0
    {
1614
0
        IgnoreError(HandleDatagram(*message, aRxInfo.GetSrcAddr()));
1615
0
    }
1616
0
    else
1617
0
    {
1618
0
        LogLowpanHcFrameDrop(error, aRxInfo);
1619
0
        FreeMessage(message);
1620
0
    }
1621
0
}
1622
1623
Error MeshForwarder::HandleDatagram(Message &aMessage, const Mac::Address &aMacSource)
1624
0
{
1625
0
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
1626
0
    Get<Utils::HistoryTracker>().RecordRxMessage(aMessage, aMacSource);
1627
0
#endif
1628
1629
0
    LogMessage(kMessageReceive, aMessage, kErrorNone, &aMacSource);
1630
1631
0
    mCounters.UpdateOnRx(aMessage);
1632
1633
0
    aMessage.SetLoopbackToHostAllowed(true);
1634
0
    aMessage.SetOrigin(Message::kOriginThreadNetif);
1635
1636
0
    return Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&aMessage));
1637
0
}
1638
1639
Error MeshForwarder::GetFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority)
1640
0
{
1641
0
    Error error = kErrorNone;
1642
1643
0
    SuccessOrExit(error = aRxInfo.ParseIp6Headers());
1644
1645
0
    aPriority = Ip6::Ip6::DscpToPriority(aRxInfo.mIp6Headers.GetIp6Header().GetDscp());
1646
1647
    // Only ICMPv6 error messages are prioritized.
1648
0
    if (aRxInfo.mIp6Headers.IsIcmp6() && aRxInfo.mIp6Headers.GetIcmpHeader().IsError())
1649
0
    {
1650
0
        aPriority = Message::kPriorityNet;
1651
0
    }
1652
1653
0
    if (aRxInfo.mIp6Headers.IsUdp())
1654
0
    {
1655
0
        uint16_t destPort = aRxInfo.mIp6Headers.GetUdpHeader().GetDestinationPort();
1656
1657
0
        if (destPort == Mle::kUdpPort)
1658
0
        {
1659
0
            aPriority = Message::kPriorityNet;
1660
0
        }
1661
0
        else if (Get<Tmf::Agent>().IsTmfMessage(aRxInfo.mIp6Headers.GetSourceAddress(),
1662
0
                                                aRxInfo.mIp6Headers.GetDestinationAddress(), destPort))
1663
0
        {
1664
0
            aPriority = Tmf::Agent::DscpToPriority(aRxInfo.mIp6Headers.GetIp6Header().GetDscp());
1665
0
        }
1666
0
    }
1667
1668
0
exit:
1669
0
    return error;
1670
0
}
1671
1672
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1673
Error MeshForwarder::SendEmptyMessage(void)
1674
{
1675
    Error             error = kErrorNone;
1676
    OwnedPtr<Message> messagePtr;
1677
1678
    VerifyOrExit(mEnabled && !Get<Mac::Mac>().GetRxOnWhenIdle() &&
1679
                     Get<Mle::Mle>().GetParent().IsStateValidOrRestoring(),
1680
                 error = kErrorInvalidState);
1681
1682
    messagePtr.Reset(Get<MessagePool>().Allocate(Message::kTypeMacEmptyData));
1683
    VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs);
1684
1685
    SendMessage(messagePtr.PassOwnership());
1686
1687
exit:
1688
    LogDebg("Send empty message, error:%s", ErrorToString(error));
1689
    return error;
1690
}
1691
#endif
1692
1693
// LCOV_EXCL_START
1694
1695
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
1696
1697
const char *MeshForwarder::MessageActionToString(MessageAction aAction, Error aError)
1698
{
1699
    static const char *const kMessageActionStrings[] = {
1700
        "Received",                    // (0) kMessageReceive
1701
        "Sent",                        // (1) kMessageTransmit
1702
        "Prepping indir tx",           // (2) kMessagePrepareIndirect
1703
        "Dropping",                    // (3) kMessageDrop
1704
        "Dropping (reassembly queue)", // (4) kMessageReassemblyDrop
1705
        "Evicting",                    // (5) kMessageEvict
1706
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
1707
        "Marked ECN",            // (6) kMessageMarkEcn
1708
        "Dropping (queue mgmt)", // (7) kMessageQueueMgmtDrop
1709
#endif
1710
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
1711
        "Dropping (dir queue full)", // (8) kMessageFullQueueDrop
1712
#endif
1713
    };
1714
1715
    const char *string = kMessageActionStrings[aAction];
1716
1717
    struct MessageActionChecker
1718
    {
1719
        InitEnumValidatorCounter();
1720
1721
        ValidateNextEnum(kMessageReceive);
1722
        ValidateNextEnum(kMessageTransmit);
1723
        ValidateNextEnum(kMessagePrepareIndirect);
1724
        ValidateNextEnum(kMessageDrop);
1725
        ValidateNextEnum(kMessageReassemblyDrop);
1726
        ValidateNextEnum(kMessageEvict);
1727
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
1728
        ValidateNextEnum(kMessageMarkEcn);
1729
        ValidateNextEnum(kMessageQueueMgmtDrop);
1730
#endif
1731
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
1732
        ValidateNextEnum(kMessageFullQueueDrop);
1733
#endif
1734
    };
1735
1736
    if ((aAction == kMessageTransmit) && (aError != kErrorNone))
1737
    {
1738
        string = "Failed to send";
1739
    }
1740
1741
    return string;
1742
}
1743
1744
const char *MeshForwarder::MessagePriorityToString(const Message &aMessage)
1745
{
1746
    return Message::PriorityToString(aMessage.GetPriority());
1747
}
1748
1749
#if OPENTHREAD_CONFIG_LOG_SRC_DST_IP_ADDRESSES
1750
void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &aHeaders, LogLevel aLogLevel)
1751
{
1752
    LogIp6AddressAndPort("src", aHeaders.GetSourceAddress(), aHeaders.GetSourcePort(), aLogLevel);
1753
    LogIp6AddressAndPort("dst", aHeaders.GetDestinationAddress(), aHeaders.GetDestinationPort(), aLogLevel);
1754
}
1755
1756
void MeshForwarder::LogIp6AddressAndPort(const char         *aLabel,
1757
                                         const Ip6::Address &aAddress,
1758
                                         uint16_t            aPort,
1759
                                         LogLevel            aLogLevel)
1760
{
1761
    Ip6::SockAddr::InfoString string;
1762
1763
    string.Append("[%s]", aAddress.ToString().AsCString());
1764
1765
    if (aPort != 0)
1766
    {
1767
        string.Append(":%u", aPort);
1768
    }
1769
1770
    LogAt(aLogLevel, "    %s:%s", aLabel, string.AsCString());
1771
}
1772
1773
#else
1774
void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &, LogLevel) {}
1775
#endif
1776
1777
void MeshForwarder::LogIp6Message(MessageAction       aAction,
1778
                                  const Message      &aMessage,
1779
                                  const Mac::Address *aMacAddress,
1780
                                  Error               aError,
1781
                                  LogLevel            aLogLevel)
1782
{
1783
    Ip6::Headers              headers;
1784
    String<kMaxLogStringSize> string;
1785
1786
    SuccessOrExit(headers.ParseFrom(aMessage));
1787
1788
    string.Append("%s IPv6 %s msg, len:%u, chksum:%04x, ecn:%s, ", MessageActionToString(aAction, aError),
1789
                  Ip6::Ip6::IpProtoToString(headers.GetIpProto()), aMessage.GetLength(), headers.GetChecksum(),
1790
                  Ip6::Ip6::EcnToString(headers.GetEcn()));
1791
1792
    AppendMacAddrToLogString(string, aAction, aMacAddress);
1793
    AppendSecErrorPrioRssRadioLabelsToLogString(string, aAction, aMessage, aError);
1794
1795
    LogAt(aLogLevel, "%s", string.AsCString());
1796
1797
    if (aAction != kMessagePrepareIndirect)
1798
    {
1799
        LogIp6SourceDestAddresses(headers, aLogLevel);
1800
    }
1801
1802
exit:
1803
    return;
1804
}
1805
1806
void MeshForwarder::AppendMacAddrToLogString(StringWriter       &aString,
1807
                                             MessageAction       aAction,
1808
                                             const Mac::Address *aMacAddress)
1809
{
1810
    VerifyOrExit(aMacAddress != nullptr);
1811
1812
    if (aAction == kMessageReceive)
1813
    {
1814
        aString.Append("from:");
1815
    }
1816
    else
1817
    {
1818
        aString.Append("to:");
1819
    }
1820
1821
    aString.Append("%s, ", aMacAddress->ToString().AsCString());
1822
1823
exit:
1824
    return;
1825
}
1826
1827
void MeshForwarder::AppendSecErrorPrioRssRadioLabelsToLogString(StringWriter  &aString,
1828
                                                                MessageAction  aAction,
1829
                                                                const Message &aMessage,
1830
                                                                Error          aError)
1831
{
1832
    aString.Append("sec:%s, ", ToYesNo(aMessage.IsLinkSecurityEnabled()));
1833
1834
    if (aError != kErrorNone)
1835
    {
1836
        aString.Append("error:%s, ", ErrorToString(aError));
1837
    }
1838
1839
    aString.Append("prio:%s", MessagePriorityToString(aMessage));
1840
1841
    if ((aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop))
1842
    {
1843
        aString.Append(", rss:%s", aMessage.GetRssAverager().ToString().AsCString());
1844
    }
1845
1846
#if OPENTHREAD_CONFIG_MULTI_RADIO
1847
    aString.Append(", radio:%s", aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all");
1848
#endif
1849
}
1850
1851
void MeshForwarder::LogMessage(MessageAction aAction, const Message &aMessage)
1852
{
1853
    LogMessage(aAction, aMessage, kErrorNone);
1854
}
1855
1856
void MeshForwarder::LogMessage(MessageAction aAction, const Message &aMessage, Error aError)
1857
{
1858
    LogMessage(aAction, aMessage, aError, nullptr);
1859
}
1860
1861
void MeshForwarder::LogMessage(MessageAction       aAction,
1862
                               const Message      &aMessage,
1863
                               Error               aError,
1864
                               const Mac::Address *aMacAddress)
1865
1866
{
1867
    LogLevel logLevel = kLogLevelInfo;
1868
1869
    switch (aAction)
1870
    {
1871
    case kMessageReceive:
1872
    case kMessageTransmit:
1873
    case kMessagePrepareIndirect:
1874
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
1875
    case kMessageMarkEcn:
1876
#endif
1877
        logLevel = (aError == kErrorNone) ? kLogLevelInfo : kLogLevelNote;
1878
        break;
1879
1880
    case kMessageDrop:
1881
    case kMessageReassemblyDrop:
1882
    case kMessageEvict:
1883
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
1884
    case kMessageQueueMgmtDrop:
1885
#endif
1886
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
1887
    case kMessageFullQueueDrop:
1888
#endif
1889
        logLevel = kLogLevelNote;
1890
        break;
1891
    }
1892
1893
    VerifyOrExit(Instance::GetLogLevel() >= logLevel);
1894
1895
    switch (aMessage.GetType())
1896
    {
1897
    case Message::kTypeIp6:
1898
        LogIp6Message(aAction, aMessage, aMacAddress, aError, logLevel);
1899
        break;
1900
1901
#if OPENTHREAD_FTD
1902
    case Message::kType6lowpan:
1903
        LogMeshMessage(aAction, aMessage, aMacAddress, aError, logLevel);
1904
        break;
1905
#endif
1906
1907
    default:
1908
        break;
1909
    }
1910
1911
exit:
1912
    return;
1913
}
1914
1915
void MeshForwarder::LogFrame(const char *aActionText, const Mac::Frame &aFrame, Error aError)
1916
{
1917
    if (aError != kErrorNone)
1918
    {
1919
        LogNote("%s, aError:%s, %s", aActionText, ErrorToString(aError), aFrame.ToInfoString().AsCString());
1920
    }
1921
    else
1922
    {
1923
        LogInfo("%s, %s", aActionText, aFrame.ToInfoString().AsCString());
1924
    }
1925
}
1926
1927
void MeshForwarder::LogFragmentFrameDrop(Error                         aError,
1928
                                         const RxInfo                 &aRxInfo,
1929
                                         const Lowpan::FragmentHeader &aFragmentHeader)
1930
{
1931
    LogNote("Dropping rx frag frame, error:%s, %s, tag:%d, offset:%d, dglen:%d", ErrorToString(aError),
1932
            aRxInfo.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(),
1933
            aFragmentHeader.GetDatagramSize());
1934
}
1935
1936
void MeshForwarder::LogLowpanHcFrameDrop(Error aError, const RxInfo &aRxInfo)
1937
{
1938
    LogNote("Dropping rx lowpan HC frame, error:%s, %s", ErrorToString(aError), aRxInfo.ToString().AsCString());
1939
}
1940
1941
MeshForwarder::RxInfo::InfoString MeshForwarder::RxInfo::ToString(void) const
1942
{
1943
    InfoString string;
1944
1945
    string.Append("len:%d, src:%s, dst:%s, sec:%s", mFrameData.GetLength(), GetSrcAddr().ToString().AsCString(),
1946
                  GetDstAddr().ToString().AsCString(), ToYesNo(IsLinkSecurityEnabled()));
1947
1948
    return string;
1949
}
1950
1951
#else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE)
1952
1953
0
void MeshForwarder::LogMessage(MessageAction, const Message &) {}
1954
1955
174
void MeshForwarder::LogMessage(MessageAction, const Message &, Error) {}
1956
1957
21.3k
void MeshForwarder::LogMessage(MessageAction, const Message &, Error, const Mac::Address *) {}
1958
1959
0
void MeshForwarder::LogFrame(const char *, const Mac::Frame &, Error) {}
1960
1961
0
void MeshForwarder::LogFragmentFrameDrop(Error, const RxInfo &, const Lowpan::FragmentHeader &) {}
1962
1963
0
void MeshForwarder::LogLowpanHcFrameDrop(Error, const RxInfo &) {}
1964
1965
#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE)
1966
1967
// LCOV_EXCL_STOP
1968
1969
} // namespace ot