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 "instance/instance.hpp"
39
40 namespace ot {
41
42 RegisterLogModule("MeshForwarder");
43
SendMessage(OwnedPtr<Message> aMessagePtr)44 void MeshForwarder::SendMessage(OwnedPtr<Message> aMessagePtr)
45 {
46 Message &message = *aMessagePtr.Release();
47
48 message.SetOffset(0);
49 message.SetDatagramTag(0);
50 message.SetTimestampToNow();
51 mSendQueue.Enqueue(message);
52
53 switch (message.GetType())
54 {
55 case Message::kTypeIp6:
56 {
57 Ip6::Header ip6Header;
58 const Ip6::Address &destination = ip6Header.GetDestination();
59
60 IgnoreError(message.Read(0, ip6Header));
61
62 if (destination.IsMulticast())
63 {
64 // For traffic destined to multicast address larger than realm local, generally it uses IP-in-IP
65 // encapsulation (RFC2473), with outer destination as ALL_MPL_FORWARDERS. So here if the destination
66 // is multicast address larger than realm local, it should be for indirection transmission for the
67 // device's sleepy child, thus there should be no direct transmission.
68 if (!destination.IsMulticastLargerThanRealmLocal())
69 {
70 message.SetDirectTransmission();
71 }
72
73 if (message.GetSubType() != Message::kSubTypeMplRetransmission)
74 {
75 // Check if we need to forward the multicast message
76 // to any sleepy child. This is skipped for MPL retx
77 // (only the first MPL transmission is forwarded to
78 // sleepy children).
79
80 bool destinedForAll = ((destination == Get<Mle::Mle>().GetLinkLocalAllThreadNodesAddress()) ||
81 (destination == Get<Mle::Mle>().GetRealmLocalAllThreadNodesAddress()));
82
83 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
84 {
85 if (!child.IsRxOnWhenIdle() && (destinedForAll || child.HasIp6Address(destination)))
86 {
87 mIndirectSender.AddMessageForSleepyChild(message, child);
88 }
89 }
90 }
91 }
92 else // Destination is unicast
93 {
94 Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(destination);
95
96 if ((neighbor != nullptr) && !neighbor->IsRxOnWhenIdle() && !message.IsDirectTransmission() &&
97 Get<ChildTable>().Contains(*neighbor))
98 {
99 mIndirectSender.AddMessageForSleepyChild(message, *static_cast<Child *>(neighbor));
100 }
101 else
102 {
103 message.SetDirectTransmission();
104 }
105 }
106
107 break;
108 }
109
110 case Message::kTypeSupervision:
111 {
112 Child *child = Get<ChildSupervisor>().GetDestination(message);
113 OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle());
114 mIndirectSender.AddMessageForSleepyChild(message, *child);
115 break;
116 }
117
118 default:
119 message.SetDirectTransmission();
120 break;
121 }
122
123 // Ensure that the message is marked for direct tx and/or for indirect tx
124 // to a sleepy child. Otherwise, remove the message.
125
126 if (RemoveMessageIfNoPendingTx(message))
127 {
128 ExitNow();
129 }
130
131 #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
132 ApplyDirectTxQueueLimit(message);
133 #endif
134
135 mScheduleTransmissionTask.Post();
136
137 exit:
138 return;
139 }
140
HandleResolved(const Ip6::Address & aEid,Error aError)141 void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError)
142 {
143 Ip6::Address ip6Dst;
144 bool didUpdate = false;
145
146 for (Message &message : mSendQueue)
147 {
148 if (!message.IsResolvingAddress())
149 {
150 continue;
151 }
152
153 IgnoreError(message.Read(Ip6::Header::kDestinationFieldOffset, ip6Dst));
154
155 if (ip6Dst != aEid)
156 {
157 continue;
158 }
159
160 if (aError != kErrorNone)
161 {
162 LogMessage(kMessageDrop, message, kErrorAddressQuery);
163 FinalizeMessageDirectTx(message, kErrorAddressQuery);
164 RemoveMessageIfNoPendingTx(message);
165 continue;
166 }
167
168 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
169 // Pass back to IPv6 layer for DUA destination resolved
170 // by Backbone Query
171 if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(ip6Dst) &&
172 Get<Mle::Mle>().HasRloc16(Get<AddressResolver>().LookUp(ip6Dst)))
173 {
174 uint8_t hopLimit;
175
176 mSendQueue.Dequeue(message);
177
178 // Avoid decreasing Hop Limit twice
179 IgnoreError(message.Read(Ip6::Header::kHopLimitFieldOffset, hopLimit));
180 hopLimit++;
181 message.Write(Ip6::Header::kHopLimitFieldOffset, hopLimit);
182 message.SetLoopbackToHostAllowed(true);
183 message.SetOrigin(Message::kOriginHostTrusted);
184
185 IgnoreError(Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&message)));
186 continue;
187 }
188 #endif
189
190 message.SetResolvingAddress(false);
191 didUpdate = true;
192 }
193
194 if (didUpdate)
195 {
196 mScheduleTransmissionTask.Post();
197 }
198 }
199
EvictMessage(Message::Priority aPriority)200 Error MeshForwarder::EvictMessage(Message::Priority aPriority)
201 {
202 Error error = kErrorNotFound;
203 Message *evict = nullptr;
204
205 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
206 error = RemoveAgedMessages();
207 VerifyOrExit(error == kErrorNotFound);
208 #endif
209
210 // Search for a lower priority message to evict
211 for (uint8_t priority = 0; priority < aPriority; priority++)
212 {
213 for (Message *message = mSendQueue.GetHeadForPriority(static_cast<Message::Priority>(priority)); message;
214 message = message->GetNext())
215 {
216 if (message->GetPriority() != priority)
217 {
218 break;
219 }
220
221 if (message->GetDoNotEvict())
222 {
223 continue;
224 }
225
226 evict = message;
227 error = kErrorNone;
228 ExitNow();
229 }
230 }
231
232 for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++)
233 {
234 // search for an equal or higher priority indirect message to evict
235 for (Message *message = mSendQueue.GetHeadForPriority(aPriority); message; message = message->GetNext())
236 {
237 if (message->GetPriority() != priority)
238 {
239 break;
240 }
241
242 if (message->GetDoNotEvict())
243 {
244 continue;
245 }
246
247 if (!message->GetIndirectTxChildMask().IsEmpty())
248 {
249 evict = message;
250 ExitNow(error = kErrorNone);
251 }
252 }
253 }
254
255 exit:
256 if ((error == kErrorNone) && (evict != nullptr))
257 {
258 FinalizeAndRemoveMessage(*evict, kErrorNoBufs, kMessageEvict);
259 }
260
261 return error;
262 }
263
RemoveMessagesForChild(Child & aChild,MessageChecker & aMessageChecker)264 void MeshForwarder::RemoveMessagesForChild(Child &aChild, MessageChecker &aMessageChecker)
265 {
266 for (Message &message : mSendQueue)
267 {
268 if (!aMessageChecker(message))
269 {
270 continue;
271 }
272
273 if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
274 {
275 const Neighbor *neighbor = nullptr;
276
277 if (message.GetType() == Message::kTypeIp6)
278 {
279 Ip6::Header ip6header;
280
281 IgnoreError(message.Read(0, ip6header));
282 neighbor = Get<NeighborTable>().FindNeighbor(ip6header.GetDestination());
283 }
284 else if (message.GetType() == Message::kType6lowpan)
285 {
286 Lowpan::MeshHeader meshHeader;
287
288 IgnoreError(meshHeader.ParseFrom(message));
289 neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
290 }
291
292 if (&aChild == neighbor)
293 {
294 message.ClearDirectTransmission();
295 }
296 }
297
298 RemoveMessageIfNoPendingTx(message);
299 }
300 }
301
FinalizeMessageIndirectTxs(Message & aMessage)302 void MeshForwarder::FinalizeMessageIndirectTxs(Message &aMessage)
303 {
304 VerifyOrExit(!aMessage.GetIndirectTxChildMask().IsEmpty());
305
306 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
307 {
308 IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(aMessage, child));
309 VerifyOrExit(!aMessage.GetIndirectTxChildMask().IsEmpty());
310 }
311
312 exit:
313 return;
314 }
315
RemoveDataResponseMessages(void)316 void MeshForwarder::RemoveDataResponseMessages(void)
317 {
318 for (Message &message : mSendQueue)
319 {
320 if (message.IsMleCommand(Mle::kCommandDataResponse))
321 {
322 FinalizeAndRemoveMessage(message, kErrorDrop, kMessageDrop);
323 }
324 }
325 }
326
SendMesh(Message & aMessage,Mac::TxFrame & aFrame)327 void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
328 {
329 Mac::TxFrame::Info frameInfo;
330
331 frameInfo.mType = Mac::Frame::kTypeData;
332 frameInfo.mAddrs = mMacAddrs;
333 frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32;
334 frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1;
335 frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
336
337 PrepareMacHeaders(aFrame, frameInfo, &aMessage);
338
339 // write payload
340 OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
341 aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength());
342 aFrame.SetPayloadLength(aMessage.GetLength());
343
344 mMessageNextOffset = aMessage.GetLength();
345 }
346
UpdateMeshRoute(Message & aMessage)347 Error MeshForwarder::UpdateMeshRoute(Message &aMessage)
348 {
349 Error error = kErrorNone;
350 Lowpan::MeshHeader meshHeader;
351 Neighbor *neighbor;
352 uint16_t nextHop;
353
354 IgnoreError(meshHeader.ParseFrom(aMessage));
355
356 nextHop = Get<RouterTable>().GetNextHop(meshHeader.GetDestination());
357
358 if (nextHop != Mle::kInvalidRloc16)
359 {
360 neighbor = Get<NeighborTable>().FindNeighbor(nextHop);
361 }
362 else
363 {
364 neighbor = Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination());
365 }
366
367 if (neighbor == nullptr)
368 {
369 ExitNow(error = kErrorDrop);
370 }
371
372 mMacAddrs.mDestination.SetShort(neighbor->GetRloc16());
373 mMacAddrs.mSource.SetShort(Get<Mle::Mle>().GetRloc16());
374
375 mAddMeshHeader = true;
376 mMeshDest = meshHeader.GetDestination();
377 mMeshSource = meshHeader.GetSource();
378
379 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
380 if (mMacAddrs.mDestination.GetShort() != mMeshDest)
381 {
382 mDelayNextTx = true;
383 }
384 #endif
385
386 exit:
387 return error;
388 }
389
UpdateIp6RouteFtd(const Ip6::Header & aIp6Header,Message & aMessage)390 Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage)
391 {
392 Mle::MleRouter &mle = Get<Mle::MleRouter>();
393 Error error = kErrorNone;
394 Neighbor *neighbor;
395
396 if (aMessage.GetOffset() > 0)
397 {
398 mMeshDest = aMessage.GetMeshDest();
399 }
400 else if (mle.IsRoutingLocator(aIp6Header.GetDestination()))
401 {
402 uint16_t rloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
403 VerifyOrExit(Mle::IsRouterIdValid(Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop);
404 mMeshDest = rloc16;
405 }
406 else if (mle.IsAnycastLocator(aIp6Header.GetDestination()))
407 {
408 uint16_t aloc16 = aIp6Header.GetDestination().GetIid().GetLocator();
409
410 SuccessOrExit(error = Get<NetworkData::Leader>().AnycastLookup(aloc16, mMeshDest));
411
412 // If the selected ALOC destination, `mMeshDest`, is a sleepy
413 // child of this device, prepare the message for indirect tx
414 // to the sleepy child and un-mark message for direct tx.
415
416 if (mle.IsRouterOrLeader() && Mle::IsChildRloc16(mMeshDest) && mle.HasMatchingRouterIdWith(mMeshDest))
417 {
418 Child *child = Get<ChildTable>().FindChild(mMeshDest, Child::kInStateValid);
419
420 VerifyOrExit(child != nullptr, error = kErrorDrop);
421
422 if (!child->IsRxOnWhenIdle())
423 {
424 mIndirectSender.AddMessageForSleepyChild(aMessage, *child);
425 aMessage.ClearDirectTransmission();
426 }
427 }
428 }
429 else if ((neighbor = Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination())) != nullptr)
430 {
431 mMeshDest = neighbor->GetRloc16();
432 }
433 else if (Get<NetworkData::Leader>().IsOnMesh(aIp6Header.GetDestination()))
434 {
435 SuccessOrExit(error = Get<AddressResolver>().Resolve(aIp6Header.GetDestination(), mMeshDest));
436 }
437 else
438 {
439 IgnoreError(
440 Get<NetworkData::Leader>().RouteLookup(aIp6Header.GetSource(), aIp6Header.GetDestination(), mMeshDest));
441 }
442
443 VerifyOrExit(mMeshDest != Mle::kInvalidRloc16, error = kErrorDrop);
444
445 mMeshSource = Get<Mle::Mle>().GetRloc16();
446
447 SuccessOrExit(error = CheckReachability(mMeshDest, aIp6Header));
448 aMessage.SetMeshDest(mMeshDest);
449 mMacAddrs.mDestination.SetShort(Get<RouterTable>().GetNextHop(mMeshDest));
450
451 if (mMacAddrs.mDestination.GetShort() != mMeshDest)
452 {
453 // destination is not neighbor
454 mMacAddrs.mSource.SetShort(mMeshSource);
455 mAddMeshHeader = true;
456 #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
457 mDelayNextTx = true;
458 #endif
459 }
460
461 exit:
462 return error;
463 }
464
SendIcmpErrorIfDstUnreach(const Message & aMessage,const Mac::Addresses & aMacAddrs)465 void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs)
466 {
467 Error error;
468 Ip6::Headers ip6Headers;
469 Child *child;
470
471 VerifyOrExit(aMacAddrs.mSource.IsShort() && aMacAddrs.mDestination.IsShort());
472
473 child = Get<ChildTable>().FindChild(aMacAddrs.mSource.GetShort(), Child::kInStateAnyExceptInvalid);
474 VerifyOrExit((child == nullptr) || child->IsFullThreadDevice());
475
476 SuccessOrExit(ip6Headers.ParseFrom(aMessage));
477
478 VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() &&
479 Get<NetworkData::Leader>().IsOnMesh(ip6Headers.GetDestinationAddress()));
480
481 error = CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
482
483 if (error == kErrorNoRoute)
484 {
485 SendDestinationUnreachable(aMacAddrs.mSource.GetShort(), ip6Headers);
486 }
487
488 exit:
489 return;
490 }
491
CheckReachability(RxInfo & aRxInfo)492 Error MeshForwarder::CheckReachability(RxInfo &aRxInfo)
493 {
494 Error error;
495
496 error = aRxInfo.ParseIp6Headers();
497
498 switch (error)
499 {
500 case kErrorNone:
501 break;
502 case kErrorNotFound:
503 // Frame may not contain an IPv6 header.
504 error = kErrorNone;
505 OT_FALL_THROUGH;
506 default:
507 ExitNow();
508 }
509
510 error = CheckReachability(aRxInfo.GetDstAddr().GetShort(), aRxInfo.mIp6Headers.GetIp6Header());
511
512 if (error == kErrorNoRoute)
513 {
514 SendDestinationUnreachable(aRxInfo.GetSrcAddr().GetShort(), aRxInfo.mIp6Headers);
515 }
516
517 exit:
518 return error;
519 }
520
CheckReachability(uint16_t aMeshDest,const Ip6::Header & aIp6Header)521 Error MeshForwarder::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
522 {
523 bool isReachable = false;
524
525 if (Get<Mle::Mle>().IsChild())
526 {
527 if (Get<Mle::Mle>().HasRloc16(aMeshDest))
528 {
529 isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination());
530 }
531 else
532 {
533 isReachable = true;
534 }
535
536 ExitNow();
537 }
538
539 if (Get<Mle::Mle>().HasRloc16(aMeshDest))
540 {
541 isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()) ||
542 (Get<NeighborTable>().FindNeighbor(aIp6Header.GetDestination()) != nullptr);
543 ExitNow();
544 }
545
546 if (Get<Mle::Mle>().HasMatchingRouterIdWith(aMeshDest))
547 {
548 isReachable = (Get<ChildTable>().FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr);
549 ExitNow();
550 }
551
552 isReachable = (Get<RouterTable>().GetNextHop(aMeshDest) != Mle::kInvalidRloc16);
553
554 exit:
555 return isReachable ? kErrorNone : kErrorNoRoute;
556 }
557
SendDestinationUnreachable(uint16_t aMeshSource,const Ip6::Headers & aIp6Headers)558 void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers)
559 {
560 Ip6::MessageInfo messageInfo;
561
562 messageInfo.GetPeerAddr().SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(), aMeshSource);
563
564 IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
565 Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers));
566 }
567
HandleMesh(RxInfo & aRxInfo)568 void MeshForwarder::HandleMesh(RxInfo &aRxInfo)
569 {
570 Error error = kErrorNone;
571 Lowpan::MeshHeader meshHeader;
572 Mac::Address neighborMacSource;
573
574 // Security Check: only process Mesh Header frames that had security enabled.
575 VerifyOrExit(aRxInfo.IsLinkSecurityEnabled(), error = kErrorSecurity);
576
577 SuccessOrExit(error = meshHeader.ParseFrom(aRxInfo.mFrameData));
578
579 neighborMacSource = aRxInfo.GetSrcAddr();
580
581 // Switch the `aRxInfo.mMacAddrs` to the mesh header source/destination
582
583 aRxInfo.mMacAddrs.mSource.SetShort(meshHeader.GetSource());
584 aRxInfo.mMacAddrs.mDestination.SetShort(meshHeader.GetDestination());
585
586 UpdateRoutes(aRxInfo);
587
588 if (Get<Mle::Mle>().HasRloc16(aRxInfo.GetDstAddr().GetShort()) ||
589 Get<ChildTable>().HasMinimalChild(aRxInfo.GetDstAddr().GetShort()))
590 {
591 if (Lowpan::FragmentHeader::IsFragmentHeader(aRxInfo.mFrameData))
592 {
593 HandleFragment(aRxInfo);
594 }
595 else if (Lowpan::Lowpan::IsLowpanHc(aRxInfo.mFrameData))
596 {
597 HandleLowpanHc(aRxInfo);
598 }
599 else
600 {
601 ExitNow(error = kErrorParse);
602 }
603 }
604 else if (meshHeader.GetHopsLeft() > 0)
605 {
606 OwnedPtr<Message> messagePtr;
607 Message::Priority priority = Message::kPriorityNormal;
608
609 ResolveRoutingLoops(neighborMacSource.GetShort(), aRxInfo.GetDstAddr().GetShort());
610
611 SuccessOrExit(error = CheckReachability(aRxInfo));
612
613 meshHeader.DecrementHopsLeft();
614
615 GetForwardFramePriority(aRxInfo, priority);
616 messagePtr.Reset(
617 Get<MessagePool>().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority)));
618 VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs);
619
620 SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
621 SuccessOrExit(error = messagePtr->AppendData(aRxInfo.mFrameData));
622
623 messagePtr->UpdateLinkInfoFrom(aRxInfo.mLinkInfo);
624
625 LogMessage(kMessageReceive, *messagePtr, kErrorNone, &neighborMacSource);
626
627 #if OPENTHREAD_CONFIG_MULTI_RADIO
628 // Since the message will be forwarded, we clear the radio
629 // type on the message to allow the radio type for tx to be
630 // selected later (based on the radios supported by the next
631 // hop).
632 messagePtr->ClearRadioType();
633 #endif
634
635 SendMessage(messagePtr.PassOwnership());
636 }
637
638 exit:
639
640 if (error != kErrorNone)
641 {
642 LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error),
643 aRxInfo.mFrameData.GetLength(), neighborMacSource.ToString().AsCString(),
644 ToYesNo(aRxInfo.IsLinkSecurityEnabled()));
645 }
646 }
647
ResolveRoutingLoops(uint16_t aSourceRloc16,uint16_t aDestRloc16)648 void MeshForwarder::ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16)
649 {
650 // Resolves 2-hop routing loops.
651
652 Router *router;
653
654 if (aSourceRloc16 != Get<RouterTable>().GetNextHop(aDestRloc16))
655 {
656 ExitNow();
657 }
658
659 router = Get<RouterTable>().FindRouterByRloc16(aDestRloc16);
660 VerifyOrExit(router != nullptr);
661
662 router->SetNextHopToInvalid();
663 Get<Mle::MleRouter>().ResetAdvertiseInterval();
664
665 exit:
666 return;
667 }
668
UpdateRoutes(RxInfo & aRxInfo)669 void MeshForwarder::UpdateRoutes(RxInfo &aRxInfo)
670 {
671 Neighbor *neighbor;
672
673 VerifyOrExit(!aRxInfo.GetDstAddr().IsBroadcast() && aRxInfo.GetSrcAddr().IsShort());
674
675 SuccessOrExit(aRxInfo.ParseIp6Headers());
676
677 if (!aRxInfo.mIp6Headers.GetSourceAddress().GetIid().IsLocator() &&
678 Get<NetworkData::Leader>().IsOnMesh(aRxInfo.mIp6Headers.GetSourceAddress()))
679 {
680 // FTDs MAY add/update EID-to-RLOC Map Cache entries by
681 // inspecting packets being received only for on mesh
682 // addresses.
683
684 Get<AddressResolver>().UpdateSnoopedCacheEntry(
685 aRxInfo.mIp6Headers.GetSourceAddress(), aRxInfo.GetSrcAddr().GetShort(), aRxInfo.GetDstAddr().GetShort());
686 }
687
688 neighbor = Get<NeighborTable>().FindNeighbor(aRxInfo.mIp6Headers.GetSourceAddress());
689 VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice());
690
691 if (!Get<Mle::Mle>().HasMatchingRouterIdWith(aRxInfo.GetSrcAddr().GetShort()))
692 {
693 Get<Mle::MleRouter>().RemoveNeighbor(*neighbor);
694 }
695
696 exit:
697 return;
698 }
699
UpdateFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aFragmentLength,uint16_t aSrcRloc16,Message::Priority aPriority)700 void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
701 uint16_t aFragmentLength,
702 uint16_t aSrcRloc16,
703 Message::Priority aPriority)
704 {
705 FwdFrameInfo *entry;
706
707 entry = FindFwdFrameInfoEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
708
709 if (entry == nullptr)
710 {
711 VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
712
713 entry = mFwdFrameInfoArray.PushBack();
714 VerifyOrExit(entry != nullptr);
715
716 entry->Init(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
717 Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
718
719 ExitNow();
720 }
721
722 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
723 OT_UNUSED_VARIABLE(aFragmentLength);
724 #else
725 // We can remove the entry in `mFwdFrameInfoArray` if it is the
726 // last fragment. But if "delay aware active queue management" is
727 // used we need to keep entry until the message is sent.
728 if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
729 {
730 mFwdFrameInfoArray.Remove(*entry);
731 }
732 else
733 #endif
734 {
735 entry->ResetLifetime();
736 }
737
738 exit:
739 return;
740 }
741
Init(uint16_t aSrcRloc16,uint16_t aDatagramTag,Message::Priority aPriority)742 void MeshForwarder::FwdFrameInfo::Init(uint16_t aSrcRloc16, uint16_t aDatagramTag, Message::Priority aPriority)
743 {
744 mSrcRloc16 = aSrcRloc16;
745 mDatagramTag = aDatagramTag;
746 mLifetime = kLifetime;
747 mPriority = aPriority;
748 #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
749 mShouldDrop = false;
750 #endif
751 }
752
Matches(const Info & aInfo) const753 bool MeshForwarder::FwdFrameInfo::Matches(const Info &aInfo) const
754 {
755 return (mSrcRloc16 == aInfo.mSrcRloc16) && (mDatagramTag == aInfo.mDatagramTag);
756 }
757
FindFwdFrameInfoEntry(uint16_t aSrcRloc16,uint16_t aDatagramTag)758 MeshForwarder::FwdFrameInfo *MeshForwarder::FindFwdFrameInfoEntry(uint16_t aSrcRloc16, uint16_t aDatagramTag)
759 {
760 FwdFrameInfo::Info info;
761
762 info.mSrcRloc16 = aSrcRloc16;
763 info.mDatagramTag = aDatagramTag;
764
765 return mFwdFrameInfoArray.FindMatching(info);
766 }
767
UpdateFwdFrameInfoArrayOnTimeTick(void)768 bool MeshForwarder::UpdateFwdFrameInfoArrayOnTimeTick(void)
769 {
770 for (FwdFrameInfo &entry : mFwdFrameInfoArray)
771 {
772 entry.DecrementLifetime();
773 }
774
775 mFwdFrameInfoArray.RemoveAllMatching(FwdFrameInfo::kIsExpired);
776
777 return !mFwdFrameInfoArray.IsEmpty();
778 }
779
GetFragmentPriority(Lowpan::FragmentHeader & aFragmentHeader,uint16_t aSrcRloc16,Message::Priority & aPriority)780 Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
781 uint16_t aSrcRloc16,
782 Message::Priority &aPriority)
783 {
784 Error error = kErrorNone;
785 const FwdFrameInfo *entry = FindFwdFrameInfoEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
786
787 VerifyOrExit(entry != nullptr, error = kErrorNotFound);
788 aPriority = entry->GetPriority();
789
790 exit:
791 return error;
792 }
793
GetForwardFramePriority(RxInfo & aRxInfo,Message::Priority & aPriority)794 void MeshForwarder::GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority)
795 {
796 // Determines the message priority to use for forwarding a
797 // received mesh-header LowPAN frame towards its final
798 // destination.
799
800 Error error = kErrorNone;
801 bool isFragment = false;
802 Lowpan::FragmentHeader fragmentHeader;
803 FrameData savedFrameData;
804
805 // We save the `aRxInfo.mFrameData` before parsing the fragment
806 // header which may update it to skip over the parsed header. We
807 // restore the original frame data on `aRxInfo` before
808 // returning.
809
810 savedFrameData = aRxInfo.mFrameData;
811
812 if (fragmentHeader.ParseFrom(aRxInfo.mFrameData) == kErrorNone)
813 {
814 isFragment = true;
815
816 if (fragmentHeader.GetDatagramOffset() > 0)
817 {
818 // Get priority from the pre-buffered info
819 ExitNow(error = GetFragmentPriority(fragmentHeader, aRxInfo.GetSrcAddr().GetShort(), aPriority));
820 }
821 }
822
823 // Get priority from IPv6 header or UDP destination port directly
824 error = GetFramePriority(aRxInfo, aPriority);
825
826 exit:
827 if (error != kErrorNone)
828 {
829 LogNote("Failed to get forwarded frame priority, error:%s, %s", ErrorToString(error),
830 aRxInfo.ToString().AsCString());
831 }
832 else if (isFragment)
833 {
834 UpdateFragmentPriority(fragmentHeader, aRxInfo.mFrameData.GetLength(), aRxInfo.GetSrcAddr().GetShort(),
835 aPriority);
836 }
837
838 aRxInfo.mFrameData = savedFrameData;
839 }
840
841 // LCOV_EXCL_START
842
843 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
844
LogMeshFragmentHeader(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,uint16_t & aOffset,Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)845 Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction,
846 const Message &aMessage,
847 const Mac::Address *aMacAddress,
848 Error aError,
849 uint16_t &aOffset,
850 Mac::Addresses &aMeshAddrs,
851 LogLevel aLogLevel)
852 {
853 Error error = kErrorFailed;
854 bool hasFragmentHeader = false;
855 Lowpan::MeshHeader meshHeader;
856 Lowpan::FragmentHeader fragmentHeader;
857 uint16_t headerLength;
858 String<kMaxLogStringSize> string;
859
860 SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength));
861
862 aMeshAddrs.mSource.SetShort(meshHeader.GetSource());
863 aMeshAddrs.mDestination.SetShort(meshHeader.GetDestination());
864
865 aOffset = headerLength;
866
867 if (fragmentHeader.ParseFrom(aMessage, aOffset, headerLength) == kErrorNone)
868 {
869 hasFragmentHeader = true;
870 aOffset += headerLength;
871 }
872
873 string.Append("%s mesh frame, len:%u, ", MessageActionToString(aAction, aError), aMessage.GetLength());
874
875 AppendMacAddrToLogString(string, aAction, aMacAddress);
876
877 string.Append("msrc:%s, mdst:%s, hops:%d, frag:%s, ", aMeshAddrs.mSource.ToString().AsCString(),
878 aMeshAddrs.mDestination.ToString().AsCString(),
879 meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), ToYesNo(hasFragmentHeader));
880
881 AppendSecErrorPrioRssRadioLabelsToLogString(string, aAction, aMessage, aError);
882
883 LogAt(aLogLevel, "%s", string.AsCString());
884
885 if (hasFragmentHeader)
886 {
887 LogAt(aLogLevel, " Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
888 fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
889
890 VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
891 }
892
893 error = kErrorNone;
894
895 exit:
896 return error;
897 }
898
LogMeshIpHeader(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMeshAddrs,LogLevel aLogLevel)899 void MeshForwarder::LogMeshIpHeader(const Message &aMessage,
900 uint16_t aOffset,
901 const Mac::Addresses &aMeshAddrs,
902 LogLevel aLogLevel)
903 {
904 Ip6::Headers headers;
905
906 SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshAddrs));
907
908 LogAt(aLogLevel, " IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()),
909 headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage));
910
911 LogIp6SourceDestAddresses(headers, aLogLevel);
912
913 exit:
914 return;
915 }
916
LogMeshMessage(MessageAction aAction,const Message & aMessage,const Mac::Address * aMacAddress,Error aError,LogLevel aLogLevel)917 void MeshForwarder::LogMeshMessage(MessageAction aAction,
918 const Message &aMessage,
919 const Mac::Address *aMacAddress,
920 Error aError,
921 LogLevel aLogLevel)
922 {
923 uint16_t offset;
924 Mac::Addresses meshAddrs;
925
926 SuccessOrExit(LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshAddrs, aLogLevel));
927
928 // When log action is `kMessageTransmit` we do not include
929 // the IPv6 header info in the logs, as the same info is
930 // logged when the same Mesh Header message was received
931 // and info about it was logged.
932
933 VerifyOrExit(aAction != kMessageTransmit);
934
935 LogMeshIpHeader(aMessage, offset, meshAddrs, aLogLevel);
936
937 exit:
938 return;
939 }
940
941 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
942
943 // LCOV_EXCL_STOP
944
945 } // namespace ot
946
947 #endif // OPENTHREAD_FTD
948