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
21.3k
    : InstanceLocator(aInstance)
52
21.3k
    , mReceiveFilterEnabled(false)
53
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
54
    , mTmfOriginFilterEnabled(true)
55
#endif
56
21.3k
    , mSendQueueTask(aInstance)
57
21.3k
    , mIcmp(aInstance)
58
21.3k
    , mUdp(aInstance)
59
21.3k
    , mMpl(aInstance)
60
#if OPENTHREAD_CONFIG_TCP_ENABLE
61
21.3k
    , mTcp(aInstance)
62
#endif
63
21.3k
{
64
21.3k
#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
65
21.3k
    ResetBorderRoutingCounters();
66
21.3k
#endif
67
21.3k
}
68
69
3.06k
Message *Ip6::NewMessage(void) { return NewMessage(0); }
70
71
4.74k
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
276k
{
75
276k
    return Get<MessagePool>().Allocate(
76
276k
        Message::kTypeIp6, sizeof(Header) + sizeof(HopByHopHeader) + sizeof(MplOption) + aReserved, aSettings);
77
276k
}
78
79
Message *Ip6::NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings)
80
89.2k
{
81
89.2k
    Message          *message  = nullptr;
82
89.2k
    Message::Settings settings = aSettings;
83
89.2k
    const Header     *header;
84
85
89.2k
    VerifyOrExit((aData != nullptr) && (aDataLength >= sizeof(Header)));
86
87
    // Determine priority from IPv6 header
88
88.6k
    header = reinterpret_cast<const Header *>(aData);
89
88.6k
    VerifyOrExit(header->IsValid());
90
88.0k
    VerifyOrExit(sizeof(Header) + header->GetPayloadLength() == aDataLength);
91
87.5k
    settings.mPriority = DscpToPriority(header->GetDscp());
92
93
87.5k
    message = Get<MessagePool>().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, settings);
94
95
87.5k
    VerifyOrExit(message != nullptr);
96
97
86.8k
    if (message->AppendBytes(aData, aDataLength) != kErrorNone)
98
66
    {
99
66
        message->Free();
100
66
        message = nullptr;
101
66
    }
102
103
89.2k
exit:
104
89.2k
    return message;
105
86.8k
}
106
107
Message::Priority Ip6::DscpToPriority(uint8_t aDscp)
108
87.5k
{
109
87.5k
    Message::Priority priority;
110
87.5k
    uint8_t           cs = aDscp & kDscpCsMask;
111
112
87.5k
    switch (cs)
113
87.5k
    {
114
4.52k
    case kDscpCs1:
115
12.3k
    case kDscpCs2:
116
12.3k
        priority = Message::kPriorityLow;
117
12.3k
        break;
118
119
3.89k
    case kDscpCs0:
120
4.06k
    case kDscpCs3:
121
4.06k
        priority = Message::kPriorityNormal;
122
4.06k
        break;
123
124
2.16k
    case kDscpCs4:
125
2.61k
    case kDscpCs5:
126
70.7k
    case kDscpCs6:
127
71.1k
    case kDscpCs7:
128
71.1k
        priority = Message::kPriorityHigh;
129
71.1k
        break;
130
131
0
    default:
132
0
        priority = Message::kPriorityNormal;
133
0
        break;
134
87.5k
    }
135
136
87.5k
    return priority;
137
87.5k
}
138
139
uint8_t Ip6::PriorityToDscp(Message::Priority aPriority)
140
205k
{
141
205k
    uint8_t dscp = kDscpCs0;
142
143
205k
    switch (aPriority)
144
205k
    {
145
147
    case Message::kPriorityLow:
146
147
        dscp = kDscpCs1;
147
147
        break;
148
149
3.14k
    case Message::kPriorityNormal:
150
205k
    case Message::kPriorityNet:
151
205k
        dscp = kDscpCs0;
152
205k
        break;
153
154
14
    case Message::kPriorityHigh:
155
14
        dscp = kDscpCs4;
156
14
        break;
157
205k
    }
158
159
205k
    return dscp;
160
205k
}
161
162
Error Ip6::AddMplOption(Message &aMessage, Header &aHeader)
163
21.3k
{
164
21.3k
    Error          error = kErrorNone;
165
21.3k
    HopByHopHeader hbhHeader;
166
21.3k
    MplOption      mplOption;
167
21.3k
    PadOption      padOption;
168
169
21.3k
    hbhHeader.SetNextHeader(aHeader.GetNextHeader());
170
21.3k
    hbhHeader.SetLength(0);
171
21.3k
    mMpl.InitOption(mplOption, aHeader.GetSource());
172
173
    // Check if MPL option may require padding
174
21.3k
    if (padOption.InitToPadHeaderWithSize(sizeof(HopByHopHeader) + mplOption.GetSize()) == kErrorNone)
175
580
    {
176
580
        SuccessOrExit(error = aMessage.PrependBytes(&padOption, padOption.GetSize()));
177
580
    }
178
179
21.1k
    SuccessOrExit(error = aMessage.PrependBytes(&mplOption, mplOption.GetSize()));
180
20.6k
    SuccessOrExit(error = aMessage.Prepend(hbhHeader));
181
20.6k
    aHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(hbhHeader) + sizeof(mplOption));
182
20.6k
    aHeader.SetNextHeader(kProtoHopOpts);
183
184
21.3k
exit:
185
21.3k
    return error;
186
20.6k
}
187
188
Error Ip6::PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader)
189
18.2k
{
190
18.2k
    Error          error = kErrorNone;
191
18.2k
    Header         tunnelHeader;
192
18.2k
    const Address *source;
193
194
18.2k
#if OPENTHREAD_FTD
195
18.2k
    if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() &&
196
18.2k
        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
18.2k
#endif
210
211
    // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address.
212
18.2k
    tunnelHeader.InitVersionTrafficClassFlow();
213
18.2k
    tunnelHeader.SetHopLimit(kDefaultHopLimit);
214
18.2k
    tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader));
215
18.2k
    tunnelHeader.GetDestination().SetToRealmLocalAllMplForwarders();
216
18.2k
    tunnelHeader.SetNextHeader(kProtoIp6);
217
218
18.2k
    source = SelectSourceAddress(tunnelHeader.GetDestination());
219
18.2k
    VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress);
