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