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