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