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