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