220
221
18.0k
    tunnelHeader.SetSource(*source);
222
223
18.0k
    SuccessOrExit(error = AddMplOption(aMessage, tunnelHeader));
224
17.2k
    SuccessOrExit(error = aMessage.Prepend(tunnelHeader));
225
226
18.2k
exit:
227
18.2k
    return error;
228
17.2k
}
229
230
Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader)
231
14.7k
{
232
14.7k
    Error error = kErrorNone;
233
234
14.7k
    if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal())
235
4.83k
    {
236
4.83k
        error = PrepareMulticastToLargerThanRealmLocal(aMessage, aHeader);
237
4.83k
        ExitNow();
238
4.83k
    }
239
240
9.91k
    VerifyOrExit(aHeader.GetDestination().IsRealmLocalMulticast());
241
242
6.48k
    aMessage.RemoveHeader(sizeof(aHeader));
243
244
6.48k
    if (aHeader.GetNextHeader() == kProtoHopOpts)
245
3.26k
    {
246
3.26k
        HopByHopHeader hbh;
247
3.26k
        uint16_t       hbhSize;
248
3.26k
        MplOption      mplOption;
249
3.26k
        PadOption      padOption;
250
251
        // Read existing hop-by-hop option header
252
3.26k
        SuccessOrExit(error = aMessage.Read(0, hbh));
253
3.06k
        hbhSize = hbh.GetSize();
254
255
3.06k
        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
2.85k
        hbh.SetLength(hbh.GetLength() + 1);
260
2.85k
        aMessage.Write(0, hbh);
261
262
        // Make space for MPL Option + padding (8 bytes) at the end
263
        // of hop-by-hop header
264
2.85k
        SuccessOrExit(error = aMessage.InsertHeader(hbhSize, ExtensionHeader::kLengthUnitSize));
265
266
        // Insert MPL Option
267
2.85k
        mMpl.InitOption(mplOption, aHeader.GetSource());
268
2.85k
        aMessage.WriteBytes(hbhSize, &mplOption, mplOption.GetSize());
269
270
        // Insert Pad Option (if needed)
271
2.85k
        if (padOption.InitToPadHeaderWithSize(mplOption.GetSize()) == kErrorNone)
272
2.85k
        {
273
2.85k
            aMessage.WriteBytes(hbhSize + mplOption.GetSize(), &padOption, padOption.GetSize());
274
2.85k
        }
275
276
        // Update IPv6 Payload Length
277
2.85k
        aHeader.SetPayloadLength(aHeader.GetPayloadLength() + ExtensionHeader::kLengthUnitSize);
278
2.85k
    }
279
3.21k
    else
280
3.21k
    {
281
3.21k
        SuccessOrExit(error = AddMplOption(aMessage, aHeader));
282
3.21k
    }
283
284
6.07k
    SuccessOrExit(error = aMessage.Prepend(aHeader));
285
286
14.7k
exit:
287
14.7k
    return error;
288
6.07k
}
289
290
Error Ip6::RemoveMplOption(Message &aMessage)
291
200k
{
292
200k
    enum Action : uint8_t
293
200k
    {
294
200k
        kNoMplOption,
295
200k
        kShrinkHbh,
296
200k
        kRemoveHbh,
297
200k
        kReplaceMplWithPad,
298
200k
    };
299
300
200k
    Error          error  = kErrorNone;
301
200k
    Action         action = kNoMplOption;
302
200k
    Header         ip6Header;
303
200k
    HopByHopHeader hbh;
304
200k
    Option         option;
305
200k
    OffsetRange    offsetRange;
306
200k
    OffsetRange    mplOffsetRange;
307
200k
    PadOption      padOption;
308
309
200k
    offsetRange.InitFromMessageFullLength(aMessage);
310
311
200k
    IgnoreError(aMessage.Read(offsetRange, ip6Header));
312
200k
    offsetRange.AdvanceOffset(sizeof(ip6Header));
313
314
200k
    VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts);
315
316
3.64k
    SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbh));
317
318
18.6k
    for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
319
15.2k
    {
320
15.2k
        SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
321
322
15.2k
        if (option.IsPadding())
323
6.94k
        {
324
6.94k
            continue;
325
6.94k
        }
326
327
8.33k
        if (option.GetType() == MplOption::kType)
328
5.35k
        {
329
            // If multiple MPL options exist, discard packet
330
5.35k
            VerifyOrExit(action == kNoMplOption, error = kErrorParse);
331
332
            // `Option::ParseFrom()` already validated that the entire
333
            // option is present in the `offsetRange`.
334
335
5.21k
            mplOffsetRange = offsetRange;
336
5.21k
            mplOffsetRange.ShrinkLength(option.GetSize());
337
338
5.21k
            VerifyOrExit(option.GetSize() <= sizeof(MplOption), error = kErrorParse);
339
340
5.09k
            if (mplOffsetRange.GetOffset() == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0)
341
1.04k
            {
342
                // First and only IPv6 Option, remove IPv6 HBH Option header
343
1.04k
                action = kRemoveHbh;
344
1.04k
            }
345
4.05k
            else if (mplOffsetRange.GetOffset() + ExtensionHeader::kLengthUnitSize == offsetRange.GetEndOffset())
346
1.56k
            {
347
                // Last IPv6 Option, shrink the last 8 bytes
348
1.56k
                action = kShrinkHbh;
349
1.56k
            }
350
5.09k
        }
351
2.97k
        else if (action != kNoMplOption)
352
198
        {
353
            // Encountered another option, now just replace
354
            // MPL Option with Pad Option
355
198
            action = kReplaceMplWithPad;
356
198
        }
357
8.33k
    }
358
359
3.38k
    switch (action)
360
3.38k
    {
361
922
    case kNoMplOption:
362
922
        break;
363
364
1.35k
    case kShrinkHbh:
365
2.32k
    case kRemoveHbh:
366
        // Last IPv6 Option, shrink HBH Option header by
367
        // 8 bytes (`kLengthUnitSize`)
368
2.32k
        aMessage.RemoveHeader(offsetRange.GetEndOffset() - ExtensionHeader::kLengthUnitSize,
369
2.32k
                              ExtensionHeader::kLengthUnitSize);
370
371
2.32k
        if (action == kRemoveHbh)
372
971
        {
373
971
            ip6Header.SetNextHeader(hbh.GetNextHeader());
374
971
        }
375
1.35k
        else
376
1.35k
        {
377
            // Update HBH header length, decrement by one
378
            // which decreases its total size by 8 bytes.
379
380
1.35k
            hbh.SetLength(hbh.GetLength() - 1);
381
1.35k
            aMessage.Write(sizeof(ip6Header), hbh);
382
1.35k
        }
383
384
2.32k
        ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - ExtensionHeader::kLengthUnitSize);
