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