• 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 "common/locator_getters.hpp"
39 #include "meshcop/meshcop.hpp"
40 #include "net/ip6.hpp"
41 #include "net/tcp6.hpp"
42 #include "net/udp6.hpp"
43 
44 namespace ot {
45 
46 RegisterLogModule("MeshForwarder");
47 
SendMessage(Message & aMessage)48 Error MeshForwarder::SendMessage(Message &aMessage)
49 {
50     Mle::MleRouter &mle   = Get<Mle::MleRouter>();
51     Error           error = kErrorNone;
52     Neighbor *      neighbor;
53 
54     aMessage.SetOffset(0);
55     aMessage.SetDatagramTag(0);
56     aMessage.SetTimestampToNow();
57     mSendQueue.Enqueue(aMessage);
58 
59     switch (aMessage.GetType())
60     {
61     case Message::kTypeIp6:
62     {
63         Ip6::Header ip6Header;
64 
65         IgnoreError(aMessage.Read(0, ip6Header));
66 
67         if (ip6Header.GetDestination().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 (!ip6Header.GetDestination().IsMulticastLargerThanRealmLocal())
74             {
75                 // schedule direct transmission
76                 aMessage.SetDirectTransmission();
77             }
78 
79             if (aMessage.GetSubType() != Message::kSubTypeMplRetransmission)
80             {
81                 if (ip6Header.GetDestination() == mle.GetLinkLocalAllThreadNodesAddress() ||
82                     ip6Header.GetDestination() == mle.GetRealmLocalAllThreadNodesAddress())
83                 {
84                     // destined for all sleepy children
85                     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
86                     {
87                         if (!child.IsRxOnWhenIdle())
88                         {
89                             mIndirectSender.AddMessageForSleepyChild(aMessage, child);
90                         }
91                     }
92                 }
93                 else
94                 {
95                     // destined for some sleepy children which subscribed the multicast address.
96                     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
97                     {
98                         if (!child.IsRxOnWhenIdle() && child.HasIp6Address(ip6Header.GetDestination()))
99                         {
100                             mIndirectSender.AddMessageForSleepyChild(aMessage, child);
101                         }
102                     }
103                 }
104             }
105         }
106         else if ((neighbor = Get<NeighborTable>().FindNeighbor(ip6Header.GetDestination())) != nullptr &&
107                  !neighbor->IsRxOnWhenIdle() && !aMessage.IsDirectTransmission())
108         {
109             // destined for a sleepy child
110             Child &child = *static_cast<Child *>(neighbor);
111             mIndirectSender.AddMessageForSleepyChild(aMessage, child);
112         }
113         else
114         {
115             // schedule direct transmission
116             aMessage.SetDirectTransmission();
117         }
118 
119         break;
120     }
121 
122 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
123     case Message::kTypeSupervision:
124     {
125         Child *child = Get<Utils::ChildSupervisor>().GetDestination(aMessage);
126         OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle());
127         mIndirectSender.AddMessageForSleepyChild(aMessage, *child);
128         break;
129     }
130 #endif
131 
132     default:
133         aMessage.SetDirectTransmission();
134         break;
135     }
136 
137 #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
138     ApplyDirectTxQueueLimit(aMessage);
139 #endif
140 
141     mScheduleTransmissionTask.Post();
142 
143     return error;
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             mSendQueue.DequeueAndFree(message);
169             continue;
170         }
171 
172 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
173         // Pass back to IPv6 layer for DUA destination resolved
174         // by Backbone Query
175         if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(ip6Dst) &&
176             Get<AddressResolver>().LookUp(ip6Dst) == Get<Mle::MleRouter>().GetRloc16())
177         {
178             uint8_t hopLimit;
179 
180             mSendQueue.Dequeue(message);
181 
182             // Avoid decreasing Hop Limit twice
183             IgnoreError(message.Read(Ip6::Header::kHopLimitFieldOffset, hopLimit));
184             hopLimit++;
185             message.Write(Ip6::Header::kHopLimitFieldOffset, hopLimit);
186 
187             IgnoreError(Get<Ip6::Ip6>().HandleDatagram(message, nullptr, nullptr, /* aFromHost */ false));
188             continue;
189         }
190 #endif
191 
192         message.SetResolvingAddress(false);
193         didUpdate = true;
194     }
195 
196     if (didUpdate)
197     {
198         mScheduleTransmissionTask.Post();
199     }
200 }
201 
EvictMessage(Message::Priority aPriority)202 Error MeshForwarder::EvictMessage(Message::Priority aPriority)
203 {
204     Error    error = kErrorNotFound;
205     Message *evict = nullptr;
206 
207 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
208     error = RemoveAgedMessages();
209     VerifyOrExit(error == kErrorNotFound);
210 #endif
211 
212     // Search for a lower priority message to evict
213     for (uint8_t priority = 0; priority < aPriority; priority++)
214     {
215         for (Message *message = mSendQueue.GetHeadForPriority(static_cast<Message::Priority>(priority)); message;
216              message          = message->GetNext())
217         {
218             if (message->GetPriority() != priority)
219             {
220                 break;
221             }
222 
223             if (message->GetDoNotEvict())
224             {
225                 continue;
226             }
227 
228             evict = message;
229             error = kErrorNone;
230             ExitNow();
231         }
232     }
233 
234     for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++)
235     {
236         // search for an equal or higher priority indirect message to evict
237         for (Message *message = mSendQueue.GetHeadForPriority(aPriority); message; message = message->GetNext())
238         {
239             if (message->GetPriority() != priority)
240             {
241                 break;
242             }
243 
244             if (message->GetDoNotEvict())
245             {
246                 continue;
247             }
248 
249             if (message->IsChildPending())
250             {
251                 evict = message;
252                 ExitNow(error = kErrorNone);
253             }
254         }
255     }
256 
257 exit:
258     if ((error == kErrorNone) && (evict != nullptr))
259     {
260         RemoveMessage(*evict);
261     }
262 
263     return error;
264 }
265 
RemoveMessages(Child & aChild,Message::SubType aSubType)266 void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType)
267 {
268     for (Message &message : mSendQueue)
269     {
270         if ((aSubType != Message::kSubTypeNone) && (aSubType != message.GetSubType()))
271         {
272             continue;
273         }
274 
275         if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
276         {
277             switch (message.GetType())
278             {
279             case Message::kTypeIp6:
280             {
281                 Ip6::Header ip6header;
282 
283                 IgnoreError(message.Read(0, ip6header));
284 
285                 if (&aChild == static_cast<Child *>(Get<NeighborTable>().FindNeighbor(ip6header.GetDestination())))
286                 {
287                     message.ClearDirectTransmission();
288                 }
289 
290                 break;
291             }
292 
293             case Message::kType6lowpan:
294             {
295                 Lowpan::MeshHeader meshHeader;
296 
297                 IgnoreError(meshHeader.ParseFrom(message));
298 
299                 if (&aChild == static_cast<Child *>(Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination())))
300                 {
301                     message.ClearDirectTransmission();
302                 }
303 
304                 break;
305             }
306 
307             default:
308                 break;
309             }
310         }
311 
312         RemoveMessageIfNoPendingTx(message);
313     }
314 }
315 
RemoveDataResponseMessages(void)316 void MeshForwarder::RemoveDataResponseMessages(void)
317 {
318     Ip6::Header ip6Header;
319 
320     for (Message &message : mSendQueue)
321     {
322         if (message.GetSubType() != Message::kSubTypeMleDataResponse)
323         {
324             continue;
325         }
326 
327         IgnoreError(message.Read(0, ip6Header));
328 
329         if (!(ip6Header.GetDestination().IsMulticast()))
330         {
331             for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
332             {
333                 IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(message, child));
334             }
335         }
336 
337         if (mSendMessage == &message)
338         {
339             mSendMessage = nullptr;
340         }
341 
342         LogMessage(kMessageDrop, message);
343         mSendQueue.DequeueAndFree(message);
344     }
345 }
346 
SendMesh(Message & aMessage,Mac::TxFrame & aFrame)347 void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
348 {
349     uint16_t fcf;
350     bool     iePresent = CalcIePresent(&aMessage);
351 
352     // initialize MAC header
353     fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfDstAddrShort |
354           Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfAckRequest | Mac::Frame::kFcfSecurityEnabled;
355 
356     if (iePresent)
357     {
358         fcf |= Mac::Frame::kFcfIePresent;
359     }
360 
361     fcf |= CalcFrameVersion(Get<NeighborTable>().FindNeighbor(mMacDest), iePresent);
362 
363     aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
364     aFrame.SetDstPanId(Get<Mac::Mac>().GetPanId());
365     aFrame.SetDstAddr(mMacDest.GetShort());
366     aFrame.SetSrcAddr(mMacSource.GetShort());
367 
368 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
369     if (iePresent)
370     {
371         AppendHeaderIe(&aMessage, aFrame);
372     }
373 #endif
374 
375     // write payload
376     OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
377     aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength());
378     aFrame.SetPayloadLength(aMessage.GetLength());
379 
380     mMessageNextOffset = aMessage.GetLength();
381 }
382 
UpdateMeshRoute(Message & aMessage)383 Error MeshForwarder::UpdateMeshRoute(Message &aMessage)
384 {
385     Error              error = kErrorNone;
386     Lowpan::MeshHeader meshHeader;
387     Neighbor *         neighbor;
388     uint16_t           nextHop;
389 
390     IgnoreError(meshHeader.ParseFrom(aMessage));
391 
392     nextHop = Get<Mle::MleRouter>().GetNextHop(meshHeader.GetDestination());
393 
394     if (nextHop != Mac::kShortAddrInvalid)
395     {
396         neighbor = Get<NeighborTable>().FindNeighbor(nextHop);
397     }
398     else
399     {
400         neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
401     }
402 
403     if (neighbor == nullptr)
404     {
405         ExitNow(error = kErrorDrop);
406     }
407 
408     mMacDest.SetShort(neighbor->GetRloc16());
409     mMacSource.SetShort(Get<Mac::Mac>().GetShortAddress());
410 
411     mAddMeshHeader = true;
412     mMeshDest      = meshHeader.GetDestination();
413     mMeshSource    = meshHeader.GetSource();
414 
415 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
416     if (mMacDest.GetShort() != mMeshDest)
417     {
418         mDelayNextTx = true;
419     }
420 #endif
421 
422 exit:
423     return error;
424 }
425 
EvaluateRoutingCost(uint16_t aDest,uint8_t & aBestCost,uint16_t & aBestDest) const426 void MeshForwarder::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const
427 {
428     const Neighbor *neighbor;
429     uint8_t         curCost = 0x00;
430 
431     // Path cost
432     curCost = Get<Mle::MleRouter>().GetCost(aDest);
433 
434     if (!Mle::MleRouter::IsActiveRouter(aDest))
435     {
436         // Assume best link between remote child server and its parent.
437         curCost += 1;
438     }
439 
440     // Cost if the server is direct neighbor.
441     neighbor = Get<NeighborTable>().FindNeighbor(aDest);
442 
443     if (neighbor != nullptr && neighbor->IsStateValid())
444     {
445         uint8_t cost;
446 
447         if (!Mle::MleRouter::IsActiveRouter(aDest))
448         {
449             // Cost calculated only from Link Quality In as the parent only maintains
450             // one-direction link info.
451             cost = Mle::MleRouter::LinkQualityToCost(neighbor->GetLinkInfo().GetLinkQuality());
452         }
453         else
454         {
455             cost = Get<Mle::MleRouter>().GetLinkCost(Mle::Mle::RouterIdFromRloc16(aDest));
456         }
457 
458         // Choose the minimum cost
459         curCost = OT_MIN(curCost, cost);
460     }
461 
462     if ((aBestDest == Mac::kShortAddrInvalid) || (curCost < aBestCost))
463     {
464         aBestDest = aDest;
465         aBestCost = curCost;
466     }
467 }
468 
AnycastRouteLookup(uint8_t aServiceId,AnycastType aType,uint16_t & aMeshDest) const469 Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aMeshDest) const
470 {
471     NetworkData::Iterator iterator = NetworkData::kIteratorInit;
472     uint8_t               bestCost = Mle::kMaxRouteCost;
473     uint16_t              bestDest = Mac::kShortAddrInvalid;
474     uint8_t               routerId;
475 
476     switch (aType)
477     {
478     case kAnycastDhcp6Agent:
479     case kAnycastNeighborDiscoveryAgent:
480     {
481         NetworkData::OnMeshPrefixConfig config;
482         Lowpan::Context                 context;
483 
484         SuccessOrExit(Get<NetworkData::Leader>().GetContext(aServiceId, context));
485 
486         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
487         {
488             if (config.GetPrefix() != context.mPrefix)
489             {
490                 continue;
491             }
492 
493             switch (aType)
494             {
495             case kAnycastDhcp6Agent:
496                 if (!(config.mDhcp || config.mConfigure))
497                 {
498                     continue;
499                 }
500                 break;
501             case kAnycastNeighborDiscoveryAgent:
502                 if (!config.mNdDns)
503                 {
504                     continue;
505                 }
506                 break;
507             default:
508                 OT_ASSERT(false);
509                 break;
510             }
511 
512             EvaluateRoutingCost(config.mRloc16, bestCost, bestDest);
513         }
514 
515         break;
516     }
517     case kAnycastService:
518     {
519         NetworkData::ServiceConfig config;
520 
521         while (Get<NetworkData::Leader>().GetNextService(iterator, config) == kErrorNone)
522         {
523             if (config.mServiceId != aServiceId)
524             {
525                 continue;
526             }
527 
528             EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest);
529         }
530 
531         break;
532     }
533     }
534 
535     routerId = Mle::Mle::RouterIdFromRloc16(bestDest);
536 
537     if (!(Mle::Mle::IsActiveRouter(bestDest) ||
538           Mle::Mle::Rloc16FromRouterId(routerId) == Get<Mle::MleRouter>().GetRloc16()))
539     {
540         // if agent is neither active router nor child of this device
541         // use the parent of the ED Agent as Dest
542         bestDest = Mle::Mle::Rloc16FromRouterId(routerId);
543     }
544 
545     aMeshDest = bestDest;
546 
547 exit:
548     return (bestDest != Mac::kShortAddrInvalid) ? kErrorNone : kErrorNoRoute;
549 }
550 
UpdateIp6RouteFtd(Ip6::Header & ip6Header,Message & aMessage)551 Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage)
552 {
553     Mle::MleRouter &mle   = Get<Mle::MleRouter>();
554     Error           error = kErrorNone;
555     Neighbor *      neighbor;
556 
557     if (aMessage.GetOffset() > 0)
558     {
559         mMeshDest = aMessage.GetMeshDest();
560     }
561     else if (mle.IsRoutingLocator(ip6Header.GetDestination()))
562     {
563         uint16_t rloc16 = ip6Header.GetDestination().GetIid().GetLocator();
564         VerifyOrExit(mle.IsRouterIdValid(Mle::Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop);
565         mMeshDest = rloc16;
566     }
567     else if (mle.IsAnycastLocator(ip6Header.GetDestination()))
568     {
569         uint16_t aloc16 = ip6Header.GetDestination().GetIid().GetLocator();
570 
571         if (aloc16 == Mle::kAloc16Leader)
572         {
573             mMeshDest = Mle::Mle::Rloc16FromRouterId(mle.GetLeaderId());
574         }
575         else if (aloc16 <= Mle::kAloc16DhcpAgentEnd)
576         {
577             uint8_t contextId = static_cast<uint8_t>(aloc16 - Mle::kAloc16DhcpAgentStart + 1);
578             SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastDhcp6Agent, mMeshDest));
579         }
580         else if (aloc16 <= Mle::kAloc16ServiceEnd)
581         {
582             uint8_t serviceId = static_cast<uint8_t>(aloc16 - Mle::kAloc16ServiceStart);
583             SuccessOrExit(error = AnycastRouteLookup(serviceId, kAnycastService, mMeshDest));
584         }
585         else if (aloc16 <= Mle::kAloc16CommissionerEnd)
586         {
587             SuccessOrExit(error = MeshCoP::GetBorderAgentRloc(Get<ThreadNetif>(), mMeshDest));
588         }
589 
590 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
591         else if (aloc16 == Mle::kAloc16BackboneRouterPrimary)
592         {
593             VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorDrop);
594             mMeshDest = Get<BackboneRouter::Leader>().GetServer16();
595         }
596 #endif
597         else if ((aloc16 >= Mle::kAloc16NeighborDiscoveryAgentStart) &&
598                  (aloc16 <= Mle::kAloc16NeighborDiscoveryAgentEnd))
599         {
600             uint8_t contextId = static_cast<uint8_t>(aloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1);
601             SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastNeighborDiscoveryAgent, mMeshDest));
602         }
603         else
604         {
605             ExitNow(error = kErrorDrop);
606         }
607     }
608     else if ((neighbor = Get<NeighborTable>().FindNeighbor(ip6Header.GetDestination())) != nullptr)
609     {
610         mMeshDest = neighbor->GetRloc16();
611     }
612     else if (Get<NetworkData::Leader>().IsOnMesh(ip6Header.GetDestination()))
613     {
614         SuccessOrExit(error = Get<AddressResolver>().Resolve(ip6Header.GetDestination(), mMeshDest));
615     }
616     else
617     {
618         IgnoreError(Get<NetworkData::Leader>().RouteLookup(ip6Header.GetSource(), ip6Header.GetDestination(), nullptr,
619                                                            &mMeshDest));
620     }
621 
622     VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = kErrorDrop);
623 
624     mMeshSource = Get<Mac::Mac>().GetShortAddress();
625 
626     SuccessOrExit(error = mle.CheckReachability(mMeshDest, ip6Header));
627     aMessage.SetMeshDest(mMeshDest);
628     mMacDest.SetShort(mle.GetNextHop(mMeshDest));
629 
630     if (mMacDest.GetShort() != mMeshDest)
631     {
632         // destination is not neighbor
633         mMacSource.SetShort(mMeshSource);
634         mAddMeshHeader = true;
635 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
636         mDelayNextTx = true;
637 #endif
638     }
639 
640 exit:
641     return error;
642 }
643 
SendIcmpErrorIfDstUnreach(const Message & aMessage,const Mac::Address & aMacSource,const Mac::Address & aMacDest)644 void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &     aMessage,
645                                               const Mac::Address &aMacSource,
646                                               const Mac::Address &aMacDest)
647 {
648     Error        error;
649     Ip6::Headers ip6Headers;
650     Child *      child;
651 
652     VerifyOrExit(aMacSource.IsShort() && aMacDest.IsShort());
653 
654     child = Get<ChildTable>().FindChild(aMacSource.GetShort(), Child::kInStateAnyExceptInvalid);
655     VerifyOrExit((child == nullptr) || child->IsFullThreadDevice());
656 
657     SuccessOrExit(ip6Headers.ParseFrom(aMessage));
658 
659     VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() &&
660                  Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetDestinationAddress()));
661 
662     error = Get<Mle::MleRouter>().CheckReachability(aMacDest.GetShort(), ip6Headers.GetIp6Header());
663 
664     if (error == kErrorNoRoute)
665     {
666         SendDestinationUnreachable(aMacSource.GetShort(), ip6Headers);
667     }
668 
669 exit:
670     return;
671 }
672 
CheckReachability(const FrameData & aFrameData,const Mac::Address & aMeshSource,const Mac::Address & aMeshDest)673 Error MeshForwarder::CheckReachability(const FrameData &   aFrameData,
674                                        const Mac::Address &aMeshSource,
675                                        const Mac::Address &aMeshDest)
676 {
677     Error        error;
678     Ip6::Headers ip6Headers;
679 
680     error = ip6Headers.DecompressFrom(aFrameData, aMeshSource, aMeshDest, GetInstance());
681 
682     if (error == kErrorNotFound)
683     {
684         // Frame may not contain an IPv6 header.
685         ExitNow(error = kErrorNone);
686     }
687 
688     error = Get<Mle::MleRouter>().CheckReachability(aMeshDest.GetShort(), ip6Headers.GetIp6Header());
689 
690     if (error == kErrorNoRoute)
691     {
692         SendDestinationUnreachable(aMeshSource.GetShort(), ip6Headers);
693     }
694 
695 exit:
696     return error;
697 }
698 
SendDestinationUnreachable(uint16_t aMeshSource,const Ip6::Headers & aIp6Headers)699 void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers)
700 {
701     Ip6::MessageInfo messageInfo;
702 
703     messageInfo.GetPeerAddr() = Get<Mle::MleRouter>().GetMeshLocal16();
704     messageInfo.GetPeerAddr().GetIid().SetLocator(aMeshSource);
705 
706     IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
707                                            Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers));
708 }
709 
HandleMesh(FrameData & aFrameData,const Mac::Address & aMacSource,const ThreadLinkInfo & aLinkInfo)710 void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo)
711 {
712     Error              error   = kErrorNone;
713     Message *          message = nullptr;
714     Mac::Address       meshDest;
715     Mac::Address       meshSource;
716     Lowpan::MeshHeader meshHeader;
717 
718     // Security Check: only process Mesh Header frames that had security enabled.
719     VerifyOrExit(aLinkInfo.IsLinkSecurityEnabled(), error = kErrorSecurity);
720 
721     SuccessOrExit(error = meshHeader.ParseFrom(aFrameData));
722 
723     meshSource.SetShort(meshHeader.GetSource());
724     meshDest.SetShort(meshHeader.GetDestination());
725 
726     UpdateRoutes(aFrameData, meshSource, meshDest);
727 
728     if (meshDest.GetShort() == Get<Mac::Mac>().GetShortAddress() ||
729         Get<Mle::MleRouter>().IsMinimalChild(meshDest.GetShort()))
730     {
731         if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData))
732         {
733             HandleFragment(aFrameData, meshSource, meshDest, aLinkInfo);
734         }
735         else if (Lowpan::Lowpan::IsLowpanHc(aFrameData))
736         {
737             HandleLowpanHC(aFrameData, meshSource, meshDest, aLinkInfo);
738         }
739         else
740         {
741             ExitNow(error = kErrorParse);
742         }
743     }
744     else if (meshHeader.GetHopsLeft() > 0)
745     {
746         Message::Priority priority = Message::kPriorityNormal;
747 
748         Get<Mle::MleRouter>().ResolveRoutingLoops(aMacSource.GetShort(), meshDest.GetShort());
749 
750         SuccessOrExit(error = CheckReachability(aFrameData, meshSource, meshDest));
751 
752         meshHeader.DecrementHopsLeft();
753 
754         GetForwardFramePriority(aFrameData, meshSource, meshDest, priority);
755         message =
756             Get<MessagePool>().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority));
757         VerifyOrExit(message != nullptr, error = kErrorNoBufs);
758 
759         SuccessOrExit(error = meshHeader.AppendTo(*message));
760         SuccessOrExit(error = message->AppendData(aFrameData));
761 
762         message->SetLinkInfo(aLinkInfo);
763 
764 #if OPENTHREAD_CONFIG_MULTI_RADIO
765         // We set the received radio type on the message in order for it
766         // to be logged correctly from LogMessage().
767         message->SetRadioType(static_cast<Mac::RadioType>(aLinkInfo.mRadioType));
768 #endif
769 
770         LogMessage(kMessageReceive, *message, kErrorNone, &aMacSource);
771 
772 #if OPENTHREAD_CONFIG_MULTI_RADIO
773         // Since the message will be forwarded, we clear the radio
774         // type on the message to allow the radio type for tx to be
775         // selected later (based on the radios supported by the next
776         // hop).
777         message->ClearRadioType();
778 #endif
779 
780         IgnoreError(SendMessage(*message));
781     }
782 
783 exit:
784 
785     if (error != kErrorNone)
786     {
787         LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error),
788                 aFrameData.GetLength(), aMacSource.ToString().AsCString(), ToYesNo(aLinkInfo.IsLinkSecurityEnabled()));
789         FreeMessage(message);
790     }
791 }
792 
UpdateRoutes(const FrameData & aFrameData,const Mac::Address & aMeshSource,const Mac::Address & aMeshDest)793 void MeshForwarder::UpdateRoutes(const FrameData &   aFrameData,
794                                  const Mac::Address &aMeshSource,
795                                  const Mac::Address &aMeshDest)
796 {
797     Ip6::Headers ip6Headers;
798     Neighbor *   neighbor;
799 
800     VerifyOrExit(!aMeshDest.IsBroadcast() && aMeshSource.IsShort());
801 
802     SuccessOrExit(ip6Headers.DecompressFrom(aFrameData, aMeshSource, aMeshDest, GetInstance()));
803 
804     if (!ip6Headers.GetSourceAddress().GetIid().IsLocator() &&
805         Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetSourceAddress()))
806     {
807         // FTDs MAY add/update EID-to-RLOC Map Cache entries by
808         // inspecting packets being received only for on mesh
809         // addresses.
810 
811         Get<AddressResolver>().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aMeshSource.GetShort(),
812                                                        aMeshDest.GetShort());
813     }
814 
815     neighbor = Get<NeighborTable>().FindNeighbor(ip6Headers.GetSourceAddress());
816     VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice());
817 
818     if (!Mle::Mle::RouterIdMatch(aMeshSource.GetShort(), Get<Mac::Mac>().GetShortAddress()))
819     {
820         Get<Mle::MleRouter>().RemoveNeighbor(*neighbor);
821     }
822 
823 exit:
824     return;
825 }
826 
UpdateOnTimeTick(void)827 bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void)
828 {
829     bool contineRxingTicks = false;
830 
831     for (Entry &entry : mEntries)
832     {
833         if (!entry.IsExpired())
834         {
835             entry.DecrementLifetime();
836 
837             if (!entry.IsExpired())
838             {
839                 contineRxingTicks = true;
840             }
841         }
842     }
843 
844     return contineRxingTicks;
845 }
846 
UpdateFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aFragmentLength,uint16_t aSrcRloc16,Message::Priority aPriority)847 void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
848                                            uint16_t                aFragmentLength,
849                                            uint16_t                aSrcRloc16,
850                                            Message::Priority       aPriority)
851 {
852     FragmentPriorityList::Entry *entry;
853 
854     entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
855 
856     if (entry == nullptr)
857     {
858         VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
859 
860         mFragmentPriorityList.AllocateEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
861         Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
862         ExitNow();
863     }
864 
865 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
866     OT_UNUSED_VARIABLE(aFragmentLength);
867 #else
868     // We can clear the entry in `mFragmentPriorityList` if it is the
869     // last fragment. But if "delay aware active queue management" is
870     // used we need to keep entry until the message is sent.
871     if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
872     {
873         entry->Clear();
874     }
875     else
876 #endif
877     {
878         entry->ResetLifetime();
879     }
880 
881 exit:
882     return;
883 }
884 
FindEntry(uint16_t aSrcRloc16,uint16_t aTag)885 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::FindEntry(uint16_t aSrcRloc16,
886                                                                                            uint16_t aTag)
887 {
888     Entry *rval = nullptr;
889 
890     for (Entry &entry : mEntries)
891     {
892         if (!entry.IsExpired() && entry.Matches(aSrcRloc16, aTag))
893         {
894             rval = &entry;
895             break;
896         }
897     }
898 
899     return rval;
900 }
901 
AllocateEntry(uint16_t aSrcRloc16,uint16_t aTag,Message::Priority aPriority)902 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::AllocateEntry(
903     uint16_t          aSrcRloc16,
904     uint16_t          aTag,
905     Message::Priority aPriority)
906 {
907     Entry *newEntry = nullptr;
908 
909     for (Entry &entry : mEntries)
910     {
911         if (entry.IsExpired())
912         {
913             entry.Clear();
914             entry.mSrcRloc16   = aSrcRloc16;
915             entry.mDatagramTag = aTag;
916             entry.mPriority    = aPriority;
917             entry.ResetLifetime();
918             newEntry = &entry;
919             break;
920         }
921     }
922 
923     return newEntry;
924 }
925 
GetFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aSrcRloc16,Message::Priority & aPriority)926 Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
927                                          uint16_t                aSrcRloc16,
928                                          Message::Priority &     aPriority)
929 {
930     Error                        error = kErrorNone;
931     FragmentPriorityList::Entry *entry;
932 
933     entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
934     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
935     aPriority = entry->GetPriority();
936 
937 exit:
938     return error;
939 }
940 
GetForwardFramePriority(const FrameData & aFrameData,const Mac::Address & aMeshSource,const Mac::Address & aMeshDest,Message::Priority & aPriority)941 void MeshForwarder::GetForwardFramePriority(const FrameData &   aFrameData,
942                                             const Mac::Address &aMeshSource,
943                                             const Mac::Address &aMeshDest,
944                                             Message::Priority & aPriority)
945 {
946     Error                  error      = kErrorNone;
947     FrameData              frameData  = aFrameData;
948     bool                   isFragment = false;
949     Lowpan::FragmentHeader fragmentHeader;
950 
951     if (fragmentHeader.ParseFrom(frameData) == kErrorNone)
952     {
953         isFragment = true;
954 
955         if (fragmentHeader.GetDatagramOffset() > 0)
956         {
957             // Get priority from the pre-buffered info
958             ExitNow(error = GetFragmentPriority(fragmentHeader, aMeshSource.GetShort(), aPriority));
959         }
960     }
961 
962     // Get priority from IPv6 header or UDP destination port directly
963     error = GetFramePriority(frameData, aMeshSource, aMeshDest, aPriority);
964 
965 exit:
966     if (error != kErrorNone)
967     {
968         LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%d, dst:%s", ErrorToString(error),
969                 frameData.GetLength(), aMeshSource.ToString().AsCString(), aMeshDest.ToString().AsCString());
970     }
971     else if (isFragment)
972     {
973         UpdateFragmentPriority(fragmentHeader, frameData.GetLength(), aMeshSource.GetShort(), aPriority);
974     }
975 }
976 
977 // LCOV_EXCL_START
978 
979 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
980 
LogMeshFragmentHeader(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,uint16_t & aOffset,Mac::Address & aMeshSource,Mac::Address & aMeshDest,LogLevel aLogLevel)981 Error MeshForwarder::LogMeshFragmentHeader(MessageAction       aAction,
982                                            const Message &     aMessage,
983                                            const Mac::Address *aMacAddress,
984                                            Error               aError,
985                                            uint16_t &          aOffset,
986                                            Mac::Address &      aMeshSource,
987                                            Mac::Address &      aMeshDest,
988                                            LogLevel            aLogLevel)
989 {
990     Error                  error             = kErrorFailed;
991     bool                   hasFragmentHeader = false;
992     bool                   shouldLogRss;
993     Lowpan::MeshHeader     meshHeader;
994     Lowpan::FragmentHeader fragmentHeader;
995     uint16_t               headerLength;
996     bool                   shouldLogRadio = false;
997     const char *           radioString    = "";
998 
999     SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength));
1000 
1001     aMeshSource.SetShort(meshHeader.GetSource());
1002     aMeshDest.SetShort(meshHeader.GetDestination());
1003 
1004     aOffset = headerLength;
1005 
1006     if (fragmentHeader.ParseFrom(aMessage, aOffset, headerLength) == kErrorNone)
1007     {
1008         hasFragmentHeader = true;
1009         aOffset += headerLength;
1010     }
1011 
1012     shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop);
1013 
1014 #if OPENTHREAD_CONFIG_MULTI_RADIO
1015     shouldLogRadio = true;
1016     radioString    = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all";
1017 #endif
1018 
1019     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",
1020           MessageActionToString(aAction, aError), aMessage.GetLength(),
1021           (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
1022           (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), aMeshSource.ToString().AsCString(),
1023           aMeshDest.ToString().AsCString(), meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0),
1024           ToYesNo(hasFragmentHeader), ToYesNo(aMessage.IsLinkSecurityEnabled()),
1025           (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
1026           shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "",
1027           shouldLogRadio ? ", radio:" : "", radioString);
1028 
1029     if (hasFragmentHeader)
1030     {
1031         LogAt(aLogLevel, "    Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
1032               fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
1033 
1034         VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
1035     }
1036 
1037     error = kErrorNone;
1038 
1039 exit:
1040     return error;
1041 }
1042 
LogMeshIpHeader(const Message & aMessage,uint16_t aOffset,const Mac::Address & aMeshSource,const Mac::Address & aMeshDest,LogLevel aLogLevel)1043 void MeshForwarder::LogMeshIpHeader(const Message &     aMessage,
1044                                     uint16_t            aOffset,
1045                                     const Mac::Address &aMeshSource,
1046                                     const Mac::Address &aMeshDest,
1047                                     LogLevel            aLogLevel)
1048 {
1049     Ip6::Headers headers;
1050 
1051     SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshSource, aMeshDest));
1052 
1053     LogAt(aLogLevel, "    IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()),
1054           headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage));
1055 
1056     LogIp6SourceDestAddresses(headers, aLogLevel);
1057 
1058 exit:
1059     return;
1060 }
1061 
LogMeshMessage(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,LogLevel aLogLevel)1062 void MeshForwarder::LogMeshMessage(MessageAction       aAction,
1063                                    const Message &     aMessage,
1064                                    const Mac::Address *aMacAddress,
1065                                    Error               aError,
1066                                    LogLevel            aLogLevel)
1067 {
1068     uint16_t     offset;
1069     Mac::Address meshSource;
1070     Mac::Address meshDest;
1071 
1072     SuccessOrExit(
1073         LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshSource, meshDest, aLogLevel));
1074 
1075     // When log action is `kMessageTransmit` we do not include
1076     // the IPv6 header info in the logs, as the same info is
1077     // logged when the same Mesh Header message was received
1078     // and info about it was logged.
1079 
1080     VerifyOrExit(aAction != kMessageTransmit);
1081 
1082     LogMeshIpHeader(aMessage, offset, meshSource, meshDest, aLogLevel);
1083 
1084 exit:
1085     return;
1086 }
1087 
1088 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
1089 
1090 // LCOV_EXCL_STOP
1091 
1092 } // namespace ot
1093 
1094 #endif // OPENTHREAD_FTD
1095