Coverage Report

Created: 2025-05-12 06:47

/src/openthread/src/core/net/tcp6.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (c) 2021, 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 TCP/IPv6 sockets.
32
 */
33
34
#include "tcp6.hpp"
35
36
#if OPENTHREAD_CONFIG_TCP_ENABLE
37
38
#include "instance/instance.hpp"
39
40
#include "../../third_party/tcplp/tcplp.h"
41
42
namespace ot {
43
namespace Ip6 {
44
45
RegisterLogModule("Tcp");
46
47
static_assert(sizeof(struct tcpcb) == sizeof(Tcp::Endpoint::mTcb), "mTcb field in otTcpEndpoint is sized incorrectly");
48
static_assert(alignof(struct tcpcb) == alignof(decltype(Tcp::Endpoint::mTcb)),
49
              "mTcb field in otTcpEndpoint is aligned incorrectly");
50
static_assert(offsetof(Tcp::Endpoint, mTcb) == 0, "mTcb field in otTcpEndpoint has nonzero offset");
51
52
static_assert(sizeof(struct tcpcb_listen) == sizeof(Tcp::Listener::mTcbListen),
53
              "mTcbListen field in otTcpListener is sized incorrectly");
54
static_assert(alignof(struct tcpcb_listen) == alignof(decltype(Tcp::Listener::mTcbListen)),
55
              "mTcbListen field in otTcpListener is aligned incorrectly");
56
static_assert(offsetof(Tcp::Listener, mTcbListen) == 0, "mTcbListen field in otTcpEndpoint has nonzero offset");
57
58
Tcp::Tcp(Instance &aInstance)
59
5.14k
    : InstanceLocator(aInstance)
60
5.14k
    , mTimer(aInstance)
61
5.14k
    , mTasklet(aInstance)
62
5.14k
    , mEphemeralPort(kDynamicPortMin)
63
5.14k
{
64
5.14k
    OT_UNUSED_VARIABLE(mEphemeralPort);
65
5.14k
}
66
67
Error Tcp::Endpoint::Initialize(Instance &aInstance, const otTcpEndpointInitializeArgs &aArgs)
68
0
{
69
0
    Error         error;
70
0
    struct tcpcb &tp = GetTcb();
71
72
0
    ClearAllBytes(tp);
73
74
0
    SuccessOrExit(error = aInstance.Get<Tcp>().mEndpoints.Add(*this));
75
76
0
    mContext                  = aArgs.mContext;
77
0
    mEstablishedCallback      = aArgs.mEstablishedCallback;
78
0
    mSendDoneCallback         = aArgs.mSendDoneCallback;
79
0
    mForwardProgressCallback  = aArgs.mForwardProgressCallback;
80
0
    mReceiveAvailableCallback = aArgs.mReceiveAvailableCallback;
81
0
    mDisconnectedCallback     = aArgs.mDisconnectedCallback;
82
83
0
    ClearAllBytes(mTimers);
84
0
    ClearAllBytes(mSockAddr);
85
0
    mPendingCallbacks = 0;
86
87
    /*
88
     * Initialize buffers --- formerly in initialize_tcb.
89
     */
90
0
    {
91
0
        uint8_t *recvbuf    = static_cast<uint8_t *>(aArgs.mReceiveBuffer);
92
0
        size_t   recvbuflen = aArgs.mReceiveBufferSize - ((aArgs.mReceiveBufferSize + 8) / 9);
93
0
        uint8_t *reassbmp   = recvbuf + recvbuflen;
94
95
0
        lbuf_init(&tp.sendbuf);
96
0
        cbuf_init(&tp.recvbuf, recvbuf, recvbuflen);
97
0
        tp.reassbmp = reassbmp;
98
0
        bmp_init(tp.reassbmp, BITS_TO_BYTES(recvbuflen));
99
0
    }
100
101
0
    tp.accepted_from = nullptr;
102
0
    initialize_tcb(&tp);
103
104
    /* Note that we do not need to zero-initialize mReceiveLinks. */
105
106
0
    tp.instance = &aInstance;
107
108
0
exit:
109
0
    return error;
110
0
}
111
112
0
Instance &Tcp::Endpoint::GetInstance(void) const { return AsNonConst(AsCoreType(GetTcb().instance)); }
113
114
const SockAddr &Tcp::Endpoint::GetLocalAddress(void) const
115
0
{
116
0
    const struct tcpcb &tp = GetTcb();
117
118
0
    static otSockAddr temp;
119
120
0
    memcpy(&temp.mAddress, &tp.laddr, sizeof(temp.mAddress));
121
0
    temp.mPort = BigEndian::HostSwap16(tp.lport);
122
123
0
    return AsCoreType(&temp);
124
0
}
125
126
const SockAddr &Tcp::Endpoint::GetPeerAddress(void) const
127
0
{
128
0
    const struct tcpcb &tp = GetTcb();
129
130
0
    static otSockAddr temp;
131
132
0
    memcpy(&temp.mAddress, &tp.faddr, sizeof(temp.mAddress));
133
0
    temp.mPort = BigEndian::HostSwap16(tp.fport);
134
135
0
    return AsCoreType(&temp);
136
0
}
137
138
Error Tcp::Endpoint::Bind(const SockAddr &aSockName)
139
0
{
140
0
    Error         error;
141
0
    struct tcpcb &tp = GetTcb();
142
143
0
    VerifyOrExit(!AsCoreType(&aSockName.mAddress).IsUnspecified(), error = kErrorInvalidArgs);
144
0
    VerifyOrExit(Get<Tcp>().CanBind(aSockName), error = kErrorInvalidState);
145
146
0
    memcpy(&tp.laddr, &aSockName.mAddress, sizeof(tp.laddr));
147
0
    tp.lport = BigEndian::HostSwap16(aSockName.mPort);
148
0
    error    = kErrorNone;
149
150
0
exit:
151
0
    return error;
152
0
}
153
154
Error Tcp::Endpoint::Connect(const SockAddr &aSockName, uint32_t aFlags)
155
0
{
156
0
    Error         error = kErrorNone;
157
0
    struct tcpcb &tp    = GetTcb();
158
159
0
    VerifyOrExit(tp.t_state == TCP6S_CLOSED, error = kErrorInvalidState);
160
161
0
    if (aFlags & OT_TCP_CONNECT_NO_FAST_OPEN)
162
0
    {
163
0
        struct sockaddr_in6 sin6p;
164
165
0
        tp.t_flags &= ~TF_FASTOPEN;
166
0
        memcpy(&sin6p.sin6_addr, &aSockName.mAddress, sizeof(sin6p.sin6_addr));
167
0
        sin6p.sin6_port = BigEndian::HostSwap16(aSockName.mPort);
168
0
        error           = BsdErrorToOtError(tcp6_usr_connect(&tp, &sin6p));
169
0
    }
170
0
    else
171
0
    {
172
0
        tp.t_flags |= TF_FASTOPEN;
173
174
        /* Stash the destination address in tp. */
175
0
        memcpy(&tp.faddr, &aSockName.mAddress, sizeof(tp.faddr));
176
0
        tp.fport = BigEndian::HostSwap16(aSockName.mPort);
177
0
    }
178
179
0
exit:
180
0
    return error;
181
0
}
182
183
Error Tcp::Endpoint::SendByReference(otLinkedBuffer &aBuffer, uint32_t aFlags)
184
0
{
185
0
    Error         error;
186
0
    struct tcpcb &tp = GetTcb();
187
188
0
    size_t backlogBefore = GetBacklogBytes();
189
0
    size_t sent          = aBuffer.mLength;
190
191
0
    struct sockaddr_in6  sin6p;
192
0
    struct sockaddr_in6 *name = nullptr;
193
194
0
    if (IS_FASTOPEN(tp.t_flags))
195
0
    {
196
0
        memcpy(&sin6p.sin6_addr, &tp.faddr, sizeof(sin6p.sin6_addr));
197
0
        sin6p.sin6_port = tp.fport;
198
0
        name            = &sin6p;
199
0
    }
200
0
    SuccessOrExit(
201
0
        error = BsdErrorToOtError(tcp_usr_send(&tp, (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0, &aBuffer, 0, name)));
202
203
0
    PostCallbacksAfterSend(sent, backlogBefore);
204
205
0
exit:
206
0
    return error;
207
0
}
208
209
Error Tcp::Endpoint::SendByExtension(size_t aNumBytes, uint32_t aFlags)
210
0
{
211
0
    Error         error;
212
0
    bool          moreToCome    = (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0;
213
0
    struct tcpcb &tp            = GetTcb();
214
0
    size_t        backlogBefore = GetBacklogBytes();
215
0
    int           bsdError;
216
217
0
    struct sockaddr_in6  sin6p;
218
0
    struct sockaddr_in6 *name = nullptr;
219
220
0
    VerifyOrExit(lbuf_head(&tp.sendbuf) != nullptr, error = kErrorInvalidState);
221
222
0
    if (IS_FASTOPEN(tp.t_flags))
223
0
    {
224
0
        memcpy(&sin6p.sin6_addr, &tp.faddr, sizeof(sin6p.sin6_addr));
225
0
        sin6p.sin6_port = tp.fport;
226
0
        name            = &sin6p;
227
0
    }
228
229
0
    bsdError = tcp_usr_send(&tp, moreToCome ? 1 : 0, nullptr, aNumBytes, name);
230
0
    SuccessOrExit(error = BsdErrorToOtError(bsdError));
231
232
0
    PostCallbacksAfterSend(aNumBytes, backlogBefore);
233
234
0
exit:
235
0
    return error;
236
0
}
237
238
Error Tcp::Endpoint::ReceiveByReference(const otLinkedBuffer *&aBuffer)
239
0
{
240
0
    struct tcpcb &tp = GetTcb();
241
242
0
    cbuf_reference(&tp.recvbuf, &mReceiveLinks[0], &mReceiveLinks[1]);
243
0
    aBuffer = &mReceiveLinks[0];
244
245
0
    return kErrorNone;
246
0
}
247
248
Error Tcp::Endpoint::ReceiveContiguify(void)
249
0
{
250
0
    struct tcpcb &tp = GetTcb();
251
252
0
    cbuf_contiguify(&tp.recvbuf, tp.reassbmp);
253
254
0
    return kErrorNone;
255
0
}
256
257
Error Tcp::Endpoint::CommitReceive(size_t aNumBytes, uint32_t aFlags)
258
0
{
259
0
    Error         error = kErrorNone;
260
0
    struct tcpcb &tp    = GetTcb();
261
262
0
    OT_UNUSED_VARIABLE(aFlags);
263
264
0
    VerifyOrExit(cbuf_used_space(&tp.recvbuf) >= aNumBytes, error = kErrorFailed);
265
0
    VerifyOrExit(aNumBytes > 0, error = kErrorNone);
266
267
0
    cbuf_pop(&tp.recvbuf, aNumBytes);
268
0
    error = BsdErrorToOtError(tcp_usr_rcvd(&tp));
269
270
0
exit:
271
0
    return error;
272
0
}
273
274
Error Tcp::Endpoint::SendEndOfStream(void)
275
0
{
276
0
    struct tcpcb &tp = GetTcb();
277
278
0
    return BsdErrorToOtError(tcp_usr_shutdown(&tp));
279
0
}
280
281
Error Tcp::Endpoint::Abort(void)
282
0
{
283
0
    struct tcpcb &tp = GetTcb();
284
285
0
    tcp_usr_abort(&tp);
286
    /* connection_lost will do any reinitialization work for this socket. */
287
0
    return kErrorNone;
288
0
}
289
290
Error Tcp::Endpoint::Deinitialize(void)
291
0
{
292
0
    Error error;
293
294
0
    SuccessOrExit(error = Get<Tcp>().mEndpoints.Remove(*this));
295
0
    SetNext(nullptr);
296
297
0
    SuccessOrExit(error = Abort());
298
299
0
exit:
300
0
    return error;
301
0
}
302
303
0
bool Tcp::Endpoint::IsClosed(void) const { return GetTcb().t_state == TCP6S_CLOSED; }
304
305
uint8_t Tcp::Endpoint::TimerFlagToIndex(uint8_t aTimerFlag)
306
0
{
307
0
    uint8_t timerIndex = 0;
308
309
0
    switch (aTimerFlag)
310
0
    {
311
0
    case TT_DELACK:
312
0
        timerIndex = kTimerDelack;
313
0
        break;
314
0
    case TT_REXMT:
315
0
    case TT_PERSIST:
316
0
        timerIndex = kTimerRexmtPersist;
317
0
        break;
318
0
    case TT_KEEP:
319
0
        timerIndex = kTimerKeep;
320
0
        break;
321
0
    case TT_2MSL:
322
0
        timerIndex = kTimer2Msl;
323
0
        break;
324
0
    }
325
326
0
    return timerIndex;
327
0
}
328
329
bool Tcp::Endpoint::IsTimerActive(uint8_t aTimerIndex)
330
0
{
331
0
    bool          active = false;
332
0
    struct tcpcb *tp     = &GetTcb();
333
334
0
    OT_ASSERT(aTimerIndex < kNumTimers);
335
0
    switch (aTimerIndex)
336
0
    {
337
0
    case kTimerDelack:
338
0
        active = tcp_timer_active(tp, TT_DELACK);
339
0
        break;
340
0
    case kTimerRexmtPersist:
341
0
        active = tcp_timer_active(tp, TT_REXMT) || tcp_timer_active(tp, TT_PERSIST);
342
0
        break;
343
0
    case kTimerKeep:
344
0
        active = tcp_timer_active(tp, TT_KEEP);
345
0
        break;
346
0
    case kTimer2Msl:
347
0
        active = tcp_timer_active(tp, TT_2MSL);
348
0
        break;
349
0
    }
350
351
0
    return active;
352
0
}
353
354
void Tcp::Endpoint::SetTimer(uint8_t aTimerFlag, uint32_t aDelay)
355
0
{
356
    /*
357
     * TCPlp has already set the flag for this timer to record that it's
358
     * running. So, all that's left to do is record the expiry time and
359
     * (re)set the main timer as appropriate.
360
     */
361
362
0
    TimeMilli now         = TimerMilli::GetNow();
363
0
    TimeMilli newFireTime = now + aDelay;
364
0
    uint8_t   timerIndex  = TimerFlagToIndex(aTimerFlag);
365
366
0
    mTimers[timerIndex] = newFireTime.GetValue();
367
0
    LogDebg("Endpoint %p set timer %u to %u ms", static_cast<void *>(this), static_cast<unsigned int>(timerIndex),
368
0
            static_cast<unsigned int>(aDelay));
369
370
0
    Get<Tcp>().mTimer.FireAtIfEarlier(newFireTime);
371
0
}
372
373
void Tcp::Endpoint::CancelTimer(uint8_t aTimerFlag)
374
0
{
375
    /*
376
     * TCPlp has already cleared the timer flag before calling this. Since the
377
     * main timer's callback properly handles the case where no timers are
378
     * actually due, there's actually no work to be done here.
379
     */
380
381
0
    OT_UNUSED_VARIABLE(aTimerFlag);
382
383
0
    LogDebg("Endpoint %p cancelled timer %u", static_cast<void *>(this),
384
0
            static_cast<unsigned int>(TimerFlagToIndex(aTimerFlag)));
385
0
}
386
387
bool Tcp::Endpoint::FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, TimeMilli &aEarliestFutureExpiry)
388
0
{
389
0
    bool          calledUserCallback = false;
390
0
    struct tcpcb *tp                 = &GetTcb();
391
392
    /*
393
     * NOTE: Firing a timer might potentially activate/deactivate other timers.
394
     * If timers x and y expire at the same time, but the callback for timer x
395
     * (for x < y) cancels or postpones timer y, should timer y's callback be
396
     * called? Our answer is no, since timer x's callback has updated the
397
     * TCP stack's state in such a way that it no longer expects timer y's
398
     * callback to to be called. Because the TCP stack thinks that timer y
399
     * has been cancelled, calling timer y's callback could potentially cause
400
     * problems.
401
     *
402
     * If the timer callbacks set other timers, then they may not be taken
403
     * into account when setting aEarliestFutureExpiry. But mTimer's expiry
404
     * time will be updated by those, so we can just compare against mTimer's
405
     * expiry time when resetting mTimer.
406
     */
407
0
    for (uint8_t timerIndex = 0; timerIndex != kNumTimers; timerIndex++)
408
0
    {
409
0
        if (IsTimerActive(timerIndex))
410
0
        {
411
0
            TimeMilli expiry(mTimers[timerIndex]);
412
413
0
            if (expiry <= aNow)
414
0
            {
415
                /*
416
                 * If a user callback is called, then return true. For TCPlp,
417
                 * this only happens if the connection is dropped (e.g., it
418
                 * times out).
419
                 */
420
0
                int dropped = 0;
421
422
0
                switch (timerIndex)
423
0
                {
424
0
                case kTimerDelack:
425
0
                    dropped = tcp_timer_delack(tp);
426
0
                    break;
427
0
                case kTimerRexmtPersist:
428
0
                    if (tcp_timer_active(tp, TT_REXMT))
429
0
                    {
430
0
                        dropped = tcp_timer_rexmt(tp);
431
0
                    }
432
0
                    else
433
0
                    {
434
0
                        dropped = tcp_timer_persist(tp);
435
0
                    }
436
0
                    break;
437
0
                case kTimerKeep:
438
0
                    dropped = tcp_timer_keep(tp);
439
0
                    break;
440
0
                case kTimer2Msl:
441
0
                    dropped = tcp_timer_2msl(tp);
442
0
                    break;
443
0
                }
444
0
                VerifyOrExit(dropped == 0, calledUserCallback = true);
445
0
            }
446
0
            else
447
0
            {
448
0
                aHasFutureTimer       = true;
449
0
                aEarliestFutureExpiry = Min(aEarliestFutureExpiry, expiry);
450
0
            }
451
0
        }
452
0
    }
453
454
0
exit:
455
0
    return calledUserCallback;
456
0
}
457
458
void Tcp::Endpoint::PostCallbacksAfterSend(size_t aSent, size_t aBacklogBefore)
459
0
{
460
0
    size_t backlogAfter = GetBacklogBytes();
461
462
0
    if (backlogAfter < aBacklogBefore + aSent && mForwardProgressCallback != nullptr)
463
0
    {
464
0
        mPendingCallbacks |= kForwardProgressCallbackFlag;
465
0
        Get<Tcp>().mTasklet.Post();
466
0
    }
467
0
}
468
469
bool Tcp::Endpoint::FirePendingCallbacks(void)
470
0
{
471
0
    bool calledUserCallback = false;
472
473
0
    if ((mPendingCallbacks & kForwardProgressCallbackFlag) != 0 && mForwardProgressCallback != nullptr)
474
0
    {
475
0
        mForwardProgressCallback(this, GetSendBufferBytes(), GetBacklogBytes());
476
0
        calledUserCallback = true;
477
0
    }
478
479
0
    mPendingCallbacks = 0;
480
481
0
    return calledUserCallback;
482
0
}
483
484
size_t Tcp::Endpoint::GetSendBufferBytes(void) const
485
0
{
486
0
    const struct tcpcb &tp = GetTcb();
487
0
    return lbuf_used_space(&tp.sendbuf);
488
0
}
489
490
size_t Tcp::Endpoint::GetInFlightBytes(void) const
491
0
{
492
0
    const struct tcpcb &tp = GetTcb();
493
0
    return tp.snd_max - tp.snd_una;
494
0
}
495
496
0
size_t Tcp::Endpoint::GetBacklogBytes(void) const { return GetSendBufferBytes() - GetInFlightBytes(); }
497
498
0
Address &Tcp::Endpoint::GetLocalIp6Address(void) { return *reinterpret_cast<Address *>(&GetTcb().laddr); }
499
500
const Address &Tcp::Endpoint::GetLocalIp6Address(void) const
501
0
{
502
0
    return *reinterpret_cast<const Address *>(&GetTcb().laddr);
503
0
}
504
505
0
Address &Tcp::Endpoint::GetForeignIp6Address(void) { return *reinterpret_cast<Address *>(&GetTcb().faddr); }
506
507
const Address &Tcp::Endpoint::GetForeignIp6Address(void) const
508
0
{
509
0
    return *reinterpret_cast<const Address *>(&GetTcb().faddr);
510
0
}
511
512
bool Tcp::Endpoint::Matches(const MessageInfo &aMessageInfo) const
513
0
{
514
0
    bool                matches = false;
515
0
    const struct tcpcb *tp      = &GetTcb();
516
517
0
    VerifyOrExit(tp->t_state != TCP6S_CLOSED);
518
0
    VerifyOrExit(tp->lport == BigEndian::HostSwap16(aMessageInfo.GetSockPort()));
519
0
    VerifyOrExit(tp->fport == BigEndian::HostSwap16(aMessageInfo.GetPeerPort()));
520
0
    VerifyOrExit(GetLocalIp6Address().IsUnspecified() || GetLocalIp6Address() == aMessageInfo.GetSockAddr());
521
0
    VerifyOrExit(GetForeignIp6Address() == aMessageInfo.GetPeerAddr());
522
523
0
    matches = true;
524
525
0
exit:
526
0
    return matches;
527
0
}
528
529
Error Tcp::Listener::Initialize(Instance &aInstance, const otTcpListenerInitializeArgs &aArgs)
530
0
{
531
0
    Error                error;
532
0
    struct tcpcb_listen *tpl = &GetTcbListen();
533
534
0
    SuccessOrExit(error = aInstance.Get<Tcp>().mListeners.Add(*this));
535
536
0
    mContext             = aArgs.mContext;
537
0
    mAcceptReadyCallback = aArgs.mAcceptReadyCallback;
538
0
    mAcceptDoneCallback  = aArgs.mAcceptDoneCallback;
539
540
0
    ClearAllBytes(*tpl);
541
0
    tpl->instance = &aInstance;
542
543
0
exit:
544
0
    return error;
545
0
}
546
547
0
Instance &Tcp::Listener::GetInstance(void) const { return AsNonConst(AsCoreType(GetTcbListen().instance)); }
548
549
Error Tcp::Listener::Listen(const SockAddr &aSockName)
550
0
{
551
0
    Error                error;
552
0
    uint16_t             port = BigEndian::HostSwap16(aSockName.mPort);
553
0
    struct tcpcb_listen *tpl  = &GetTcbListen();
554
555
0
    VerifyOrExit(Get<Tcp>().CanBind(aSockName), error = kErrorInvalidState);
556
557
0
    memcpy(&tpl->laddr, &aSockName.mAddress, sizeof(tpl->laddr));
558
0
    tpl->lport   = port;
559
0
    tpl->t_state = TCP6S_LISTEN;
560
0
    error        = kErrorNone;
561
562
0
exit:
563
0
    return error;
564
0
}
565
566
Error Tcp::Listener::StopListening(void)
567
0
{
568
0
    struct tcpcb_listen *tpl = &GetTcbListen();
569
570
0
    ClearAllBytes(tpl->laddr);
571
0
    tpl->lport   = 0;
572
0
    tpl->t_state = TCP6S_CLOSED;
573
0
    return kErrorNone;
574
0
}
575
576
Error Tcp::Listener::Deinitialize(void)
577
0
{
578
0
    Error error;
579
580
0
    SuccessOrExit(error = Get<Tcp>().mListeners.Remove(*this));
581
0
    SetNext(nullptr);
582
583
0
exit:
584
0
    return error;
585
0
}
586
587
0
bool Tcp::Listener::IsClosed(void) const { return GetTcbListen().t_state == TCP6S_CLOSED; }
588
589
0
Address &Tcp::Listener::GetLocalIp6Address(void) { return *reinterpret_cast<Address *>(&GetTcbListen().laddr); }
590
591
const Address &Tcp::Listener::GetLocalIp6Address(void) const
592
0
{
593
0
    return *reinterpret_cast<const Address *>(&GetTcbListen().laddr);
594
0
}
595
596
bool Tcp::Listener::Matches(const MessageInfo &aMessageInfo) const
597
0
{
598
0
    bool                       matches = false;
599
0
    const struct tcpcb_listen *tpl     = &GetTcbListen();
600
601
0
    VerifyOrExit(tpl->t_state == TCP6S_LISTEN);
602
0
    VerifyOrExit(tpl->lport == BigEndian::HostSwap16(aMessageInfo.GetSockPort()));
603
0
    VerifyOrExit(GetLocalIp6Address().IsUnspecified() || GetLocalIp6Address() == aMessageInfo.GetSockAddr());
604
605
0
    matches = true;
606
607
0
exit:
608
0
    return matches;
609
0
}
610
611
Error Tcp::HandleMessage(ot::Ip6::Header &aIp6Header, Message &aMessage, MessageInfo &aMessageInfo)
612
28
{
613
28
    Error error = kErrorNotImplemented;
614
615
    /*
616
     * The type uint32_t was chosen for alignment purposes. The size is the
617
     * maximum TCP header size, including options.
618
     */
619
28
    uint32_t header[15];
620
621
28
    uint16_t length = aIp6Header.GetPayloadLength();
622
28
    uint8_t  headerSize;
623
624
28
    struct ip6_hdr *ip6Header;
625
28
    struct tcphdr  *tcpHeader;
626
627
28
    Endpoint *endpoint;
628
28
    Listener *listener;
629
630
28
    struct tcplp_signals sig;
631
28
    int                  nextAction;
632
633
28
    VerifyOrExit(length == aMessage.GetLength() - aMessage.GetOffset(), error = kErrorParse);
634
27
    VerifyOrExit(length >= sizeof(Tcp::Header), error = kErrorParse);
635
24
    SuccessOrExit(error = aMessage.Read(aMessage.GetOffset() + offsetof(struct tcphdr, th_off_x2), headerSize));
636
24
    headerSize = static_cast<uint8_t>((headerSize >> TH_OFF_SHIFT) << 2);
637
24
    VerifyOrExit(headerSize >= sizeof(struct tcphdr) && headerSize <= sizeof(header) &&
638
24
                     static_cast<uint16_t>(headerSize) <= length,
639
24
                 error = kErrorParse);
640
13
    SuccessOrExit(error = Checksum::VerifyMessageChecksum(aMessage, aMessageInfo, kProtoTcp));
641
4
    SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), &header[0], headerSize));
642
643
4
    ip6Header = reinterpret_cast<struct ip6_hdr *>(&aIp6Header);
644
4
    tcpHeader = reinterpret_cast<struct tcphdr *>(&header[0]);
645
4
    tcp_fields_to_host(tcpHeader);
646
647
4
    aMessageInfo.mPeerPort = BigEndian::HostSwap16(tcpHeader->th_sport);
648
4
    aMessageInfo.mSockPort = BigEndian::HostSwap16(tcpHeader->th_dport);
649
650
4
    endpoint = mEndpoints.FindMatching(aMessageInfo);
651
652
4
    if (endpoint != nullptr)
653
0
    {
654
0
        struct tcpcb *tp = &endpoint->GetTcb();
655
656
0
        otLinkedBuffer *priorHead    = lbuf_head(&tp->sendbuf);
657
0
        size_t          priorBacklog = endpoint->GetSendBufferBytes() - endpoint->GetInFlightBytes();
658
659
0
        ClearAllBytes(sig);
660
0
        nextAction = tcplp_input(ip6Header, tcpHeader, &aMessage, tp, nullptr, &sig);
661
0
        if (nextAction != RELOOKUP_REQUIRED)
662
0
        {
663
0
            ProcessSignals(*endpoint, priorHead, priorBacklog, sig);
664
0
            ExitNow();
665
0
        }
666
        /* If the matching socket was in the TIME-WAIT state, then we try passive sockets. */
667
0
    }
668
669
4
    listener = mListeners.FindMatching(aMessageInfo);
670
671
4
    if (listener != nullptr)
672
0
    {
673
0
        struct tcpcb_listen *tpl = &listener->GetTcbListen();
674
675
0
        ClearAllBytes(sig);
676
0
        nextAction = tcplp_input(ip6Header, tcpHeader, &aMessage, nullptr, tpl, &sig);
677
0
        OT_ASSERT(nextAction != RELOOKUP_REQUIRED);
678
0
        if (sig.accepted_connection != nullptr)
679
0
        {
680
0
            ProcessSignals(Tcp::Endpoint::FromTcb(*sig.accepted_connection), nullptr, 0, sig);
681
0
        }
682
0
        ExitNow();
683
0
    }
684
685
4
    tcp_dropwithreset(ip6Header, tcpHeader, nullptr, &InstanceLocator::GetInstance(), length - headerSize,
686
4
                      ECONNREFUSED);
687
688
28
exit:
689
28
    return error;
690
4
}
691
692
void Tcp::ProcessSignals(Endpoint             &aEndpoint,
693
                         otLinkedBuffer       *aPriorHead,
694
                         size_t                aPriorBacklog,
695
                         struct tcplp_signals &aSignals) const
