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