• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2018, 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 FTD-specific mesh forwarding of IPv6/6LoWPAN messages.
32  */
33 
34 #include "mesh_forwarder.hpp"
35 
36 #if OPENTHREAD_FTD
37 
38 #include "instance/instance.hpp"
39 
40 namespace ot {
41 
42 RegisterLogModule("MeshForwarder");
43 
SendMessage(OwnedPtr<Message> aMessagePtr)44 void MeshForwarder::SendMessage(OwnedPtr<Message> aMessagePtr)
45 {
46     Message &message = *aMessagePtr.Release();
47 
48     message.SetOffset(0);
49     message.SetDatagramTag(0);
50     message.SetTimestampToNow();
51     mSendQueue.Enqueue(message);
52 
53     switch (message.GetType())
54     {
55     case Message::kTypeIp6:
56     {
57         Ip6::Header         ip6Header;
58         const Ip6::Address &destination = ip6Header.GetDestination();
59 
60         IgnoreError(message.Read(0, ip6Header));
61 
62         if (destination.IsMulticast())
63         {
64             // For traffic destined to multicast address larger than realm local, generally it uses IP-in-IP
65             // encapsulation (RFC2473), with outer destination as ALL_MPL_FORWARDERS. So here if the destination
66             // is multicast address larger than realm local, it should be for indirection transmission for the
67             // device's sleepy child, thus there should be no direct transmission.
68             if (!destination.IsMulticastLargerThanRealmLocal())
69             {
70                 message.SetDirectTransmission();
71             }
72 
73             if (message.GetSubType() != Message::kSubTypeMplRetransmission)
74             {
75                 // Check if we need to forward the multicast message
76                 // to any sleepy child. This is skipped for MPL retx
77                 // (only the first MPL transmission is forwarded to
78                 // sleepy children).
79 
80                 bool destinedForAll = ((destination == Get<Mle::Mle>().GetLinkLocalAllThreadNodesAddress()) ||
81                                        (destination == Get<Mle::Mle>().GetRealmLocalAllThreadNodesAddress()));
82 
83                 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
84                 {
85                     if (!child.IsRxOnWhenIdle() && (destinedForAll || child.HasIp6Address(destination)))
86                     {
87                         mIndirectSender.AddMessageForSleepyChild(message, child);
88                     }
89                 }
90             }
91         }
92         else // Destination is unicast
93         {
94             Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(destination);
95 
96             if ((neighbor != nullptr) && !neighbor->IsRxOnWhenIdle() && !message.IsDirectTransmission() &&
97                 Get<ChildTable>().Contains(*neighbor))
98             {
99                 mIndirectSender.AddMessageForSleepyChild(message, *static_cast<Child *>(neighbor));
100             }
101             else
102             {
103                 message.SetDirectTransmission();
104             }
105         }
106 
107         break;
108     }
109 
110     case Message::kTypeSupervision:
111     {
112         Child *child = Get<ChildSupervisor>().GetDestination(message);
113         OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle());
114         mIndirectSender.AddMessageForSleepyChild(message, *child);
115         break;
116     }
117 
118     default:
119         message.SetDirectTransmission();
120         break;
121     }
122 
123     // Ensure that the message is marked for direct tx and/or for indirect tx
124     // to a sleepy child. Otherwise, remove the message.
125 
126     if (RemoveMessageIfNoPendingTx(message))
127     {
128         ExitNow();
129     }
130 
131 #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
132     ApplyDirectTxQueueLimit(message);
133 #endif
134 
135     mScheduleTransmissionTask.Post();
136 
137 exit:
138     return;
139 }
140 
HandleResolved(const Ip6::Address & aEid,Error aError)141 void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError)
142 {
143     Ip6::Address ip6Dst;
144     bool         didUpdate = false;
145 
146     for (Message &message : mSendQueue)
147     {
148         if (!message.IsResolvingAddress())
149         {
150             continue;
151         }
152 
153         IgnoreError(message.Read(Ip6::Header::kDestinationFieldOffset, ip6Dst));
154 
155         if (ip6Dst != aEid)
156         {
157             continue;
158         }
159 
160         if (aError != kErrorNone)
161         {
162             LogMessage(kMessageDrop, message, kErrorAddressQuery);
163             FinalizeMessageDirectTx(message, kErrorAddressQuery);
164             RemoveMessageIfNoPendingTx(message);
165             continue;
166         }
167 
168 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
169         // Pass back to IPv6 layer for DUA destination resolved
170         // by Backbone Query
171         if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(ip6Dst) &&
172             Get<Mle::Mle>().HasRloc16(Get<AddressResolver>().LookUp(ip6Dst)))
173         {
174             uint8_t hopLimit;
175 
176             mSendQueue.Dequeue(message);
177 
178             // Avoid decreasing Hop Limit twice
179             IgnoreError(message.Read(Ip6::Header::kHopLimitFieldOffset, hopLimit));
180             hopLimit++;
181             message.Write(Ip6::Header::kHopLimitFieldOffset, hopLimit);
182             message.SetLoopbackToHostAllowed(true);
183             message.SetOrigin(Message::kOriginHostTrusted);
184 
185             IgnoreError(Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&message)));
186             continue;
187         }
188 #endif
189 
190         message.SetResolvingAddress(false);
191         didUpdate = true;
192     }
193 
194     if (didUpdate)
195     {
196         mScheduleTransmissionTask.Post();
197     }
198 }
199 
EvictMessage(Message::Priority aPriority)200 Error MeshForwarder::EvictMessage(Message::Priority aPriority)
201 {
202     Error    error = kErrorNotFound;
203     Message *evict = nullptr;
204 
205 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
206     error = RemoveAgedMessages();
207     VerifyOrExit(error == kErrorNotFound);
208 #endif
209 
210     // Search for a lower priority message to evict
211     for (uint8_t priority = 0; priority < aPriority; priority++)
212     {
213         for (Message *message = mSendQueue.GetHeadForPriority(static_cast<Message::Priority>(priority)); message;
214              message          = message->GetNext())
215         {
216             if (message->GetPriority() != priority)
217             {
218                 break;
219             }
220 
221             if (message->GetDoNotEvict())
222             {
223                 continue;
224             }
225 
226             evict = message;
227             error = kErrorNone;
228             ExitNow();
229         }
230     }
231 
232     for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++)
233     {
234         // search for an equal or higher priority indirect message to evict
235         for (Message *message = mSendQueue.GetHeadForPriority(aPriority); message; message = message->GetNext())
236         {
237             if (message->GetPriority() != priority)
238             {
239                 break;
240             }
241 
242             if (message->GetDoNotEvict())
243             {
244                 continue;
245             }
246 
247             if (!message->GetIndirectTxChildMask().IsEmpty())
248             {
249                 evict = message;
250                 ExitNow(error = kErrorNone);
251             }
252         }
253     }
254 
255 exit:
256     if ((error == kErrorNone) && (evict != nullptr))
257     {
258         FinalizeAndRemoveMessage(*evict, kErrorNoBufs, kMessageEvict);
259     }
260 
261     return error;
262 }
263 
RemoveMessagesForChild(Child & aChild,MessageChecker & aMessageChecker)264 void MeshForwarder::RemoveMessagesForChild(Child &aChild, MessageChecker &aMessageChecker)
265 {
266     for (Message &message : mSendQueue)
267     {
268         if (!aMessageChecker(message))
269         {
270             continue;
271         }
272 
273         if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
274         {
275             const Neighbor *neighbor = nullptr;
276 
277             if (message.GetType() == Message::kTypeIp6)
278             {
279                 Ip6::Header ip6header;
280 
281                 IgnoreError(message.Read(0, ip6header));
282                 neighbor = Get<NeighborTable>().FindNeighbor(ip6header.GetDestination());
283             }
284             else if (message.GetType() == Message::kType6lowpan)
285             {
286                 Lowpan::MeshHeader meshHeader;
287 
288                 IgnoreError(meshHeader.ParseFrom(message));
289                 neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
290             }
291 
292             if (&aChild == neighbor)
293             {
294                 message.ClearDirectTransmission();
295             }
296         }
297 
298         RemoveMessageIfNoPendingTx(message);
299     }
300 }
301 
FinalizeMessageIndirectTxs(Message & aMessage)302 void MeshForwarder::FinalizeMessageIndirectTxs(Message &aMessage)
303 {
304     VerifyOrExit(!aMessage.GetIndirectTxChildMask().IsEmpty());
305 
306     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
307     {
308         IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(aMessage, child));
309         VerifyOrExit(!aMessage.GetIndirectTxChildMask().IsEmpty());
310     }
311 
312 exit:
313     return;
314 }
315 
RemoveDataResponseMessages(void)316 void MeshForwarder::RemoveDataResponseMessages(void)
317 {
318     for (Message &message : mSendQueue)
319     {
320         if (message.IsMleCommand(Mle::kCommandDataResponse))
321         {
322             FinalizeAndRemoveMessage(message, kErrorDrop, kMessageDrop);
323         }
324     }
325 }
326 
SendMesh(Message & aMessage,Mac::TxFrame & aFrame)327 void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
328 {
329     Mac::TxFrame::Info frameInfo;
330 
331     frameInfo.mType          = Mac::Frame::kTypeData;
332     frameInfo.mAddrs         = mMacAddrs;
333     frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32;
334     frameInfo.mKeyIdMode     = Mac::Frame::kKeyIdMode1;
335     frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
336 
337     PrepareMacHeaders(aFrame, frameInfo, &aMessage);
338 
339     // write payload
340     OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
341     aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength());
342     aFrame.SetPayloadLength(aMessage.GetLength());
343 
344     mMessageNextOffset = aMessage.GetLength();
345 }
346 
UpdateMeshRoute(Message & aMessage)347 Error MeshForwarder::UpdateMeshRoute(Message &aMessage)
348 {
349     Error              error = kErrorNone;
350     Lowpan::MeshHeader meshHeader;
351     Neighbor          *neighbor;
352     uint16_t           nextHop;
353 
354     IgnoreError(meshHeader.ParseFrom(aMessage));
355 
356     nextHop = Get<RouterTable>().GetNextHop(meshHeader.GetDestination());
357 
358     if (nextHop != Mle::kInvalidRloc16)
359     {
360         neighbor = Get<NeighborTable>().FindNeighbor(nextHop);
361     }
362     else
363     {
364         neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
365     }
366 
367     if (neighbor == nullptr)
368     {
369         ExitNow(error = kErrorDrop);
370     }
371 
372     mMacAddrs.mDestination.SetShort(neighbor->GetRloc16());
373     mMacAddrs.mSource.SetShort(Get<Mle::Mle>().GetRloc16());
374 
375     mAddMeshHeader = true;
376     mMeshDest      = meshHeader.GetDestination();
377     mMeshSource    = meshHeader.GetSource();
378 
379 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
380     if (mMacAddrs.mDestination.GetShort() != mMeshDest)
381     {
382         mDelayNextTx = true;
383     }
384 #endif
385 
386 exit:
387     return error;
388 }
389 
UpdateIp6RouteFtd(const Ip6::Header & aIp6Header,Message & aMessage)390 Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage)
391 {
392     Mle::MleRouter &mle   = Get<Mle::MleRouter>();
393     Error           error = kErrorNone;
394     Neighbor       *neighbor;
395 
396     if (aMessage.GetOffset() > 0)
397     {
398         mMeshDest = aMessage.GetMeshDest();
399     }
400     else if (mle.IsRoutingLocator(aIp6Header.GetDestination()))
401     {
402         uint16_t rloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
403         VerifyOrExit(Mle::IsRouterIdValid(Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop);
404         mMeshDest = rloc16;
405     }
406     else if (mle.IsAnycastLocator(aIp6Header.GetDestination()))
407     {
408         uint16_t aloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
409 
410         SuccessOrExit(error = Get<NetworkData::Leader>().AnycastLookup(aloc16, mMeshDest));
411 
412         // If the selected ALOC destination, `mMeshDest`, is a sleepy
413         // child of this device, prepare the message for indirect tx
414         // to the sleepy child and un-mark message for direct tx.
415 
416         if (mle.IsRouterOrLeader() && Mle::IsChildRloc16(mMeshDest) && mle.HasMatchingRouterIdWith(mMeshDest))
417         {
418             Child *child = Get<ChildTable>().FindChild(mMeshDest, Child::kInStateValid);
419 
420             VerifyOrExit(child != nullptr, error = kErrorDrop);
421 
422             if (!child->IsRxOnWhenIdle())
423             {
424                 mIndirectSender.AddMessageForSleepyChild(aMessage, *child);
425                 aMessage.ClearDirectTransmission();
426             }
427         }
428     }
429     else if ((neighbor = Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination())) != nullptr)
430     {
431         mMeshDest = neighbor->GetRloc16();
432     }
433     else if (Get<NetworkData::Leader>().IsOnMesh(aIp6Header.GetDestination()))
434     {
435         SuccessOrExit(error = Get<AddressResolver>().Resolve(aIp6Header.GetDestination(), mMeshDest));
436     }
437     else
438     {
439         IgnoreError(
440             Get<NetworkData::Leader>().RouteLookup(aIp6Header.GetSource(), aIp6Header.GetDestination(), mMeshDest));
441     }
442 
443     VerifyOrExit(mMeshDest != Mle::kInvalidRloc16, error = kErrorDrop);
444 
445     mMeshSource = Get<Mle::Mle>().GetRloc16();
446 
447     SuccessOrExit(error = CheckReachability(mMeshDest, aIp6Header));
448     aMessage.SetMeshDest(mMeshDest);
449     mMacAddrs.mDestination.SetShort(Get<RouterTable>().GetNextHop(mMeshDest));
450 
451     if (mMacAddrs.mDestination.GetShort() != mMeshDest)
452     {
453         // destination is not neighbor
454         mMacAddrs.mSource.SetShort(mMeshSource);
455         mAddMeshHeader = true;
456 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
457         mDelayNextTx = true;
458 #endif
459     }
460 
461 exit:
462     return error;
463 }
464 
SendIcmpErrorIfDstUnreach(const Message & aMessage,const Mac::Addresses & aMacAddrs)465 void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs)
466 {
467     Error        error;
468     Ip6::Headers ip6Headers;
469     Child       *child;
470 
471     VerifyOrExit(aMacAddrs.mSource.IsShort() && aMacAddrs.mDestination.IsShort());
472 
473     child = Get<ChildTable>().FindChild(aMacAddrs.mSource.GetShort(), Child::kInStateAnyExceptInvalid);
474     VerifyOrExit((child == nullptr) || child->IsFullThreadDevice());
475 
476     SuccessOrExit(ip6Headers.ParseFrom(aMessage));
477 
478     VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() &&
479                  Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetDestinationAddress()));
480 
481     error = CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
482 
483     if (error == kErrorNoRoute)
484     {
485         SendDestinationUnreachable(aMacAddrs.mSource.GetShort(), ip6Headers);
486     }
487 
488 exit:
489     return;
490 }
491 
CheckReachability(RxInfo & aRxInfo)492 Error MeshForwarder::CheckReachability(RxInfo &aRxInfo)
493 {
494     Error error;
495 
496     error = aRxInfo.ParseIp6Headers();
497 
498     switch (error)
499     {
500     case kErrorNone:
501         break;
502     case kErrorNotFound:
503         // Frame may not contain an IPv6 header.
504         error = kErrorNone;
505         OT_FALL_THROUGH;
506     default:
507         ExitNow();
508     }
509 
510     error = CheckReachability(aRxInfo.GetDstAddr().GetShort(), aRxInfo.mIp6Headers.GetIp6Header());
511 
512     if (error == kErrorNoRoute)
513     {
514         SendDestinationUnreachable(aRxInfo.GetSrcAddr().GetShort(), aRxInfo.mIp6Headers);
515     }
516 
517 exit:
518     return error;
519 }
520 
CheckReachability(uint16_t aMeshDest,const Ip6::Header & aIp6Header)521 Error MeshForwarder::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
522 {
523     bool isReachable = false;
524 
525     if (Get<Mle::Mle>().IsChild())
526     {
527         if (Get<Mle::Mle>().HasRloc16(aMeshDest))
528         {
529             isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination());
530         }
531         else
532         {
533             isReachable = true;
534         }
535 
536         ExitNow();
537     }
538 
539     if (Get<Mle::Mle>().HasRloc16(aMeshDest))
540     {
541         isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()) ||
542                       (Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination()) != nullptr);
543         ExitNow();
544     }
545 
546     if (Get<Mle::Mle>().HasMatchingRouterIdWith(aMeshDest))
547     {
548         isReachable = (Get<ChildTable>().FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr);
549         ExitNow();
550     }
551 
552     isReachable = (Get<RouterTable>().GetNextHop(aMeshDest) != Mle::kInvalidRloc16);
553 
554 exit:
555     return isReachable ? kErrorNone : kErrorNoRoute;
556 }
557 
SendDestinationUnreachable(uint16_t aMeshSource,const Ip6::Headers & aIp6Headers)558 void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers)
559 {
560     Ip6::MessageInfo messageInfo;
561 
562     messageInfo.GetPeerAddr().SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(), aMeshSource);
563 
564     IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
565                                            Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers));
566 }
567 
HandleMesh(RxInfo & aRxInfo)568 void MeshForwarder::HandleMesh(RxInfo &aRxInfo)
569 {
570     Error              error = kErrorNone;
571     Lowpan::MeshHeader meshHeader;
572     Mac::Address       neighborMacSource;
573 
574     // Security Check: only process Mesh Header frames that had security enabled.
575     VerifyOrExit(aRxInfo.IsLinkSecurityEnabled(), error = kErrorSecurity);
576 
577     SuccessOrExit(error = meshHeader.ParseFrom(aRxInfo.mFrameData));
578 
579     neighborMacSource = aRxInfo.GetSrcAddr();
580 
581     // Switch the `aRxInfo.mMacAddrs` to the mesh header source/destination
582 
583     aRxInfo.mMacAddrs.mSource.SetShort(meshHeader.GetSource());
584     aRxInfo.mMacAddrs.mDestination.SetShort(meshHeader.GetDestination());
585 
586     UpdateRoutes(aRxInfo);
587 
588     if (Get<Mle::Mle>().HasRloc16(aRxInfo.GetDstAddr().GetShort()) ||
589         Get<ChildTable>().HasMinimalChild(aRxInfo.GetDstAddr().GetShort()))
590     {
591         if (Lowpan::FragmentHeader::IsFragmentHeader(aRxInfo.mFrameData))
592         {
593             HandleFragment(aRxInfo);
594         }
595         else if (Lowpan::Lowpan::IsLowpanHc(aRxInfo.mFrameData))
596         {
597             HandleLowpanHc(aRxInfo);
598         }
599         else
600         {
601             ExitNow(error = kErrorParse);
602         }
603     }
604     else if (meshHeader.GetHopsLeft() > 0)
605     {
606         OwnedPtr<Message> messagePtr;
607         Message::Priority priority = Message::kPriorityNormal;
608 
609         ResolveRoutingLoops(neighborMacSource.GetShort(), aRxInfo.GetDstAddr().GetShort());
610 
611         SuccessOrExit(error = CheckReachability(aRxInfo));
612 
613         meshHeader.DecrementHopsLeft();
614 
615         GetForwardFramePriority(aRxInfo, priority);
616         messagePtr.Reset(
617             Get<MessagePool>().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority)));
618         VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs);
619 
620         SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
621         SuccessOrExit(error = messagePtr->AppendData(aRxInfo.mFrameData));
622 
623         messagePtr->UpdateLinkInfoFrom(aRxInfo.mLinkInfo);
624 
625         LogMessage(kMessageReceive, *messagePtr, kErrorNone, &neighborMacSource);
626 
627 #if OPENTHREAD_CONFIG_MULTI_RADIO
628         // Since the message will be forwarded, we clear the radio
629         // type on the message to allow the radio type for tx to be
630         // selected later (based on the radios supported by the next
631         // hop).
632         messagePtr->ClearRadioType();
633 #endif
634 
635         SendMessage(messagePtr.PassOwnership());
636     }
637 
638 exit:
639 
640     if (error != kErrorNone)
641     {
642         LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error),
643                 aRxInfo.mFrameData.GetLength(), neighborMacSource.ToString().AsCString(),
644                 ToYesNo(aRxInfo.IsLinkSecurityEnabled()));
645     }
646 }
647 
ResolveRoutingLoops(uint16_t aSourceRloc16,uint16_t aDestRloc16)648 void MeshForwarder::ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16)
649 {
650     // Resolves 2-hop routing loops.
651 
652     Router *router;
653 
654     if (aSourceRloc16 != Get<RouterTable>().GetNextHop(aDestRloc16))
655     {
656         ExitNow();
657     }
658 
659     router = Get<RouterTable>().FindRouterByRloc16(aDestRloc16);
660     VerifyOrExit(router != nullptr);
661 
662     router->SetNextHopToInvalid();
663     Get<Mle::MleRouter>().ResetAdvertiseInterval();
664 
665 exit:
666     return;
667 }
668 
UpdateRoutes(RxInfo & aRxInfo)669 void MeshForwarder::UpdateRoutes(RxInfo &aRxInfo)
670 {
671     Neighbor *neighbor;
672 
673     VerifyOrExit(!aRxInfo.GetDstAddr().IsBroadcast() && aRxInfo.GetSrcAddr().IsShort());
674 
675     SuccessOrExit(aRxInfo.ParseIp6Headers());
676 
677     if (!aRxInfo.mIp6Headers.GetSourceAddress().GetIid().IsLocator() &&
678         Get<NetworkData::Leader>().IsOnMesh(aRxInfo.mIp6Headers.GetSourceAddress()))
679     {
680         // FTDs MAY add/update EID-to-RLOC Map Cache entries by
681         // inspecting packets being received only for on mesh
682         // addresses.
683 
684         Get<AddressResolver>().UpdateSnoopedCacheEntry(
685             aRxInfo.mIp6Headers.GetSourceAddress(), aRxInfo.GetSrcAddr().GetShort(), aRxInfo.GetDstAddr().GetShort());
686     }
687 
688     neighbor = Get<NeighborTable>().FindNeighbor(aRxInfo.mIp6Headers.GetSourceAddress());
689     VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice());
690 
691     if (!Get<Mle::Mle>().HasMatchingRouterIdWith(aRxInfo.GetSrcAddr().GetShort()))
692     {
693         Get<Mle::MleRouter>().RemoveNeighbor(*neighbor);
694     }
695 
696 exit:
697     return;
698 }
699 
UpdateFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aFragmentLength,uint16_t aSrcRloc16,Message::Priority aPriority)700 void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
701                                            uint16_t                aFragmentLength,
702                                            uint16_t                aSrcRloc16,
703                                            Message::Priority       aPriority)
704 {
705     FwdFrameInfo *entry;
706 
707     entry = FindFwdFrameInfoEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
708 
709     if (entry == nullptr)
710     {
711         VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
712 
713         entry = mFwdFrameInfoArray.PushBack();
714         VerifyOrExit(entry != nullptr);
715 
716         entry->Init(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
717         Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
718 
719         ExitNow();
720     }
721 
722 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
723     OT_UNUSED_VARIABLE(aFragmentLength);
724 #else
725     // We can remove the entry in `mFwdFrameInfoArray` if it is the
726     // last fragment. But if "delay aware active queue management" is
727     // used we need to keep entry until the message is sent.
728     if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
729     {
730         mFwdFrameInfoArray.Remove(*entry);
731     }
732     else
733 #endif
734     {
735         entry->ResetLifetime();
736     }
737 
738 exit:
739     return;
740 }
741 
Init(uint16_t aSrcRloc16,uint16_t aDatagramTag,Message::Priority aPriority)742 void MeshForwarder::FwdFrameInfo::Init(uint16_t aSrcRloc16, uint16_t aDatagramTag, Message::Priority aPriority)
743 {
744     mSrcRloc16   = aSrcRloc16;
745     mDatagramTag = aDatagramTag;
746     mLifetime    = kLifetime;
747     mPriority    = aPriority;
748 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
749     mShouldDrop = false;
750 #endif
751 }
752 
Matches(const Info & aInfo) const753 bool MeshForwarder::FwdFrameInfo::Matches(const Info &aInfo) const
754 {
755     return (mSrcRloc16 == aInfo.mSrcRloc16) && (mDatagramTag == aInfo.mDatagramTag);
756 }
757 
FindFwdFrameInfoEntry(uint16_t aSrcRloc16,uint16_t aDatagramTag)758 MeshForwarder::FwdFrameInfo *MeshForwarder::FindFwdFrameInfoEntry(uint16_t aSrcRloc16, uint16_t aDatagramTag)
759 {
760     FwdFrameInfo::Info info;
761 
762     info.mSrcRloc16   = aSrcRloc16;
763     info.mDatagramTag = aDatagramTag;
764 
765     return mFwdFrameInfoArray.FindMatching(info);
766 }
767 
UpdateFwdFrameInfoArrayOnTimeTick(void)768 bool MeshForwarder::UpdateFwdFrameInfoArrayOnTimeTick(void)
769 {
770     for (FwdFrameInfo &entry : mFwdFrameInfoArray)
771     {
772         entry.DecrementLifetime();
773     }
774 
775     mFwdFrameInfoArray.RemoveAllMatching(FwdFrameInfo::kIsExpired);
776 
777     return !mFwdFrameInfoArray.IsEmpty();
778 }
779 
GetFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aSrcRloc16,Message::Priority & aPriority)780 Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
781                                          uint16_t                aSrcRloc16,
782                                          Message::Priority      &aPriority)
783 {
784     Error               error = kErrorNone;
785     const FwdFrameInfo *entry = FindFwdFrameInfoEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
786 
787     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
788     aPriority = entry->GetPriority();
789 
790 exit:
791     return error;
792 }
793 
GetForwardFramePriority(RxInfo & aRxInfo,Message::Priority & aPriority)794 void MeshForwarder::GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority)
795 {
796     // Determines the message priority to use for forwarding a
797     // received mesh-header LowPAN frame towards its final
798     // destination.
799 
800     Error                  error      = kErrorNone;
801     bool                   isFragment = false;
802     Lowpan::FragmentHeader fragmentHeader;
803     FrameData              savedFrameData;
804 
805     // We save the `aRxInfo.mFrameData` before parsing the fragment
806     // header which may update it to skip over the parsed header. We
807     // restore the original frame data on `aRxInfo` before
808     // returning.
809 
810     savedFrameData = aRxInfo.mFrameData;
811 
812     if (fragmentHeader.ParseFrom(aRxInfo.mFrameData) == kErrorNone)
813     {
814         isFragment = true;
815 
816         if (fragmentHeader.GetDatagramOffset() > 0)
817         {
818             // Get priority from the pre-buffered info
819             ExitNow(error = GetFragmentPriority(fragmentHeader, aRxInfo.GetSrcAddr().GetShort(), aPriority));
820         }
821     }
822 
823     // Get priority from IPv6 header or UDP destination port directly
824     error = GetFramePriority(aRxInfo, aPriority);
825 
826 exit:
827     if (error != kErrorNone)
828     {
829         LogNote("Failed to get forwarded frame priority, error:%s, %s", ErrorToString(error),
830                 aRxInfo.ToString().AsCString());
831     }
832     else if (isFragment)
833     {
834         UpdateFragmentPriority(fragmentHeader, aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().GetShort(),
835                                aPriority);
836     }
837 
838     aRxInfo.mFrameData = savedFrameData;
839 }
840 
841 // LCOV_EXCL_START
842 
843 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
844 
LogMeshFragmentHeader(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,uint16_t & aOffset,Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)845 Error MeshForwarder::LogMeshFragmentHeader(MessageAction       aAction,
846                                            const Message      &aMessage,
847                                            const Mac::Address *aMacAddress,
848                                            Error               aError,
849                                            uint16_t           &aOffset,
850                                            Mac::Addresses     &aMeshAddrs,
851                                            LogLevel            aLogLevel)
852 {
853     Error                     error             = kErrorFailed;
854     bool                      hasFragmentHeader = false;
855     Lowpan::MeshHeader        meshHeader;
856     Lowpan::FragmentHeader    fragmentHeader;
857     uint16_t                  headerLength;
858     String<kMaxLogStringSize> string;
859 
860     SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength));
861 
862     aMeshAddrs.mSource.SetShort(meshHeader.GetSource());
863     aMeshAddrs.mDestination.SetShort(meshHeader.GetDestination());
864 
865     aOffset = headerLength;
866 
867     if (fragmentHeader.ParseFrom(aMessage, aOffset, headerLength) == kErrorNone)
868     {
869         hasFragmentHeader = true;
870         aOffset += headerLength;
871     }
872 
873     string.Append("%s mesh frame, len:%u, ", MessageActionToString(aAction, aError), aMessage.GetLength());
874 
875     AppendMacAddrToLogString(string, aAction, aMacAddress);
876 
877     string.Append("msrc:%s, mdst:%s, hops:%d, frag:%s, ", aMeshAddrs.mSource.ToString().AsCString(),
878                   aMeshAddrs.mDestination.ToString().AsCString(),
879                   meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), ToYesNo(hasFragmentHeader));
880 
881     AppendSecErrorPrioRssRadioLabelsToLogString(string, aAction, aMessage, aError);
882 
883     LogAt(aLogLevel, "%s", string.AsCString());
884 
885     if (hasFragmentHeader)
886     {
887         LogAt(aLogLevel, "    Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
888               fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
889 
890         VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
891     }
892 
893     error = kErrorNone;
894 
895 exit:
896     return error;
897 }
898 
LogMeshIpHeader(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)899 void MeshForwarder::LogMeshIpHeader(const Message        &aMessage,
900                                     uint16_t              aOffset,
901                                     const Mac::Addresses &aMeshAddrs,
902                                     LogLevel              aLogLevel)
903 {
904     Ip6::Headers headers;
905 
906     SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshAddrs));
907 
908     LogAt(aLogLevel, "    IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()),
909           headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage));
910 
911     LogIp6SourceDestAddresses(headers, aLogLevel);
912 
913 exit:
914     return;
915 }
916 
LogMeshMessage(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,LogLevel aLogLevel)917 void MeshForwarder::LogMeshMessage(MessageAction       aAction,
918                                    const Message      &aMessage,
919                                    const Mac::Address *aMacAddress,
920                                    Error               aError,
921                                    LogLevel            aLogLevel)
922 {
923     uint16_t       offset;
924     Mac::Addresses meshAddrs;
925 
926     SuccessOrExit(LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshAddrs, aLogLevel));
927 
928     // When log action is `kMessageTransmit` we do not include
929     // the IPv6 header info in the logs, as the same info is
930     // logged when the same Mesh Header message was received
931     // and info about it was logged.
932 
933     VerifyOrExit(aAction != kMessageTransmit);
934 
935     LogMeshIpHeader(aMessage, offset, meshAddrs, aLogLevel);
936 
937 exit:
938     return;
939 }
940 
941 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
942 
943 // LCOV_EXCL_STOP
944 
945 } // namespace ot
946 
947 #endif // OPENTHREAD_FTD
948