696
0
{
697
0
    VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
698
0
    if (aSignals.conn_established && aEndpoint.mEstablishedCallback != nullptr)
699
0
    {
700
0
        aEndpoint.mEstablishedCallback(&aEndpoint);
701
0
    }
702
703
0
    VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
704
0
    if (aEndpoint.mSendDoneCallback != nullptr)
705
0
    {
706
0
        otLinkedBuffer *curr = aPriorHead;
707
708
0
        OT_ASSERT(curr != nullptr || aSignals.links_popped == 0);
709
710
0
        for (uint32_t i = 0; i != aSignals.links_popped; i++)
711
0
        {
712
0
            otLinkedBuffer *next = curr->mNext;
713
714
0
            VerifyOrExit(i == 0 || (IsInitialized(aEndpoint) && !aEndpoint.IsClosed()));
715
716
0
            curr->mNext = nullptr;
717
0
            aEndpoint.mSendDoneCallback(&aEndpoint, curr);
718
0
            curr = next;
719
0
        }
720
0
    }
721
722
0
    VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
723
0
    if (aEndpoint.mForwardProgressCallback != nullptr)
724
0
    {
725
0
        size_t backlogBytes = aEndpoint.GetBacklogBytes();
726
727
0
        if (aSignals.bytes_acked > 0 || backlogBytes < aPriorBacklog)
728
0
        {
729
0
            aEndpoint.mForwardProgressCallback(&aEndpoint, aEndpoint.GetSendBufferBytes(), backlogBytes);
730
0
            aEndpoint.mPendingCallbacks &= ~kForwardProgressCallbackFlag;
731
0
        }
732
0
    }
733
734
0
    VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
735
0
    if ((aSignals.recvbuf_added || aSignals.rcvd_fin) && aEndpoint.mReceiveAvailableCallback != nullptr)
736
0
    {
737
0
        aEndpoint.mReceiveAvailableCallback(&aEndpoint, cbuf_used_space(&aEndpoint.GetTcb().recvbuf),
738
0
                                            aEndpoint.GetTcb().reass_fin_index != -1,
739
0
                                            cbuf_free_space(&aEndpoint.GetTcb().recvbuf));
740
0
    }
741
742
0
    VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
743
0
    if (aEndpoint.GetTcb().t_state == TCP6S_TIME_WAIT && aEndpoint.mDisconnectedCallback != nullptr)
744
0
    {
745
0
        aEndpoint.mDisconnectedCallback(&aEndpoint, OT_TCP_DISCONNECTED_REASON_TIME_WAIT);
746
0
    }
747
748
0
exit:
749
0
    return;
750
0
}
751
752
Error Tcp::BsdErrorToOtError(int aBsdError)
753
0
{
754
0
    Error error = kErrorFailed;
755
756
0
    switch (aBsdError)
757
0
    {
758
0
    case 0:
759
0
        error = kErrorNone;
760
0
        break;
761
0
    }
762
763
0
    return error;
764
0
}
765
766
bool Tcp::CanBind(const SockAddr &aSockName)
767
0
{
768
0
    uint16_t port    = BigEndian::HostSwap16(aSockName.mPort);
769
0
    bool     allowed = false;
770
771
0
    for (Endpoint &endpoint : mEndpoints)
772
0
    {
773
0
        struct tcpcb *tp = &endpoint.GetTcb();
774
775
0
        if (tp->lport == port)
776
0
        {
777
0
            VerifyOrExit(!aSockName.GetAddress().IsUnspecified());
778
0
            VerifyOrExit(!reinterpret_cast<Address *>(&tp->laddr)->IsUnspecified());
779
0
            VerifyOrExit(memcmp(&endpoint.GetTcb().laddr, &aSockName.mAddress, sizeof(tp->laddr)) != 0);
780
0
        }
781
0
    }
782
783
0
    for (Listener &listener : mListeners)
784
0
    {
785
0
        struct tcpcb_listen *tpl = &listener.GetTcbListen();
786
787
0
        if (tpl->lport == port)
788
0
        {
789
0
            VerifyOrExit(!aSockName.GetAddress().IsUnspecified());
790
0
            VerifyOrExit(!reinterpret_cast<Address *>(&tpl->laddr)->IsUnspecified());
791
0
            VerifyOrExit(memcmp(&tpl->laddr, &aSockName.mAddress, sizeof(tpl->laddr)) != 0);
792
0
        }
793
0
    }
794
795
0
    allowed = true;
796
797
0
exit:
798
0
    return allowed;
799
0
}
800
801
bool Tcp::AutoBind(const SockAddr &aPeer, SockAddr &aToBind, bool aBindAddress, bool aBindPort)
802
0
{
803
0
    bool success;
804
805
0
    if (aBindAddress)
806
0
    {
807
0
        const Address *source;
808
809
0
        source = Get<Ip6>().SelectSourceAddress(aPeer.GetAddress());
810
0
        VerifyOrExit(source != nullptr, success = false);
811
0
        aToBind.SetAddress(*source);
812
0
    }
813
814
0
    if (aBindPort)
815
0
    {
816
        /*
817
         * TODO: Use a less naive algorithm to allocate ephemeral ports. For
818
         * example, see RFC 6056.
819
         */
820
821
0
        for (uint16_t i = 0; i != kDynamicPortMax - kDynamicPortMin + 1; i++)
822
0
        {
823
0
            aToBind.SetPort(mEphemeralPort);
824
825
0
            if (mEphemeralPort == kDynamicPortMax)
826
0
            {
827
0
                mEphemeralPort = kDynamicPortMin;
828
0
            }
829
0
            else
830
0
            {
831
0
                mEphemeralPort++;
832
0
            }
833
834
0
            if (CanBind(aToBind))
835
0
            {
836
0
                ExitNow(success = true);
837
0
            }
838
0
        }
839
840
0
        ExitNow(success = false);
841
0
    }
842
843
0
    success = CanBind(aToBind);
844
845
0
exit:
846
0
    return success;
847
0
}
848
849
void Tcp::HandleTimer(void)
850
0
{
851
0
    TimeMilli now = TimerMilli::GetNow();
852
0
    bool      pendingTimer;
853
0
    TimeMilli earliestPendingTimerExpiry;
854
855
0
    LogDebg("Main TCP timer expired");
856
857
    /*
858
     * The timer callbacks could potentially set/reset/cancel timers.
859
     * Importantly, Endpoint::SetTimer and Endpoint::CancelTimer do not call
860
     * this function to recompute the timer. If they did, we'd have a
861
     * re-entrancy problem, where the callbacks called in this function could
862
     * wind up re-entering this function in a nested call frame.
863
     *
864
     * In general, calling this function from Endpoint::SetTimer and
865
     * Endpoint::CancelTimer could be inefficient, since those functions are
866
     * called multiple times on each received TCP segment. If we want to
867
     * prevent the main timer from firing except when an actual TCP timer
868
     * expires, a better alternative is to reset the main timer in
869
     * HandleMessage, right before processing signals. That would achieve that
870
     * objective while avoiding re-entrancy issues altogether.
871
     */
872
0
restart:
873
0
    pendingTimer               = false;
874
0
    earliestPendingTimerExpiry = now.GetDistantFuture();
875
876
0
    for (Endpoint &endpoint : mEndpoints)
877
0
    {
878
0
        if (endpoint.FirePendingTimers(now, pendingTimer, earliestPendingTimerExpiry))
879
0
        {
880
            /*
881
             * If a non-OpenThread callback is called --- which, in practice,
882
             * happens if the connection times out and the user-defined
883
             * connection lost callback is called --- then we might have to
884
             * start over. The reason is that the user might deinitialize
885
             * endpoints, changing the structure of the linked list. For
886
             * example, if the user deinitializes both this endpoint and the
887
             * next one in the linked list, then we can't continue traversing
888
             * the linked list.
889
             */
890
0
            goto restart;
891
0
        }
892
0
    }
893
894
0
    if (pendingTimer)
895
0
    {
896
        /*
897
         * We need to use Timer::FireAtIfEarlier instead of timer::FireAt
898
         * because one of the earlier callbacks might have set TCP timers,
899
         * in which case `mTimer` would have been set to the earliest of those
900
         * timers.
901
         */
902
0
        mTimer.FireAtIfEarlier(earliestPendingTimerExpiry);
903
0
        LogDebg("Reset main TCP timer to %u ms", static_cast<unsigned int>(earliestPendingTimerExpiry - now));
904
0
    }
905
0
    else
906
0
    {
907
0
        LogDebg("Did not reset main TCP timer");
908
0
    }
909
0
}
910
911
void Tcp::ProcessCallbacks(void)
912
0
{
913
0
    for (Endpoint &endpoint : mEndpoints)
914
0
    {
915
0
        if (endpoint.FirePendingCallbacks())
916
0
        {
917
0
            mTasklet.Post();
918
0
            break;
919
0
        }
920
0
    }
921
0
}
922
923
} // namespace Ip6
924
} // namespace ot
925
926
/*
927
 * Implement TCPlp system stubs declared in tcplp.h.
928
 *
929
 * Because these functions have C linkage, it is important that only one
930
 * definition is given for each function name, regardless of the namespace it
931
 * in. For example, if we give two definitions of tcplp_sys_new_message, we
932
 * will get errors, even if they are in different namespaces. To avoid
933
 * confusion, I've put these functions outside of any namespace.
934
 */
