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