• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, 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 IPv6 networking.
32  */
33 
34 #include "ip6.hpp"
35 
36 #include "instance/instance.hpp"
37 
38 using IcmpType = ot::Ip6::Icmp::Header::Type;
39 
40 static const IcmpType kForwardIcmpTypes[] = {
41     IcmpType::kTypeDstUnreach,       IcmpType::kTypePacketToBig, IcmpType::kTypeTimeExceeded,
42     IcmpType::kTypeParameterProblem, IcmpType::kTypeEchoRequest, IcmpType::kTypeEchoReply,
43 };
44 
45 namespace ot {
46 namespace Ip6 {
47 
48 RegisterLogModule("Ip6");
49 
Ip6(Instance & aInstance)50 Ip6::Ip6(Instance &aInstance)
51     : InstanceLocator(aInstance)
52     , mReceiveFilterEnabled(false)
53 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
54     , mTmfOriginFilterEnabled(true)
55 #endif
56     , mSendQueueTask(aInstance)
57     , mIcmp(aInstance)
58     , mUdp(aInstance)
59     , mMpl(aInstance)
60 #if OPENTHREAD_CONFIG_TCP_ENABLE
61     , mTcp(aInstance)
62 #endif
63 {
64 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
65     ResetBorderRoutingCounters();
66 #endif
67 }
68 
NewMessage(void)69 Message *Ip6::NewMessage(void) { return NewMessage(0); }
70 
NewMessage(uint16_t aReserved)71 Message *Ip6::NewMessage(uint16_t aReserved) { return NewMessage(aReserved, Message::Settings::GetDefault()); }
72 
NewMessage(uint16_t aReserved,const Message::Settings & aSettings)73 Message *Ip6::NewMessage(uint16_t aReserved, const Message::Settings &aSettings)
74 {
75     return Get<MessagePool>().Allocate(
76         Message::kTypeIp6, sizeof(Header) + sizeof(HopByHopHeader) + sizeof(MplOption) + aReserved, aSettings);
77 }
78 
NewMessageFromData(const uint8_t * aData,uint16_t aDataLength,const Message::Settings & aSettings)79 Message *Ip6::NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings)
80 {
81     Message          *message  = nullptr;
82     Message::Settings settings = aSettings;
83     const Header     *header;
84 
85     VerifyOrExit((aData != nullptr) && (aDataLength >= sizeof(Header)));
86 
87     // Determine priority from IPv6 header
88     header = reinterpret_cast<const Header *>(aData);
89     VerifyOrExit(header->IsValid());
90     VerifyOrExit(sizeof(Header) + header->GetPayloadLength() == aDataLength);
91     settings.mPriority = DscpToPriority(header->GetDscp());
92 
93     message = Get<MessagePool>().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, settings);
94 
95     VerifyOrExit(message != nullptr);
96 
97     if (message->AppendBytes(aData, aDataLength) != kErrorNone)
98     {
99         message->Free();
100         message = nullptr;
101     }
102 
103 exit:
104     return message;
105 }
106 
DscpToPriority(uint8_t aDscp)107 Message::Priority Ip6::DscpToPriority(uint8_t aDscp)
108 {
109     Message::Priority priority;
110     uint8_t           cs = aDscp & kDscpCsMask;
111 
112     switch (cs)
113     {
114     case kDscpCs1:
115     case kDscpCs2:
116         priority = Message::kPriorityLow;
117         break;
118 
119     case kDscpCs0:
120     case kDscpCs3:
121         priority = Message::kPriorityNormal;
122         break;
123 
124     case kDscpCs4:
125     case kDscpCs5:
126     case kDscpCs6:
127     case kDscpCs7:
128         priority = Message::kPriorityHigh;
129         break;
130 
131     default:
132         priority = Message::kPriorityNormal;
133         break;
134     }
135 
136     return priority;
137 }
138 
PriorityToDscp(Message::Priority aPriority)139 uint8_t Ip6::PriorityToDscp(Message::Priority aPriority)
140 {
141     uint8_t dscp = kDscpCs0;
142 
143     switch (aPriority)
144     {
145     case Message::kPriorityLow:
146         dscp = kDscpCs1;
147         break;
148 
149     case Message::kPriorityNormal:
150     case Message::kPriorityNet:
151         dscp = kDscpCs0;
152         break;
153 
154     case Message::kPriorityHigh:
155         dscp = kDscpCs4;
156         break;
157     }
158 
159     return dscp;
160 }
161 
AddMplOption(Message & aMessage,Header & aHeader)162 Error Ip6::AddMplOption(Message &aMessage, Header &aHeader)
163 {
164     Error          error = kErrorNone;
165     HopByHopHeader hbhHeader;
166     MplOption      mplOption;
167     PadOption      padOption;
168 
169     hbhHeader.SetNextHeader(aHeader.GetNextHeader());
170     hbhHeader.SetLength(0);
171     mMpl.InitOption(mplOption, aHeader.GetSource());
172 
173     // Check if MPL option may require padding
174     if (padOption.InitToPadHeaderWithSize(sizeof(HopByHopHeader) + mplOption.GetSize()) == kErrorNone)
175     {
176         SuccessOrExit(error = aMessage.PrependBytes(&padOption, padOption.GetSize()));
177     }
178 
179     SuccessOrExit(error = aMessage.PrependBytes(&mplOption, mplOption.GetSize()));
180     SuccessOrExit(error = aMessage.Prepend(hbhHeader));
181     aHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(hbhHeader) + sizeof(mplOption));
182     aHeader.SetNextHeader(kProtoHopOpts);
183 
184 exit:
185     return error;
186 }
187 
PrepareMulticastToLargerThanRealmLocal(Message & aMessage,const Header & aHeader)188 Error Ip6::PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader)
189 {
190     Error          error = kErrorNone;
191     Header         tunnelHeader;
192     const Address *source;
193 
194 #if OPENTHREAD_FTD
195     if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() &&
196         Get<ChildTable>().HasSleepyChildWithAddress(aHeader.GetDestination()))
197     {
198         Message *messageCopy = aMessage.Clone();
199 
200         if (messageCopy != nullptr)
201         {
202             EnqueueDatagram(*messageCopy);
203         }
204         else
205         {
206             LogWarn("Failed to clone mcast message for indirect tx to sleepy children");
207         }
208     }
209 #endif
210 
211     // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address.
212     tunnelHeader.InitVersionTrafficClassFlow();
213     tunnelHeader.SetHopLimit(kDefaultHopLimit);
214     tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader));
215     tunnelHeader.GetDestination().SetToRealmLocalAllMplForwarders();
216     tunnelHeader.SetNextHeader(kProtoIp6);
217 
218     source = SelectSourceAddress(tunnelHeader.GetDestination());
219     VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress);
220 
221     tunnelHeader.SetSource(*source);
222 
223     SuccessOrExit(error = AddMplOption(aMessage, tunnelHeader));
224     SuccessOrExit(error = aMessage.Prepend(tunnelHeader));
225 
226 exit:
227     return error;
228 }
229 
InsertMplOption(Message & aMessage,Header & aHeader)230 Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader)
231 {
232     Error error = kErrorNone;
233 
234     if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal())
235     {
236         error = PrepareMulticastToLargerThanRealmLocal(aMessage, aHeader);
237         ExitNow();
238     }
239 
240     VerifyOrExit(aHeader.GetDestination().IsRealmLocalMulticast());
241 
242     aMessage.RemoveHeader(sizeof(aHeader));
243 
244     if (aHeader.GetNextHeader() == kProtoHopOpts)
245     {
246         HopByHopHeader hbh;
247         uint16_t       hbhSize;
248         MplOption      mplOption;
249         PadOption      padOption;
250 
251         // Read existing hop-by-hop option header
252         SuccessOrExit(error = aMessage.Read(0, hbh));
253         hbhSize = hbh.GetSize();
254 
255         VerifyOrExit(hbhSize <= aHeader.GetPayloadLength(), error = kErrorParse);
256 
257         // Increment hop-by-hop option header length by one which
258         // increases its total size by 8 bytes.
259         hbh.SetLength(hbh.GetLength() + 1);
260         aMessage.Write(0, hbh);
261 
262         // Make space for MPL Option + padding (8 bytes) at the end
263         // of hop-by-hop header
264         SuccessOrExit(error = aMessage.InsertHeader(hbhSize, ExtensionHeader::kLengthUnitSize));
265 
266         // Insert MPL Option
267         mMpl.InitOption(mplOption, aHeader.GetSource());
268         aMessage.WriteBytes(hbhSize, &mplOption, mplOption.GetSize());
269 
270         // Insert Pad Option (if needed)
271         if (padOption.InitToPadHeaderWithSize(mplOption.GetSize()) == kErrorNone)
272         {
273             aMessage.WriteBytes(hbhSize + mplOption.GetSize(), &padOption, padOption.GetSize());
274         }
275 
276         // Update IPv6 Payload Length
277         aHeader.SetPayloadLength(aHeader.GetPayloadLength() + ExtensionHeader::kLengthUnitSize);
278     }
279     else
280     {
281         SuccessOrExit(error = AddMplOption(aMessage, aHeader));
282     }
283 
284     SuccessOrExit(error = aMessage.Prepend(aHeader));
285 
286 exit:
287     return error;
288 }
289 
RemoveMplOption(Message & aMessage)290 Error Ip6::RemoveMplOption(Message &aMessage)
291 {
292     enum Action : uint8_t
293     {
294         kNoMplOption,
295         kShrinkHbh,
296         kRemoveHbh,
297         kReplaceMplWithPad,
298     };
299 
300     Error          error  = kErrorNone;
301     Action         action = kNoMplOption;
302     Header         ip6Header;
303     HopByHopHeader hbh;
304     Option         option;
305     OffsetRange    offsetRange;
306     OffsetRange    mplOffsetRange;
307     PadOption      padOption;
308 
309     offsetRange.InitFromMessageFullLength(aMessage);
310 
311     IgnoreError(aMessage.Read(offsetRange, ip6Header));
312     offsetRange.AdvanceOffset(sizeof(ip6Header));
313 
314     VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts);
315 
316     SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbh));
317 
318     for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
319     {
320         SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
321 
322         if (option.IsPadding())
323         {
324             continue;
325         }
326 
327         if (option.GetType() == MplOption::kType)
328         {
329             // If multiple MPL options exist, discard packet
330             VerifyOrExit(action == kNoMplOption, error = kErrorParse);
331 
332             // `Option::ParseFrom()` already validated that the entire
333             // option is present in the `offsetRange`.
334 
335             mplOffsetRange = offsetRange;
336             mplOffsetRange.ShrinkLength(option.GetSize());
337 
338             VerifyOrExit(option.GetSize() <= sizeof(MplOption), error = kErrorParse);
339 
340             if (mplOffsetRange.GetOffset() == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0)
341             {
342                 // First and only IPv6 Option, remove IPv6 HBH Option header
343                 action = kRemoveHbh;
344             }
345             else if (mplOffsetRange.GetOffset() + ExtensionHeader::kLengthUnitSize == offsetRange.GetEndOffset())
346             {
347                 // Last IPv6 Option, shrink the last 8 bytes
348                 action = kShrinkHbh;
349             }
350         }
351         else if (action != kNoMplOption)
352         {
353             // Encountered another option, now just replace
354             // MPL Option with Pad Option
355             action = kReplaceMplWithPad;
356         }
357     }
358 
359     switch (action)
360     {
361     case kNoMplOption:
362         break;
363 
364     case kShrinkHbh:
365     case kRemoveHbh:
366         // Last IPv6 Option, shrink HBH Option header by
367         // 8 bytes (`kLengthUnitSize`)
368         aMessage.RemoveHeader(offsetRange.GetEndOffset() - ExtensionHeader::kLengthUnitSize,
369                               ExtensionHeader::kLengthUnitSize);
370 
371         if (action == kRemoveHbh)
372         {
373             ip6Header.SetNextHeader(hbh.GetNextHeader());
374         }
375         else
376         {
377             // Update HBH header length, decrement by one
378             // which decreases its total size by 8 bytes.
379 
380             hbh.SetLength(hbh.GetLength() - 1);
381             aMessage.Write(sizeof(ip6Header), hbh);
382         }
383 
384         ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - ExtensionHeader::kLengthUnitSize);
385         aMessage.Write(0, ip6Header);
386         break;
387 
388     case kReplaceMplWithPad:
389         padOption.InitForPadSize(static_cast<uint8_t>(mplOffsetRange.GetLength()));
390         aMessage.WriteBytes(mplOffsetRange.GetOffset(), &padOption, padOption.GetSize());
391         break;
392     }
393 
394 exit:
395     return error;
396 }
397 
EnqueueDatagram(Message & aMessage)398 void Ip6::EnqueueDatagram(Message &aMessage)
399 {
400     mSendQueue.Enqueue(aMessage);
401     mSendQueueTask.Post();
402 }
403 
SendDatagram(Message & aMessage,MessageInfo & aMessageInfo,uint8_t aIpProto)404 Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto)
405 {
406     Error    error = kErrorNone;
407     Header   header;
408     uint8_t  dscp;
409     uint16_t payloadLength = aMessage.GetLength();
410 
411     if ((aIpProto == kProtoUdp) &&
412         Get<Tmf::Agent>().IsTmfMessage(aMessageInfo.GetSockAddr(), aMessageInfo.GetPeerAddr(),
413                                        aMessageInfo.GetPeerPort()))
414     {
415         dscp = Tmf::Agent::PriorityToDscp(aMessage.GetPriority());
416     }
417     else
418     {
419         dscp = PriorityToDscp(aMessage.GetPriority());
420     }
421 
422     header.InitVersionTrafficClassFlow();
423     header.SetDscp(dscp);
424     header.SetEcn(aMessageInfo.GetEcn());
425     header.SetPayloadLength(payloadLength);
426     header.SetNextHeader(aIpProto);
427 
428     if (aMessageInfo.GetHopLimit() != 0 || aMessageInfo.ShouldAllowZeroHopLimit())
429     {
430         header.SetHopLimit(aMessageInfo.GetHopLimit());
431     }
432     else
433     {
434         header.SetHopLimit(kDefaultHopLimit);
435     }
436 
437     if (aMessageInfo.GetSockAddr().IsUnspecified() || aMessageInfo.GetSockAddr().IsMulticast())
438     {
439         const Address *source = SelectSourceAddress(aMessageInfo.GetPeerAddr());
440 
441         VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress);
442         header.SetSource(*source);
443     }
444     else
445     {
446         header.SetSource(aMessageInfo.GetSockAddr());
447     }
448 
449     header.SetDestination(aMessageInfo.GetPeerAddr());
450 
451     if (aMessageInfo.GetPeerAddr().IsRealmLocalMulticast())
452     {
453         SuccessOrExit(error = AddMplOption(aMessage, header));
454     }
455 
456     SuccessOrExit(error = aMessage.Prepend(header));
457 
458     Checksum::UpdateMessageChecksum(aMessage, header.GetSource(), header.GetDestination(), aIpProto);
459 
460     if (aMessageInfo.GetPeerAddr().IsMulticastLargerThanRealmLocal())
461     {
462         SuccessOrExit(error = PrepareMulticastToLargerThanRealmLocal(aMessage, header));
463     }
464 
465     aMessage.SetMulticastLoop(aMessageInfo.GetMulticastLoop());
466 
467     if (aMessage.GetLength() > kMaxDatagramLength)
468     {
469         error = FragmentDatagram(aMessage, aIpProto);
470     }
471     else
472     {
473         EnqueueDatagram(aMessage);
474     }
475 
476 exit:
477 
478     return error;
479 }
480 
HandleSendQueue(void)481 void Ip6::HandleSendQueue(void)
482 {
483     Message *message;
484 
485     while ((message = mSendQueue.GetHead()) != nullptr)
486     {
487         mSendQueue.Dequeue(*message);
488         IgnoreError(HandleDatagram(OwnedPtr<Message>(message)));
489     }
490 }
491 
ReadHopByHopHeader(const Message & aMessage,OffsetRange & aOffsetRange,HopByHopHeader & aHbhHeader) const492 Error Ip6::ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const
493 {
494     // Reads the HBH header from the message at the given offset range.
495     // On success, updates `aOffsetRange` to indicate the location of
496     // options within the HBH header.
497 
498     Error error;
499 
500     SuccessOrExit(error = aMessage.Read(aOffsetRange, aHbhHeader));
501     VerifyOrExit(aOffsetRange.Contains(aHbhHeader.GetSize()), error = kErrorParse);
502     aOffsetRange.ShrinkLength(aHbhHeader.GetSize());
503     aOffsetRange.AdvanceOffset(sizeof(HopByHopHeader));
504 
505 exit:
506     return error;
507 }
508 
HandleOptions(Message & aMessage,const Header & aHeader,bool & aReceive)509 Error Ip6::HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive)
510 {
511     Error          error = kErrorNone;
512     HopByHopHeader hbhHeader;
513     Option         option;
514     OffsetRange    offsetRange;
515 
516     offsetRange.InitFromMessageOffsetToEnd(aMessage);
517 
518     SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbhHeader));
519 
520     for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
521     {
522         SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
523 
524         if (option.IsPadding())
525         {
526             continue;
527         }
528 
529         if (option.GetType() == MplOption::kType)
530         {
531             SuccessOrExit(error = mMpl.ProcessOption(aMessage, offsetRange, aHeader.GetSource(), aReceive));
532             continue;
533         }
534 
535         VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop);
536     }
537 
538     aMessage.SetOffset(offsetRange.GetEndOffset());
539 
540 exit:
541     return error;
542 }
543 
544 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
FragmentDatagram(Message & aMessage,uint8_t aIpProto)545 Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
546 {
547     Error          error = kErrorNone;
548     Header         header;
549     FragmentHeader fragmentHeader;
550     Message       *fragment        = nullptr;
551     uint16_t       fragmentCnt     = 0;
552     uint16_t       payloadFragment = 0;
553     uint16_t       offset          = 0;
554 
555     uint16_t maxPayloadFragment =
556         FragmentHeader::MakeDivisibleByEight(kMinimalMtu - aMessage.GetOffset() - sizeof(fragmentHeader));
557     uint16_t payloadLeft = aMessage.GetLength() - aMessage.GetOffset();
558 
559     SuccessOrExit(error = aMessage.Read(0, header));
560     header.SetNextHeader(kProtoFragment);
561 
562     fragmentHeader.Init();
563     fragmentHeader.SetIdentification(Random::NonCrypto::GetUint32());
564     fragmentHeader.SetNextHeader(aIpProto);
565     fragmentHeader.SetMoreFlag();
566 
567     while (payloadLeft != 0)
568     {
569         if (payloadLeft < maxPayloadFragment)
570         {
571             fragmentHeader.ClearMoreFlag();
572 
573             payloadFragment = payloadLeft;
574             payloadLeft     = 0;
575 
576             LogDebg("Last Fragment");
577         }
578         else
579         {
580             payloadLeft -= maxPayloadFragment;
581             payloadFragment = maxPayloadFragment;
582         }
583 
584         offset = fragmentCnt * FragmentHeader::BytesToFragmentOffset(maxPayloadFragment);
585         fragmentHeader.SetOffset(offset);
586 
587         VerifyOrExit((fragment = NewMessage()) != nullptr, error = kErrorNoBufs);
588         IgnoreError(fragment->SetPriority(aMessage.GetPriority()));
589         SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment));
590 
591         header.SetPayloadLength(payloadFragment + sizeof(fragmentHeader));
592         fragment->Write(0, header);
593 
594         fragment->SetOffset(aMessage.GetOffset());
595         fragment->Write(aMessage.GetOffset(), fragmentHeader);
596 
597         fragment->WriteBytesFromMessage(
598             /* aWriteOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), aMessage,
599             /* aReadOffset */ aMessage.GetOffset() + FragmentHeader::FragmentOffsetToBytes(offset),
600             /* aLength */ payloadFragment);
601 
602         EnqueueDatagram(*fragment);
603 
604         fragmentCnt++;
605         fragment = nullptr;
606 
607         LogInfo("Fragment %d with %d bytes sent", fragmentCnt, payloadFragment);
608     }
609 
610     aMessage.Free();
611 
612 exit:
613 
614     if (error == kErrorNoBufs)
615     {
616         LogWarn("No buffer for Ip6 fragmentation");
617     }
618 
619     FreeMessageOnError(fragment, error);
620     return error;
621 }
622 
HandleFragment(Message & aMessage)623 Error Ip6::HandleFragment(Message &aMessage)
624 {
625     Error          error = kErrorNone;
626     Header         header, headerBuffer;
627     FragmentHeader fragmentHeader;
628     Message       *message         = nullptr;
629     uint16_t       offset          = 0;
630     uint16_t       payloadFragment = 0;
631     bool           isFragmented    = true;
632 
633     SuccessOrExit(error = aMessage.Read(0, header));
634     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader));
635 
636     if (fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet())
637     {
638         isFragmented = false;
639         aMessage.MoveOffset(sizeof(fragmentHeader));
640         ExitNow();
641     }
642 
643     for (Message &msg : mReassemblyList)
644     {
645         SuccessOrExit(error = msg.Read(0, headerBuffer));
646 
647         if (msg.GetDatagramTag() == fragmentHeader.GetIdentification() &&
648             headerBuffer.GetSource() == header.GetSource() && headerBuffer.GetDestination() == header.GetDestination())
649         {
650             message = &msg;
651             break;
652         }
653     }
654 
655     offset          = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset());
656     payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader);
657 
658     LogInfo("Fragment with id %lu received > %u bytes, offset %u", ToUlong(fragmentHeader.GetIdentification()),
659             payloadFragment, offset);
660 
661     if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength)
662     {
663         LogWarn("Packet too large for fragment buffer");
664         ExitNow(error = kErrorNoBufs);
665     }
666 
667     if (message == nullptr)
668     {
669         LogDebg("start reassembly");
670         VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs);
671         mReassemblyList.Enqueue(*message);
672 
673         message->SetTimestampToNow();
674         message->SetOffset(0);
675         message->SetDatagramTag(fragmentHeader.GetIdentification());
676 
677         // copying the non-fragmentable header to the fragmentation buffer
678         SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetOffset()));
679 
680         Get<TimeTicker>().RegisterReceiver(TimeTicker::kIp6FragmentReassembler);
681     }
682 
683     // increase message buffer if necessary
684     if (message->GetLength() < offset + payloadFragment + aMessage.GetOffset())
685     {
686         SuccessOrExit(error = message->SetLength(offset + payloadFragment + aMessage.GetOffset()));
687     }
688 
689     // copy the fragment payload into the message buffer
690     message->WriteBytesFromMessage(
691         /* aWriteOffset */ aMessage.GetOffset() + offset, aMessage,
692         /* aReadOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), /* aLength */ payloadFragment);
693 
694     // check if it is the last frame
695     if (!fragmentHeader.IsMoreFlagSet())
696     {
697         // use the offset value for the whole ip message length
698         message->SetOffset(aMessage.GetOffset() + offset + payloadFragment);
699 
700         // creates the header for the reassembled ipv6 package
701         SuccessOrExit(error = aMessage.Read(0, header));
702         header.SetPayloadLength(message->GetLength() - sizeof(header));
703         header.SetNextHeader(fragmentHeader.GetNextHeader());
704         message->Write(0, header);
705 
706         LogDebg("Reassembly complete.");
707 
708         mReassemblyList.Dequeue(*message);
709 
710         IgnoreError(HandleDatagram(OwnedPtr<Message>(message), /* aIsReassembled */ true));
711     }
712 
713 exit:
714     if (error != kErrorDrop && error != kErrorNone && isFragmented)
715     {
716         if (message != nullptr)
717         {
718             mReassemblyList.DequeueAndFree(*message);
719         }
720 
721         LogWarnOnError(error, "reassemble");
722     }
723 
724     if (isFragmented)
725     {
726         // drop all fragments, the payload is stored in the fragment buffer
727         error = kErrorDrop;
728     }
729 
730     return error;
731 }
732 
CleanupFragmentationBuffer(void)733 void Ip6::CleanupFragmentationBuffer(void) { mReassemblyList.DequeueAndFreeAll(); }
734 
HandleTimeTick(void)735 void Ip6::HandleTimeTick(void)
736 {
737     UpdateReassemblyList();
738 
739     if (mReassemblyList.GetHead() == nullptr)
740     {
741         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kIp6FragmentReassembler);
742     }
743 }
744 
UpdateReassemblyList(void)745 void Ip6::UpdateReassemblyList(void)
746 {
747     TimeMilli now = TimerMilli::GetNow();
748 
749     for (Message &message : mReassemblyList)
750     {
751         if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kReassemblyTimeout))
752         {
753             LogNote("Reassembly timeout.");
754             SendIcmpError(message, Icmp::Header::kTypeTimeExceeded, Icmp::Header::kCodeFragmReasTimeEx);
755 
756             mReassemblyList.DequeueAndFree(message);
757         }
758     }
759 }
760 
SendIcmpError(Message & aMessage,Icmp::Header::Type aIcmpType,Icmp::Header::Code aIcmpCode)761 void Ip6::SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode)
762 {
763     Error       error = kErrorNone;
764     Header      header;
765     MessageInfo messageInfo;
766 
767     SuccessOrExit(error = aMessage.Read(0, header));
768 
769     messageInfo.SetPeerAddr(header.GetSource());
770     messageInfo.SetSockAddr(header.GetDestination());
771     messageInfo.SetHopLimit(header.GetHopLimit());
772 
773     error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, aMessage);
774 
775 exit:
776     LogWarnOnError(error, "send ICMP");
777     OT_UNUSED_VARIABLE(error);
778 }
779 
780 #else
FragmentDatagram(Message & aMessage,uint8_t aIpProto)781 Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
782 {
783     OT_UNUSED_VARIABLE(aIpProto);
784 
785     EnqueueDatagram(aMessage);
786 
787     return kErrorNone;
788 }
789 
HandleFragment(Message & aMessage)790 Error Ip6::HandleFragment(Message &aMessage)
791 {
792     Error          error = kErrorNone;
793     FragmentHeader fragmentHeader;
794 
795     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader));
796 
797     VerifyOrExit(fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet(), error = kErrorDrop);
798 
799     aMessage.MoveOffset(sizeof(fragmentHeader));
800 
801 exit:
802     return error;
803 }
804 #endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
805 
HandleExtensionHeaders(OwnedPtr<Message> & aMessagePtr,const Header & aHeader,uint8_t & aNextHeader,bool & aReceive)806 Error Ip6::HandleExtensionHeaders(OwnedPtr<Message> &aMessagePtr,
807                                   const Header      &aHeader,
808                                   uint8_t           &aNextHeader,
809                                   bool              &aReceive)
810 {
811     Error           error = kErrorNone;
812     ExtensionHeader extHeader;
813 
814     while (aReceive || aNextHeader == kProtoHopOpts)
815     {
816         SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), extHeader));
817 
818         switch (aNextHeader)
819         {
820         case kProtoHopOpts:
821         case kProtoDstOpts:
822             SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive));
823             break;
824 
825         case kProtoFragment:
826             IgnoreError(PassToHost(aMessagePtr, aHeader, aNextHeader, aReceive, Message::kCopyToUse));
827             SuccessOrExit(error = HandleFragment(*aMessagePtr));
828             break;
829 
830         case kProtoIp6:
831             ExitNow();
832 
833         case kProtoRouting:
834         case kProtoNone:
835             ExitNow(error = kErrorDrop);
836 
837         default:
838             ExitNow();
839         }
840 
841         aNextHeader = extHeader.GetNextHeader();
842     }
843 
844 exit:
845     return error;
846 }
847 
TakeOrCopyMessagePtr(OwnedPtr<Message> & aTargetPtr,OwnedPtr<Message> & aMessagePtr,Message::Ownership aMessageOwnership)848 Error Ip6::TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr,
849                                 OwnedPtr<Message> &aMessagePtr,
850                                 Message::Ownership aMessageOwnership)
851 {
852     switch (aMessageOwnership)
853     {
854     case Message::kTakeCustody:
855         aTargetPtr = aMessagePtr.PassOwnership();
856         break;
857 
858     case Message::kCopyToUse:
859         aTargetPtr.Reset(aMessagePtr->Clone());
860         break;
861     }
862 
863     return (aTargetPtr != nullptr) ? kErrorNone : kErrorNoBufs;
864 }
865 
Receive(Header & aIp6Header,OwnedPtr<Message> & aMessagePtr,uint8_t aIpProto,Message::Ownership aMessageOwnership)866 Error Ip6::Receive(Header            &aIp6Header,
867                    OwnedPtr<Message> &aMessagePtr,
868                    uint8_t            aIpProto,
869                    Message::Ownership aMessageOwnership)
870 {
871     Error             error = kErrorNone;
872     OwnedPtr<Message> messagePtr;
873     MessageInfo       messageInfo;
874 
875     messageInfo.Clear();
876     messageInfo.SetPeerAddr(aIp6Header.GetSource());
877     messageInfo.SetSockAddr(aIp6Header.GetDestination());
878     messageInfo.SetHopLimit(aIp6Header.GetHopLimit());
879     messageInfo.SetEcn(aIp6Header.GetEcn());
880 
881     switch (aIpProto)
882     {
883     case kProtoUdp:
884     case kProtoIcmp6:
885         break;
886 #if OPENTHREAD_CONFIG_TCP_ENABLE
887     case kProtoTcp:
888         break;
889 #endif
890     default:
891         ExitNow();
892     }
893 
894     SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership));
895 
896     switch (aIpProto)
897     {
898 #if OPENTHREAD_CONFIG_TCP_ENABLE
899     case kProtoTcp:
900         error = mTcp.HandleMessage(aIp6Header, *messagePtr, messageInfo);
901         break;
902 #endif
903     case kProtoUdp:
904         error = mUdp.HandleMessage(*messagePtr, messageInfo);
905         break;
906 
907     case kProtoIcmp6:
908         error = mIcmp.HandleMessage(*messagePtr, messageInfo);
909         break;
910 
911     default:
912         break;
913     }
914 
915 exit:
916     LogWarnOnError(error, "handle payload");
917     return error;
918 }
919 
PassToHost(OwnedPtr<Message> & aMessagePtr,const Header & aHeader,uint8_t aIpProto,bool aReceive,Message::Ownership aMessageOwnership)920 Error Ip6::PassToHost(OwnedPtr<Message> &aMessagePtr,
921                       const Header      &aHeader,
922                       uint8_t            aIpProto,
923                       bool               aReceive,
924                       Message::Ownership aMessageOwnership)
925 {
926     // This method passes the message to host by invoking the
927     // registered IPv6 receive callback. When NAT64 is enabled, it
928     // may also perform translation and invoke IPv4 receive
929     // callback.
930 
931     Error             error = kErrorNone;
932     OwnedPtr<Message> messagePtr;
933 
934     VerifyOrExit(aMessagePtr->IsLoopbackToHostAllowed());
935 
936     VerifyOrExit(mReceiveCallback.IsSet(), error = kErrorNoRoute);
937 
938     // Do not pass IPv6 packets that exceed kMinimalMtu.
939     VerifyOrExit(aMessagePtr->GetLength() <= kMinimalMtu, error = kErrorDrop);
940 
941 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
942     if (!aReceive)
943     {
944         Get<BorderRouter::RoutingManager>().CheckReachabilityToSendIcmpError(*aMessagePtr, aHeader);
945     }
946 #endif
947 
948     // If the sender used mesh-local address as source, do not pass to
949     // host unless this message is intended for this device itself.
950     if (Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource()))
951     {
952         VerifyOrExit(aReceive, error = kErrorDrop);
953     }
954 
955     if (mReceiveFilterEnabled && aReceive)
956     {
957         switch (aIpProto)
958         {
959         case kProtoIcmp6:
960             if (mIcmp.ShouldHandleEchoRequest(aHeader.GetDestination()))
961             {
962                 Icmp::Header icmp;
963 
964                 IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), icmp));
965                 VerifyOrExit(icmp.GetType() != Icmp::Header::kTypeEchoRequest, error = kErrorDrop);
966             }
967 
968             break;
969 
970         case kProtoUdp:
971         {
972             Udp::Header udp;
973 
974             IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), udp));
975             VerifyOrExit(!Get<Udp>().IsPortInUse(udp.GetDestinationPort()), error = kErrorNoRoute);
976             break;
977         }
978 
979 #if OPENTHREAD_CONFIG_TCP_ENABLE
980         // Do not pass TCP message to avoid dual processing from both
981         // OpenThread and POSIX TCP stacks.
982         case kProtoTcp:
983             error = kErrorNoRoute;
984             ExitNow();
985 #endif
986 
987         default:
988             break;
989         }
990     }
991 
992     SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership));
993 
994     IgnoreError(RemoveMplOption(*messagePtr));
995 
996 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
997     switch (Get<Nat64::Translator>().TranslateFromIp6(*messagePtr))
998     {
999     case Nat64::Translator::kNotTranslated:
1000         break;
1001 
1002     case Nat64::Translator::kDrop:
1003         ExitNow(error = kErrorDrop);
1004 
1005     case Nat64::Translator::kForward:
1006         VerifyOrExit(mIp4ReceiveCallback.IsSet(), error = kErrorNoRoute);
1007         // Pass message to callback transferring its ownership.
1008         mIp4ReceiveCallback.Invoke(messagePtr.Release());
1009         ExitNow();
1010     }
1011 #endif
1012 
1013 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1014     UpdateBorderRoutingCounters(aHeader, messagePtr->GetLength(), /* aIsInbound */ false);
1015 #endif
1016 
1017 #if OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC
1018     // Some platforms (e.g. Android) currently doesn't restrict link-local/mesh-local source
1019     // addresses when forwarding multicast packets.
1020     // For a multicast packet sent from link-local/mesh-local address to scope larger
1021     // than realm-local, set the hop limit to 1 before sending to host, so this packet
1022     // will not be forwarded by host.
1023     if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() &&
1024         (aHeader.GetSource().IsLinkLocalUnicast() || (Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource()))))
1025     {
1026         messagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, 1);
1027     }
1028 #endif
1029 
1030     // Pass message to callback transferring its ownership.
1031     mReceiveCallback.Invoke(messagePtr.Release());
1032 
1033 exit:
1034     return error;
1035 }
1036 
SendRaw(OwnedPtr<Message> aMessagePtr)1037 Error Ip6::SendRaw(OwnedPtr<Message> aMessagePtr)
1038 {
1039     Error  error = kErrorNone;
1040     Header header;
1041 
1042     SuccessOrExit(error = header.ParseFrom(*aMessagePtr));
1043     VerifyOrExit(!header.GetSource().IsMulticast(), error = kErrorInvalidSourceAddress);
1044 
1045 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
1046     // The filtering rules don't apply to packets from DUA.
1047     if (!Get<BackboneRouter::Leader>().IsDomainUnicast(header.GetSource()))
1048 #endif
1049     {
1050         // When the packet is forwarded from host to Thread, if its source is on-mesh or its destination is
1051         // mesh-local, we'll drop the packet unless the packet originates from this device.
1052         if (Get<NetworkData::Leader>().IsOnMesh(header.GetSource()) ||
1053             Get<Mle::Mle>().IsMeshLocalAddress(header.GetDestination()))
1054         {
1055             VerifyOrExit(Get<ThreadNetif>().HasUnicastAddress(header.GetSource()), error = kErrorDrop);
1056         }
1057     }
1058 
1059     if (header.GetDestination().IsMulticast())
1060     {
1061         SuccessOrExit(error = InsertMplOption(*aMessagePtr, header));
1062     }
1063 
1064 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1065     UpdateBorderRoutingCounters(header, aMessagePtr->GetLength(), /* aIsInbound */ true);
1066 #endif
1067 
1068     error = HandleDatagram(aMessagePtr.PassOwnership());
1069 
1070 exit:
1071     return error;
1072 }
1073 
HandleDatagram(OwnedPtr<Message> aMessagePtr,bool aIsReassembled)1074 Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
1075 {
1076     Error   error;
1077     Header  header;
1078     bool    receive;
1079     bool    forwardThread;
1080     bool    forwardHost;
1081     uint8_t nextHeader;
1082 
1083     receive       = false;
1084     forwardThread = false;
1085     forwardHost   = false;
1086 
1087     SuccessOrExit(error = header.ParseFrom(*aMessagePtr));
1088 
1089     if (!aMessagePtr->IsOriginHostTrusted())
1090     {
1091         VerifyOrExit(!header.GetSource().IsLoopback() && !header.GetDestination().IsLoopback(), error = kErrorDrop);
1092     }
1093 
1094     // Determine `forwardThread`, `forwardHost` and `receive`
1095     // based on the destination address.
1096 
1097     if (header.GetDestination().IsMulticast())
1098     {
1099         // Destination is multicast
1100 
1101         forwardThread = !aMessagePtr->IsOriginThreadNetif();
1102 
1103 #if OPENTHREAD_FTD
1104         if (aMessagePtr->IsOriginThreadNetif() && header.GetDestination().IsMulticastLargerThanRealmLocal() &&
1105             Get<ChildTable>().HasSleepyChildWithAddress(header.GetDestination()))
1106         {
1107             forwardThread = true;
1108         }
1109 #endif
1110 
1111         // Always forward multicast packets to host network stack
1112         forwardHost = true;
1113 
1114         if ((aMessagePtr->IsOriginThreadNetif() || aMessagePtr->GetMulticastLoop()) &&
1115             Get<ThreadNetif>().IsMulticastSubscribed(header.GetDestination()))
1116         {
1117             receive = true;
1118         }
1119     }
1120     else
1121     {
1122         // Destination is unicast
1123 
1124         if (Get<ThreadNetif>().HasUnicastAddress(header.GetDestination()))
1125         {
1126             receive = true;
1127         }
1128         else if (!aMessagePtr->IsOriginThreadNetif() || !header.GetDestination().IsLinkLocalUnicast())
1129         {
1130             if (header.GetDestination().IsLinkLocalUnicast())
1131             {
1132                 forwardThread = true;
1133             }
1134             else if (IsOnLink(header.GetDestination()))
1135             {
1136 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
1137                 forwardThread = (!aMessagePtr->IsLoopbackToHostAllowed() ||
1138                                  !Get<BackboneRouter::Manager>().ShouldForwardDuaToBackbone(header.GetDestination()));
1139 #else
1140                 forwardThread = true;
1141 #endif
1142             }
1143             else if (RouteLookup(header.GetSource(), header.GetDestination()) == kErrorNone)
1144             {
1145                 forwardThread = true;
1146             }
1147 
1148             forwardHost = !forwardThread;
1149         }
1150     }
1151 
1152     aMessagePtr->SetOffset(sizeof(header));
1153 
1154     // Process IPv6 Extension Headers
1155     nextHeader = header.GetNextHeader();
1156     SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, header, nextHeader, receive));
1157 
1158     if (receive && (nextHeader == kProtoIp6))
1159     {
1160         // Process the embedded IPv6 message in an IPv6 tunnel message.
1161         // If we need to `forwardThread` we create a copy by cloning
1162         // the message, otherwise we take ownership of `aMessage`
1163         // itself and use it directly. The encapsulating header is
1164         // then removed before processing the embedded message.
1165 
1166         OwnedPtr<Message> messagePtr;
1167         bool              multicastLoop = aMessagePtr->GetMulticastLoop();
1168 
1169         SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr,
1170                                                    forwardThread ? Message::kCopyToUse : Message::kTakeCustody));
1171         messagePtr->SetMulticastLoop(multicastLoop);
1172         messagePtr->RemoveHeader(messagePtr->GetOffset());
1173 
1174         Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr);
1175 
1176         IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled));
1177 
1178         receive     = false;
1179         forwardHost = false;
1180     }
1181 
1182     if ((forwardHost || receive) && !aIsReassembled)
1183     {
1184         error = PassToHost(aMessagePtr, header, nextHeader, receive,
1185                            (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody);
1186     }
1187 
1188     if (receive)
1189     {
1190         error = Receive(header, aMessagePtr, nextHeader, forwardThread ? Message::kCopyToUse : Message::kTakeCustody);
1191     }
1192 
1193     if (forwardThread)
1194     {
1195         if (aMessagePtr->IsOriginThreadNetif())
1196         {
1197             VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader());
1198             header.SetHopLimit(header.GetHopLimit() - 1);
1199         }
1200 
1201         VerifyOrExit(header.GetHopLimit() > 0, error = kErrorDrop);
1202 
1203         aMessagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, header.GetHopLimit());
1204 
1205         if (nextHeader == kProtoIcmp6)
1206         {
1207             uint8_t icmpType;
1208 
1209             SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), icmpType));
1210 
1211             error = kErrorDrop;
1212 
1213             for (IcmpType type : kForwardIcmpTypes)
1214             {
1215                 if (icmpType == type)
1216                 {
1217                     error = kErrorNone;
1218                     break;
1219                 }
1220             }
1221 
1222             SuccessOrExit(error);
1223         }
1224 
1225 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1226         if (mTmfOriginFilterEnabled)
1227 #endif
1228         {
1229             if (aMessagePtr->IsOriginHostUntrusted() && (nextHeader == kProtoUdp))
1230             {
1231                 Udp::Header udpHeader;
1232 
1233                 SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), udpHeader));
1234 
1235                 if (udpHeader.GetDestinationPort() == Tmf::kUdpPort)
1236                 {
1237                     LogNote("Dropping TMF message from untrusted origin");
1238                     ExitNow(error = kErrorDrop);
1239                 }
1240             }
1241         }
1242 
1243 #if OPENTHREAD_CONFIG_MULTI_RADIO
1244         // Since the message will be forwarded, we clear the radio
1245         // type on the message to allow the radio type for tx to be
1246         // selected later (based on the radios supported by the next
1247         // hop).
1248         aMessagePtr->ClearRadioType();
1249 #endif
1250 
1251         Get<MeshForwarder>().SendMessage(aMessagePtr.PassOwnership());
1252     }
1253 
1254 exit:
1255     return error;
1256 }
1257 
SelectSourceAddress(MessageInfo & aMessageInfo) const1258 Error Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) const
1259 {
1260     Error          error = kErrorNone;
1261     const Address *source;
1262 
1263     source = SelectSourceAddress(aMessageInfo.GetPeerAddr());
1264     VerifyOrExit(source != nullptr, error = kErrorNotFound);
1265     aMessageInfo.SetSockAddr(*source);
1266 
1267 exit:
1268     return error;
1269 }
1270 
SelectSourceAddress(const Address & aDestination) const1271 const Address *Ip6::SelectSourceAddress(const Address &aDestination) const
1272 {
1273     uint8_t                      destScope    = aDestination.GetScope();
1274     bool                         destIsRloc   = Get<Mle::Mle>().IsRoutingLocator(aDestination);
1275     const Netif::UnicastAddress *bestAddr     = nullptr;
1276     uint8_t                      bestMatchLen = 0;
1277 
1278     for (const Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
1279     {
1280         bool    newAddrIsPreferred = false;
1281         uint8_t matchLen;
1282         uint8_t overrideScope;
1283 
1284         if (Get<Mle::Mle>().IsAnycastLocator(addr.GetAddress()))
1285         {
1286             // Don't use anycast address as source address.
1287             continue;
1288         }
1289 
1290         matchLen = aDestination.PrefixMatch(addr.GetAddress());
1291 
1292         if (matchLen >= addr.mPrefixLength)
1293         {
1294             matchLen      = addr.mPrefixLength;
1295             overrideScope = addr.GetScope();
1296         }
1297         else
1298         {
1299             overrideScope = destScope;
1300         }
1301 
1302         if (addr.GetAddress() == aDestination)
1303         {
1304             // Rule 1: Prefer same address
1305             bestAddr = &addr;
1306             ExitNow();
1307         }
1308 
1309         if (bestAddr == nullptr)
1310         {
1311             newAddrIsPreferred = true;
1312         }
1313         else if (addr.GetScope() < bestAddr->GetScope())
1314         {
1315             // Rule 2: Prefer appropriate scope
1316             newAddrIsPreferred = (addr.GetScope() >= overrideScope);
1317         }
1318         else if (addr.GetScope() > bestAddr->GetScope())
1319         {
1320             newAddrIsPreferred = (bestAddr->GetScope() < overrideScope);
1321         }
1322         else if (addr.mPreferred != bestAddr->mPreferred)
1323         {
1324             // Rule 3: Avoid deprecated addresses
1325             newAddrIsPreferred = addr.mPreferred;
1326         }
1327         else if (matchLen > bestMatchLen)
1328         {
1329             // Rule 6: Prefer matching label
1330             // Rule 7: Prefer public address
1331             // Rule 8: Use longest prefix matching
1332 
1333             newAddrIsPreferred = true;
1334         }
1335         else if ((matchLen == bestMatchLen) && (destIsRloc == Get<Mle::Mle>().IsRoutingLocator(addr.GetAddress())))
1336         {
1337             // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else
1338             newAddrIsPreferred = true;
1339         }
1340 
1341         if (newAddrIsPreferred)
1342         {
1343             bestAddr     = &addr;
1344             bestMatchLen = matchLen;
1345 
1346             // Infer destination scope based on prefix match
1347             if (bestMatchLen >= bestAddr->mPrefixLength)
1348             {
1349                 destScope = bestAddr->GetScope();
1350             }
1351         }
1352     }
1353 
1354 exit:
1355     return (bestAddr != nullptr) ? &bestAddr->GetAddress() : nullptr;
1356 }
1357 
IsOnLink(const Address & aAddress) const1358 bool Ip6::IsOnLink(const Address &aAddress) const
1359 {
1360     bool isOnLink = false;
1361 
1362     if (Get<NetworkData::Leader>().IsOnMesh(aAddress))
1363     {
1364         ExitNow(isOnLink = true);
1365     }
1366 
1367     for (const Netif::UnicastAddress &unicastAddr : Get<ThreadNetif>().GetUnicastAddresses())
1368     {
1369         if (unicastAddr.GetAddress().PrefixMatch(aAddress) >= unicastAddr.mPrefixLength)
1370         {
1371             ExitNow(isOnLink = true);
1372         }
1373     }
1374 
1375 exit:
1376     return isOnLink;
1377 }
1378 
RouteLookup(const Address & aSource,const Address & aDestination) const1379 Error Ip6::RouteLookup(const Address &aSource, const Address &aDestination) const
1380 {
1381     Error    error;
1382     uint16_t rloc;
1383 
1384     error = Get<NetworkData::Leader>().RouteLookup(aSource, aDestination, rloc);
1385 
1386     if (error == kErrorNone)
1387     {
1388         if (rloc == Get<Mle::MleRouter>().GetRloc16())
1389         {
1390             error = kErrorNoRoute;
1391         }
1392     }
1393     else
1394     {
1395         LogNote("Failed to find valid route for: %s", aDestination.ToString().AsCString());
1396     }
1397 
1398     return error;
1399 }
1400 
1401 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1402 
UpdateBorderRoutingCounters(const Header & aHeader,uint16_t aMessageLength,bool aIsInbound)1403 void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound)
1404 {
1405     static constexpr uint8_t kPrefixLength = 48;
1406 
1407     otPacketsAndBytes *counter         = nullptr;
1408     otPacketsAndBytes *internetCounter = nullptr;
1409 
1410     VerifyOrExit(!aHeader.GetSource().IsLinkLocalUnicast());
1411     VerifyOrExit(!aHeader.GetDestination().IsLinkLocalUnicast());
1412     VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource()));
1413     VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetDestination()));
1414 
1415     if (aIsInbound)
1416     {
1417         VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetSource()));
1418 
1419         if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
1420         {
1421             internetCounter = &mBrCounters.mInboundInternet;
1422         }
1423 
1424         if (aHeader.GetDestination().IsMulticast())
1425         {
1426             VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
1427             counter = &mBrCounters.mInboundMulticast;
1428         }
1429         else
1430         {
1431             counter = &mBrCounters.mInboundUnicast;
1432         }
1433     }
1434     else
1435     {
1436         VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetDestination()));
1437 
1438         if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
1439         {
1440             internetCounter = &mBrCounters.mOutboundInternet;
1441         }
1442 
1443         if (aHeader.GetDestination().IsMulticast())
1444         {
1445             VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
1446             counter = &mBrCounters.mOutboundMulticast;
1447         }
1448         else
1449         {
1450             counter = &mBrCounters.mOutboundUnicast;
1451         }
1452     }
1453 
1454 exit:
1455 
1456     if (counter)
1457     {
1458         counter->mPackets++;
1459         counter->mBytes += aMessageLength;
1460     }
1461     if (internetCounter)
1462     {
1463         internetCounter->mPackets++;
1464         internetCounter->mBytes += aMessageLength;
1465     }
1466 }
1467 
1468 #endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1469 
1470 // LCOV_EXCL_START
1471 
IpProtoToString(uint8_t aIpProto)1472 const char *Ip6::IpProtoToString(uint8_t aIpProto)
1473 {
1474     static constexpr Stringify::Entry kIpProtoTable[] = {
1475         {kProtoHopOpts, "HopOpts"}, {kProtoTcp, "TCP"},         {kProtoUdp, "UDP"},
1476         {kProtoIp6, "IP6"},         {kProtoRouting, "Routing"}, {kProtoFragment, "Frag"},
1477         {kProtoIcmp6, "ICMP6"},     {kProtoNone, "None"},       {kProtoDstOpts, "DstOpts"},
1478     };
1479 
1480     static_assert(Stringify::IsSorted(kIpProtoTable), "kIpProtoTable is not sorted");
1481 
1482     return Stringify::Lookup(aIpProto, kIpProtoTable, "Unknown");
1483 }
1484 
EcnToString(Ecn aEcn)1485 const char *Ip6::EcnToString(Ecn aEcn)
1486 {
1487     static const char *const kEcnStrings[] = {
1488         "no", // (0) kEcnNotCapable
1489         "e1", // (1) kEcnCapable1  (ECT1)
1490         "e0", // (2) kEcnCapable0  (ECT0)
1491         "ce", // (3) kEcnMarked    (Congestion Encountered)
1492     };
1493 
1494     struct EnumCheck
1495     {
1496         InitEnumValidatorCounter();
1497         ValidateNextEnum(kEcnNotCapable);
1498         ValidateNextEnum(kEcnCapable1);
1499         ValidateNextEnum(kEcnCapable0);
1500         ValidateNextEnum(kEcnMarked);
1501     };
1502 
1503     return kEcnStrings[aEcn];
1504 }
1505 
1506 // LCOV_EXCL_STOP
1507 
1508 //---------------------------------------------------------------------------------------------------------------------
1509 // Headers
1510 
ParseFrom(const Message & aMessage)1511 Error Headers::ParseFrom(const Message &aMessage)
1512 {
1513     Error error = kErrorParse;
1514 
1515     Clear();
1516 
1517     SuccessOrExit(mIp6Header.ParseFrom(aMessage));
1518 
1519     switch (mIp6Header.GetNextHeader())
1520     {
1521     case kProtoUdp:
1522         SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mUdp));
1523         break;
1524     case kProtoTcp:
1525         SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mTcp));
1526         break;
1527     case kProtoIcmp6:
1528         SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mIcmp));
1529         break;
1530     default:
1531         break;
1532     }
1533 
1534     error = kErrorNone;
1535 
1536 exit:
1537     return error;
1538 }
1539 
DecompressFrom(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMacAddrs)1540 Error Headers::DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs)
1541 {
1542     static constexpr uint16_t kReadLength = sizeof(Lowpan::FragmentHeader::NextFrag) + sizeof(Headers);
1543 
1544     uint8_t   frameBuffer[kReadLength];
1545     uint16_t  frameLength;
1546     FrameData frameData;
1547 
1548     frameLength = aMessage.ReadBytes(aOffset, frameBuffer, sizeof(frameBuffer));
1549     frameData.Init(frameBuffer, frameLength);
1550 
1551     return DecompressFrom(frameData, aMacAddrs, aMessage.GetInstance());
1552 }
1553 
DecompressFrom(const FrameData & aFrameData,const Mac::Addresses & aMacAddrs,Instance & aInstance)1554 Error Headers::DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance)
1555 {
1556     Error                  error     = kErrorNone;
1557     FrameData              frameData = aFrameData;
1558     Lowpan::FragmentHeader fragmentHeader;
1559     bool                   nextHeaderCompressed;
1560 
1561     if (fragmentHeader.ParseFrom(frameData) == kErrorNone)
1562     {
1563         // Only the first fragment header is followed by a LOWPAN_IPHC header
1564         VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0, error = kErrorNotFound);
1565     }
1566 
1567     VerifyOrExit(Lowpan::Lowpan::IsLowpanHc(frameData), error = kErrorNotFound);
1568 
1569     SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressBaseHeader(mIp6Header, nextHeaderCompressed,
1570                                                                                aMacAddrs, frameData));
1571 
1572     switch (mIp6Header.GetNextHeader())
1573     {
1574     case kProtoUdp:
1575         if (nextHeaderCompressed)
1576         {
1577             SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressUdpHeader(mHeader.mUdp, frameData));
1578         }
1579         else
1580         {
1581             SuccessOrExit(error = frameData.Read(mHeader.mUdp));
1582         }
1583         break;
1584 
1585     case kProtoTcp:
1586         SuccessOrExit(error = frameData.Read(mHeader.mTcp));
1587         break;
1588 
1589     case kProtoIcmp6:
1590         SuccessOrExit(error = frameData.Read(mHeader.mIcmp));
1591         break;
1592 
1593     default:
1594         break;
1595     }
1596 
1597 exit:
1598     return error;
1599 }
1600 
GetSourcePort(void) const1601 uint16_t Headers::GetSourcePort(void) const
1602 {
1603     uint16_t port = 0;
1604 
1605     switch (GetIpProto())
1606     {
1607     case kProtoUdp:
1608         port = mHeader.mUdp.GetSourcePort();
1609         break;
1610 
1611     case kProtoTcp:
1612         port = mHeader.mTcp.GetSourcePort();
1613         break;
1614 
1615     default:
1616         break;
1617     }
1618 
1619     return port;
1620 }
1621 
SetSourcePort(uint16_t aSrcPort)1622 void Headers::SetSourcePort(uint16_t aSrcPort)
1623 {
1624     switch (GetIpProto())
1625     {
1626     case kProtoUdp:
1627         mHeader.mUdp.SetSourcePort(aSrcPort);
1628         break;
1629 
1630     case kProtoTcp:
1631         mHeader.mTcp.SetSourcePort(aSrcPort);
1632         break;
1633 
1634     default:
1635         break;
1636     }
1637 }
1638 
GetDestinationPort(void) const1639 uint16_t Headers::GetDestinationPort(void) const
1640 {
1641     uint16_t port = 0;
1642 
1643     switch (GetIpProto())
1644     {
1645     case kProtoUdp:
1646         port = mHeader.mUdp.GetDestinationPort();
1647         break;
1648 
1649     case kProtoTcp:
1650         port = mHeader.mTcp.GetDestinationPort();
1651         break;
1652 
1653     default:
1654         break;
1655     }
1656 
1657     return port;
1658 }
1659 
GetChecksum(void) const1660 uint16_t Headers::GetChecksum(void) const
1661 {
1662     uint16_t checksum = 0;
1663 
1664     switch (GetIpProto())
1665     {
1666     case kProtoUdp:
1667         checksum = mHeader.mUdp.GetChecksum();
1668         break;
1669 
1670     case kProtoTcp:
1671         checksum = mHeader.mTcp.GetChecksum();
1672         break;
1673 
1674     case kProtoIcmp6:
1675         checksum = mHeader.mIcmp.GetChecksum();
1676         break;
1677 
1678     default:
1679         break;
1680     }
1681 
1682     return checksum;
1683 }
1684 
1685 } // namespace Ip6
1686 } // namespace ot
1687