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