935
936
using namespace ot;
937
using namespace ot::Ip6;
938
939
extern "C" {
940
941
otMessage *tcplp_sys_new_message(otInstance *aInstance)
942
3
{
943
3
    Instance &instance = AsCoreType(aInstance);
944
3
    Message  *message  = instance.Get<ot::Ip6::Ip6>().NewMessage(0);
945
946
3
    if (message)
947
3
    {
948
3
        message->SetLinkSecurityEnabled(true);
949
3
    }
950
951
3
    return message;
952
3
}
953
954
void tcplp_sys_free_message(otInstance *aInstance, otMessage *aMessage)
955
0
{
956
0
    OT_UNUSED_VARIABLE(aInstance);
957
0
    Message &message = AsCoreType(aMessage);
958
0
    message.Free();
959
0
}
960
961
void tcplp_sys_send_message(otInstance *aInstance, otMessage *aMessage, otMessageInfo *aMessageInfo)
962
3
{
963
3
    Instance    &instance = AsCoreType(aInstance);
964
3
    Message     &message  = AsCoreType(aMessage);
965
3
    MessageInfo &info     = AsCoreType(aMessageInfo);
966
967
3
    LogDebg("Sending TCP segment: payload_size = %d", static_cast<int>(message.GetLength()));
968
969
3
    IgnoreError(instance.Get<ot::Ip6::Ip6>().SendDatagram(message, info, kProtoTcp));
970
3
}
971
972
0
uint32_t tcplp_sys_get_ticks(void) { return TimerMilli::GetNow().GetValue(); }
973
974
0
uint32_t tcplp_sys_get_millis(void) { return TimerMilli::GetNow().GetValue(); }
975
976
void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay)
977
0
{
978
0
    Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aTcb);