385
2.32k
        aMessage.Write(0, ip6Header);
386
2.32k
        break;
387
388
133
    case kReplaceMplWithPad:
389
133
        padOption.InitForPadSize(static_cast<uint8_t>(mplOffsetRange.GetLength()));
390
133
        aMessage.WriteBytes(mplOffsetRange.GetOffset(), &padOption, padOption.GetSize());
391
133
        break;
392
3.38k
    }
393
394
200k
exit:
395
200k
    return error;
396
3.38k
}
397
398
void Ip6::EnqueueDatagram(Message &aMessage)
399
217k
{
400
217k
    mSendQueue.Enqueue(aMessage);
401
217k
    mSendQueueTask.Post();
402
217k
}
403
404
Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto)
405
227k
{
406
227k
    Error    error = kErrorNone;
407
227k
    Header   header;
408
227k
    uint8_t  dscp;
409
227k
    uint16_t payloadLength = aMessage.GetLength();
410
411
227k
    if ((aIpProto == kProtoUdp) &&
412
227k
        Get<Tmf::Agent>().IsTmfMessage(aMessageInfo.GetSockAddr(), aMessageInfo.GetPeerAddr(),
413
226k
                                       aMessageInfo.GetPeerPort()))
414
22.0k
    {
415
22.0k
        dscp = Tmf::Agent::PriorityToDscp(aMessage.GetPriority());
416
22.0k
    }
417
205k
    else
418
205k
    {
419
205k
        dscp = PriorityToDscp(aMessage.GetPriority());
420
205k
    }
421
422
227k
    header.InitVersionTrafficClassFlow();
423
227k
    header.SetDscp(dscp);
424
227k
    header.SetEcn(aMessageInfo.GetEcn());
425
227k
    header.SetPayloadLength(payloadLength);
426
227k
    header.SetNextHeader(aIpProto);
427
428
227k
    if (aMessageInfo.GetHopLimit() != 0 || aMessageInfo.ShouldAllowZeroHopLimit())
429
201k
    {
430
201k
        header.SetHopLimit(aMessageInfo.GetHopLimit());
431
201k
    }
432
26.2k
    else
433
26.2k
    {
434
26.2k
        header.SetHopLimit(kDefaultHopLimit);
435
26.2k
    }
436
437
227k
    if (aMessageInfo.GetSockAddr().IsUnspecified() || aMessageInfo.GetSockAddr().IsMulticast())
438
12.3k
    {
439
12.3k
        const Address *source = SelectSourceAddress(aMessageInfo.GetPeerAddr());
440
441
12.3k
        VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress);
442
12.3k
        header.SetSource(*source);
443
12.3k
    }
444
215k
    else
445
215k
    {
446
215k
        header.SetSource(aMessageInfo.GetSockAddr());
447
215k
    }
448
449
227k
    header.SetDestination(aMessageInfo.GetPeerAddr());
450
451
227k
    if (aMessageInfo.GetPeerAddr().IsRealmLocalMulticast())
452
135
    {
453
135
        SuccessOrExit(error = AddMplOption(aMessage, header));
454
135
    }
455
456
227k
    SuccessOrExit(error = aMessage.Prepend(header));
457
458
227k
    Checksum::UpdateMessageChecksum(aMessage, header.GetSource(), header.GetDestination(), aIpProto);
459
460
227k
    if (aMessageInfo.GetPeerAddr().IsMulticastLargerThanRealmLocal())
461
13.4k
    {
462
13.4k
        SuccessOrExit(error = PrepareMulticastToLargerThanRealmLocal(aMessage, header));
463
13.4k
    }
464
465
217k
    aMessage.SetMulticastLoop(aMessageInfo.GetMulticastLoop());
466
467
217k
    if (aMessage.GetLength() > kMaxDatagramLength)
468
0
    {
469
0
        error = FragmentDatagram(aMessage, aIpProto);
470
0
    }
471
217k
    else
472
217k
    {
473
217k
        EnqueueDatagram(aMessage);
474
217k
    }
475
476
227k
exit:
477
478
227k
    return error;
479
217k
}
480
481
void Ip6::HandleSendQueue(void)
482
191k
{
483
191k
    Message *message;
484
485
382k
    while ((message = mSendQueue.GetHead()) != nullptr)
486
191k
    {
487
191k
        mSendQueue.Dequeue(*message);
488
191k
        IgnoreError(HandleDatagram(OwnedPtr<Message>(message)));
489
191k
    }
490
191k
}
491
492
Error Ip6::ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const
493
23.8k
{
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
23.8k
    Error error;
499
500
23.8k
    SuccessOrExit(error = aMessage.Read(aOffsetRange, aHbhHeader));
501
23.8k
    VerifyOrExit(aOffsetRange.Contains(aHbhHeader.GetSize()), error = kErrorParse);
502
23.4k
    aOffsetRange.ShrinkLength(aHbhHeader.GetSize());
503
23.4k
    aOffsetRange.AdvanceOffset(sizeof(HopByHopHeader));
504
505
23.8k
exit:
506
23.8k
    return error;
507
23.4k
}
508
509
Error Ip6::HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive)
510
20.2k
{
511
20.2k
    Error          error = kErrorNone;
512
20.2k
    HopByHopHeader hbhHeader;
513
20.2k
    Option         option;
514
20.2k
    OffsetRange    offsetRange;
515
516
20.2k
    offsetRange.InitFromMessageOffsetToEnd(aMessage);
517
518
20.2k
    SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbhHeader));
519
520
97.3k
    for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
