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