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