521
80.5k
    {
522
80.5k
        SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
523
524
79.2k
        if (option.IsPadding())
525
56.7k
        {
526
56.7k
            continue;
527
56.7k
        }
528
529
22.5k
        if (option.GetType() == MplOption::kType)
530
17.0k
        {
531
17.0k
            SuccessOrExit(error = mMpl.ProcessOption(aMessage, offsetRange, aHeader.GetSource(), aReceive));
532
15.8k
            continue;
533
17.0k
        }
534
535
5.53k
        VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop);
536
5.53k
    }
537
538
16.8k
    aMessage.SetOffset(offsetRange.GetEndOffset());
539
540
20.2k
exit:
541
20.2k
    return error;
542
16.8k
}
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
7.61k
{
625
7.61k
    Error          error = kErrorNone;
626
7.61k
    Header         header, headerBuffer;
627
7.61k
    FragmentHeader fragmentHeader;
628
7.61k
    Message       *message         = nullptr;
629
7.61k
    uint16_t       offset          = 0;
630
7.61k
    uint16_t       payloadFragment = 0;
631
7.61k
    bool           isFragmented    = true;
632
633
7.61k
    SuccessOrExit(error = aMessage.Read(0, header));
634
7.61k
    SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader));
635
636
6.48k
    if (fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet())
637
2.22k
    {
638
2.22k
        isFragmented = false;
639
2.22k
        aMessage.MoveOffset(sizeof(fragmentHeader));
640
2.22k
        ExitNow();
641
2.22k
    }
642
643
4.25k
    for (Message &msg : mReassemblyList)
644
5.75k
    {
645
5.75k
        SuccessOrExit(error = msg.Read(0, headerBuffer));
646
647
5.75k
        if (msg.GetDatagramTag() == fragmentHeader.GetIdentification() &&
648
5.75k
            headerBuffer.GetSource() == header.GetSource() && headerBuffer.GetDestination() == header.GetDestination())
649
927
        {
650
927
            message = &msg;
651
927
            break;
652
927
        }
653
5.75k
    }
654
655
4.25k
    offset          = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset());
656
4.25k
    payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader);
657
658
4.25k
    LogInfo("Fragment with id %lu received > %u bytes, offset %u", ToUlong(fragmentHeader.GetIdentification()),
659
4.25k
            payloadFragment, offset);
660
661
4.25k
    if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength)
662
277
    {
663
277
        LogWarn("Packet too large for fragment buffer");
664
277
        ExitNow(error = kErrorNoBufs);
665
277
    }
666
667
3.97k
    if (message == nullptr)
668
3.06k
    {
669
3.06k
        LogDebg("start reassembly");
670
3.06k
        VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs);
671
1.83k
        mReassemblyList.Enqueue(*message);
672
673
1.83k
        message->SetTimestampToNow();
674
1.83k
        message->SetOffset(0);
675
1.83k
        message->SetDatagramTag(fragmentHeader.GetIdentification());
676
677
        // copying the non-fragmentable header to the fragmentation buffer
678
1.83k
        SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetOffset()));
679
680
1.83k
        Get<TimeTicker>().RegisterReceiver(TimeTicker::kIp6FragmentReassembler);
681
1.83k
    }
682
683
    // increase message buffer if necessary
684
2.75k
    if (message->GetLength() < offset + payloadFragment + aMessage.GetOffset())
685
1.87k
    {
686
1.87k
        SuccessOrExit(error = message->SetLength(offset + payloadFragment + aMessage.GetOffset()));
687
1.87k
    }
688
689
    // copy the fragment payload into the message buffer
690
2.59k
    message->WriteBytesFromMessage(
691
2.59k
        /* aWriteOffset */ aMessage.GetOffset() + offset, aMessage,
692
2.59k
        /* aReadOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), /* aLength */ payloadFragment);
693
694
    // check if it is the last frame
695
2.59k
    if (!fragmentHeader.IsMoreFlagSet())
696
1.27k
    {
697
        // use the offset value for the whole ip message length
698
1.27k
        message->SetOffset(aMessage.GetOffset() + offset + payloadFragment);
699
700
        // creates the header for the reassembled ipv6 package
701
1.27k
        SuccessOrExit(error = aMessage.Read(0, header));
702
1.27k
        header.SetPayloadLength(message->GetLength() - sizeof(header));
703
1.27k
        header.SetNextHeader(fragmentHeader.GetNextHeader());
704
1.27k
        message->Write(0, header);
705
706
1.27k
        LogDebg("Reassembly complete.");
707
708
1.27k
        mReassemblyList.Dequeue(*message);
709
710
1.27k
        IgnoreError(HandleDatagram(OwnedPtr<Message>(message), /* aIsReassembled */ true));
711
1.27k
    }
712
713
7.61k
exit:
714
7.61k
    if (error != kErrorDrop && error != kErrorNone && isFragmented)
715
2.78k
    {
716
2.78k
        if (message != nullptr)
717
162
        {
718
162
            mReassemblyList.DequeueAndFree(*message);
719
162
        }
720
721
2.78k
        LogWarnOnError(error, "reassemble");
722
2.78k
    }
723
724
7.61k
    if (isFragmented)
725
5.38k
    {
726
        // drop all fragments, the payload is stored in the fragment buffer
727
5.38k
        error = kErrorDrop;
728
5.38k
    }
729
730
7.61k
    return error;
731
2.59k
}
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
276k
{
811
276k
    Error           error = kErrorNone;
812
276k
    ExtensionHeader extHeader;
813
814
295k
    while (aReceive || aNextHeader == kProtoHopOpts)
815
80.7k
    {
816
80.7k
        SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), extHeader));
817
818
76.7k
        switch (aNextHeader)
819
76.7k
        {
820
20.0k
        case kProtoHopOpts:
821
20.2k
        case kProtoDstOpts:
822
20.2k
            SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive));
823
16.8k
            break;
824
825
16.8k
        case kProtoFragment:
826
7.61k
            IgnoreError(PassToHost(aMessagePtr, aHeader, aNextHeader, aReceive, Message::kCopyToUse));
827
7.61k
            SuccessOrExit(error = HandleFragment(*aMessagePtr));
828
2.22k
            break;
829
830
2.22k
        case kProtoIp6:
831
1.23k
            ExitNow();
832
833
207
        case kProtoRouting:
834
401
        case kProtoNone:
835
401
            ExitNow(error = kErrorDrop);
836
837
47.2k
        default:
838
47.2k
            ExitNow();
839
76.7k
        }
