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
Initialize(void * aDataBuffer,size_t aCapacity)45 void TcpCircularSendBuffer::Initialize(void *aDataBuffer, size_t aCapacity)
46 {
47 mDataBuffer = static_cast<uint8_t *>(aDataBuffer);
48 mCapacity = aCapacity;
49 ForceDiscardAll();
50 }
51
Write(Tcp::Endpoint & aEndpoint,const void * aData,size_t aLength,size_t & aWritten,uint32_t aFlags)52 Error TcpCircularSendBuffer::Write(Tcp::Endpoint &aEndpoint,
53 const void *aData,
54 size_t aLength,
55 size_t &aWritten,
56 uint32_t aFlags)
57 {
58 Error error = kErrorNone;
59 size_t bytesFree = GetFreeSpace();
60 size_t writeIndex;
61 uint32_t flags = 0;
62 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 aLength = Min(aLength, bytesFree);
69 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 if (mCapacityUsed == 0)
76 {
77 mStartIndex = 0;
78 }
79
80 writeIndex = GetIndex(mStartIndex, mCapacityUsed);
81
82 if ((aFlags & OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME) != 0 && aLength < bytesFree)
83 {
84 flags |= OT_TCP_SEND_MORE_TO_COME;
85 }
86
87 bytesUntilWrap = mCapacity - writeIndex;
88 if (aLength <= bytesUntilWrap)
89 {
90 memcpy(&mDataBuffer[writeIndex], aData, aLength);
91 if (writeIndex == 0)
92 {
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 uint8_t linkIndex;
100 if (mCapacityUsed == 0)
101 {
102 linkIndex = mFirstSendLinkIndex;
103 }
104 else
105 {
106 linkIndex = 1 - mFirstSendLinkIndex;
107 }
108 {
109 otLinkedBuffer &dataSendLink = mSendLinks[linkIndex];
110
111 dataSendLink.mNext = nullptr;
112 dataSendLink.mData = &mDataBuffer[writeIndex];
113 dataSendLink.mLength = aLength;
114
115 LogDebg("Appending link %u (points to index %u, length %u)", static_cast<unsigned>(linkIndex),
116 static_cast<unsigned>(writeIndex), static_cast<unsigned>(aLength));
117 error = aEndpoint.SendByReference(dataSendLink, flags);
118 }
119 }
120 else
121 {
122 LogDebg("Extending tail link by length %u", static_cast<unsigned>(aLength));
123 error = aEndpoint.SendByExtension(aLength, flags);
124 }
125 VerifyOrExit(error == kErrorNone, aLength = 0);
126 }
127 else
128 {
129 const uint8_t *dataIndexable = static_cast<const uint8_t *>(aData);
130 size_t bytesWrapped = aLength - bytesUntilWrap;
131
132 memcpy(&mDataBuffer[writeIndex], &dataIndexable[0], bytesUntilWrap);
133 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 LogDebg("Extending tail link by length %u (wrapping)", static_cast<unsigned>(bytesUntilWrap));
140 error = aEndpoint.SendByExtension(bytesUntilWrap, flags | OT_TCP_SEND_MORE_TO_COME);
141 VerifyOrExit(error == kErrorNone, aLength = 0);
142
143 {
144 otLinkedBuffer &wrappedDataSendLink = mSendLinks[1 - mFirstSendLinkIndex];
145
146 wrappedDataSendLink.mNext = nullptr;
147 wrappedDataSendLink.mData = &mDataBuffer[0];
148 wrappedDataSendLink.mLength = bytesWrapped;
149
150 LogDebg("Appending link %u (wrapping)", static_cast<unsigned>(1 - mFirstSendLinkIndex));
151 error = aEndpoint.SendByReference(wrappedDataSendLink, flags);
152 VerifyOrExit(error == kErrorNone, aLength = bytesUntilWrap);
153 }
154 }
155
156 exit:
157 mCapacityUsed += aLength;
158 aWritten = aLength;
159 return error;
160 }
161
HandleForwardProgress(size_t aInSendBuffer)162 void TcpCircularSendBuffer::HandleForwardProgress(size_t aInSendBuffer)
163 {
164 size_t bytesRemoved;
165 size_t bytesUntilWrap;
166
167 OT_ASSERT(aInSendBuffer <= mCapacityUsed);
168 LogDebg("Forward progress: %u bytes in send buffer\n", static_cast<unsigned>(aInSendBuffer));
169 bytesRemoved = mCapacityUsed - aInSendBuffer;
170 bytesUntilWrap = mCapacity - mStartIndex;
171
172 if (bytesRemoved < bytesUntilWrap)
173 {
174 mStartIndex += bytesRemoved;
175 }
176 else
177 {
178 mStartIndex = bytesRemoved - bytesUntilWrap;
179 /* The otLinkedBuffer for the pre-wrap data is now empty. */
180 LogDebg("Pre-wrap linked buffer now empty: switching first link index from %u to %u\n",
181 static_cast<unsigned>(mFirstSendLinkIndex), static_cast<unsigned>(1 - mFirstSendLinkIndex));
182 mFirstSendLinkIndex = 1 - mFirstSendLinkIndex;
183 }
184 mCapacityUsed = aInSendBuffer;
185 }
186
GetFreeSpace(void) const187 size_t TcpCircularSendBuffer::GetFreeSpace(void) const { return mCapacity - mCapacityUsed; }
188
ForceDiscardAll(void)189 void TcpCircularSendBuffer::ForceDiscardAll(void)
190 {
191 mStartIndex = 0;
192 mCapacityUsed = 0;
193 mFirstSendLinkIndex = 0;
194 }
195
Deinitialize(void)196 Error TcpCircularSendBuffer::Deinitialize(void) { return (mCapacityUsed != 0) ? kErrorBusy : kErrorNone; }
197
GetIndex(size_t aStart,size_t aOffsetFromStart) const198 size_t TcpCircularSendBuffer::GetIndex(size_t aStart, size_t aOffsetFromStart) const
199 {
200 size_t bytesUntilWrap;
201 size_t index;
202
203 OT_ASSERT(aStart < mCapacity);
204 bytesUntilWrap = mCapacity - aStart;
205 if (aOffsetFromStart < bytesUntilWrap)
206 {
207 index = aStart + aOffsetFromStart;
208 }
209 else
210 {
211 index = aOffsetFromStart - bytesUntilWrap;
212 }
213
214 return index;
215 }
216
217 } // namespace Ip6
218 } // namespace ot
219
220 #endif // OPENTHREAD_CONFIG_TCP_ENABLE
221