1 /*
2 * Copyright (c) 2018, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements FTD-specific mesh forwarding of IPv6/6LoWPAN messages.
32 */
33
34 #include "mesh_forwarder.hpp"
35
36 #if OPENTHREAD_FTD
37
38 #include "common/locator_getters.hpp"
39 #include "common/num_utils.hpp"
40 #include "meshcop/meshcop.hpp"
41 #include "net/ip6.hpp"
42 #include "net/tcp6.hpp"
43 #include "net/udp6.hpp"
44
45 namespace ot {
46
47 RegisterLogModule("MeshForwarder");
48
SendMessage(OwnedPtr<Message> aMessagePtr)49 void MeshForwarder::SendMessage(OwnedPtr<Message> aMessagePtr)
50 {
51 Message &message = *aMessagePtr.Release();
52
53 message.SetOffset(0);
54 message.SetDatagramTag(0);
55 message.SetTimestampToNow();
56 mSendQueue.Enqueue(message);
57
58 switch (message.GetType())
59 {
60 case Message::kTypeIp6:
61 {
62 Ip6::Header ip6Header;
63 const Ip6::Address &destination = ip6Header.GetDestination();
64
65 IgnoreError(message.Read(0, ip6Header));
66
67 if (destination.IsMulticast())
68 {
69 // For traffic destined to multicast address larger than realm local, generally it uses IP-in-IP
70 // encapsulation (RFC2473), with outer destination as ALL_MPL_FORWARDERS. So here if the destination
71 // is multicast address larger than realm local, it should be for indirection transmission for the
72 // device's sleepy child, thus there should be no direct transmission.
73 if (!destination.IsMulticastLargerThanRealmLocal())
74 {
75 message.SetDirectTransmission();
76 }
77
78 if (message.GetSubType() != Message::kSubTypeMplRetransmission)
79 {
80 // Check if we need to forward the multicast message
81 // to any sleepy child. This is skipped for MPL retx
82 // (only the first MPL transmission is forwarded to
83 // sleepy children).
84
85 bool destinedForAll = ((destination == Get<Mle::Mle>().GetLinkLocalAllThreadNodesAddress()) ||
86 (destination == Get<Mle::Mle>().GetRealmLocalAllThreadNodesAddress()));
87
88 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
89 {
90 if (!child.IsRxOnWhenIdle() && (destinedForAll || child.HasIp6Address(destination)))
91 {
92 mIndirectSender.AddMessageForSleepyChild(message, child);
93 }
94 }
95 }
96 }
97 else // Destination is unicast
98 {
99 Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(destination);
100
101 if ((neighbor != nullptr) && !neighbor->IsRxOnWhenIdle() && !message.IsDirectTransmission() &&
102 Get<ChildTable>().Contains(*neighbor))
103 {
104 mIndirectSender.AddMessageForSleepyChild(message, *static_cast<Child *>(neighbor));
105 }
106 else
107 {
108 message.SetDirectTransmission();
109 }
110 }
111
112 break;
113 }
114
115 case Message::kTypeSupervision:
116 {
117 Child *child = Get<ChildSupervisor>().GetDestination(message);
118 OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle());
119 mIndirectSender.AddMessageForSleepyChild(message, *child);
120 break;
121 }
122
123 default:
124 message.SetDirectTransmission();
125 break;
126 }
127
128 // Ensure that the message is marked for direct tx and/or for indirect tx
129 // to a sleepy child. Otherwise, remove the message.
130
131 if (RemoveMessageIfNoPendingTx(message))
132 {
133 ExitNow();
134 }
135
136 #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
137 ApplyDirectTxQueueLimit(message);
138 #endif
139
140 mScheduleTransmissionTask.Post();
141
142 exit:
143 return;
144 }
145
HandleResolved(const Ip6::Address & aEid,Error aError)146 void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError)
147 {
148 Ip6::Address ip6Dst;
149 bool didUpdate = false;
150
151 for (Message &message : mSendQueue)
152 {
153 if (!message.IsResolvingAddress())
154 {
155 continue;
156 }
157
158 IgnoreError(message.Read(Ip6::Header::kDestinationFieldOffset, ip6Dst));
159
160 if (ip6Dst != aEid)
161 {
162 continue;
163 }
164
165 if (aError != kErrorNone)
166 {
167 LogMessage(kMessageDrop, message, kErrorAddressQuery);
168 FinalizeMessageDirectTx(message, kErrorAddressQuery);
169 mSendQueue.DequeueAndFree(message);
170 continue;
171 }
172
173 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
174 // Pass back to IPv6 layer for DUA destination resolved
175 // by Backbone Query
176 if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(ip6Dst) &&
177 Get<AddressResolver>().LookUp(ip6Dst) == Get<Mle::MleRouter>().GetRloc16())
178 {
179 uint8_t hopLimit;
180
181 mSendQueue.Dequeue(message);
182
183 // Avoid decreasing Hop Limit twice
184 IgnoreError(message.Read(Ip6::Header::kHopLimitFieldOffset, hopLimit));
185 hopLimit++;
186 message.Write(Ip6::Header::kHopLimitFieldOffset, hopLimit);
187 message.SetLoopbackToHostAllowed(true);
188 message.SetOrigin(Message::kOriginHostTrusted);
189
190 IgnoreError(Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&message)));
191 continue;
192 }
193 #endif
194
195 message.SetResolvingAddress(false);
196 didUpdate = true;
197 }
198
199 if (didUpdate)
200 {
201 mScheduleTransmissionTask.Post();
202 }
203 }
204
EvictMessage(Message::Priority aPriority)205 Error MeshForwarder::EvictMessage(Message::Priority aPriority)
206 {
207 Error error = kErrorNotFound;
208 Message *evict = nullptr;
209
210 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
211 error = RemoveAgedMessages();
212 VerifyOrExit(error == kErrorNotFound);
213 #endif
214
215 // Search for a lower priority message to evict
216 for (uint8_t priority = 0; priority < aPriority; priority++)
217 {
218 for (Message *message = mSendQueue.GetHeadForPriority(static_cast<Message::Priority>(priority)); message;
219 message = message->GetNext())
220 {
221 if (message->GetPriority() != priority)
222 {
223 break;
224 }
225
226 if (message->GetDoNotEvict())
227 {
228 continue;
229 }
230
231 evict = message;
232 error = kErrorNone;
233 ExitNow();
234 }
235 }
236
237 for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++)
238 {
239 // search for an equal or higher priority indirect message to evict
240 for (Message *message = mSendQueue.GetHeadForPriority(aPriority); message; message = message->GetNext())
241 {
242 if (message->GetPriority() != priority)
243 {
244 break;
245 }
246
247 if (message->GetDoNotEvict())
248 {
249 continue;
250 }
251
252 if (message->IsChildPending())
253 {
254 evict = message;
255 ExitNow(error = kErrorNone);
256 }
257 }
258 }
259
260 exit:
261 if ((error == kErrorNone) && (evict != nullptr))
262 {
263 EvictMessage(*evict);
264 }
265
266 return error;
267 }
268
RemoveMessages(Child & aChild,Message::SubType aSubType)269 void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType)
270 {
271 for (Message &message : mSendQueue)
272 {
273 if ((aSubType != Message::kSubTypeNone) && (aSubType != message.GetSubType()))
274 {
275 continue;
276 }
277
278 if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
279 {
280 switch (message.GetType())
281 {
282 case Message::kTypeIp6:
283 {
284 Ip6::Header ip6header;
285
286 IgnoreError(message.Read(0, ip6header));
287
288 if (&aChild == Get<NeighborTable>().FindNeighbor(ip6header.GetDestination()))
289 {
290 message.ClearDirectTransmission();
291 }
292
293 break;
294 }
295
296 case Message::kType6lowpan:
297 {
298 Lowpan::MeshHeader meshHeader;
299
300 IgnoreError(meshHeader.ParseFrom(message));
301
302 if (&aChild == Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination()))
303 {
304 message.ClearDirectTransmission();
305 }
306
307 break;
308 }
309
310 default:
311 break;
312 }
313 }
314
315 RemoveMessageIfNoPendingTx(message);
316 }
317 }
318
RemoveDataResponseMessages(void)319 void MeshForwarder::RemoveDataResponseMessages(void)
320 {
321 Ip6::Header ip6Header;
322
323 for (Message &message : mSendQueue)
324 {
325 if (message.GetSubType() != Message::kSubTypeMleDataResponse)
326 {
327 continue;
328 }
329
330 IgnoreError(message.Read(0, ip6Header));
331
332 if (!(ip6Header.GetDestination().IsMulticast()))
333 {
334 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
335 {
336 IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(message, child));
337 }
338 }
339
340 if (mSendMessage == &message)
341 {
342 mSendMessage = nullptr;
343 }
344
345 LogMessage(kMessageDrop, message);
346 FinalizeMessageDirectTx(message, kErrorDrop);
347 mSendQueue.DequeueAndFree(message);
348 }
349 }
350
SendMesh(Message & aMessage,Mac::TxFrame & aFrame)351 void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
352 {
353 Mac::PanIds panIds;
354
355 panIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
356
357 PrepareMacHeaders(aFrame, Mac::Frame::kTypeData, mMacAddrs, panIds, Mac::Frame::kSecurityEncMic32,
358 Mac::Frame::kKeyIdMode1, &aMessage);
359
360 // write payload
361 OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
362 aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength());
363 aFrame.SetPayloadLength(aMessage.GetLength());
364
365 mMessageNextOffset = aMessage.GetLength();
366 }
367
UpdateMeshRoute(Message & aMessage)368 Error MeshForwarder::UpdateMeshRoute(Message &aMessage)
369 {
370 Error error = kErrorNone;
371 Lowpan::MeshHeader meshHeader;
372 Neighbor *neighbor;
373 uint16_t nextHop;
374
375 IgnoreError(meshHeader.ParseFrom(aMessage));
376
377 nextHop = Get<Mle::MleRouter>().GetNextHop(meshHeader.GetDestination());
378
379 if (nextHop != Mac::kShortAddrInvalid)
380 {
381 neighbor = Get<NeighborTable>().FindNeighbor(nextHop);
382 }
383 else
384 {
385 neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
386 }
387
388 if (neighbor == nullptr)
389 {
390 ExitNow(error = kErrorDrop);
391 }
392
393 mMacAddrs.mDestination.SetShort(neighbor->GetRloc16());
394 mMacAddrs.mSource.SetShort(Get<Mac::Mac>().GetShortAddress());
395
396 mAddMeshHeader = true;
397 mMeshDest = meshHeader.GetDestination();
398 mMeshSource = meshHeader.GetSource();
399
400 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
401 if (mMacAddrs.mDestination.GetShort() != mMeshDest)
402 {
403 mDelayNextTx = true;
404 }
405 #endif
406
407 exit:
408 return error;
409 }
410
EvaluateRoutingCost(uint16_t aDest,uint8_t & aBestCost,uint16_t & aBestDest) const411 void MeshForwarder::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const
412 {
413 uint8_t cost = Get<RouterTable>().GetPathCost(aDest);
414
415 if ((aBestDest == Mac::kShortAddrInvalid) || (cost < aBestCost))
416 {
417 aBestDest = aDest;
418 aBestCost = cost;
419 }
420 }
421
AnycastRouteLookup(uint8_t aServiceId,AnycastType aType,uint16_t & aMeshDest) const422 Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aMeshDest) const
423 {
424 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
425 uint8_t bestCost = Mle::kMaxRouteCost;
426 uint16_t bestDest = Mac::kShortAddrInvalid;
427 uint8_t routerId;
428
429 switch (aType)
430 {
431 case kAnycastDhcp6Agent:
432 case kAnycastNeighborDiscoveryAgent:
433 {
434 NetworkData::OnMeshPrefixConfig config;
435 Lowpan::Context context;
436
437 SuccessOrExit(Get<NetworkData::Leader>().GetContext(aServiceId, context));
438
439 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
440 {
441 if (config.GetPrefix() != context.mPrefix)
442 {
443 continue;
444 }
445
446 switch (aType)
447 {
448 case kAnycastDhcp6Agent:
449 if (!(config.mDhcp || config.mConfigure))
450 {
451 continue;
452 }
453 break;
454 case kAnycastNeighborDiscoveryAgent:
455 if (!config.mNdDns)
456 {
457 continue;
458 }
459 break;
460 default:
461 OT_ASSERT(false);
462 break;
463 }
464
465 EvaluateRoutingCost(config.mRloc16, bestCost, bestDest);
466 }
467
468 break;
469 }
470 case kAnycastService:
471 {
472 NetworkData::ServiceConfig config;
473
474 while (Get<NetworkData::Leader>().GetNextService(iterator, config) == kErrorNone)
475 {
476 if (config.mServiceId != aServiceId)
477 {
478 continue;
479 }
480
481 EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest);
482 }
483
484 break;
485 }
486 }
487
488 routerId = Mle::RouterIdFromRloc16(bestDest);
489
490 if (!(Mle::IsActiveRouter(bestDest) || Mle::Rloc16FromRouterId(routerId) == Get<Mle::MleRouter>().GetRloc16()))
491 {
492 // if agent is neither active router nor child of this device
493 // use the parent of the ED Agent as Dest
494 bestDest = Mle::Rloc16FromRouterId(routerId);
495 }
496
497 aMeshDest = bestDest;
498
499 exit:
500 return (bestDest != Mac::kShortAddrInvalid) ? kErrorNone : kErrorNoRoute;
501 }
502
UpdateIp6RouteFtd(const Ip6::Header & aIp6Header,Message & aMessage)503 Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage)
504 {
505 Mle::MleRouter &mle = Get<Mle::MleRouter>();
506 Error error = kErrorNone;
507 Neighbor *neighbor;
508
509 if (aMessage.GetOffset() > 0)
510 {
511 mMeshDest = aMessage.GetMeshDest();
512 }
513 else if (mle.IsRoutingLocator(aIp6Header.GetDestination()))
514 {
515 uint16_t rloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
516 VerifyOrExit(Mle::IsRouterIdValid(Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop);
517 mMeshDest = rloc16;
518 }
519 else if (mle.IsAnycastLocator(aIp6Header.GetDestination()))
520 {
521 uint16_t aloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
522
523 if (aloc16 == Mle::kAloc16Leader)
524 {
525 mMeshDest = Mle::Rloc16FromRouterId(mle.GetLeaderId());
526 }
527 else if (aloc16 <= Mle::kAloc16DhcpAgentEnd)
528 {
529 uint8_t contextId = static_cast<uint8_t>(aloc16 - Mle::kAloc16DhcpAgentStart + 1);
530 SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastDhcp6Agent, mMeshDest));
531 }
532 else if (aloc16 <= Mle::kAloc16ServiceEnd)
533 {
534 uint8_t serviceId = static_cast<uint8_t>(aloc16 - Mle::kAloc16ServiceStart);
535 SuccessOrExit(error = AnycastRouteLookup(serviceId, kAnycastService, mMeshDest));
536 }
537 else if (aloc16 <= Mle::kAloc16CommissionerEnd)
538 {
539 SuccessOrExit(error = Get<NetworkData::Leader>().FindBorderAgentRloc(mMeshDest));
540 }
541
542 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
543 else if (aloc16 == Mle::kAloc16BackboneRouterPrimary)
544 {
545 VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorDrop);
546 mMeshDest = Get<BackboneRouter::Leader>().GetServer16();
547 }
548 #endif
549 else if ((aloc16 >= Mle::kAloc16NeighborDiscoveryAgentStart) &&
550 (aloc16 <= Mle::kAloc16NeighborDiscoveryAgentEnd))
551 {
552 uint8_t contextId = static_cast<uint8_t>(aloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1);
553 SuccessOrExit(error = AnycastRouteLookup(contextId, kAnycastNeighborDiscoveryAgent, mMeshDest));
554 }
555 else
556 {
557 ExitNow(error = kErrorDrop);
558 }
559 }
560 else if ((neighbor = Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination())) != nullptr)
561 {
562 mMeshDest = neighbor->GetRloc16();
563 }
564 else if (Get<NetworkData::Leader>().IsOnMesh(aIp6Header.GetDestination()))
565 {
566 SuccessOrExit(error = Get<AddressResolver>().Resolve(aIp6Header.GetDestination(), mMeshDest));
567 }
568 else
569 {
570 IgnoreError(
571 Get<NetworkData::Leader>().RouteLookup(aIp6Header.GetSource(), aIp6Header.GetDestination(), mMeshDest));
572 }
573
574 VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = kErrorDrop);
575
576 mMeshSource = Get<Mac::Mac>().GetShortAddress();
577
578 SuccessOrExit(error = mle.CheckReachability(mMeshDest, aIp6Header));
579 aMessage.SetMeshDest(mMeshDest);
580 mMacAddrs.mDestination.SetShort(mle.GetNextHop(mMeshDest));
581
582 if (mMacAddrs.mDestination.GetShort() != mMeshDest)
583 {
584 // destination is not neighbor
585 mMacAddrs.mSource.SetShort(mMeshSource);
586 mAddMeshHeader = true;
587 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
588 mDelayNextTx = true;
589 #endif
590 }
591
592 exit:
593 return error;
594 }
595
SendIcmpErrorIfDstUnreach(const Message & aMessage,const Mac::Addresses & aMacAddrs)596 void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs)
597 {
598 Error error;
599 Ip6::Headers ip6Headers;
600 Child *child;
601
602 VerifyOrExit(aMacAddrs.mSource.IsShort() && aMacAddrs.mDestination.IsShort());
603
604 child = Get<ChildTable>().FindChild(aMacAddrs.mSource.GetShort(), Child::kInStateAnyExceptInvalid);
605 VerifyOrExit((child == nullptr) || child->IsFullThreadDevice());
606
607 SuccessOrExit(ip6Headers.ParseFrom(aMessage));
608
609 VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() &&
610 Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetDestinationAddress()));
611
612 error = Get<Mle::MleRouter>().CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
613
614 if (error == kErrorNoRoute)
615 {
616 SendDestinationUnreachable(aMacAddrs.mSource.GetShort(), ip6Headers);
617 }
618
619 exit:
620 return;
621 }
622
CheckReachability(const FrameData & aFrameData,const Mac::Addresses & aMeshAddrs)623 Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs)
624 {
625 Error error;
626 Ip6::Headers ip6Headers;
627
628 error = ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance());
629
630 switch (error)
631 {
632 case kErrorNone:
633 break;
634 case kErrorNotFound:
635 // Frame may not contain an IPv6 header.
636 error = kErrorNone;
637 OT_FALL_THROUGH;
638 default:
639 ExitNow();
640 }
641
642 error = Get<Mle::MleRouter>().CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
643
644 if (error == kErrorNoRoute)
645 {
646 SendDestinationUnreachable(aMeshAddrs.mSource.GetShort(), ip6Headers);
647 }
648
649 exit:
650 return error;
651 }
652
SendDestinationUnreachable(uint16_t aMeshSource,const Ip6::Headers & aIp6Headers)653 void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers)
654 {
655 Ip6::MessageInfo messageInfo;
656
657 messageInfo.GetPeerAddr() = Get<Mle::MleRouter>().GetMeshLocal16();
658 messageInfo.GetPeerAddr().GetIid().SetLocator(aMeshSource);
659
660 IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
661 Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers));
662 }
663
HandleMesh(FrameData & aFrameData,const Mac::Address & aMacSource,const ThreadLinkInfo & aLinkInfo)664 void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo)
665 {
666 Error error = kErrorNone;
667 Mac::Addresses meshAddrs;
668 Lowpan::MeshHeader meshHeader;
669
670 // Security Check: only process Mesh Header frames that had security enabled.
671 VerifyOrExit(aLinkInfo.IsLinkSecurityEnabled(), error = kErrorSecurity);
672
673 SuccessOrExit(error = meshHeader.ParseFrom(aFrameData));
674
675 meshAddrs.mSource.SetShort(meshHeader.GetSource());
676 meshAddrs.mDestination.SetShort(meshHeader.GetDestination());
677
678 UpdateRoutes(aFrameData, meshAddrs);
679
680 if (meshAddrs.mDestination.GetShort() == Get<Mac::Mac>().GetShortAddress() ||
681 Get<Mle::MleRouter>().IsMinimalChild(meshAddrs.mDestination.GetShort()))
682 {
683 if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData))
684 {
685 HandleFragment(aFrameData, meshAddrs, aLinkInfo);
686 }
687 else if (Lowpan::Lowpan::IsLowpanHc(aFrameData))
688 {
689 HandleLowpanHC(aFrameData, meshAddrs, aLinkInfo);
690 }
691 else
692 {
693 ExitNow(error = kErrorParse);
694 }
695 }
696 else if (meshHeader.GetHopsLeft() > 0)
697 {
698 OwnedPtr<Message> messagePtr;
699 Message::Priority priority = Message::kPriorityNormal;
700
701 Get<Mle::MleRouter>().ResolveRoutingLoops(aMacSource.GetShort(), meshAddrs.mDestination.GetShort());
702
703 SuccessOrExit(error = CheckReachability(aFrameData, meshAddrs));
704
705 meshHeader.DecrementHopsLeft();
706
707 GetForwardFramePriority(aFrameData, meshAddrs, priority);
708 messagePtr.Reset(
709 Get<MessagePool>().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority)));
710 VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs);
711
712 SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
713 SuccessOrExit(error = messagePtr->AppendData(aFrameData));
714
715 messagePtr->UpdateLinkInfoFrom(aLinkInfo);
716
717 LogMessage(kMessageReceive, *messagePtr, kErrorNone, &aMacSource);
718
719 #if OPENTHREAD_CONFIG_MULTI_RADIO
720 // Since the message will be forwarded, we clear the radio
721 // type on the message to allow the radio type for tx to be
722 // selected later (based on the radios supported by the next
723 // hop).
724 messagePtr->ClearRadioType();
725 #endif
726
727 SendMessage(messagePtr.PassOwnership());
728 }
729
730 exit:
731
732 if (error != kErrorNone)
733 {
734 LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error),
735 aFrameData.GetLength(), aMacSource.ToString().AsCString(), ToYesNo(aLinkInfo.IsLinkSecurityEnabled()));
736 }
737 }
738
UpdateRoutes(const FrameData & aFrameData,const Mac::Addresses & aMeshAddrs)739 void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs)
740 {
741 Ip6::Headers ip6Headers;
742 Neighbor *neighbor;
743
744 VerifyOrExit(!aMeshAddrs.mDestination.IsBroadcast() && aMeshAddrs.mSource.IsShort());
745
746 SuccessOrExit(ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance()));
747
748 if (!ip6Headers.GetSourceAddress().GetIid().IsLocator() &&
749 Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetSourceAddress()))
750 {
751 // FTDs MAY add/update EID-to-RLOC Map Cache entries by
752 // inspecting packets being received only for on mesh
753 // addresses.
754
755 Get<AddressResolver>().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aMeshAddrs.mSource.GetShort(),
756 aMeshAddrs.mDestination.GetShort());
757 }
758
759 neighbor = Get<NeighborTable>().FindNeighbor(ip6Headers.GetSourceAddress());
760 VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice());
761
762 if (!Mle::RouterIdMatch(aMeshAddrs.mSource.GetShort(), Get<Mac::Mac>().GetShortAddress()))
763 {
764 Get<Mle::MleRouter>().RemoveNeighbor(*neighbor);
765 }
766
767 exit:
768 return;
769 }
770
UpdateOnTimeTick(void)771 bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void)
772 {
773 bool continueRxingTicks = false;
774
775 for (Entry &entry : mEntries)
776 {
777 if (!entry.IsExpired())
778 {
779 entry.DecrementLifetime();
780
781 if (!entry.IsExpired())
782 {
783 continueRxingTicks = true;
784 }
785 }
786 }
787
788 return continueRxingTicks;
789 }
790
UpdateFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aFragmentLength,uint16_t aSrcRloc16,Message::Priority aPriority)791 void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
792 uint16_t aFragmentLength,
793 uint16_t aSrcRloc16,
794 Message::Priority aPriority)
795 {
796 FragmentPriorityList::Entry *entry;
797
798 entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
799
800 if (entry == nullptr)
801 {
802 VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
803
804 mFragmentPriorityList.AllocateEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
805 Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
806 ExitNow();
807 }
808
809 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
810 OT_UNUSED_VARIABLE(aFragmentLength);
811 #else
812 // We can clear the entry in `mFragmentPriorityList` if it is the
813 // last fragment. But if "delay aware active queue management" is
814 // used we need to keep entry until the message is sent.
815 if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
816 {
817 entry->Clear();
818 }
819 else
820 #endif
821 {
822 entry->ResetLifetime();
823 }
824
825 exit:
826 return;
827 }
828
FindEntry(uint16_t aSrcRloc16,uint16_t aTag)829 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::FindEntry(uint16_t aSrcRloc16,
830 uint16_t aTag)
831 {
832 Entry *rval = nullptr;
833
834 for (Entry &entry : mEntries)
835 {
836 if (!entry.IsExpired() && entry.Matches(aSrcRloc16, aTag))
837 {
838 rval = &entry;
839 break;
840 }
841 }
842
843 return rval;
844 }
845
AllocateEntry(uint16_t aSrcRloc16,uint16_t aTag,Message::Priority aPriority)846 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::AllocateEntry(
847 uint16_t aSrcRloc16,
848 uint16_t aTag,
849 Message::Priority aPriority)
850 {
851 Entry *newEntry = nullptr;
852
853 for (Entry &entry : mEntries)
854 {
855 if (entry.IsExpired())
856 {
857 entry.Clear();
858 entry.mSrcRloc16 = aSrcRloc16;
859 entry.mDatagramTag = aTag;
860 entry.mPriority = aPriority;
861 entry.ResetLifetime();
862 newEntry = &entry;
863 break;
864 }
865 }
866
867 return newEntry;
868 }
869
GetFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aSrcRloc16,Message::Priority & aPriority)870 Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
871 uint16_t aSrcRloc16,
872 Message::Priority &aPriority)
873 {
874 Error error = kErrorNone;
875 FragmentPriorityList::Entry *entry;
876
877 entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
878 VerifyOrExit(entry != nullptr, error = kErrorNotFound);
879 aPriority = entry->GetPriority();
880
881 exit:
882 return error;
883 }
884
GetForwardFramePriority(const FrameData & aFrameData,const Mac::Addresses & aMeshAddrs,Message::Priority & aPriority)885 void MeshForwarder::GetForwardFramePriority(const FrameData &aFrameData,
886 const Mac::Addresses &aMeshAddrs,
887 Message::Priority &aPriority)
888 {
889 Error error = kErrorNone;
890 FrameData frameData = aFrameData;
891 bool isFragment = false;
892 Lowpan::FragmentHeader fragmentHeader;
893
894 if (fragmentHeader.ParseFrom(frameData) == kErrorNone)
895 {
896 isFragment = true;
897
898 if (fragmentHeader.GetDatagramOffset() > 0)
899 {
900 // Get priority from the pre-buffered info
901 ExitNow(error = GetFragmentPriority(fragmentHeader, aMeshAddrs.mSource.GetShort(), aPriority));
902 }
903 }
904
905 // Get priority from IPv6 header or UDP destination port directly
906 error = GetFramePriority(frameData, aMeshAddrs, aPriority);
907
908 exit:
909 if (error != kErrorNone)
910 {
911 LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%s, dst:%s", ErrorToString(error),
912 frameData.GetLength(), aMeshAddrs.mSource.ToString().AsCString(),
913 aMeshAddrs.mDestination.ToString().AsCString());
914 }
915 else if (isFragment)
916 {
917 UpdateFragmentPriority(fragmentHeader, frameData.GetLength(), aMeshAddrs.mSource.GetShort(), aPriority);
918 }
919 }
920
921 // LCOV_EXCL_START
922
923 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
924
LogMeshFragmentHeader(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,uint16_t & aOffset,Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)925 Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction,
926 const Message &aMessage,
927 const Mac::Address *aMacAddress,
928 Error aError,
929 uint16_t &aOffset,
930 Mac::Addresses &aMeshAddrs,
931 LogLevel aLogLevel)
932 {
933 Error error = kErrorFailed;
934 bool hasFragmentHeader = false;
935 bool shouldLogRss;
936 Lowpan::MeshHeader meshHeader;
937 Lowpan::FragmentHeader fragmentHeader;
938 uint16_t headerLength;
939 bool shouldLogRadio = false;
940 const char *radioString = "";
941
942 SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength));
943
944 aMeshAddrs.mSource.SetShort(meshHeader.GetSource());
945 aMeshAddrs.mDestination.SetShort(meshHeader.GetDestination());
946
947 aOffset = headerLength;
948
949 if (fragmentHeader.ParseFrom(aMessage, aOffset, headerLength) == kErrorNone)
950 {
951 hasFragmentHeader = true;
952 aOffset += headerLength;
953 }
954
955 shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop);
956
957 #if OPENTHREAD_CONFIG_MULTI_RADIO
958 shouldLogRadio = true;
959 radioString = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all";
960 #endif
961
962 LogAt(aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s%s%s",
963 MessageActionToString(aAction, aError), aMessage.GetLength(),
964 (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
965 (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(),
966 aMeshAddrs.mSource.ToString().AsCString(), aMeshAddrs.mDestination.ToString().AsCString(),
967 meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), ToYesNo(hasFragmentHeader),
968 ToYesNo(aMessage.IsLinkSecurityEnabled()),
969 (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
970 shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "",
971 shouldLogRadio ? ", radio:" : "", radioString);
972
973 if (hasFragmentHeader)
974 {
975 LogAt(aLogLevel, " Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
976 fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
977
978 VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
979 }
980
981 error = kErrorNone;
982
983 exit:
984 return error;
985 }
986
LogMeshIpHeader(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)987 void MeshForwarder::LogMeshIpHeader(const Message &aMessage,
988 uint16_t aOffset,
989 const Mac::Addresses &aMeshAddrs,
990 LogLevel aLogLevel)
991 {
992 Ip6::Headers headers;
993
994 SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshAddrs));
995
996 LogAt(aLogLevel, " IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()),
997 headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage));
998
999 LogIp6SourceDestAddresses(headers, aLogLevel);
1000
1001 exit:
1002 return;
1003 }
1004
LogMeshMessage(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,LogLevel aLogLevel)1005 void MeshForwarder::LogMeshMessage(MessageAction aAction,
1006 const Message &aMessage,
1007 const Mac::Address *aMacAddress,
1008 Error aError,
1009 LogLevel aLogLevel)
1010 {
1011 uint16_t offset;
1012 Mac::Addresses meshAddrs;
1013
1014 SuccessOrExit(LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshAddrs, aLogLevel));
1015
1016 // When log action is `kMessageTransmit` we do not include
1017 // the IPv6 header info in the logs, as the same info is
1018 // logged when the same Mesh Header message was received
1019 // and info about it was logged.
1020
1021 VerifyOrExit(aAction != kMessageTransmit);
1022
1023 LogMeshIpHeader(aMessage, offset, meshAddrs, aLogLevel);
1024
1025 exit:
1026 return;
1027 }
1028
1029 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
1030
1031 // LCOV_EXCL_STOP
1032
1033 } // namespace ot
1034
1035 #endif // OPENTHREAD_FTD
1036