Coverage Report

Created: 2025-05-12 06:47

/src/openthread/src/core/net/tcp6_ext.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (c) 2022, 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 socket extensions.
32
 */
33
34
#include "tcp6_ext.hpp"
35
36
#if OPENTHREAD_CONFIG_TCP_ENABLE
37
38
#include "instance/instance.hpp"
39
40
namespace ot {
41
namespace Ip6 {
42
43
RegisterLogModule("TcpExt");
44
45
void TcpCircularSendBuffer::Initialize(void *aDataBuffer, size_t aCapacity)
46
16
{
47
16
    mDataBuffer = static_cast<uint8_t *>(aDataBuffer);
48
16
    mCapacity   = aCapacity;
49
16
    ForceDiscardAll();
50
16
}
51
52
Error TcpCircularSendBuffer::Write(Tcp::Endpoint &aEndpoint,
53
                                   const void    *aData,
54
                                   size_t         aLength,
55
                                   size_t        &aWritten,
56
                                   uint32_t       aFlags)
57
78
{
58
78
    Error    error     = kErrorNone;
59
78
    size_t   bytesFree = GetFreeSpace();
60
78
    size_t   writeIndex;
61
78
    uint32_t flags = 0;
62
78
    size_t   bytesUntilWrap;
63
64
    /*
65
     * Handle the case where we don't have enough space to accommodate all of the
66
     * provided data.
67
     */
68
78
    aLength = Min(aLength, bytesFree);
69
78
    VerifyOrExit(aLength != 0);
70
71
    /*
72
     * This is a "simplifying" if statement the removes an edge case from the logic
73
     * below. It guarantees that a write to an empty buffer will never wrap.
74
     */
75
78
    if (mCapacityUsed == 0)
76
78
    {
77
78
        mStartIndex = 0;
78
78
    }
79
80
78
    writeIndex = GetIndex(mStartIndex, mCapacityUsed);
81
82
78
    if ((aFlags & OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME) != 0 && aLength < bytesFree)
83
56
    {
84
56
        flags |= OT_TCP_SEND_MORE_TO_COME;
85
56
    }
86
87
78
    bytesUntilWrap = mCapacity - writeIndex;
88
78
    if (aLength <= bytesUntilWrap)
89
78
    {
90
78
        memcpy(&mDataBuffer[writeIndex], aData, aLength);
91
78
        if (writeIndex == 0)
92
78
        {
93
            /*
94
             * mCapacityUsed == 0 corresponds to the case where we're writing
95
             * to an empty buffer. mCapacityUsed != 0 && writeIndex == 0
96
             * corresponds to the case where the buffer is not empty and this is
97
             * writing the first bytes that wrap.
98
             */
99
78
            uint8_t linkIndex;
100
78
            if (mCapacityUsed == 0)
101
78
            {
102
78
                linkIndex = mFirstSendLinkIndex;
103
78
            }
104
0
            else
105
0
            {
106
0
                linkIndex = 1 - mFirstSendLinkIndex;
107
0
            }
108
78
            {
109
78
                otLinkedBuffer &dataSendLink = mSendLinks[linkIndex];
110
111
78
                dataSendLink.mNext   = nullptr;
112
78
                dataSendLink.mData   = &mDataBuffer[writeIndex];
113
78
                dataSendLink.mLength = aLength;
114
115
78
                LogDebg("Appending link %u (points to index %u, length %u)", static_cast<unsigned>(linkIndex),
116
78
                        static_cast<unsigned>(writeIndex), static_cast<unsigned>(aLength));
117
78
                error = aEndpoint.SendByReference(dataSendLink, flags);
118
78
            }
119
78
        }
120
0
        else
121
0
        {
122
0
            LogDebg("Extending tail link by length %u", static_cast<unsigned>(aLength));
123
0
            error = aEndpoint.SendByExtension(aLength, flags);
124
0
        }
125
78
        VerifyOrExit(error == kErrorNone, aLength = 0);
126
78
    }
127
0
    else
128
0
    {
129
0
        const uint8_t *dataIndexable = static_cast<const uint8_t *>(aData);
130
0
        size_t         bytesWrapped  = aLength - bytesUntilWrap;
131
132
0
        memcpy(&mDataBuffer[writeIndex], &dataIndexable[0], bytesUntilWrap);
133
0
        memcpy(&mDataBuffer[0], &dataIndexable[bytesUntilWrap], bytesWrapped);
134
135
        /*
136
         * Because of the "simplifying" if statement at the top, we don't
137
         * have to worry about starting from an empty buffer in this case.
138
         */
139
0
        LogDebg("Extending tail link by length %u (wrapping)", static_cast<unsigned>(bytesUntilWrap));
140
0
        error = aEndpoint.SendByExtension(bytesUntilWrap, flags | OT_TCP_SEND_MORE_TO_COME);
141
0
        VerifyOrExit(error == kErrorNone, aLength = 0);
142
143
0
        {
144
0
            otLinkedBuffer &wrappedDataSendLink = mSendLinks[1 - mFirstSendLinkIndex];
145
146
0
            wrappedDataSendLink.mNext   = nullptr;
147
0
            wrappedDataSendLink.mData   = &mDataBuffer[0];
148
0
            wrappedDataSendLink.mLength = bytesWrapped;
149
150
0
            LogDebg("Appending link %u (wrapping)", static_cast<unsigned>(1 - mFirstSendLinkIndex));
151
0
            error = aEndpoint.SendByReference(wrappedDataSendLink, flags);
152
0
            VerifyOrExit(error == kErrorNone, aLength = bytesUntilWrap);
153
0
        }
154
0
    }
155
156
78
exit:
157
78
    mCapacityUsed += aLength;
158
78
    aWritten = aLength;
159
78
    return error;
160
78
}
161
162
void TcpCircularSendBuffer::HandleForwardProgress(size_t aInSendBuffer)
163
0
{
164
0
    size_t bytesRemoved;
165
0
    size_t bytesUntilWrap;
166
167
0
    OT_ASSERT(aInSendBuffer <= mCapacityUsed);
168
0
    LogDebg("Forward progress: %u bytes in send buffer\n", static_cast<unsigned>(aInSendBuffer));
169
0
    bytesRemoved   = mCapacityUsed - aInSendBuffer;
170
0
    bytesUntilWrap = mCapacity - mStartIndex;
171
172
0
    if (bytesRemoved < bytesUntilWrap)
173
0
    {
174
0
        mStartIndex += bytesRemoved;
175
0
    }
176
0
    else
177
0
    {
178
0
        mStartIndex = bytesRemoved - bytesUntilWrap;
179
        /* The otLinkedBuffer for the pre-wrap data is now empty. */
180
0
        LogDebg("Pre-wrap linked buffer now empty: switching first link index from %u to %u\n",
181
0
                static_cast<unsigned>(mFirstSendLinkIndex), static_cast<unsigned>(1 - mFirstSendLinkIndex));
182
0
        mFirstSendLinkIndex = 1 - mFirstSendLinkIndex;
183
0
    }
184
0
    mCapacityUsed = aInSendBuffer;
185
0
}
186
187
156
size_t TcpCircularSendBuffer::GetFreeSpace(void) const { return mCapacity - mCapacityUsed; }
188
189
void TcpCircularSendBuffer::ForceDiscardAll(void)
190
54
{
191
54
    mStartIndex         = 0;
192
54
    mCapacityUsed       = 0;
193
54
    mFirstSendLinkIndex = 0;
194
54
}
195
196
38
Error TcpCircularSendBuffer::Deinitialize(void) { return (mCapacityUsed != 0) ? kErrorBusy : kErrorNone; }
197
198
size_t TcpCircularSendBuffer::GetIndex(size_t aStart, size_t aOffsetFromStart) const
199
78
{
200
78
    size_t bytesUntilWrap;
201
78
    size_t index;
202
203
78
    OT_ASSERT(aStart < mCapacity);
204
78
    bytesUntilWrap = mCapacity - aStart;
205
78
    if (aOffsetFromStart < bytesUntilWrap)
206
78
    {
207
78
        index = aStart + aOffsetFromStart;
208
78
    }
209
0
    else
210
0
    {
211
0
        index = aOffsetFromStart - bytesUntilWrap;
212
0
    }
213
214
78
    return index;
215
78
}
216
217
} // namespace Ip6
218
} // namespace ot
219
220
#endif // OPENTHREAD_CONFIG_TCP_ENABLE