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