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