/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 |