840
841
19.0k
        aNextHeader = extHeader.GetNextHeader();
842
19.0k
    }
843
844
276k
exit:
845
276k
    return error;
846
276k
}
847
848
Error Ip6::TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr,
849
                                OwnedPtr<Message> &aMessagePtr,
850
                                Message::Ownership aMessageOwnership)
851
261k
{
852
261k
    switch (aMessageOwnership)
853
261k
    {
854
49.6k
    case Message::kTakeCustody:
855
49.6k
        aTargetPtr = aMessagePtr.PassOwnership();
856
49.6k
        break;
857
858
212k
    case Message::kCopyToUse:
859
212k
        aTargetPtr.Reset(aMessagePtr->Clone());
860
212k
        break;
861
261k
    }
862
863
261k
    return (aTargetPtr != nullptr) ? kErrorNone : kErrorNoBufs;
864
261k
}
865
866
Error Ip6::Receive(Header            &aIp6Header,
867
                   OwnedPtr<Message> &aMessagePtr,
868
                   uint8_t            aIpProto,
869
                   Message::Ownership aMessageOwnership)
870
47.2k
{
871
47.2k
    Error             error = kErrorNone;
872
47.2k
    OwnedPtr<Message> messagePtr;
873
47.2k
    MessageInfo       messageInfo;
874
875
47.2k
    messageInfo.Clear();
876
47.2k
    messageInfo.SetPeerAddr(aIp6Header.GetSource());
877
47.2k
    messageInfo.SetSockAddr(aIp6Header.GetDestination());
878
47.2k
    messageInfo.SetHopLimit(aIp6Header.GetHopLimit());
879
47.2k
    messageInfo.SetEcn(aIp6Header.GetEcn());
880
881
47.2k
    switch (aIpProto)
882
47.2k
    {
883
41.2k
    case kProtoUdp:
884
44.8k
    case kProtoIcmp6:
885
44.8k
        break;
886
0
#if OPENTHREAD_CONFIG_TCP_ENABLE
887
2.10k
    case kProtoTcp:
888
2.10k
        break;
889
0
#endif
890
322
    default:
891
322
        ExitNow();
892
47.2k
    }
893
894
46.9k
    SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership));
895
896
46.9k
    switch (aIpProto)
897
46.9k
    {
898
0
#if OPENTHREAD_CONFIG_TCP_ENABLE
899
2.10k
    case kProtoTcp:
900
2.10k
        error = mTcp.HandleMessage(aIp6Header, *messagePtr, messageInfo);
901
2.10k
        break;
902
0
#endif
903
41.2k
    case kProtoUdp:
904
41.2k
        error = mUdp.HandleMessage(*messagePtr, messageInfo);
905
41.2k
        break;
906
907
3.54k
    case kProtoIcmp6:
908
3.54k
        error = mIcmp.HandleMessage(*messagePtr, messageInfo);
909
3.54k
        break;
910
911
0
    default:
912
0
        break;
913
46.9k
    }
914
915
47.2k
exit:
916
47.2k
    LogWarnOnError(error, "handle payload");
917
47.2k
    return error;
918
46.9k
}
919
920
Error Ip6::PassToHost(OwnedPtr<Message> &aMessagePtr,
921
                      const Header      &aHeader,
922
                      uint8_t            aIpProto,
923
                      bool               aReceive,
924
                      Message::Ownership aMessageOwnership)
925
259k
{
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
259k
    Error             error = kErrorNone;
932
259k
    OwnedPtr<Message> messagePtr;
933
934
259k
    VerifyOrExit(aMessagePtr->IsLoopbackToHostAllowed());
935
936
259k
    VerifyOrExit(mReceiveCallback.IsSet(), error = kErrorNoRoute);
937
938
    // Do not pass IPv6 packets that exceed kMinimalMtu.
939
259k
    VerifyOrExit(aMessagePtr->GetLength() <= kMinimalMtu, error = kErrorDrop);
940
941
259k
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
942
259k
    if (!aReceive)
943
205k
    {
944
205k
        Get<BorderRouter::RoutingManager>().CheckReachabilityToSendIcmpError(*aMessagePtr, aHeader);
945
205k
    }
946
259k
#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
259k
    if (Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource()))
951
28.7k
    {
952
28.7k
        VerifyOrExit(aReceive, error = kErrorDrop);
953
28.7k
    }
954
955
256k
    if (mReceiveFilterEnabled && aReceive)
956
53.6k
    {
957
53.6k
        switch (aIpProto)
958
53.6k
        {
959
3.18k
        case kProtoIcmp6:
960
3.18k
            if (mIcmp.ShouldHandleEchoRequest(aHeader.GetDestination()))
961
1.19k
            {
962
1.19k
                Icmp::Header icmp;
963
964
1.19k
                IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), icmp));
965
1.19k
                VerifyOrExit(icmp.GetType() != Icmp::Header::kTypeEchoRequest, error = kErrorDrop);
966
1.19k
            }
967
968
2.21k
            break;
969
970
41.0k
        case kProtoUdp:
971
41.0k
        {
972
41.0k
            Udp::Header udp;
973
974
41.0k
            IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), udp));
975
41.0k
            VerifyOrExit(!Get<Udp>().IsPortInUse(udp.GetDestinationPort()), error = kErrorNoRoute);
976
993
            break;
977
41.0k
        }
978
979
993
#if OPENTHREAD_CONFIG_TCP_ENABLE
980
        // Do not pass TCP message to avoid dual processing from both
981
        // OpenThread and POSIX TCP stacks.
982
2.09k
        case kProtoTcp:
983
2.09k
            error = kErrorNoRoute;
984
2.09k
            ExitNow();
985
0
#endif
986
987
7.28k
        default:
988
7.28k
            break;
989
53.6k
        }
990
53.6k
    }
991
992
213k
    SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership));
993
994
200k
    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
200k
#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1014
200k
    UpdateBorderRoutingCounters(aHeader, messagePtr->GetLength(), /* aIsInbound */ false);
1015
200k
#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
200k
    mReceiveCallback.Invoke(messagePtr.Release());
1032
1033
259k
exit:
1034
259k
    return error;
