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