979
0
    endpoint.SetTimer(aTimerFlag, aDelay);
980
0
}
981
982
void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag)
983
0
{
984
0
    Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aTcb);
985
0
    endpoint.CancelTimer(aTimerFlag);
986
0
}
987
988
struct tcpcb *tcplp_sys_accept_ready(struct tcpcb_listen *aTcbListen, struct in6_addr *aAddr, uint16_t aPort)
989
0
{
990
0
    Tcp::Listener                &listener = Tcp::Listener::FromTcbListen(*aTcbListen);
991
0
    Tcp                          &tcp      = listener.Get<Tcp>();
992
0
    struct tcpcb                 *rv       = (struct tcpcb *)-1;
993
0
    otSockAddr                    addr;
994
0
    otTcpEndpoint                *endpointPtr;
995
0
    otTcpIncomingConnectionAction action;
996
997
0
    VerifyOrExit(listener.mAcceptReadyCallback != nullptr);
998
999
0
    memcpy(&addr.mAddress, aAddr, sizeof(addr.mAddress));
1000
0
    addr.mPort = BigEndian::HostSwap16(aPort);
1001
0
    action     = listener.mAcceptReadyCallback(&listener, &addr, &endpointPtr);
1002
1003
0
    VerifyOrExit(tcp.IsInitialized(listener) && !listener.IsClosed());
1004
1005
0
    switch (action)
1006
0
    {
1007
0
    case OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT:
1008
0
    {
1009
0
        Tcp::Endpoint &endpoint = AsCoreType(endpointPtr);
1010
1011
        /*
1012
         * The documentation says that the user must initialize the
1013
         * endpoint before passing it here, so we do a sanity check to make
1014
         * sure the endpoint is initialized and closed. That check may not
1015
         * be necessary, but we do it anyway.
1016
         */
1017
0
        VerifyOrExit(tcp.IsInitialized(endpoint) && endpoint.IsClosed());
1018
1019
0
        rv = &endpoint.GetTcb();
1020
1021
0
        break;
1022
0
    }
1023
0
    case OT_TCP_INCOMING_CONNECTION_ACTION_DEFER:
1024
0
        rv = nullptr;
1025
0
        break;
1026
0
    case OT_TCP_INCOMING_CONNECTION_ACTION_REFUSE:
1027
0
        rv = (struct tcpcb *)-1;
1028
0
        break;
1029
0
    }
1030
1031
0
exit:
1032
0
    return rv;
1033
0
}
1034
1035
bool tcplp_sys_accepted_connection(struct tcpcb_listen *aTcbListen,
1036
                                   struct tcpcb        *aAccepted,
1037
                                   struct in6_addr     *aAddr,
1038
                                   uint16_t             aPort)
