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