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