1039
0
{
1040
0
    Tcp::Listener &listener = Tcp::Listener::FromTcbListen(*aTcbListen);
1041
0
    Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aAccepted);
1042
0
    Tcp           &tcp      = endpoint.Get<Tcp>();
1043
0
    bool           accepted = true;
1044
1045
0
    if (listener.mAcceptDoneCallback != nullptr)
1046
0
    {
1047
0
        otSockAddr addr;
1048
1049
0
        memcpy(&addr.mAddress, aAddr, sizeof(addr.mAddress));
1050
0
        addr.mPort = BigEndian::HostSwap16(aPort);
1051
0
        listener.mAcceptDoneCallback(&listener, &endpoint, &addr);
1052
1053
0
        if (!tcp.IsInitialized(endpoint) || endpoint.IsClosed())
1054
0
        {
1055
0
            accepted = false;
1056
0
        }
1057
0
    }
1058
1059
0
    return accepted;
1060
0
}
1061
1062
void tcplp_sys_connection_lost(struct tcpcb *aTcb, uint8_t aErrNum)
1063
0
{
1064
0
    Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aTcb);
1065
1066
0
    if (endpoint.mDisconnectedCallback != nullptr)
1067
0
    {
1068
0
        otTcpDisconnectedReason reason;
1069
1070
0
        switch (aErrNum)
1071
0
        {
1072
0
        case CONN_LOST_NORMAL:
1073
0
            reason = OT_TCP_DISCONNECTED_REASON_NORMAL;
1074
0
            break;
1075
0
        case ECONNREFUSED:
1076
0
            reason = OT_TCP_DISCONNECTED_REASON_REFUSED;
1077
0
            break;
1078
0
        case ETIMEDOUT:
1079
0
            reason = OT_TCP_DISCONNECTED_REASON_TIMED_OUT;
1080
0
            break;
1081
0
        case ECONNRESET:
1082
0
        default:
1083
0
            reason = OT_TCP_DISCONNECTED_REASON_RESET;
1084
0
            break;
1085
0
        }
1086
0
        endpoint.mDisconnectedCallback(&endpoint, reason);
1087
0
    }
