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