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