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