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