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