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