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