1035
200k
}
1036
1037
Error Ip6::SendRaw(OwnedPtr<Message> aMessagePtr)
1038
86.7k
{
1039
86.7k
    Error  error = kErrorNone;
1040
86.7k
    Header header;
1041
1042
86.7k
    SuccessOrExit(error = header.ParseFrom(*aMessagePtr));
1043
86.7k
    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
86.5k
    {
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
86.5k
        if (Get<NetworkData::Leader>().IsOnMesh(header.GetSource()) ||
1053
86.5k
            Get<Mle::Mle>().IsMeshLocalAddress(header.GetDestination()))
1054
28.3k
        {
1055
28.3k
            VerifyOrExit(Get<ThreadNetif>().HasUnicastAddress(header.GetSource()), error = kErrorDrop);
1056
28.3k
        }
1057
86.5k
    }
1058
1059
85.5k
    if (header.GetDestination().IsMulticast())
1060
14.7k
    {
1061
14.7k
        SuccessOrExit(error = InsertMplOption(*aMessagePtr, header));
1062
14.7k
    }
1063
1064
84.1k
#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1065
84.1k
    UpdateBorderRoutingCounters(header, aMessagePtr->GetLength(), /* aIsInbound */ true);
1066
84.1k
#endif
1067
1068
84.1k
    error = HandleDatagram(aMessagePtr.PassOwnership());
1069
1070
86.7k
exit:
1071
86.7k
    return error;
1072
84.1k
}
1073
1074
void Ip6::DetermineAction(const Message &aMessage,
1075
                          const Header  &aHeader,
1076
                          bool          &aForwardThread,
1077
                          bool          &aForwardHost,
1078
                          bool          &aReceive) const
1079
276k
{
1080
    // Determine `aForwardThread`, `aForwardHost` and `aReceive`
1081
    // based on the destination address and message origin.
1082
1083
276k
    uint16_t rloc16;
1084
1085
276k
    aForwardThread = false;
1086
276k
    aForwardHost   = false;
1087
276k
    aReceive       = false;
1088
1089
276k
    if (aHeader.GetDestination().IsMulticast())
1090
204k
    {
1091
        // Destination is multicast
1092
1093
        // Forward multicast message to thread unless we received it
1094
        // on Thread netif.
1095
1096
204k
        aForwardThread = !aMessage.IsOriginThreadNetif();
1097
1098
204k
#if OPENTHREAD_FTD
1099
204k
        if (aMessage.IsOriginThreadNetif() && aHeader.GetDestination().IsMulticastLargerThanRealmLocal() &&
1100
204k
            Get<ChildTable>().HasSleepyChildWithAddress(aHeader.GetDestination()))
1101
0
        {
1102
0
            aForwardThread = true;
1103
0
        }
1104
204k
#endif
1105
1106
        // Always forward multicast packets to host network stack
1107
204k
        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
204k
        if ((aMessage.IsOriginThreadNetif() || aMessage.GetMulticastLoop()) &&
1113
204k
            Get<ThreadNetif>().IsMulticastSubscribed(aHeader.GetDestination()))
1114
0
        {
1115
0
            aReceive = true;
1116
0
        }
1117
1118
204k
        ExitNow();
1119
204k
    }
1120
1121
    // Destination is unicast
1122
1123
71.8k
    if (Get<ThreadNetif>().HasUnicastAddress(aHeader.GetDestination()))
1124
54.8k
    {
1125
54.8k
        aReceive = true;
1126
54.8k
        ExitNow();
1127
54.8k
    }
1128
1129
17.0k
    if (aHeader.GetDestination().IsLinkLocalUnicast())
1130
6.62k
    {
1131
        // Forward a message with a link-local destination address
1132
        // to thread, unless it is received on the Thread netif.
1133
1134
6.62k
        aForwardThread = !aMessage.IsOriginThreadNetif();
1135
6.62k
        ExitNow();
1136
6.62k
    }
1137
1138
10.3k
    if (IsOnLink(aHeader.GetDestination()))
1139
2.72k
    {
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
2.72k
        aForwardThread = true;
1146
2.72k
#endif
1147
2.72k
        ExitNow();
1148
2.72k
    }
1149
1150
7.65k
    if (Get<NetworkData::Leader>().RouteLookup(aHeader.GetSource(), aHeader.GetDestination(), rloc16) != kErrorNone)
1151
7.65k
    {
1152
        // No route in mesh, forward to host (as a last resort).
1153
7.65k
        LogNote("Failed to find valid route for: %s", aHeader.GetDestination().ToString().AsCString());
1154
7.65k
        aForwardHost = true;
1155
7.65k
        ExitNow();
1156
7.65k
    }
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
276k
exit:
1172
276k
    return;
1173
0
}
1174
1175
Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
1176
277k
{
1177
277k
    Error   error;
1178
277k
    Header  header;
1179
277k
    bool    receive;
1180
277k
    bool    forwardThread;
1181
277k
    bool    forwardHost;
1182
277k
    uint8_t nextHeader;
1183
1184
277k
    SuccessOrExit(error = header.ParseFrom(*aMessagePtr));
1185
1186
276k
    if (!aMessagePtr->IsOriginHostTrusted())
1187
84.1k
    {
1188
84.1k
        VerifyOrExit(!header.GetSource().IsLoopback() && !header.GetDestination().IsLoopback(), error = kErrorDrop);
1189
84.1k
    }
1190
1191
276k
    DetermineAction(*aMessagePtr, header, forwardThread, forwardHost, receive);
1192
1193
276k
    aMessagePtr->SetOffset(sizeof(header));
1194
1195
    // Process IPv6 Extension Headers
1196
276k
    nextHeader = header.GetNextHeader();
1197
276k
    SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, header, nextHeader, receive));
1198
1199
262k
    if (receive && (nextHeader == kProtoIp6))
1200
1.23k
    {
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
1.23k
        OwnedPtr<Message> messagePtr;
1208
1.23k
        bool              multicastLoop = aMessagePtr->GetMulticastLoop();
1209
1210
1.23k
        SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr,
1211
1.23k
                                                   forwardThread ? Message::kCopyToUse : Message::kTakeCustody));
1212
1.23k
        messagePtr->SetMulticastLoop(multicastLoop);
1213
1.23k
        messagePtr->RemoveHeader(messagePtr->GetOffset());
1214
1215
1.23k
        Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr);
1216
1217
1.23k
        IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled));
