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