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