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 RemoveMessageIfNoPendingTx(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<Mle::Mle>().HasRloc16(Get<AddressResolver>().LookUp(ip6Dst)))
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
RemoveMessagesForChild(Child & aChild,MessageChecker & aMessageChecker)269 void MeshForwarder::RemoveMessagesForChild(Child &aChild, MessageChecker &aMessageChecker)
270 {
271 for (Message &message : mSendQueue)
272 {
273 if (!aMessageChecker(message))
274 {
275 continue;
276 }
277
278 if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
279 {
280 const Neighbor *neighbor = nullptr;
281
282 if (message.GetType() == Message::kTypeIp6)
283 {
284 Ip6::Header ip6header;
285
286 IgnoreError(message.Read(0, ip6header));
287 neighbor = Get<NeighborTable>().FindNeighbor(ip6header.GetDestination());
288 }
289 else if (message.GetType() == Message::kType6lowpan)
290 {
291 Lowpan::MeshHeader meshHeader;
292
293 IgnoreError(meshHeader.ParseFrom(message));
294 neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
295 }
296
297 if (&aChild == neighbor)
298 {
299 message.ClearDirectTransmission();
300 }
301 }
302
303 RemoveMessageIfNoPendingTx(message);
304 }
305 }
306
RemoveDataResponseMessages(void)307 void MeshForwarder::RemoveDataResponseMessages(void)
308 {
309 Ip6::Header ip6Header;
310
311 for (Message &message : mSendQueue)
312 {
313 if (message.GetSubType() != Message::kSubTypeMleDataResponse)
314 {
315 continue;
316 }
317
318 IgnoreError(message.Read(0, ip6Header));
319
320 if (!(ip6Header.GetDestination().IsMulticast()))
321 {
322 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
323 {
324 IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(message, child));
325 }
326 }
327
328 LogMessage(kMessageDrop, message);
329 FinalizeMessageDirectTx(message, kErrorDrop);
330 RemoveMessageIfNoPendingTx(message);
331 }
332 }
333
SendMesh(Message & aMessage,Mac::TxFrame & aFrame)334 void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
335 {
336 Mac::PanIds panIds;
337
338 panIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
339
340 PrepareMacHeaders(aFrame, Mac::Frame::kTypeData, mMacAddrs, panIds, Mac::Frame::kSecurityEncMic32,
341 Mac::Frame::kKeyIdMode1, &aMessage);
342
343 // write payload
344 OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
345 aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength());
346 aFrame.SetPayloadLength(aMessage.GetLength());
347
348 mMessageNextOffset = aMessage.GetLength();
349 }
350
UpdateMeshRoute(Message & aMessage)351 Error MeshForwarder::UpdateMeshRoute(Message &aMessage)
352 {
353 Error error = kErrorNone;
354 Lowpan::MeshHeader meshHeader;
355 Neighbor *neighbor;
356 uint16_t nextHop;
357
358 IgnoreError(meshHeader.ParseFrom(aMessage));
359
360 nextHop = Get<RouterTable>().GetNextHop(meshHeader.GetDestination());
361
362 if (nextHop != Mle::kInvalidRloc16)
363 {
364 neighbor = Get<NeighborTable>().FindNeighbor(nextHop);
365 }
366 else
367 {
368 neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
369 }
370
371 if (neighbor == nullptr)
372 {
373 ExitNow(error = kErrorDrop);
374 }
375
376 mMacAddrs.mDestination.SetShort(neighbor->GetRloc16());
377 mMacAddrs.mSource.SetShort(Get<Mle::Mle>().GetRloc16());
378
379 mAddMeshHeader = true;
380 mMeshDest = meshHeader.GetDestination();
381 mMeshSource = meshHeader.GetSource();
382
383 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
384 if (mMacAddrs.mDestination.GetShort() != mMeshDest)
385 {
386 mDelayNextTx = true;
387 }
388 #endif
389
390 exit:
391 return error;
392 }
393
UpdateIp6RouteFtd(const Ip6::Header & aIp6Header,Message & aMessage)394 Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage)
395 {
396 Mle::MleRouter &mle = Get<Mle::MleRouter>();
397 Error error = kErrorNone;
398 Neighbor *neighbor;
399
400 if (aMessage.GetOffset() > 0)
401 {
402 mMeshDest = aMessage.GetMeshDest();
403 }
404 else if (mle.IsRoutingLocator(aIp6Header.GetDestination()))
405 {
406 uint16_t rloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
407 VerifyOrExit(Mle::IsRouterIdValid(Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop);
408 mMeshDest = rloc16;
409 }
410 else if (mle.IsAnycastLocator(aIp6Header.GetDestination()))
411 {
412 uint16_t aloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
413
414 SuccessOrExit(error = Get<NetworkData::Leader>().AnycastLookup(aloc16, mMeshDest));
415
416 // If the selected ALOC destination, `mMeshDest`, is a sleepy
417 // child of this device, prepare the message for indirect tx
418 // to the sleepy child and un-mark message for direct tx.
419
420 if (mle.IsRouterOrLeader() && Mle::IsChildRloc16(mMeshDest) && mle.HasMatchingRouterIdWith(mMeshDest))
421 {
422 Child *child = Get<ChildTable>().FindChild(mMeshDest, Child::kInStateValid);
423
424 VerifyOrExit(child != nullptr, error = kErrorDrop);
425
426 if (!child->IsRxOnWhenIdle())
427 {
428 mIndirectSender.AddMessageForSleepyChild(aMessage, *child);
429 aMessage.ClearDirectTransmission();
430 }
431 }
432 }
433 else if ((neighbor = Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination())) != nullptr)
434 {
435 mMeshDest = neighbor->GetRloc16();
436 }
437 else if (Get<NetworkData::Leader>().IsOnMesh(aIp6Header.GetDestination()))
438 {
439 SuccessOrExit(error = Get<AddressResolver>().Resolve(aIp6Header.GetDestination(), mMeshDest));
440 }
441 else
442 {
443 IgnoreError(
444 Get<NetworkData::Leader>().RouteLookup(aIp6Header.GetSource(), aIp6Header.GetDestination(), mMeshDest));
445 }
446
447 VerifyOrExit(mMeshDest != Mle::kInvalidRloc16, error = kErrorDrop);
448
449 mMeshSource = Get<Mle::Mle>().GetRloc16();
450
451 SuccessOrExit(error = CheckReachability(mMeshDest, aIp6Header));
452 aMessage.SetMeshDest(mMeshDest);
453 mMacAddrs.mDestination.SetShort(Get<RouterTable>().GetNextHop(mMeshDest));
454
455 if (mMacAddrs.mDestination.GetShort() != mMeshDest)
456 {
457 // destination is not neighbor
458 mMacAddrs.mSource.SetShort(mMeshSource);
459 mAddMeshHeader = true;
460 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
461 mDelayNextTx = true;
462 #endif
463 }
464
465 exit:
466 return error;
467 }
468
SendIcmpErrorIfDstUnreach(const Message & aMessage,const Mac::Addresses & aMacAddrs)469 void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs)
470 {
471 Error error;
472 Ip6::Headers ip6Headers;
473 Child *child;
474
475 VerifyOrExit(aMacAddrs.mSource.IsShort() && aMacAddrs.mDestination.IsShort());
476
477 child = Get<ChildTable>().FindChild(aMacAddrs.mSource.GetShort(), Child::kInStateAnyExceptInvalid);
478 VerifyOrExit((child == nullptr) || child->IsFullThreadDevice());
479
480 SuccessOrExit(ip6Headers.ParseFrom(aMessage));
481
482 VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() &&
483 Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetDestinationAddress()));
484
485 error = CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
486
487 if (error == kErrorNoRoute)
488 {
489 SendDestinationUnreachable(aMacAddrs.mSource.GetShort(), ip6Headers);
490 }
491
492 exit:
493 return;
494 }
495
CheckReachability(RxInfo & aRxInfo)496 Error MeshForwarder::CheckReachability(RxInfo &aRxInfo)
497 {
498 Error error;
499
500 error = aRxInfo.ParseIp6Headers();
501
502 switch (error)
503 {
504 case kErrorNone:
505 break;
506 case kErrorNotFound:
507 // Frame may not contain an IPv6 header.
508 error = kErrorNone;
509 OT_FALL_THROUGH;
510 default:
511 ExitNow();
512 }
513
514 error = CheckReachability(aRxInfo.GetDstAddr().GetShort(), aRxInfo.mIp6Headers.GetIp6Header());
515
516 if (error == kErrorNoRoute)
517 {
518 SendDestinationUnreachable(aRxInfo.GetSrcAddr().GetShort(), aRxInfo.mIp6Headers);
519 }
520
521 exit:
522 return error;
523 }
524
CheckReachability(uint16_t aMeshDest,const Ip6::Header & aIp6Header)525 Error MeshForwarder::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
526 {
527 bool isReachable = false;
528
529 if (Get<Mle::Mle>().IsChild())
530 {
531 if (Get<Mle::Mle>().HasRloc16(aMeshDest))
532 {
533 isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination());
534 }
535 else
536 {
537 isReachable = true;
538 }
539
540 ExitNow();
541 }
542
543 if (Get<Mle::Mle>().HasRloc16(aMeshDest))
544 {
545 isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()) ||
546 (Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination()) != nullptr);
547 ExitNow();
548 }
549
550 if (Get<Mle::Mle>().HasMatchingRouterIdWith(aMeshDest))
551 {
552 isReachable = (Get<ChildTable>().FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr);
553 ExitNow();
554 }
555
556 isReachable = (Get<RouterTable>().GetNextHop(aMeshDest) != Mle::kInvalidRloc16);
557
558 exit:
559 return isReachable ? kErrorNone : kErrorNoRoute;
560 }
561
SendDestinationUnreachable(uint16_t aMeshSource,const Ip6::Headers & aIp6Headers)562 void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers)
563 {
564 Ip6::MessageInfo messageInfo;
565
566 messageInfo.GetPeerAddr().SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(), aMeshSource);
567
568 IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
569 Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers));
570 }
571
HandleMesh(RxInfo & aRxInfo)572 void MeshForwarder::HandleMesh(RxInfo &aRxInfo)
573 {
574 Error error = kErrorNone;
575 Lowpan::MeshHeader meshHeader;
576 Mac::Address neighborMacSource;
577
578 // Security Check: only process Mesh Header frames that had security enabled.
579 VerifyOrExit(aRxInfo.IsLinkSecurityEnabled(), error = kErrorSecurity);
580
581 SuccessOrExit(error = meshHeader.ParseFrom(aRxInfo.mFrameData));
582
583 neighborMacSource = aRxInfo.GetSrcAddr();
584
585 // Switch the `aRxInfo.mMacAddrs` to the mesh header source/destination
586
587 aRxInfo.mMacAddrs.mSource.SetShort(meshHeader.GetSource());
588 aRxInfo.mMacAddrs.mDestination.SetShort(meshHeader.GetDestination());
589
590 UpdateRoutes(aRxInfo);
591
592 if (Get<Mle::Mle>().HasRloc16(aRxInfo.GetDstAddr().GetShort()) ||
593 Get<ChildTable>().HasMinimalChild(aRxInfo.GetDstAddr().GetShort()))
594 {
595 if (Lowpan::FragmentHeader::IsFragmentHeader(aRxInfo.mFrameData))
596 {
597 HandleFragment(aRxInfo);
598 }
599 else if (Lowpan::Lowpan::IsLowpanHc(aRxInfo.mFrameData))
600 {
601 HandleLowpanHc(aRxInfo);
602 }
603 else
604 {
605 ExitNow(error = kErrorParse);
606 }
607 }
608 else if (meshHeader.GetHopsLeft() > 0)
609 {
610 OwnedPtr<Message> messagePtr;
611 Message::Priority priority = Message::kPriorityNormal;
612
613 ResolveRoutingLoops(neighborMacSource.GetShort(), aRxInfo.GetDstAddr().GetShort());
614
615 SuccessOrExit(error = CheckReachability(aRxInfo));
616
617 meshHeader.DecrementHopsLeft();
618
619 GetForwardFramePriority(aRxInfo, priority);
620 messagePtr.Reset(
621 Get<MessagePool>().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority)));
622 VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs);
623
624 SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
625 SuccessOrExit(error = messagePtr->AppendData(aRxInfo.mFrameData));
626
627 messagePtr->UpdateLinkInfoFrom(aRxInfo.mLinkInfo);
628
629 LogMessage(kMessageReceive, *messagePtr, kErrorNone, &neighborMacSource);
630
631 #if OPENTHREAD_CONFIG_MULTI_RADIO
632 // Since the message will be forwarded, we clear the radio
633 // type on the message to allow the radio type for tx to be
634 // selected later (based on the radios supported by the next
635 // hop).
636 messagePtr->ClearRadioType();
637 #endif
638
639 SendMessage(messagePtr.PassOwnership());
640 }
641
642 exit:
643
644 if (error != kErrorNone)
645 {
646 LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error),
647 aRxInfo.mFrameData.GetLength(), neighborMacSource.ToString().AsCString(),
648 ToYesNo(aRxInfo.IsLinkSecurityEnabled()));
649 }
650 }
651
ResolveRoutingLoops(uint16_t aSourceRloc16,uint16_t aDestRloc16)652 void MeshForwarder::ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16)
653 {
654 // Resolves 2-hop routing loops.
655
656 Router *router;
657
658 if (aSourceRloc16 != Get<RouterTable>().GetNextHop(aDestRloc16))
659 {
660 ExitNow();
661 }
662
663 router = Get<RouterTable>().FindRouterByRloc16(aDestRloc16);
664 VerifyOrExit(router != nullptr);
665
666 router->SetNextHopToInvalid();
667 Get<Mle::MleRouter>().ResetAdvertiseInterval();
668
669 exit:
670 return;
671 }
672
UpdateRoutes(RxInfo & aRxInfo)673 void MeshForwarder::UpdateRoutes(RxInfo &aRxInfo)
674 {
675 Neighbor *neighbor;
676
677 VerifyOrExit(!aRxInfo.GetDstAddr().IsBroadcast() && aRxInfo.GetSrcAddr().IsShort());
678
679 SuccessOrExit(aRxInfo.ParseIp6Headers());
680
681 if (!aRxInfo.mIp6Headers.GetSourceAddress().GetIid().IsLocator() &&
682 Get<NetworkData::Leader>().IsOnMesh(aRxInfo.mIp6Headers.GetSourceAddress()))
683 {
684 // FTDs MAY add/update EID-to-RLOC Map Cache entries by
685 // inspecting packets being received only for on mesh
686 // addresses.
687
688 Get<AddressResolver>().UpdateSnoopedCacheEntry(
689 aRxInfo.mIp6Headers.GetSourceAddress(), aRxInfo.GetSrcAddr().GetShort(), aRxInfo.GetDstAddr().GetShort());
690 }
691
692 neighbor = Get<NeighborTable>().FindNeighbor(aRxInfo.mIp6Headers.GetSourceAddress());
693 VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice());
694
695 if (!Get<Mle::Mle>().HasMatchingRouterIdWith(aRxInfo.GetSrcAddr().GetShort()))
696 {
697 Get<Mle::MleRouter>().RemoveNeighbor(*neighbor);
698 }
699
700 exit:
701 return;
702 }
703
UpdateOnTimeTick(void)704 bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void)
705 {
706 bool continueRxingTicks = false;
707
708 for (Entry &entry : mEntries)
709 {
710 if (!entry.IsExpired())
711 {
712 entry.DecrementLifetime();
713
714 if (!entry.IsExpired())
715 {
716 continueRxingTicks = true;
717 }
718 }
719 }
720
721 return continueRxingTicks;
722 }
723
UpdateFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aFragmentLength,uint16_t aSrcRloc16,Message::Priority aPriority)724 void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
725 uint16_t aFragmentLength,
726 uint16_t aSrcRloc16,
727 Message::Priority aPriority)
728 {
729 FragmentPriorityList::Entry *entry;
730
731 entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
732
733 if (entry == nullptr)
734 {
735 VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
736
737 mFragmentPriorityList.AllocateEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
738 Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
739 ExitNow();
740 }
741
742 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
743 OT_UNUSED_VARIABLE(aFragmentLength);
744 #else
745 // We can clear the entry in `mFragmentPriorityList` if it is the
746 // last fragment. But if "delay aware active queue management" is
747 // used we need to keep entry until the message is sent.
748 if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
749 {
750 entry->Clear();
751 }
752 else
753 #endif
754 {
755 entry->ResetLifetime();
756 }
757
758 exit:
759 return;
760 }
761
FindEntry(uint16_t aSrcRloc16,uint16_t aTag)762 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::FindEntry(uint16_t aSrcRloc16,
763 uint16_t aTag)
764 {
765 Entry *rval = nullptr;
766
767 for (Entry &entry : mEntries)
768 {
769 if (!entry.IsExpired() && entry.Matches(aSrcRloc16, aTag))
770 {
771 rval = &entry;
772 break;
773 }
774 }
775
776 return rval;
777 }
778
AllocateEntry(uint16_t aSrcRloc16,uint16_t aTag,Message::Priority aPriority)779 MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::AllocateEntry(
780 uint16_t aSrcRloc16,
781 uint16_t aTag,
782 Message::Priority aPriority)
783 {
784 Entry *newEntry = nullptr;
785
786 for (Entry &entry : mEntries)
787 {
788 if (entry.IsExpired())
789 {
790 entry.Clear();
791 entry.mSrcRloc16 = aSrcRloc16;
792 entry.mDatagramTag = aTag;
793 entry.mPriority = aPriority;
794 entry.ResetLifetime();
795 newEntry = &entry;
796 break;
797 }
798 }
799
800 return newEntry;
801 }
802
GetFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aSrcRloc16,Message::Priority & aPriority)803 Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
804 uint16_t aSrcRloc16,
805 Message::Priority &aPriority)
806 {
807 Error error = kErrorNone;
808 FragmentPriorityList::Entry *entry;
809
810 entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
811 VerifyOrExit(entry != nullptr, error = kErrorNotFound);
812 aPriority = entry->GetPriority();
813
814 exit:
815 return error;
816 }
817
GetForwardFramePriority(RxInfo & aRxInfo,Message::Priority & aPriority)818 void MeshForwarder::GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority)
819 {
820 // Determines the message priority to use for forwarding a
821 // received mesh-header LowPAN frame towards its final
822 // destination.
823
824 Error error = kErrorNone;
825 bool isFragment = false;
826 Lowpan::FragmentHeader fragmentHeader;
827 FrameData savedFrameData;
828
829 // We save the `aRxInfo.mFrameData` before parsing the fragment
830 // header which may update it to skip over the parsed header. We
831 // restore the original frame data on `aRxInfo` before
832 // returning.
833
834 savedFrameData = aRxInfo.mFrameData;
835
836 if (fragmentHeader.ParseFrom(aRxInfo.mFrameData) == kErrorNone)
837 {
838 isFragment = true;
839
840 if (fragmentHeader.GetDatagramOffset() > 0)
841 {
842 // Get priority from the pre-buffered info
843 ExitNow(error = GetFragmentPriority(fragmentHeader, aRxInfo.GetSrcAddr().GetShort(), aPriority));
844 }
845 }
846
847 // Get priority from IPv6 header or UDP destination port directly
848 error = GetFramePriority(aRxInfo, aPriority);
849
850 exit:
851 if (error != kErrorNone)
852 {
853 LogNote("Failed to get forwarded frame priority, error:%s, %s", ErrorToString(error),
854 aRxInfo.ToString().AsCString());
855 }
856 else if (isFragment)
857 {
858 UpdateFragmentPriority(fragmentHeader, aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().GetShort(),
859 aPriority);
860 }
861
862 aRxInfo.mFrameData = savedFrameData;
863 }
864
865 // LCOV_EXCL_START
866
867 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
868
LogMeshFragmentHeader(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,uint16_t & aOffset,Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)869 Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction,
870 const Message &aMessage,
871 const Mac::Address *aMacAddress,
872 Error aError,
873 uint16_t &aOffset,
874 Mac::Addresses &aMeshAddrs,
875 LogLevel aLogLevel)
876 {
877 Error error = kErrorFailed;
878 bool hasFragmentHeader = false;
879 bool shouldLogRss;
880 Lowpan::MeshHeader meshHeader;
881 Lowpan::FragmentHeader fragmentHeader;
882 uint16_t headerLength;
883 bool shouldLogRadio = false;
884 const char *radioString = "";
885
886 SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength));
887
888 aMeshAddrs.mSource.SetShort(meshHeader.GetSource());
889 aMeshAddrs.mDestination.SetShort(meshHeader.GetDestination());
890
891 aOffset = headerLength;
892
893 if (fragmentHeader.ParseFrom(aMessage, aOffset, headerLength) == kErrorNone)
894 {
895 hasFragmentHeader = true;
896 aOffset += headerLength;
897 }
898
899 shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop);
900
901 #if OPENTHREAD_CONFIG_MULTI_RADIO
902 shouldLogRadio = true;
903 radioString = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all";
904 #endif
905
906 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",
907 MessageActionToString(aAction, aError), aMessage.GetLength(),
908 (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
909 (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(),
910 aMeshAddrs.mSource.ToString().AsCString(), aMeshAddrs.mDestination.ToString().AsCString(),
911 meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), ToYesNo(hasFragmentHeader),
912 ToYesNo(aMessage.IsLinkSecurityEnabled()),
913 (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
914 shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "",
915 shouldLogRadio ? ", radio:" : "", radioString);
916
917 if (hasFragmentHeader)
918 {
919 LogAt(aLogLevel, " Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
920 fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
921
922 VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
923 }
924
925 error = kErrorNone;
926
927 exit:
928 return error;
929 }
930
LogMeshIpHeader(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)931 void MeshForwarder::LogMeshIpHeader(const Message &aMessage,
932 uint16_t aOffset,
933 const Mac::Addresses &aMeshAddrs,
934 LogLevel aLogLevel)
935 {
936 Ip6::Headers headers;
937
938 SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshAddrs));
939
940 LogAt(aLogLevel, " IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()),
941 headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage));
942
943 LogIp6SourceDestAddresses(headers, aLogLevel);
944
945 exit:
946 return;
947 }
948
LogMeshMessage(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,LogLevel aLogLevel)949 void MeshForwarder::LogMeshMessage(MessageAction aAction,
950 const Message &aMessage,
951 const Mac::Address *aMacAddress,
952 Error aError,
953 LogLevel aLogLevel)
954 {
955 uint16_t offset;
956 Mac::Addresses meshAddrs;
957
958 SuccessOrExit(LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshAddrs, aLogLevel));
959
960 // When log action is `kMessageTransmit` we do not include
961 // the IPv6 header info in the logs, as the same info is
962 // logged when the same Mesh Header message was received
963 // and info about it was logged.
964
965 VerifyOrExit(aAction != kMessageTransmit);
966
967 LogMeshIpHeader(aMessage, offset, meshAddrs, aLogLevel);
968
969 exit:
970 return;
971 }
972
973 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
974
975 // LCOV_EXCL_STOP
976
977 } // namespace ot
978
979 #endif // OPENTHREAD_FTD
980