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             mSendQueue.DequeueAndFree(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<AddressResolver>().LookUp(ip6Dst) == Get<Mle::MleRouter>().GetRloc16())
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 
RemoveMessages(Child & aChild,Message::SubType aSubType)269 void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType)
270 {
271     for (Message &message : mSendQueue)
272     {
273         if ((aSubType != Message::kSubTypeNone) && (aSubType != message.GetSubType()))
274         {
275             continue;
276         }
277 
278         if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
279         {
280             switch (message.GetType())
281             {
282             case Message::kTypeIp6:
283             {
284                 Ip6::Header ip6header;
285 
286                 IgnoreError(message.Read(0, ip6header));
287 
288                 if (&aChild == Get<NeighborTable>().FindNeighbor(ip6header.GetDestination()))
289                 {
290                     message.ClearDirectTransmission();
291                 }
292 
293                 break;
294             }
295 
296             case Message::kType6lowpan:
297             {
298                 Lowpan::MeshHeader meshHeader;
299 
300                 IgnoreError(meshHeader.ParseFrom(message));
301 
302                 if (&aChild == Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination()))
303                 {
304                     message.ClearDirectTransmission();
305                 }
306 
307                 break;
308             }
309 
310             default:
311                 break;
312             }
313         }
314 
315         RemoveMessageIfNoPendingTx(message);
316     }
317 }
318 
RemoveDataResponseMessages(void)319 void MeshForwarder::RemoveDataResponseMessages(void)
320 {
321     Ip6::Header ip6Header;
322 
323     for (Message &message : mSendQueue)
324     {
325         if (message.GetSubType() != Message::kSubTypeMleDataResponse)
326         {
327             continue;
328         }
329 
330         IgnoreError(message.Read(0, ip6Header));
331 
332         if (!(ip6Header.GetDestination().IsMulticast()))
333         {
334             for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
335             {
336                 IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(message, child));
337             }
338         }
339 
340         if (mSendMessage == &message)
341         {
342             mSendMessage = nullptr;
343         }
344 
345         LogMessage(kMessageDrop, message);
346         FinalizeMessageDirectTx(message, kErrorDrop);
347         mSendQueue.DequeueAndFree(message);
348     }
349 }
350 
SendMesh(Message & aMessage,Mac::TxFrame & aFrame)351 void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
352 {
353     Mac::PanIds panIds;
354 
355     panIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
356 
357     PrepareMacHeaders(aFrame, Mac::Frame::kTypeData, mMacAddrs, panIds, Mac::Frame::kSecurityEncMic32,
358                       Mac::Frame::kKeyIdMode1, &aMessage);
359 
360     // write payload
361     OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
362     aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength());
363     aFrame.SetPayloadLength(aMessage.GetLength());
364 
365     mMessageNextOffset = aMessage.GetLength();
366 }
367 
UpdateMeshRoute(Message & aMessage)368 Error MeshForwarder::UpdateMeshRoute(Message &aMessage)
369 {
370     Error              error = kErrorNone;
371     Lowpan::MeshHeader meshHeader;
372     Neighbor          *neighbor;
373     uint16_t           nextHop;
374 
375     IgnoreError(meshHeader.ParseFrom(aMessage));
376 
377     nextHop = Get<Mle::MleRouter>().GetNextHop(meshHeader.GetDestination());
378 
379     if (nextHop != Mac::kShortAddrInvalid)
380     {
381         neighbor = Get<NeighborTable>().FindNeighbor(nextHop);
382     }
383     else
384     {
385         neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
386     }
387 
388     if (neighbor == nullptr)
389     {
390         ExitNow(error = kErrorDrop);
391     }
392 
393     mMacAddrs.mDestination.SetShort(neighbor->GetRloc16());
394     mMacAddrs.mSource.SetShort(Get<Mac::Mac>().GetShortAddress());
395 
396     mAddMeshHeader = true;
397     mMeshDest      = meshHeader.GetDestination();
398     mMeshSource    = meshHeader.GetSource();
399 
400 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
401     if (mMacAddrs.mDestination.GetShort() != mMeshDest)
402     {
403         mDelayNextTx = true;
404     }
405 #endif
406 
407 exit:
408     return error;
409 }
410 
EvaluateRoutingCost(uint16_t aDest,uint8_t & aBestCost,uint16_t & aBestDest) const411 void MeshForwarder::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const
412 {
413     uint8_t cost = Get<RouterTable>().GetPathCost(aDest);
414 
415     if ((aBestDest == Mac::kShortAddrInvalid) || (cost < aBestCost))
416     {
417         aBestDest = aDest;
418         aBestCost = cost;
419     }
420 }
421 
AnycastRouteLookup(uint8_t aServiceId,AnycastType aType,uint16_t & aMeshDest) const422 Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aMeshDest) const
423 {
424     NetworkData::Iterator iterator = NetworkData::kIteratorInit;
425     uint8_t               bestCost = Mle::kMaxRouteCost;
426     uint16_t              bestDest = Mac::kShortAddrInvalid;
427     uint8_t               routerId;
428 
429     switch (aType)
430     {
431     case kAnycastDhcp6Agent:
432     case kAnycastNeighborDiscoveryAgent:
433     {
434         NetworkData::OnMeshPrefixConfig config;
435         Lowpan::Context                 context;
436 
437         SuccessOrExit(Get<NetworkData::Leader>().GetContext(aServiceId, context));
438 
439         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
440         {
441             if (config.GetPrefix() != context.mPrefix)
442             {
443                 continue;
444             }
445 
446             switch (aType)
447             {
448             case kAnycastDhcp6Agent:
449                 if (!(config.mDhcp || config.mConfigure))
450                 {
451                     continue;
452                 }
453                 break;
454             case kAnycastNeighborDiscoveryAgent:
455                 if (!config.mNdDns)
456                 {
457                     continue;
458                 }
459                 break;
460             default:
461                 OT_ASSERT(false);
462                 break;
463             }
464 
465             EvaluateRoutingCost(config.mRloc16, bestCost, bestDest);
466         }
467 
468         break;
469     }
470     case kAnycastService:
471     {
472         NetworkData::ServiceConfig config;
473 
474         while (Get<NetworkData::Leader>().GetNextService(iterator, config) == kErrorNone)
475         {
476             if (config.mServiceId != aServiceId)
477             {
478                 continue;
479             }
480 
481             EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest);
482         }
483 
484         break;
485     }
486     }
487 
488     routerId = Mle::RouterIdFromRloc16(bestDest);
489 
490     if (!(Mle::IsActiveRouter(bestDest) || Mle::Rloc16FromRouterId(routerId) == Get<Mle::MleRouter>().GetRloc16()))
491     {
492         // if agent is neither active router nor child of this device
493         // use the parent of the ED Agent as Dest
494         bestDest = Mle::Rloc16FromRouterId(routerId);
495     }
496 
497     aMeshDest = bestDest;
498 
499 exit:
500     return (bestDest != Mac::kShortAddrInvalid) ? kErrorNone : kErrorNoRoute;
501 }
502 
UpdateIp6RouteFtd(const Ip6::Header & aIp6Header,Message & aMessage)503 Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage)
504 {
505     Mle::MleRouter &mle   = Get<Mle::MleRouter>();
506     Error           error = kErrorNone;
507     Neighbor       *neighbor;
508 
509     if (aMessage.GetOffset() > 0)
510     {
511         mMeshDest = aMessage.GetMeshDest();
512     }
513     else if (mle.IsRoutingLocator(aIp6Header.GetDestination()))
514     {
515         uint16_t rloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
516         VerifyOrExit(Mle::IsRouterIdValid(Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop);
517         mMeshDest = rloc16;
518     }
519     else if (mle.IsAnycastLocator(aIp6Header.GetDestination()))
520     {
521         uint16_t aloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
522 
523         if (aloc16 == Mle::kAloc16Leader)
524         {
525             mMeshDest = Mle::Rloc16FromRouterId(mle.GetLeaderId());
526         }
527         else if (aloc16 <= Mle::kAloc16DhcpAgentEnd)
528         {
529             uint8_t contextId = static_cast<uint8_t>(aloc16 - Mle::kAloc16DhcpAgentStart + 1);
530             SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastDhcp6Agent, mMeshDest));
531         }
532         else if (aloc16 <= Mle::kAloc16ServiceEnd)
533         {
534             uint8_t serviceId = static_cast<uint8_t>(aloc16 - Mle::kAloc16ServiceStart);
535             SuccessOrExit(error = AnycastRouteLookup(serviceId, kAnycastService, mMeshDest));
536         }
537         else if (aloc16 <= Mle::kAloc16CommissionerEnd)
538         {
539             SuccessOrExit(error = Get<NetworkData::Leader>().FindBorderAgentRloc(mMeshDest));
540         }
541 
542 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
543         else if (aloc16 == Mle::kAloc16BackboneRouterPrimary)
544         {
545             VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorDrop);
546             mMeshDest = Get<BackboneRouter::Leader>().GetServer16();
547         }
548 #endif
549         else if ((aloc16 >= Mle::kAloc16NeighborDiscoveryAgentStart) &&
550                  (aloc16 <= Mle::kAloc16NeighborDiscoveryAgentEnd))
551         {
552             uint8_t contextId = static_cast<uint8_t>(aloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1);
553             SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastNeighborDiscoveryAgent, mMeshDest));
554         }
555         else
556         {
557             ExitNow(error = kErrorDrop);
558         }
559     }
560     else if ((neighbor = Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination())) != nullptr)
561     {
562         mMeshDest = neighbor->GetRloc16();
563     }
564     else if (Get<NetworkData::Leader>().IsOnMesh(aIp6Header.GetDestination()))
565     {
566         SuccessOrExit(error = Get<AddressResolver>().Resolve(aIp6Header.GetDestination(), mMeshDest));
567     }
568     else
569     {
570         IgnoreError(
571             Get<NetworkData::Leader>().RouteLookup(aIp6Header.GetSource(), aIp6Header.GetDestination(), mMeshDest));
572     }
573 
574     VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = kErrorDrop);
575 
576     mMeshSource = Get<Mac::Mac>().GetShortAddress();
577 
578     SuccessOrExit(error = mle.CheckReachability(mMeshDest, aIp6Header));
579     aMessage.SetMeshDest(mMeshDest);
580     mMacAddrs.mDestination.SetShort(mle.GetNextHop(mMeshDest));
581 
582     if (mMacAddrs.mDestination.GetShort() != mMeshDest)
583     {
584         // destination is not neighbor
585         mMacAddrs.mSource.SetShort(mMeshSource);
586         mAddMeshHeader = true;
587 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
588         mDelayNextTx = true;
589 #endif
590     }
591 
592 exit:
593     return error;
594 }
595 
SendIcmpErrorIfDstUnreach(const Message & aMessage,const Mac::Addresses & aMacAddrs)596 void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs)
597 {
598     Error        error;
599     Ip6::Headers ip6Headers;
600     Child       *child;
601 
602     VerifyOrExit(aMacAddrs.mSource.IsShort() && aMacAddrs.mDestination.IsShort());
603 
604     child = Get<ChildTable>().FindChild(aMacAddrs.mSource.GetShort(), Child::kInStateAnyExceptInvalid);
605     VerifyOrExit((child == nullptr) || child->IsFullThreadDevice());
606 
607     SuccessOrExit(ip6Headers.ParseFrom(aMessage));
608 
609     VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() &&
610                  Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetDestinationAddress()));
611 
612     error = Get<Mle::MleRouter>().CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
613 
614     if (error == kErrorNoRoute)
615     {
616         SendDestinationUnreachable(aMacAddrs.mSource.GetShort(), ip6Headers);
617     }
618 
619 exit:
620     return;
621 }
622 
CheckReachability(const FrameData & aFrameData,const Mac::Addresses & aMeshAddrs)623 Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs)
624 {
625     Error        error;
626     Ip6::Headers ip6Headers;
627 
628     error = ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance());
629 
630     switch (error)
631     {
632     case kErrorNone:
633         break;
634     case kErrorNotFound:
635         // Frame may not contain an IPv6 header.
636         error = kErrorNone;
637         OT_FALL_THROUGH;
638     default:
639         ExitNow();
640     }
641 
642     error = Get<Mle::MleRouter>().CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
643 
644     if (error == kErrorNoRoute)
645     {
646         SendDestinationUnreachable(aMeshAddrs.mSource.GetShort(), ip6Headers);
647     }
648 
649 exit:
650     return error;
651 }
652 
SendDestinationUnreachable(uint16_t aMeshSource,const Ip6::Headers & aIp6Headers)653 void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers)
654 {
655     Ip6::MessageInfo messageInfo;
656 
657     messageInfo.GetPeerAddr() = Get<Mle::MleRouter>().GetMeshLocal16();
658     messageInfo.GetPeerAddr().GetIid().SetLocator(aMeshSource);
659 
660     IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
661                                            Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers));
662 }
663 
HandleMesh(FrameData & aFrameData,const Mac::Address & aMacSource,const ThreadLinkInfo & aLinkInfo)664 void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo)
665 {
666     Error              error = kErrorNone;
667     Mac::Addresses     meshAddrs;
668     Lowpan::MeshHeader meshHeader;
669 
670     // Security Check: only process Mesh Header frames that had security enabled.
671     VerifyOrExit(aLinkInfo.IsLinkSecurityEnabled(), error = kErrorSecurity);
672 
673     SuccessOrExit(error = meshHeader.ParseFrom(aFrameData));
674 
675     meshAddrs.mSource.SetShort(meshHeader.GetSource());
676     meshAddrs.mDestination.SetShort(meshHeader.GetDestination());
677 
678     UpdateRoutes(aFrameData, meshAddrs);
679 
680     if (meshAddrs.mDestination.GetShort() == Get<Mac::Mac>().GetShortAddress() ||
681         Get<Mle::MleRouter>().IsMinimalChild(meshAddrs.mDestination.GetShort()))
682     {
683         if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData))
684         {
685             HandleFragment(aFrameData, meshAddrs, aLinkInfo);
686         }
687         else if (Lowpan::Lowpan::IsLowpanHc(aFrameData))
688         {
689             HandleLowpanHC(aFrameData, meshAddrs, aLinkInfo);
690         }
691         else
692         {
693             ExitNow(error = kErrorParse);
694         }
695     }
696     else if (meshHeader.GetHopsLeft() > 0)
697     {
698         OwnedPtr<Message> messagePtr;
699         Message::Priority priority = Message::kPriorityNormal;
700 
701         Get<Mle::MleRouter>().ResolveRoutingLoops(aMacSource.GetShort(), meshAddrs.mDestination.GetShort());
702 
703         SuccessOrExit(error = CheckReachability(aFrameData, meshAddrs));
704 
705         meshHeader.DecrementHopsLeft();
706 
707         GetForwardFramePriority(aFrameData, meshAddrs, priority);
708         messagePtr.Reset(
709             Get<MessagePool>().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority)));
710         VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs);
711 
712         SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
713         SuccessOrExit(error = messagePtr->AppendData(aFrameData));
714 
715         messagePtr->UpdateLinkInfoFrom(aLinkInfo);
716 
717         LogMessage(kMessageReceive, *messagePtr, kErrorNone, &aMacSource);
718 
719 #if OPENTHREAD_CONFIG_MULTI_RADIO
720         // Since the message will be forwarded, we clear the radio
721         // type on the message to allow the radio type for tx to be
722         // selected later (based on the radios supported by the next
723         // hop).
724         messagePtr->ClearRadioType();
725 #endif
726 
727         SendMessage(messagePtr.PassOwnership());
728     }
729 
730 exit:
731 
732     if (error != kErrorNone)
733     {
734         LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error),
735                 aFrameData.GetLength(), aMacSource.ToString().AsCString(), ToYesNo(aLinkInfo.IsLinkSecurityEnabled()));
736     }
737 }
738 
UpdateRoutes(const FrameData & aFrameData,const Mac::Addresses & aMeshAddrs)739 void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs)
740 {
741     Ip6::Headers ip6Headers;
742     Neighbor    *neighbor;
743 
744     VerifyOrExit(!aMeshAddrs.mDestination.IsBroadcast() && aMeshAddrs.mSource.IsShort());
745 
746     SuccessOrExit(ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance()));
747 
748     if (!ip6Headers.GetSourceAddress().GetIid().IsLocator() &&
749         Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetSourceAddress()))
750     {
751         // FTDs MAY add/update EID-to-RLOC Map Cache entries by
752         // inspecting packets being received only for on mesh
753         // addresses.
754 
755         Get<AddressResolver>().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aMeshAddrs.mSource.GetShort(),
756                                                        aMeshAddrs.mDestination.GetShort());
757     }
758 
759     neighbor = Get<NeighborTable>().FindNeighbor(ip6Headers.GetSourceAddress());
760     VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice());
761 
762     if (!Mle::RouterIdMatch(aMeshAddrs.mSource.GetShort(), Get<Mac::Mac>().GetShortAddress()))
763     {
764         Get<Mle::MleRouter>().RemoveNeighbor(*neighbor);
765     }
766 
767 exit:
768     return;
769 }
770 
UpdateOnTimeTick(void)771 bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void)
772 {
773     bool continueRxingTicks = false;
774 
775     for (Entry &entry : mEntries)
776     {
777         if (!entry.IsExpired())
778         {
779             entry.DecrementLifetime();
780 
781             if (!entry.IsExpired())
782             {
783                 continueRxingTicks = true;
784             }
785         }
786     }
787 
788     return continueRxingTicks;
789 }
790 
UpdateFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aFragmentLength,uint16_t aSrcRloc16,Message::Priority aPriority)791 void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
792                                            uint16_t                aFragmentLength,
793                                            uint16_t                aSrcRloc16,
794                                            Message::Priority       aPriority)
795 {
796     FragmentPriorityList::Entry *entry;
797 
798     entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
799 
800     if (entry == nullptr)
801     {
802         VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
803 
804         mFragmentPriorityList.AllocateEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
805         Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
806         ExitNow();
807     }
808 
809 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
810     OT_UNUSED_VARIABLE(aFragmentLength);
811 #else
812     // We can clear the entry in `mFragmentPriorityList` if it is the
813     // last fragment. But if "delay aware active queue management" is
814     // used we need to keep entry until the message is sent.
815     if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
816     {
817         entry->Clear();
818     }
819     else
820 #endif
821     {
822         entry->ResetLifetime();
823     }
824 
825 exit:
826     return;
827 }
828 
FindEntry(uint16_t aSrcRloc16,uint16_t aTag)829 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::FindEntry(uint16_t aSrcRloc16,
830                                                                                            uint16_t aTag)
831 {
832     Entry *rval = nullptr;
833 
834     for (Entry &entry : mEntries)
835     {
836         if (!entry.IsExpired() && entry.Matches(aSrcRloc16, aTag))
837         {
838             rval = &entry;
839             break;
840         }
841     }
842 
843     return rval;
844 }
845 
AllocateEntry(uint16_t aSrcRloc16,uint16_t aTag,Message::Priority aPriority)846 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::AllocateEntry(
847     uint16_t          aSrcRloc16,
848     uint16_t          aTag,
849     Message::Priority aPriority)
850 {
851     Entry *newEntry = nullptr;
852 
853     for (Entry &entry : mEntries)
854     {
855         if (entry.IsExpired())
856         {
857             entry.Clear();
858             entry.mSrcRloc16   = aSrcRloc16;
859             entry.mDatagramTag = aTag;
860             entry.mPriority    = aPriority;
861             entry.ResetLifetime();
862             newEntry = &entry;
863             break;
864         }
865     }
866 
867     return newEntry;
868 }
869 
GetFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aSrcRloc16,Message::Priority & aPriority)870 Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
871                                          uint16_t                aSrcRloc16,
872                                          Message::Priority      &aPriority)
873 {
874     Error                        error = kErrorNone;
875     FragmentPriorityList::Entry *entry;
876 
877     entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
878     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
879     aPriority = entry->GetPriority();
880 
881 exit:
882     return error;
883 }
884 
GetForwardFramePriority(const FrameData & aFrameData,const Mac::Addresses & aMeshAddrs,Message::Priority & aPriority)885 void MeshForwarder::GetForwardFramePriority(const FrameData      &aFrameData,
886                                             const Mac::Addresses &aMeshAddrs,
887                                             Message::Priority    &aPriority)
888 {
889     Error                  error      = kErrorNone;
890     FrameData              frameData  = aFrameData;
891     bool                   isFragment = false;
892     Lowpan::FragmentHeader fragmentHeader;
893 
894     if (fragmentHeader.ParseFrom(frameData) == kErrorNone)
895     {
896         isFragment = true;
897 
898         if (fragmentHeader.GetDatagramOffset() > 0)
899         {
900             // Get priority from the pre-buffered info
901             ExitNow(error = GetFragmentPriority(fragmentHeader, aMeshAddrs.mSource.GetShort(), aPriority));
902         }
903     }
904 
905     // Get priority from IPv6 header or UDP destination port directly
906     error = GetFramePriority(frameData, aMeshAddrs, aPriority);
907 
908 exit:
909     if (error != kErrorNone)
910     {
911         LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%s, dst:%s", ErrorToString(error),
912                 frameData.GetLength(), aMeshAddrs.mSource.ToString().AsCString(),
913                 aMeshAddrs.mDestination.ToString().AsCString());
914     }
915     else if (isFragment)
916     {
917         UpdateFragmentPriority(fragmentHeader, frameData.GetLength(), aMeshAddrs.mSource.GetShort(), aPriority);
918     }
919 }
920 
921 // LCOV_EXCL_START
922 
923 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
924 
LogMeshFragmentHeader(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,uint16_t & aOffset,Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)925 Error MeshForwarder::LogMeshFragmentHeader(MessageAction       aAction,
926                                            const Message      &aMessage,
927                                            const Mac::Address *aMacAddress,
928                                            Error               aError,
929                                            uint16_t           &aOffset,
930                                            Mac::Addresses     &aMeshAddrs,
931                                            LogLevel            aLogLevel)
932 {
933     Error                  error             = kErrorFailed;
934     bool                   hasFragmentHeader = false;
935     bool                   shouldLogRss;
936     Lowpan::MeshHeader     meshHeader;
937     Lowpan::FragmentHeader fragmentHeader;
938     uint16_t               headerLength;
939     bool                   shouldLogRadio = false;
940     const char            *radioString    = "";
941 
942     SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength));
943 
944     aMeshAddrs.mSource.SetShort(meshHeader.GetSource());
945     aMeshAddrs.mDestination.SetShort(meshHeader.GetDestination());
946 
947     aOffset = headerLength;
948 
949     if (fragmentHeader.ParseFrom(aMessage, aOffset, headerLength) == kErrorNone)
950     {
951         hasFragmentHeader = true;
952         aOffset += headerLength;
953     }
954 
955     shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop);
956 
957 #if OPENTHREAD_CONFIG_MULTI_RADIO
958     shouldLogRadio = true;
959     radioString    = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all";
960 #endif
961 
962     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",
963           MessageActionToString(aAction, aError), aMessage.GetLength(),
964           (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
965           (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(),
966           aMeshAddrs.mSource.ToString().AsCString(), aMeshAddrs.mDestination.ToString().AsCString(),
967           meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), ToYesNo(hasFragmentHeader),
968           ToYesNo(aMessage.IsLinkSecurityEnabled()),
969           (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
970           shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "",
971           shouldLogRadio ? ", radio:" : "", radioString);
972 
973     if (hasFragmentHeader)
974     {
975         LogAt(aLogLevel, "    Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
976               fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
977 
978         VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
979     }
980 
981     error = kErrorNone;
982 
983 exit:
984     return error;
985 }
986 
LogMeshIpHeader(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)987 void MeshForwarder::LogMeshIpHeader(const Message        &aMessage,
988                                     uint16_t              aOffset,
989                                     const Mac::Addresses &aMeshAddrs,
990                                     LogLevel              aLogLevel)
991 {
992     Ip6::Headers headers;
993 
994     SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshAddrs));
995 
996     LogAt(aLogLevel, "    IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()),
997           headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage));
998 
999     LogIp6SourceDestAddresses(headers, aLogLevel);
1000 
1001 exit:
1002     return;
1003 }
1004 
LogMeshMessage(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,LogLevel aLogLevel)1005 void MeshForwarder::LogMeshMessage(MessageAction       aAction,
1006                                    const Message      &aMessage,
1007                                    const Mac::Address *aMacAddress,
1008                                    Error               aError,
1009                                    LogLevel            aLogLevel)
1010 {
1011     uint16_t       offset;
1012     Mac::Addresses meshAddrs;
1013 
1014     SuccessOrExit(LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshAddrs, aLogLevel));
1015 
1016     // When log action is `kMessageTransmit` we do not include
1017     // the IPv6 header info in the logs, as the same info is
1018     // logged when the same Mesh Header message was received
1019     // and info about it was logged.
1020 
1021     VerifyOrExit(aAction != kMessageTransmit);
1022 
1023     LogMeshIpHeader(aMessage, offset, meshAddrs, aLogLevel);
1024 
1025 exit:
1026     return;
1027 }
1028 
1029 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
1030 
1031 // LCOV_EXCL_STOP
1032 
1033 } // namespace ot
1034 
1035 #endif // OPENTHREAD_FTD
1036