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