1088
0
}
1089
1090
void tcplp_sys_on_state_change(struct tcpcb *aTcb, int aNewState)
1091
0
{
1092
0
    OT_UNUSED_VARIABLE(aTcb);
1093
0
    OT_UNUSED_VARIABLE(aNewState);
1094
1095
    /* Any adaptive changes to the sleep interval would go here. */
1096
0
}
1097
1098
void tcplp_sys_log(const char *aFormat, ...)
1099
0
{
1100
0
    char    buffer[128];
1101
0
    va_list args;
1102
0
    va_start(args, aFormat);
1103
0
    vsnprintf(buffer, sizeof(buffer), aFormat, args);
1104
0
    va_end(args);
1105
1106
0
    LogDebg("%s", buffer);
1107
0
}
1108
1109
void tcplp_sys_panic(const char *aFormat, ...)
1110
0
{
1111
0
    char    buffer[128];
1112
0
    va_list args;
1113
0
    va_start(args, aFormat);
1114
0
    vsnprintf(buffer, sizeof(buffer), aFormat, args);
1115
0
    va_end(args);
1116
1117
0
    LogCrit("%s", buffer);
1118
1119
0
    OT_ASSERT(false);
1120
0
}
1121
1122
bool tcplp_sys_autobind(otInstance       *aInstance,
1123
                        const otSockAddr *aPeer,
1124
                        otSockAddr       *aToBind,
1125
                        bool              aBindAddress,
1126
                        bool              aBindPort)
1127
0
{
1128
0
    Instance &instance = AsCoreType(aInstance);
1129
1130
0
    return instance.Get<Tcp>().AutoBind(*static_cast<const SockAddr *>(aPeer), *static_cast<SockAddr *>(aToBind),
1131
0
                                        aBindAddress, aBindPort);
1132
0
}
1133
1134
uint32_t tcplp_sys_generate_isn()
1135
0
{
1136
0
    uint32_t isn;
1137
0
    IgnoreError(Random::Crypto::Fill(isn));
1138
0
    return isn;
1139
0
}
1140
1141
11
uint16_t tcplp_sys_hostswap16(uint16_t aHostPort) { return BigEndian::HostSwap16(aHostPort); }
1142
1143
14
uint32_t tcplp_sys_hostswap32(uint32_t aHostPort) { return BigEndian::HostSwap32(aHostPort); }
1144
}
1145
1146
#endif // OPENTHREAD_CONFIG_TCP_ENABLE