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