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