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