Coverage Report

Created: 2025-05-12 06:47

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