1218
1219
1.23k
        receive     = false;
1220
1.23k
        forwardHost = false;
1221
1.23k
    }
1222
1223
262k
    if ((forwardHost || receive) && !aIsReassembled)
1224
252k
    {
1225
252k
        error = PassToHost(aMessagePtr, header, nextHeader, receive,
1226
252k
                           (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody);
1227
252k
    }
1228
1229
262k
    if (receive)
1230
47.2k
    {
1231
47.2k
        error = Receive(header, aMessagePtr, nextHeader, forwardThread ? Message::kCopyToUse : Message::kTakeCustody);
1232
47.2k
    }
1233
1234
262k
    if (forwardThread)
1235
212k
    {
1236
212k
        if (aMessagePtr->IsOriginThreadNetif())
1237
0
        {
1238
0
            VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader());
1239
0
            header.SetHopLimit(header.GetHopLimit() - 1);
1240
0
        }
1241
1242
212k
        VerifyOrExit(header.GetHopLimit() > 0, error = kErrorDrop);
1243
1244
206k
        aMessagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, header.GetHopLimit());
1245
1246
206k
        if (nextHeader == kProtoIcmp6)
1247
1.14k
        {
1248
1.14k
            uint8_t icmpType;
1249
1250
1.14k
            SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), icmpType));
1251
1252
880
            error = kErrorDrop;
1253
1254
880
            for (IcmpType type : kForwardIcmpTypes)
1255
4.72k
            {
1256
4.72k
                if (icmpType == type)
1257
264
                {
1258
264
                    error = kErrorNone;
1259
264
                    break;
1260
264
                }
1261
4.72k
            }
1262
1263
880
            SuccessOrExit(error);
1264
880
        }
1265
1266
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1267
        if (mTmfOriginFilterEnabled)
1268
#endif
1269
205k
        {
1270
205k
            if (aMessagePtr->IsOriginHostUntrusted() && (nextHeader == kProtoUdp))
1271
1.59k
            {
1272
1.59k
                Udp::Header udpHeader;
1273
1274
1.59k
                SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), udpHeader));
1275
1276
1.30k
                if (udpHeader.GetDestinationPort() == Tmf::kUdpPort)
1277
391
                {
1278
391
                    LogNote("Dropping TMF message from untrusted origin");
1279
391
                    ExitNow(error = kErrorDrop);
1280
391
                }
1281
1.30k
            }
1282
205k
        }
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
204k
        Get<MeshForwarder>().SendMessage(aMessagePtr.PassOwnership());
1293
204k
    }
1294
1295
277k
exit:
1296
277k
    return error;
1297
262k
}
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
30.6k
{
1314
30.6k
    uint8_t                      destScope    = aDestination.GetScope();
1315
30.6k
    bool                         destIsRloc   = Get<Mle::Mle>().IsRoutingLocator(aDestination);
1316
30.6k
    const Netif::UnicastAddress *bestAddr     = nullptr;
1317
30.6k
    uint8_t                      bestMatchLen = 0;
1318
1319
30.6k
    for (const Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
1320
51.3k
    {
1321
51.3k
        bool    newAddrIsPreferred = false;
1322
51.3k
        uint8_t matchLen;
1323
51.3k
        uint8_t overrideScope;
1324
1325
51.3k
        if (Get<Mle::Mle>().IsAnycastLocator(addr.GetAddress()))
1326
376
        {
1327
            // Don't use anycast address as source address.
1328
376
            continue;
1329
376
        }
1330
1331
50.9k
        matchLen = aDestination.PrefixMatch(addr.GetAddress());
1332
1333
50.9k
        if (matchLen >= addr.mPrefixLength)
1334
8.03k
        {
1335
8.03k
            matchLen      = addr.mPrefixLength;
1336
8.03k
            overrideScope = addr.GetScope();
1337
8.03k
        }
1338
42.9k
        else
1339
42.9k
        {
1340
42.9k
            overrideScope = destScope;
1341
42.9k
        }
1342
1343
50.9k
        if (addr.GetAddress() == aDestination)
1344
99
        {
1345
            // Rule 1: Prefer same address
1346
99
            bestAddr = &addr;
1347
99
            ExitNow();
1348
99
        }
1349
1350
50.8k
        if (bestAddr == nullptr)
1351
30.4k
        {
1352
30.4k
            newAddrIsPreferred = true;
1353
30.4k
        }
1354
20.4k
        else if (addr.GetScope() < bestAddr->GetScope())
1355
15.7k
        {
1356
            // Rule 2: Prefer appropriate scope
1357
15.7k
            newAddrIsPreferred = (addr.GetScope() >= overrideScope);
1358
15.7k
        }
1359
4.69k
        else if (addr.GetScope() > bestAddr->GetScope())
1360
2.43k
        {
1361
2.43k
            newAddrIsPreferred = (bestAddr->GetScope() < overrideScope);
1362
2.43k
        }
1363
2.26k
        else if (addr.mPreferred != bestAddr->mPreferred)
1364
159
        {
1365
            // Rule 3: Avoid deprecated addresses
1366
159
            newAddrIsPreferred = addr.mPreferred;
1367
159
        }
1368
2.10k
        else if (matchLen > bestMatchLen)
1369
600
        {
1370
            // Rule 6: Prefer matching label
1371
            // Rule 7: Prefer public address
1372
            // Rule 8: Use longest prefix matching
1373
1374
600
            newAddrIsPreferred = true;
1375
600
        }
1376
1.50k
        else if ((matchLen == bestMatchLen) && (destIsRloc == Get<Mle::Mle>().IsRoutingLocator(addr.GetAddress())))
1377
1.05k
        {
1378
            // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else
1379
1.05k
            newAddrIsPreferred = true;
1380
1.05k
        }
1381
1382
50.8k
        if (newAddrIsPreferred)
1383
35.4k
        {
1384
35.4k
            bestAddr     = &addr;
1385
35.4k
            bestMatchLen = matchLen;
1386
1387
            // Infer destination scope based on prefix match
1388
35.4k
            if (bestMatchLen >= bestAddr->mPrefixLength)
1389
7.51k
            {
1390
7.51k
                destScope = bestAddr->GetScope();
1391
7.51k
            }
1392
35.4k
        }
1393
50.8k
    }
1394
1395
30.6k
exit:
1396
30.6k
    return (bestAddr != nullptr) ? &bestAddr->GetAddress() : nullptr;
1397
30.6k
}
1398
1399
bool Ip6::IsOnLink(const Address &aAddress) const
1400
10.3k
{
1401
10.3k
    bool isOnLink = false;
1402
1403
10.3k
    if (Get<NetworkData::Leader>().IsOnMesh(aAddress))
1404
766
    {
1405
766
        ExitNow(isOnLink = true);
1406
766
    }
1407
1408
9.61k
    for (const Netif::UnicastAddress &unicastAddr : Get<ThreadNetif>().GetUnicastAddresses())
1409
17.1k
    {
1410
17.1k
        if (unicastAddr.GetAddress().PrefixMatch(aAddress) >= unicastAddr.mPrefixLength)
1411
1.95k
        {
1412
1.95k
            ExitNow(isOnLink = true);
1413
1.95k
        }
1414
17.1k
    }
1415
1416
10.3k
exit:
1417
10.3k
    return isOnLink;
1418
9.61k
}
1419
1420
#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
1421
1422
void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound)
1423
285k
{
1424
285k
    static constexpr uint8_t kPrefixLength = 48;
1425
1426
285k
    otPacketsAndBytes *counter         = nullptr;
1427
285k
    otPacketsAndBytes *internetCounter = nullptr;
1428
1429
285k
    VerifyOrExit(!aHeader.GetSource().IsLinkLocalUnicast());
1430
86.3k
    VerifyOrExit(!aHeader.GetDestination().IsLinkLocalUnicast());
1431
77.2k
    VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource()));
