• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements IPv6 networking.
32  */
33 
34 #include "ip6.hpp"
35 
36 #include "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)619 Error Ip6::HandleFragment(Message &aMessage)
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), /* aIsReassembled */ true));
707     }
708 
709 exit:
710     if (error != kErrorDrop && error != kErrorNone && isFragmented)
711     {
712         if (message != nullptr)
713         {
714             mReassemblyList.DequeueAndFree(*message);
715         }
716 
717         LogWarnOnError(error, "reassemble");
718     }
719 
720     if (isFragmented)
721     {
722         // drop all fragments, the payload is stored in the fragment buffer
723         error = kErrorDrop;
724     }
725 
726     return error;
727 }
728 
CleanupFragmentationBuffer(void)729 void Ip6::CleanupFragmentationBuffer(void) { mReassemblyList.DequeueAndFreeAll(); }
730 
HandleTimeTick(void)731 void Ip6::HandleTimeTick(void)
732 {
733     UpdateReassemblyList();
734 
735     if (mReassemblyList.GetHead() == nullptr)
736     {
737         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kIp6FragmentReassembler);
738     }
739 }
740 
UpdateReassemblyList(void)741 void Ip6::UpdateReassemblyList(void)
742 {
743     TimeMilli now = TimerMilli::GetNow();
744 
745     for (Message &message : mReassemblyList)
746     {
747         if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kIp6ReassemblyTimeout))
748         {
749             LogNote("Reassembly timeout.");
750             SendIcmpError(message, Icmp::Header::kTypeTimeExceeded, Icmp::Header::kCodeFragmReasTimeEx);
751 
752             mReassemblyList.DequeueAndFree(message);
753         }
754     }
755 }
756 
SendIcmpError(Message & aMessage,Icmp::Header::Type aIcmpType,Icmp::Header::Code aIcmpCode)757 void Ip6::SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode)
758 {
759     Error       error = kErrorNone;
760     Header      header;
761     MessageInfo messageInfo;
762 
763     SuccessOrExit(error = aMessage.Read(0, header));
764 
765     messageInfo.SetPeerAddr(header.GetSource());
766     messageInfo.SetSockAddr(header.GetDestination());
767     messageInfo.SetHopLimit(header.GetHopLimit());
768 
769     error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, aMessage);
770 
771 exit:
772     LogWarnOnError(error, "send ICMP");
773     OT_UNUSED_VARIABLE(error);
774 }
775 
776 #else
FragmentDatagram(Message & aMessage,uint8_t aIpProto)777 Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
778 {
779     OT_UNUSED_VARIABLE(aIpProto);
780 
781     EnqueueDatagram(aMessage);
782 
783     return kErrorNone;
784 }
785 
HandleFragment(Message & aMessage)786 Error Ip6::HandleFragment(Message &aMessage)
787 {
788     Error          error = kErrorNone;
789     FragmentHeader fragmentHeader;
790 
791     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader));
792 
793     VerifyOrExit(fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet(), error = kErrorDrop);
794 
795     aMessage.MoveOffset(sizeof(fragmentHeader));
796 
797 exit:
798     return error;
799 }
800 #endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
801 
HandleExtensionHeaders(OwnedPtr<Message> & aMessagePtr,MessageInfo & aMessageInfo,Header & aHeader,uint8_t & aNextHeader,bool & aReceive)802 Error Ip6::HandleExtensionHeaders(OwnedPtr<Message> &aMessagePtr,
803                                   MessageInfo       &aMessageInfo,
804                                   Header            &aHeader,
805                                   uint8_t           &aNextHeader,
806                                   bool              &aReceive)
807 {
808     Error error = kErrorNone;
809 
810     ExtensionHeader extHeader;
811 
812     while (aReceive || aNextHeader == kProtoHopOpts)
813     {
814         SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), extHeader));
815 
816         switch (aNextHeader)
817         {
818         case kProtoHopOpts:
819             SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive));
820             break;
821 
822         case kProtoFragment:
823             IgnoreError(PassToHost(aMessagePtr, aMessageInfo, aNextHeader,
824                                    /* aApplyFilter */ false, aReceive, Message::kCopyToUse));
825             SuccessOrExit(error = HandleFragment(*aMessagePtr));
826             break;
827 
828         case kProtoDstOpts:
829             SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive));
830             break;
831 
832         case kProtoIp6:
833             ExitNow();
834 
835         case kProtoRouting:
836         case kProtoNone:
837             ExitNow(error = kErrorDrop);
838 
839         default:
840             ExitNow();
841         }
842 
843         aNextHeader = static_cast<uint8_t>(extHeader.GetNextHeader());
844     }
845 
846 exit:
847     return error;
848 }
849 
TakeOrCopyMessagePtr(OwnedPtr<Message> & aTargetPtr,OwnedPtr<Message> & aMessagePtr,Message::Ownership aMessageOwnership)850 Error Ip6::TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr,
851                                 OwnedPtr<Message> &aMessagePtr,
852                                 Message::Ownership aMessageOwnership)
853 {
854     switch (aMessageOwnership)
855     {
856     case Message::kTakeCustody:
857         aTargetPtr = aMessagePtr.PassOwnership();
858         break;
859 
860     case Message::kCopyToUse:
861         aTargetPtr.Reset(aMessagePtr->Clone());
862         break;
863     }
864 
865     return (aTargetPtr != nullptr) ? kErrorNone : kErrorNoBufs;
866 }
867 
HandlePayload(Header & aIp6Header,OwnedPtr<Message> & aMessagePtr,MessageInfo & aMessageInfo,uint8_t aIpProto,Message::Ownership aMessageOwnership)868 Error Ip6::HandlePayload(Header            &aIp6Header,
869                          OwnedPtr<Message> &aMessagePtr,
870                          MessageInfo       &aMessageInfo,
871                          uint8_t            aIpProto,
872                          Message::Ownership aMessageOwnership)
873 {
874 #if !OPENTHREAD_CONFIG_TCP_ENABLE
875     OT_UNUSED_VARIABLE(aIp6Header);
876 #endif
877 
878     Error             error = kErrorNone;
879     OwnedPtr<Message> messagePtr;
880 
881     switch (aIpProto)
882     {
883     case kProtoUdp:
884     case kProtoIcmp6:
885         break;
886 #if OPENTHREAD_CONFIG_TCP_ENABLE
887     case kProtoTcp:
888         break;
889 #endif
890     default:
891         ExitNow();
892     }
893 
894     SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership));
895 
896     switch (aIpProto)
897     {
898 #if OPENTHREAD_CONFIG_TCP_ENABLE
899     case kProtoTcp:
900         error = mTcp.HandleMessage(aIp6Header, *messagePtr, aMessageInfo);
901         break;
902 #endif
903     case kProtoUdp:
904         error = mUdp.HandleMessage(*messagePtr, aMessageInfo);
905         break;
906 
907     case kProtoIcmp6:
908         error = mIcmp.HandleMessage(*messagePtr, aMessageInfo);
909         break;
910 
911     default:
912         break;
913     }
914 
915 exit:
916     LogWarnOnError(error, "handle payload");
917     return error;
918 }
919 
PassToHost(OwnedPtr<Message> & aMessagePtr,const MessageInfo & aMessageInfo,uint8_t aIpProto,bool aApplyFilter,bool aReceive,Message::Ownership aMessageOwnership)920 Error Ip6::PassToHost(OwnedPtr<Message> &aMessagePtr,
921                       const MessageInfo &aMessageInfo,
922                       uint8_t            aIpProto,
923                       bool               aApplyFilter,
924                       bool               aReceive,
925                       Message::Ownership aMessageOwnership)
926 {
927     // This method passes the message to host by invoking the
928     // registered IPv6 receive callback. When NAT64 is enabled, it
929     // may also perform translation and invoke IPv4 receive
930     // callback.
931 
932     Error             error = kErrorNone;
933     OwnedPtr<Message> messagePtr;
934 
935     VerifyOrExit(aMessagePtr->IsLoopbackToHostAllowed(), error = kErrorNoRoute);
936 
937     VerifyOrExit(mReceiveIp6DatagramCallback.IsSet(), error = kErrorNoRoute);
938 
939     // Do not pass IPv6 packets that exceed kMinimalMtu.
940     VerifyOrExit(aMessagePtr->GetLength() <= kMinimalMtu, error = kErrorDrop);
941 
942     // If the sender used mesh-local address as source, do not pass to
943     // host unless this message is intended for this device itself.
944     if (Get<Mle::Mle>().IsMeshLocalAddress(aMessageInfo.GetPeerAddr()))
945     {
946         VerifyOrExit(aReceive, error = kErrorDrop);
947     }
948 
949     if (mIsReceiveIp6FilterEnabled && aApplyFilter)
950     {
951         switch (aIpProto)
952         {
953         case kProtoIcmp6:
954             if (mIcmp.ShouldHandleEchoRequest(aMessageInfo))
955             {
956                 Icmp::Header icmp;
957 
958                 IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), icmp));
959                 VerifyOrExit(icmp.GetType() != Icmp::Header::kTypeEchoRequest, error = kErrorDrop);
960             }
961 
962             break;
963 
964         case kProtoUdp:
965         {
966             Udp::Header udp;
967 
968             IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), udp));
969             VerifyOrExit(Get<Udp>().ShouldUsePlatformUdp(udp.GetDestinationPort()) &&
970                              !Get<Udp>().IsPortInUse(udp.GetDestinationPort()),
971                          error = kErrorNoRoute);
972             break;
973         }
974 
975 #if OPENTHREAD_CONFIG_TCP_ENABLE
976         // Do not pass TCP message to avoid dual processing from both
977         // OpenThread and POSIX TCP stacks.
978         case kProtoTcp:
979             error = kErrorNoRoute;
980             ExitNow();
981 #endif
982 
983         default:
984             break;
985         }
986     }
987 
988     SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership));
989 
990     IgnoreError(RemoveMplOption(*messagePtr));
991 
992 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
993     switch (Get<Nat64::Translator>().TranslateFromIp6(*messagePtr))
994     {
995     case Nat64::Translator::kNotTranslated:
996         break;
997 
998     case Nat64::Translator::kDrop:
999         ExitNow(error = kErrorDrop);
1000 
1001     case Nat64::Translator::kForward:
1002         VerifyOrExit(mReceiveIp4DatagramCallback.IsSet(), error = kErrorNoRoute);
1003         // Pass message to callback transferring its ownership.
1004         mReceiveIp4DatagramCallback.Invoke(messagePtr.Release());
1005         ExitNow();
1006     }
1007 #endif
1008 
1009 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1010     {
1011         Header header;
1012 
1013         IgnoreError(header.ParseFrom(*messagePtr));
1014         UpdateBorderRoutingCounters(header, messagePtr->GetLength(), /* aIsInbound */ false);
1015     }
1016 #endif
1017 
1018 #if OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC
1019     // Some platforms (e.g. Android) currently doesn't restrict link-local/mesh-local source
1020     // addresses when forwarding multicast packets.
1021     // For a multicast packet sent from link-local/mesh-local address to scope larger
1022     // than realm-local, set the hop limit to 1 before sending to host, so this packet
1023     // will not be forwarded by host.
1024     if (aMessageInfo.GetSockAddr().IsMulticastLargerThanRealmLocal() &&
1025         (aMessageInfo.GetPeerAddr().IsLinkLocal() || (Get<Mle::Mle>().IsMeshLocalAddress(aMessageInfo.GetPeerAddr()))))
1026     {
1027         messagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, 1);
1028     }
1029 #endif
1030 
1031     // Pass message to callback transferring its ownership.
1032     mReceiveIp6DatagramCallback.Invoke(messagePtr.Release());
1033 
1034 exit:
1035     return error;
1036 }
1037 
SendRaw(OwnedPtr<Message> aMessagePtr)1038 Error Ip6::SendRaw(OwnedPtr<Message> aMessagePtr)
1039 {
1040     Error  error = kErrorNone;
1041     Header header;
1042 
1043     SuccessOrExit(error = header.ParseFrom(*aMessagePtr));
1044     VerifyOrExit(!header.GetSource().IsMulticast(), error = kErrorInvalidSourceAddress);
1045 
1046 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
1047     // The filtering rules don't apply to packets from DUA.
1048     if (!Get<BackboneRouter::Leader>().IsDomainUnicast(header.GetSource()))
1049 #endif
1050     {
1051         // When the packet is forwarded from host to Thread, if its source is on-mesh or its destination is
1052         // mesh-local, we'll drop the packet unless the packet originates from this device.
1053         if (Get<NetworkData::Leader>().IsOnMesh(header.GetSource()) ||
1054             Get<Mle::Mle>().IsMeshLocalAddress(header.GetDestination()))
1055         {
1056             VerifyOrExit(Get<ThreadNetif>().HasUnicastAddress(header.GetSource()), error = kErrorDrop);
1057         }
1058     }
1059 
1060     if (header.GetDestination().IsMulticast())
1061     {
1062         SuccessOrExit(error = InsertMplOption(*aMessagePtr, header));
1063     }
1064 
1065 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1066     UpdateBorderRoutingCounters(header, aMessagePtr->GetLength(), /* aIsInbound */ true);
1067 #endif
1068 
1069     error = HandleDatagram(aMessagePtr.PassOwnership());
1070 
1071 exit:
1072     return error;
1073 }
1074 
HandleDatagram(OwnedPtr<Message> aMessagePtr,bool aIsReassembled)1075 Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
1076 {
1077     Error       error;
1078     MessageInfo messageInfo;
1079     Header      header;
1080     bool        receive;
1081     bool        forwardThread;
1082     bool        forwardHost;
1083     uint8_t     nextHeader;
1084 
1085     receive       = false;
1086     forwardThread = false;
1087     forwardHost   = false;
1088 
1089     SuccessOrExit(error = header.ParseFrom(*aMessagePtr));
1090 
1091     messageInfo.Clear();
1092     messageInfo.SetPeerAddr(header.GetSource());
1093     messageInfo.SetSockAddr(header.GetDestination());
1094     messageInfo.SetHopLimit(header.GetHopLimit());
1095     messageInfo.SetEcn(header.GetEcn());
1096 
1097     // Determine `forwardThread`, `forwardHost` and `receive`
1098     // based on the destination address.
1099 
1100     if (header.GetDestination().IsMulticast())
1101     {
1102         // Destination is multicast
1103 
1104         forwardThread = !aMessagePtr->IsOriginThreadNetif();
1105 
1106 #if OPENTHREAD_FTD
1107         if (aMessagePtr->IsOriginThreadNetif() && header.GetDestination().IsMulticastLargerThanRealmLocal() &&
1108             Get<ChildTable>().HasSleepyChildWithAddress(header.GetDestination()))
1109         {
1110             forwardThread = true;
1111         }
1112 #endif
1113 
1114         // Always forward multicast packets to host network stack
1115         forwardHost = true;
1116 
1117         if ((aMessagePtr->IsOriginThreadNetif() || aMessagePtr->GetMulticastLoop()) &&
1118             Get<ThreadNetif>().IsMulticastSubscribed(header.GetDestination()))
1119         {
1120             receive = true;
1121         }
1122         else if (Get<ThreadNetif>().IsMulticastPromiscuousEnabled())
1123         {
1124             forwardHost = true;
1125         }
1126     }
1127     else
1128     {
1129         // Destination is unicast
1130 
1131         if (Get<ThreadNetif>().HasUnicastAddress(header.GetDestination()))
1132         {
1133             receive = true;
1134         }
1135         else if (!aMessagePtr->IsOriginThreadNetif() || !header.GetDestination().IsLinkLocal())
1136         {
1137             if (header.GetDestination().IsLinkLocal())
1138             {
1139                 forwardThread = true;
1140             }
1141             else if (IsOnLink(header.GetDestination()))
1142             {
1143 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
1144                 forwardThread = (!aMessagePtr->IsLoopbackToHostAllowed() ||
1145                                  !Get<BackboneRouter::Manager>().ShouldForwardDuaToBackbone(header.GetDestination()));
1146 #else
1147                 forwardThread = true;
1148 #endif
1149             }
1150             else if (RouteLookup(header.GetSource(), header.GetDestination()) == kErrorNone)
1151             {
1152                 forwardThread = true;
1153             }
1154 
1155             forwardHost = !forwardThread;
1156         }
1157     }
1158 
1159     aMessagePtr->SetOffset(sizeof(header));
1160 
1161     // Process IPv6 Extension Headers
1162     nextHeader = static_cast<uint8_t>(header.GetNextHeader());
1163     SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, messageInfo, header, nextHeader, receive));
1164 
1165     if (receive && (nextHeader == kProtoIp6))
1166     {
1167         // Process the embedded IPv6 message in an IPv6 tunnel message.
1168         // If we need to `forwardThread` we create a copy by cloning
1169         // the message, otherwise we take ownership of `aMessage`
1170         // itself and use it directly. The encapsulating header is
1171         // then removed before processing the embedded message.
1172 
1173         OwnedPtr<Message> messagePtr;
1174         bool              multicastLoop = aMessagePtr->GetMulticastLoop();
1175 
1176         SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr,
1177                                                    forwardThread ? Message::kCopyToUse : Message::kTakeCustody));
1178         messagePtr->SetMulticastLoop(multicastLoop);
1179         messagePtr->RemoveHeader(messagePtr->GetOffset());
1180 
1181         Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr);
1182 
1183         IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled));
1184 
1185         receive     = false;
1186         forwardHost = false;
1187     }
1188 
1189     if ((forwardHost || receive) && !aIsReassembled)
1190     {
1191         error = PassToHost(aMessagePtr, messageInfo, nextHeader,
1192                            /* aApplyFilter */ !forwardHost, receive,
1193                            (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody);
1194     }
1195 
1196     if (receive)
1197     {
1198         error = HandlePayload(header, aMessagePtr, messageInfo, nextHeader,
1199                               forwardThread ? Message::kCopyToUse : Message::kTakeCustody);
1200     }
1201 
1202     if (forwardThread)
1203     {
1204         if (aMessagePtr->IsOriginThreadNetif())
1205         {
1206             VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader());
1207             header.SetHopLimit(header.GetHopLimit() - 1);
1208         }
1209 
1210         VerifyOrExit(header.GetHopLimit() > 0, error = kErrorDrop);
1211 
1212         aMessagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, header.GetHopLimit());
1213 
1214         if (nextHeader == kProtoIcmp6)
1215         {
1216             uint8_t icmpType;
1217 
1218             SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), icmpType));
1219 
1220             error = kErrorDrop;
1221 
1222             for (IcmpType type : kForwardIcmpTypes)
1223             {
1224                 if (icmpType == type)
1225                 {
1226                     error = kErrorNone;
1227                     break;
1228                 }
1229             }
1230 
1231             SuccessOrExit(error);
1232         }
1233 
1234         if (aMessagePtr->IsOriginHostUntrusted() && (nextHeader == kProtoUdp))
1235         {
1236             uint16_t destPort;
1237 
1238             SuccessOrExit(
1239                 error = aMessagePtr->Read(aMessagePtr->GetOffset() + Udp::Header::kDestPortFieldOffset, destPort));
1240             destPort = BigEndian::HostSwap16(destPort);
1241 
1242             if (destPort == Tmf::kUdpPort)
1243             {
1244                 LogNote("Dropping TMF message from untrusted origin");
1245                 ExitNow(error = kErrorDrop);
1246             }
1247         }
1248 
1249 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1250         if (aMessagePtr->IsOriginHostTrusted() && !aMessagePtr->IsLoopbackToHostAllowed() && (nextHeader == kProtoUdp))
1251         {
1252             uint16_t destPort;
1253 
1254             SuccessOrExit(
1255                 error = aMessagePtr->Read(aMessagePtr->GetOffset() + Udp::Header::kDestPortFieldOffset, destPort));
1256             destPort = BigEndian::HostSwap16(destPort);
1257 
1258             if (nextHeader == kProtoUdp)
1259             {
1260                 VerifyOrExit(Get<Udp>().ShouldUsePlatformUdp(destPort), error = kErrorDrop);
1261             }
1262         }
1263 #endif
1264 
1265 #if OPENTHREAD_CONFIG_MULTI_RADIO
1266         // Since the message will be forwarded, we clear the radio
1267         // type on the message to allow the radio type for tx to be
1268         // selected later (based on the radios supported by the next
1269         // hop).
1270         aMessagePtr->ClearRadioType();
1271 #endif
1272 
1273         Get<MeshForwarder>().SendMessage(aMessagePtr.PassOwnership());
1274     }
1275 
1276 exit:
1277     return error;
1278 }
1279 
SelectSourceAddress(MessageInfo & aMessageInfo) const1280 Error Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) const
1281 {
1282     Error          error = kErrorNone;
1283     const Address *source;
1284 
1285     source = SelectSourceAddress(aMessageInfo.GetPeerAddr());
1286     VerifyOrExit(source != nullptr, error = kErrorNotFound);
1287     aMessageInfo.SetSockAddr(*source);
1288 
1289 exit:
1290     return error;
1291 }
1292 
SelectSourceAddress(const Address & aDestination) const1293 const Address *Ip6::SelectSourceAddress(const Address &aDestination) const
1294 {
1295     uint8_t                      destScope    = aDestination.GetScope();
1296     bool                         destIsRloc   = Get<Mle::Mle>().IsRoutingLocator(aDestination);
1297     const Netif::UnicastAddress *bestAddr     = nullptr;
1298     uint8_t                      bestMatchLen = 0;
1299 
1300     for (const Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
1301     {
1302         bool    newAddrIsPreferred = false;
1303         uint8_t matchLen;
1304         uint8_t overrideScope;
1305 
1306         if (Get<Mle::Mle>().IsAnycastLocator(addr.GetAddress()))
1307         {
1308             // Don't use anycast address as source address.
1309             continue;
1310         }
1311 
1312         matchLen = aDestination.PrefixMatch(addr.GetAddress());
1313 
1314         if (matchLen >= addr.mPrefixLength)
1315         {
1316             matchLen      = addr.mPrefixLength;
1317             overrideScope = addr.GetScope();
1318         }
1319         else
1320         {
1321             overrideScope = destScope;
1322         }
1323 
1324         if (addr.GetAddress() == aDestination)
1325         {
1326             // Rule 1: Prefer same address
1327             bestAddr = &addr;
1328             ExitNow();
1329         }
1330 
1331         if (bestAddr == nullptr)
1332         {
1333             newAddrIsPreferred = true;
1334         }
1335         else if (addr.GetScope() < bestAddr->GetScope())
1336         {
1337             // Rule 2: Prefer appropriate scope
1338             newAddrIsPreferred = (addr.GetScope() >= overrideScope);
1339         }
1340         else if (addr.GetScope() > bestAddr->GetScope())
1341         {
1342             newAddrIsPreferred = (bestAddr->GetScope() < overrideScope);
1343         }
1344         else if (addr.mPreferred != bestAddr->mPreferred)
1345         {
1346             // Rule 3: Avoid deprecated addresses
1347             newAddrIsPreferred = addr.mPreferred;
1348         }
1349         else if (matchLen > bestMatchLen)
1350         {
1351             // Rule 6: Prefer matching label
1352             // Rule 7: Prefer public address
1353             // Rule 8: Use longest prefix matching
1354 
1355             newAddrIsPreferred = true;
1356         }
1357         else if ((matchLen == bestMatchLen) && (destIsRloc == Get<Mle::Mle>().IsRoutingLocator(addr.GetAddress())))
1358         {
1359             // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else
1360             newAddrIsPreferred = true;
1361         }
1362 
1363         if (newAddrIsPreferred)
1364         {
1365             bestAddr     = &addr;
1366             bestMatchLen = matchLen;
1367 
1368             // Infer destination scope based on prefix match
1369             if (bestMatchLen >= bestAddr->mPrefixLength)
1370             {
1371                 destScope = bestAddr->GetScope();
1372             }
1373         }
1374     }
1375 
1376 exit:
1377     return (bestAddr != nullptr) ? &bestAddr->GetAddress() : nullptr;
1378 }
1379 
IsOnLink(const Address & aAddress) const1380 bool Ip6::IsOnLink(const Address &aAddress) const
1381 {
1382     bool isOnLink = false;
1383 
1384     if (Get<NetworkData::Leader>().IsOnMesh(aAddress))
1385     {
1386         ExitNow(isOnLink = true);
1387     }
1388 
1389     for (const Netif::UnicastAddress &unicastAddr : Get<ThreadNetif>().GetUnicastAddresses())
1390     {
1391         if (unicastAddr.GetAddress().PrefixMatch(aAddress) >= unicastAddr.mPrefixLength)
1392         {
1393             ExitNow(isOnLink = true);
1394         }
1395     }
1396 
1397 exit:
1398     return isOnLink;
1399 }
1400 
RouteLookup(const Address & aSource,const Address & aDestination) const1401 Error Ip6::RouteLookup(const Address &aSource, const Address &aDestination) const
1402 {
1403     Error    error;
1404     uint16_t rloc;
1405 
1406     error = Get<NetworkData::Leader>().RouteLookup(aSource, aDestination, rloc);
1407 
1408     if (error == kErrorNone)
1409     {
1410         if (rloc == Get<Mle::MleRouter>().GetRloc16())
1411         {
1412             error = kErrorNoRoute;
1413         }
1414     }
1415     else
1416     {
1417         LogNote("Failed to find valid route for: %s", aDestination.ToString().AsCString());
1418     }
1419 
1420     return error;
1421 }
1422 
1423 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
UpdateBorderRoutingCounters(const Header & aHeader,uint16_t aMessageLength,bool aIsInbound)1424 void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound)
1425 {
1426     static constexpr uint8_t kPrefixLength   = 48;
1427     otPacketsAndBytes       *counter         = nullptr;
1428     otPacketsAndBytes       *internetCounter = nullptr;
1429 
1430     VerifyOrExit(!aHeader.GetSource().IsLinkLocal());
1431     VerifyOrExit(!aHeader.GetDestination().IsLinkLocal());
1432     VerifyOrExit(aHeader.GetSource().GetPrefix() != Get<Mle::Mle>().GetMeshLocalPrefix());
1433     VerifyOrExit(aHeader.GetDestination().GetPrefix() != Get<Mle::Mle>().GetMeshLocalPrefix());
1434 
1435     if (aIsInbound)
1436     {
1437         VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetSource()));
1438         if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
1439         {
1440             internetCounter = &mBorderRoutingCounters.mInboundInternet;
1441         }
1442         if (aHeader.GetDestination().IsMulticast())
1443         {
1444             VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
1445             counter = &mBorderRoutingCounters.mInboundMulticast;
1446         }
1447         else
1448         {
1449             counter = &mBorderRoutingCounters.mInboundUnicast;
1450         }
1451     }
1452     else
1453     {
1454         VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetDestination()));
1455         if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
1456         {
1457             internetCounter = &mBorderRoutingCounters.mOutboundInternet;
1458         }
1459         if (aHeader.GetDestination().IsMulticast())
1460         {
1461             VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
1462             counter = &mBorderRoutingCounters.mOutboundMulticast;
1463         }
1464         else
1465         {
1466             counter = &mBorderRoutingCounters.mOutboundUnicast;
1467         }
1468     }
1469 
1470 exit:
1471 
1472     if (counter)
1473     {
1474         counter->mPackets += 1;
1475         counter->mBytes += aMessageLength;
1476     }
1477     if (internetCounter)
1478     {
1479         internetCounter->mPackets += 1;
1480         internetCounter->mBytes += aMessageLength;
1481     }
1482 }
1483 #endif
1484 
1485 // LCOV_EXCL_START
1486 
IpProtoToString(uint8_t aIpProto)1487 const char *Ip6::IpProtoToString(uint8_t aIpProto)
1488 {
1489     static constexpr Stringify::Entry kIpProtoTable[] = {
1490         {kProtoHopOpts, "HopOpts"}, {kProtoTcp, "TCP"},         {kProtoUdp, "UDP"},
1491         {kProtoIp6, "IP6"},         {kProtoRouting, "Routing"}, {kProtoFragment, "Frag"},
1492         {kProtoIcmp6, "ICMP6"},     {kProtoNone, "None"},       {kProtoDstOpts, "DstOpts"},
1493     };
1494 
1495     static_assert(Stringify::IsSorted(kIpProtoTable), "kIpProtoTable is not sorted");
1496 
1497     return Stringify::Lookup(aIpProto, kIpProtoTable, "Unknown");
1498 }
1499 
EcnToString(Ecn aEcn)1500 const char *Ip6::EcnToString(Ecn aEcn)
1501 {
1502     static const char *const kEcnStrings[] = {
1503         "no", // (0) kEcnNotCapable
1504         "e1", // (1) kEcnCapable1  (ECT1)
1505         "e0", // (2) kEcnCapable0  (ECT0)
1506         "ce", // (3) kEcnMarked    (Congestion Encountered)
1507     };
1508 
1509     static_assert(0 == kEcnNotCapable, "kEcnNotCapable value is incorrect");
1510     static_assert(1 == kEcnCapable1, "kEcnCapable1 value is incorrect");
1511     static_assert(2 == kEcnCapable0, "kEcnCapable0 value is incorrect");
1512     static_assert(3 == kEcnMarked, "kEcnMarked value is incorrect");
1513 
1514     return kEcnStrings[aEcn];
1515 }
1516 
1517 // LCOV_EXCL_STOP
1518 
1519 //---------------------------------------------------------------------------------------------------------------------
1520 // Headers
1521 
ParseFrom(const Message & aMessage)1522 Error Headers::ParseFrom(const Message &aMessage)
1523 {
1524     Error error = kErrorParse;
1525 
1526     Clear();
1527 
1528     SuccessOrExit(mIp6Header.ParseFrom(aMessage));
1529 
1530     switch (mIp6Header.GetNextHeader())
1531     {
1532     case kProtoUdp:
1533         SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mUdp));
1534         break;
1535     case kProtoTcp:
1536         SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mTcp));
1537         break;
1538     case kProtoIcmp6:
1539         SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mIcmp));
1540         break;
1541     default:
1542         break;
1543     }
1544 
1545     error = kErrorNone;
1546 
1547 exit:
1548     return error;
1549 }
1550 
DecompressFrom(const Message & aMessage,uint16_t aOffset,const Mac::Addresses & aMacAddrs)1551 Error Headers::DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs)
1552 {
1553     static constexpr uint16_t kReadLength = sizeof(Lowpan::FragmentHeader::NextFrag) + sizeof(Headers);
1554 
1555     uint8_t   frameBuffer[kReadLength];
1556     uint16_t  frameLength;
1557     FrameData frameData;
1558 
1559     frameLength = aMessage.ReadBytes(aOffset, frameBuffer, sizeof(frameBuffer));
1560     frameData.Init(frameBuffer, frameLength);
1561 
1562     return DecompressFrom(frameData, aMacAddrs, aMessage.GetInstance());
1563 }
1564 
DecompressFrom(const FrameData & aFrameData,const Mac::Addresses & aMacAddrs,Instance & aInstance)1565 Error Headers::DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance)
1566 {
1567     Error                  error     = kErrorNone;
1568     FrameData              frameData = aFrameData;
1569     Lowpan::FragmentHeader fragmentHeader;
1570     bool                   nextHeaderCompressed;
1571 
1572     if (fragmentHeader.ParseFrom(frameData) == kErrorNone)
1573     {
1574         // Only the first fragment header is followed by a LOWPAN_IPHC header
1575         VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0, error = kErrorNotFound);
1576     }
1577 
1578     VerifyOrExit(Lowpan::Lowpan::IsLowpanHc(frameData), error = kErrorNotFound);
1579 
1580     SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressBaseHeader(mIp6Header, nextHeaderCompressed,
1581                                                                                aMacAddrs, frameData));
1582 
1583     switch (mIp6Header.GetNextHeader())
1584     {
1585     case kProtoUdp:
1586         if (nextHeaderCompressed)
1587         {
1588             SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressUdpHeader(mHeader.mUdp, frameData));
1589         }
1590         else
1591         {
1592             SuccessOrExit(error = frameData.Read(mHeader.mUdp));
1593         }
1594         break;
1595 
1596     case kProtoTcp:
1597         SuccessOrExit(error = frameData.Read(mHeader.mTcp));
1598         break;
1599 
1600     case kProtoIcmp6:
1601         SuccessOrExit(error = frameData.Read(mHeader.mIcmp));
1602         break;
1603 
1604     default:
1605         break;
1606     }
1607 
1608 exit:
1609     return error;
1610 }
1611 
GetSourcePort(void) const1612 uint16_t Headers::GetSourcePort(void) const
1613 {
1614     uint16_t port = 0;
1615 
1616     switch (GetIpProto())
1617     {
1618     case kProtoUdp:
1619         port = mHeader.mUdp.GetSourcePort();
1620         break;
1621 
1622     case kProtoTcp:
1623         port = mHeader.mTcp.GetSourcePort();
1624         break;
1625 
1626     default:
1627         break;
1628     }
1629 
1630     return port;
1631 }
1632 
GetDestinationPort(void) const1633 uint16_t Headers::GetDestinationPort(void) const
1634 {
1635     uint16_t port = 0;
1636 
1637     switch (GetIpProto())
1638     {
1639     case kProtoUdp:
1640         port = mHeader.mUdp.GetDestinationPort();
1641         break;
1642 
1643     case kProtoTcp:
1644         port = mHeader.mTcp.GetDestinationPort();
1645         break;
1646 
1647     default:
1648         break;
1649     }
1650 
1651     return port;
1652 }
1653 
GetChecksum(void) const1654 uint16_t Headers::GetChecksum(void) const
1655 {
1656     uint16_t checksum = 0;
1657 
1658     switch (GetIpProto())
1659     {
1660     case kProtoUdp:
1661         checksum = mHeader.mUdp.GetChecksum();
1662         break;
1663 
1664     case kProtoTcp:
1665         checksum = mHeader.mTcp.GetChecksum();
1666         break;
1667 
1668     case kProtoIcmp6:
1669         checksum = mHeader.mIcmp.GetChecksum();
1670         break;
1671 
1672     default:
1673         break;
1674     }
1675 
1676     return checksum;
1677 }
1678 
1679 } // namespace Ip6
1680 } // namespace ot
1681