1432
50.0k
    VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetDestination()));
1433
1434
49.7k
    if (aIsInbound)
1435
40.9k
    {
1436
40.9k
        VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetSource()));
1437
1438
40.2k
        if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
1439
33.6k
        {
1440
33.6k
            internetCounter = &mBrCounters.mInboundInternet;
1441
33.6k
        }
1442
1443
40.2k
        if (aHeader.GetDestination().IsMulticast())
1444
13.3k
        {
1445
13.3k
            VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
1446
3.84k
            counter = &mBrCounters.mInboundMulticast;
1447
3.84k
        }
1448
26.9k
        else
1449
26.9k
        {
1450
26.9k
            counter = &mBrCounters.mInboundUnicast;
1451
26.9k
        }
1452
40.2k
    }
1453
8.80k
    else
1454
8.80k
    {
1455
8.80k
        VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetDestination()));
1456
1457
4.26k
        if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
1458
4.00k
        {
1459
4.00k
            internetCounter = &mBrCounters.mOutboundInternet;
1460
4.00k
        }
1461
1462
4.26k
        if (aHeader.GetDestination().IsMulticast())
1463
2.98k
        {
1464
2.98k
            VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
1465
0
            counter = &mBrCounters.mOutboundMulticast;
1466
0
        }
1467
1.28k
        else
1468
1.28k
        {
1469
1.28k
            counter = &mBrCounters.mOutboundUnicast;
1470
1.28k
        }
1471
4.26k
    }
1472
1473
285k
exit:
1474
1475
285k
    if (counter)
1476
32.0k
    {
1477
32.0k
        counter->mPackets++;
1478
32.0k
        counter->mBytes += aMessageLength;
1479
32.0k
    }
1480
285k
    if (internetCounter)
1481
37.6k
    {
1482
37.6k
        internetCounter->mPackets++;
1483
37.6k
        internetCounter->mBytes += aMessageLength;
1484
37.6k
    }
1485
285k
}
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
191k
{
1532
191k
    Error error = kErrorParse;
1533
1534
191k
    Clear();
1535
1536
191k
    SuccessOrExit(mIp6Header.ParseFrom(aMessage));
1537
1538
191k
    switch (mIp6Header.GetNextHeader())
1539
191k
    {
1540
191k
    case kProtoUdp:
1541
191k
        SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mUdp));
1542
191k
        break;
1543
191k
    case kProtoTcp:
1544
0
        SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mTcp));
1545
0
        break;
1546
0
    case kProtoIcmp6:
1547
0
        SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mIcmp));
1548
0
        break;
1549
0
    default:
1550
0
        break;
1551
191k
    }
1552
1553
191k
    error = kErrorNone;
1554
1555
191k
exit:
1556
191k
    return error;
1557
191k
}
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
191k
{
1622
191k
    uint16_t port = 0;
1623
1624
191k
    switch (GetIpProto())
1625
191k
    {
1626
191k
    case kProtoUdp:
1627
191k
        port = mHeader.mUdp.GetSourcePort();
1628
191k
        break;
1629
1630
0
    case kProtoTcp:
1631
0
        port = mHeader.mTcp.GetSourcePort();
1632
0
        break;
1633
1634
0
    default:
1635
0
        break;
1636
191k
    }
1637
1638
191k
    return port;
1639
191k
}
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
0
{
1660
0
    uint16_t port = 0;
1661
1662
0
    switch (GetIpProto())
1663
0
    {
1664
0
    case kProtoUdp:
1665
0
        port = mHeader.mUdp.GetDestinationPort();
1666
0
        break;
1667
1668
0
    case kProtoTcp:
1669
0
        port = mHeader.mTcp.GetDestinationPort();
1670
0
        break;
1671
1672
0
    default:
1673
0
        break;
1674
0
    }
1675
1676
0
    return port;
1677
0
}
1678
1679
uint16_t Headers::GetChecksum(void) const
1680
0
{
1681
0
    uint16_t checksum = 0;
1682
1683
0
    switch (GetIpProto())
1684
0
    {
1685
0
    case kProtoUdp:
1686
0
        checksum = mHeader.mUdp.GetChecksum();
1687
0
        break;
1688
1689
0
    case kProtoTcp:
1690
0
        checksum = mHeader.mTcp.GetChecksum();
1691
0
        break;
1692
1693
0
    case kProtoIcmp6:
1694
0
        checksum = mHeader.mIcmp.GetChecksum();
1695
0
        break;
1696
1697
0
    default:
1698
0
        break;
1699
0
    }
1700
1701
0
    return checksum;
1702
0
}
1703
1704
} // namespace Ip6
1705
} // namespace ot