/src/openthread/src/cli/cli_tcp.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 a TCP CLI tool. |
32 | | */ |
33 | | |
34 | | #include "openthread-core-config.h" |
35 | | |
36 | | #include "cli_config.h" |
37 | | |
38 | | #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE |
39 | | |
40 | | #include "cli_tcp.hpp" |
41 | | |
42 | | #include <openthread/nat64.h> |
43 | | #include <openthread/tcp.h> |
44 | | |
45 | | #include "cli/cli.hpp" |
46 | | #include "common/encoding.hpp" |
47 | | #include "common/timer.hpp" |
48 | | |
49 | | #if OPENTHREAD_CONFIG_TLS_ENABLE |
50 | | #include <mbedtls/debug.h> |
51 | | #include <mbedtls/ecjpake.h> |
52 | | #include "crypto/mbedtls.hpp" |
53 | | #endif |
54 | | |
55 | | namespace ot { |
56 | | namespace Cli { |
57 | | |
58 | | #if OPENTHREAD_CONFIG_TLS_ENABLE |
59 | | const int TcpExample::sCipherSuites[] = {MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8, |
60 | | MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, 0}; |
61 | | #endif |
62 | | |
63 | | TcpExample::TcpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer) |
64 | 7.46k | : Utils(aInstance, aOutputImplementer) |
65 | 7.46k | , mInitialized(false) |
66 | 7.46k | , mEndpointConnected(false) |
67 | 7.46k | , mEndpointConnectedFastOpen(false) |
68 | 7.46k | , mSendBusy(false) |
69 | 7.46k | , mUseCircularSendBuffer(true) |
70 | 7.46k | , mUseTls(false) |
71 | 7.46k | , mTlsHandshakeComplete(false) |
72 | 7.46k | , mBenchmarkBytesTotal(0) |
73 | 7.46k | , mBenchmarkBytesUnsent(0) |
74 | 7.46k | , mBenchmarkTimeUsed(0) |
75 | 7.46k | { |
76 | 7.46k | mEndpointAndCircularSendBuffer.mEndpoint = &mEndpoint; |
77 | 7.46k | mEndpointAndCircularSendBuffer.mSendBuffer = &mSendBuffer; |
78 | 7.46k | } |
79 | | |
80 | | #if OPENTHREAD_CONFIG_TLS_ENABLE |
81 | | void TcpExample::MbedTlsDebugOutput(void *ctx, int level, const char *file, int line, const char *str) |
82 | 0 | { |
83 | 0 | TcpExample &tcpExample = *static_cast<TcpExample *>(ctx); |
84 | |
|
85 | 0 | tcpExample.OutputLine("%s:%d:%d: %s", file, line, level, str); |
86 | 0 | } |
87 | | #endif |
88 | | |
89 | | /** |
90 | | * @cli tcp init |
91 | | * @code |
92 | | * tcp init tls |
93 | | * Done |
94 | | * @endcode |
95 | | * @cparam tcp init [@ca{mode}] [@ca{size}] |
96 | | * * The `mode` has three possible values: |
97 | | * * `tls`: Specifies that the TCP connection between two nodes should also |
98 | | * use the TLS protocol on top of TCP. When two nodes communicate over TCP, |
99 | | * both nodes must either use TLS or neither node should use TLS because |
100 | | * a non-TLS endpoint cannot communicate with a TLS endpoint. |
101 | | * * `linked` or `circular`: Either one of these options means that TLS |
102 | | * is not to be used, and the specified buffering type should be used for TCP |
103 | | * buffering. The behavior of `linked` and `circular` is identical. Examine the code |
104 | | * for the differences between these two buffering types. |
105 | | * Two endpoints of a TCP connection are not required to use the same buffering type. |
106 | | * * The `size` parameter sets the size of the receive buffer to associate with the |
107 | | * example TCP endpoint. If left unspecified, the maximum size is used. The |
108 | | * maximum size is set in `OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE`. |
109 | | * @par |
110 | | * Initializes the example TCP listener and the example TCP endpoint provided |
111 | | * by the `tcp` CLI. |
112 | | * @sa otTcpListenerInitialize |
113 | | * @sa otTcpEndpointInitialize |
114 | | */ |
115 | | template <> otError TcpExample::Process<Cmd("init")>(Arg aArgs[]) |
116 | 54 | { |
117 | 54 | otError error = OT_ERROR_NONE; |
118 | 54 | size_t receiveBufferSize; |
119 | | |
120 | 54 | VerifyOrExit(!mInitialized, error = OT_ERROR_ALREADY); |
121 | | |
122 | 54 | if (aArgs[0].IsEmpty()) |
123 | 1 | { |
124 | 1 | mUseCircularSendBuffer = true; |
125 | 1 | mUseTls = false; |
126 | 1 | receiveBufferSize = sizeof(mReceiveBufferBytes); |
127 | 1 | } |
128 | 53 | else |
129 | 53 | { |
130 | 53 | if (aArgs[0] == "linked") |
131 | 1 | { |
132 | 1 | mUseCircularSendBuffer = false; |
133 | 1 | mUseTls = false; |
134 | 1 | } |
135 | 52 | else if (aArgs[0] == "circular") |
136 | 1 | { |
137 | 1 | mUseCircularSendBuffer = true; |
138 | 1 | mUseTls = false; |
139 | 1 | } |
140 | 51 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
141 | 51 | else if (aArgs[0] == "tls") |
142 | 50 | { |
143 | 50 | mUseCircularSendBuffer = true; |
144 | 50 | mUseTls = true; |
145 | | |
146 | 50 | mbedtls_x509_crt_init(&mSrvCert); |
147 | 50 | mbedtls_pk_init(&mPKey); |
148 | | |
149 | 50 | mbedtls_ssl_init(&mSslContext); |
150 | 50 | mbedtls_ssl_config_init(&mSslConfig); |
151 | 50 | mbedtls_ssl_conf_rng(&mSslConfig, Crypto::MbedTls::CryptoSecurePrng, nullptr); |
152 | 50 | mbedtls_ssl_conf_authmode(&mSslConfig, MBEDTLS_SSL_VERIFY_NONE); |
153 | 50 | mbedtls_ssl_conf_ciphersuites(&mSslConfig, sCipherSuites); |
154 | | |
155 | 50 | #if (MBEDTLS_VERSION_NUMBER >= 0x03020000) |
156 | 50 | mbedtls_ssl_conf_min_tls_version(&mSslConfig, MBEDTLS_SSL_VERSION_TLS1_2); |
157 | 50 | mbedtls_ssl_conf_max_tls_version(&mSslConfig, MBEDTLS_SSL_VERSION_TLS1_2); |
158 | | #else |
159 | | mbedtls_ssl_conf_min_version(&mSslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); |
160 | | mbedtls_ssl_conf_max_version(&mSslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); |
161 | | #endif |
162 | | |
163 | 50 | #if (MBEDTLS_VERSION_NUMBER >= 0x03000000) |
164 | 50 | #include "crypto/mbedtls.hpp" |
165 | 50 | int rv = mbedtls_pk_parse_key(&mPKey, reinterpret_cast<const unsigned char *>(sSrvKey), sSrvKeyLength, |
166 | 50 | nullptr, 0, Crypto::MbedTls::CryptoSecurePrng, nullptr); |
167 | | #else |
168 | | int rv = mbedtls_pk_parse_key(&mPKey, reinterpret_cast<const unsigned char *>(sSrvKey), sSrvKeyLength, |
169 | | nullptr, 0); |
170 | | #endif |
171 | 50 | if (rv != 0) |
172 | 34 | { |
173 | 34 | OutputLine("mbedtls_pk_parse_key returned %d", rv); |
174 | 34 | } |
175 | | |
176 | 50 | rv = mbedtls_x509_crt_parse(&mSrvCert, reinterpret_cast<const unsigned char *>(sSrvPem), sSrvPemLength); |
177 | 50 | if (rv != 0) |
178 | 37 | { |
179 | 37 | OutputLine("mbedtls_x509_crt_parse (1) returned %d", rv); |
180 | 37 | } |
181 | 50 | rv = mbedtls_x509_crt_parse(&mSrvCert, reinterpret_cast<const unsigned char *>(sCasPem), sCasPemLength); |
182 | 50 | if (rv != 0) |
183 | 38 | { |
184 | 38 | OutputLine("mbedtls_x509_crt_parse (2) returned %d", rv); |
185 | 38 | } |
186 | 50 | rv = mbedtls_ssl_setup(&mSslContext, &mSslConfig); |
187 | 50 | if (rv != 0) |
188 | 39 | { |
189 | 39 | OutputLine("mbedtls_ssl_setup returned %d", rv); |
190 | 39 | } |
191 | 50 | } |
192 | 1 | #endif // OPENTHREAD_CONFIG_TLS_ENABLE |
193 | 1 | else |
194 | 1 | { |
195 | 1 | ExitNow(error = OT_ERROR_INVALID_ARGS); |
196 | 1 | } |
197 | | |
198 | 52 | if (aArgs[1].IsEmpty()) |
199 | 7 | { |
200 | 7 | receiveBufferSize = sizeof(mReceiveBufferBytes); |
201 | 7 | } |
202 | 45 | else |
203 | 45 | { |
204 | 45 | uint32_t windowSize; |
205 | | |
206 | 45 | SuccessOrExit(error = aArgs[1].ParseAsUint32(windowSize)); |
207 | | |
208 | 42 | receiveBufferSize = windowSize + ((windowSize + 7) >> 3); |
209 | 42 | VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBufferBytes) && receiveBufferSize != 0, |
210 | 42 | error = OT_ERROR_INVALID_ARGS); |
211 | 42 | } |
212 | 52 | } |
213 | | |
214 | 16 | otTcpCircularSendBufferInitialize(&mSendBuffer, mSendBufferBytes, sizeof(mSendBufferBytes)); |
215 | | |
216 | 16 | { |
217 | 16 | otTcpEndpointInitializeArgs endpointArgs; |
218 | | |
219 | 16 | ClearAllBytes(endpointArgs); |
220 | 16 | endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback; |
221 | | |
222 | 16 | if (mUseCircularSendBuffer) |
223 | 15 | { |
224 | 15 | endpointArgs.mForwardProgressCallback = HandleTcpForwardProgressCallback; |
225 | 15 | } |
226 | 1 | else |
227 | 1 | { |
228 | 1 | endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback; |
229 | 1 | } |
230 | | |
231 | 16 | endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback; |
232 | 16 | endpointArgs.mDisconnectedCallback = HandleTcpDisconnectedCallback; |
233 | 16 | endpointArgs.mContext = this; |
234 | 16 | endpointArgs.mReceiveBuffer = mReceiveBufferBytes; |
235 | 16 | endpointArgs.mReceiveBufferSize = receiveBufferSize; |
236 | | |
237 | 16 | SuccessOrExit(error = otTcpEndpointInitialize(GetInstancePtr(), &mEndpoint, &endpointArgs)); |
238 | 16 | } |
239 | | |
240 | 16 | { |
241 | 16 | otTcpListenerInitializeArgs listenerArgs; |
242 | | |
243 | 16 | ClearAllBytes(listenerArgs); |
244 | 16 | listenerArgs.mAcceptReadyCallback = HandleTcpAcceptReadyCallback; |
245 | 16 | listenerArgs.mAcceptDoneCallback = HandleTcpAcceptDoneCallback; |
246 | 16 | listenerArgs.mContext = this; |
247 | | |
248 | 16 | error = otTcpListenerInitialize(GetInstancePtr(), &mListener, &listenerArgs); |
249 | | |
250 | 16 | if (error != OT_ERROR_NONE) |
251 | 0 | { |
252 | 0 | IgnoreReturnValue(otTcpEndpointDeinitialize(&mEndpoint)); |
253 | 0 | ExitNow(); |
254 | 0 | } |
255 | 16 | } |
256 | | |
257 | 16 | mInitialized = true; |
258 | | |
259 | 54 | exit: |
260 | 54 | if (error != OT_ERROR_NONE) |
261 | 38 | { |
262 | 38 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
263 | 38 | if (mUseTls) |
264 | 37 | { |
265 | 37 | mbedtls_ssl_config_free(&mSslConfig); |
266 | 37 | mbedtls_ssl_free(&mSslContext); |
267 | | |
268 | 37 | mbedtls_pk_free(&mPKey); |
269 | 37 | mbedtls_x509_crt_free(&mSrvCert); |
270 | 37 | } |
271 | 38 | #endif // OPENTHREAD_CONFIG_TLS_ENABLE |
272 | | |
273 | 38 | otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); |
274 | 38 | OT_UNUSED_VARIABLE(otTcpCircularSendBufferDeinitialize(&mSendBuffer)); |
275 | 38 | } |
276 | | |
277 | 54 | return error; |
278 | 16 | } |
279 | | |
280 | | /** |
281 | | * @cli tcp deinit |
282 | | * @code |
283 | | * tcp deinit |
284 | | * Done |
285 | | * @endcode |
286 | | * @par api_copy |
287 | | * #otTcpEndpointDeinitialize |
288 | | */ |
289 | | template <> otError TcpExample::Process<Cmd("deinit")>(Arg aArgs[]) |
290 | 2 | { |
291 | 2 | otError error = OT_ERROR_NONE; |
292 | 2 | otError endpointError; |
293 | 2 | otError bufferError; |
294 | 2 | otError listenerError; |
295 | | |
296 | 2 | VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
297 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
298 | | |
299 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
300 | 0 | if (mUseTls) |
301 | 0 | { |
302 | 0 | mbedtls_ssl_config_free(&mSslConfig); |
303 | 0 | mbedtls_ssl_free(&mSslContext); |
304 | |
|
305 | 0 | mbedtls_pk_free(&mPKey); |
306 | 0 | mbedtls_x509_crt_free(&mSrvCert); |
307 | |
|
308 | 0 | mUseTls = false; |
309 | 0 | } |
310 | 0 | #endif |
311 | |
|
312 | 0 | endpointError = otTcpEndpointDeinitialize(&mEndpoint); |
313 | 0 | mSendBusy = false; |
314 | |
|
315 | 0 | otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); |
316 | 0 | bufferError = otTcpCircularSendBufferDeinitialize(&mSendBuffer); |
317 | |
|
318 | 0 | listenerError = otTcpListenerDeinitialize(&mListener); |
319 | 0 | mInitialized = false; |
320 | |
|
321 | 0 | SuccessOrExit(error = endpointError); |
322 | 0 | SuccessOrExit(error = bufferError); |
323 | 0 | SuccessOrExit(error = listenerError); |
324 | | |
325 | 2 | exit: |
326 | 2 | return error; |
327 | 0 | } |
328 | | |
329 | | /** |
330 | | * @cli tcp bind |
331 | | * @code |
332 | | * tcp bind :: 30000 |
333 | | * Done |
334 | | * @endcode |
335 | | * @cparam tcp bind @ca{ip} @ca{port} |
336 | | * * `ip`: IPv6 address to bind to. If you wish to have the TCP/IPv6 stack assign |
337 | | * the binding IPv6 address, use the unspecified IPv6 address: `::`. |
338 | | * * `port`: TCP port number to bind to. |
339 | | * @par |
340 | | * Associates an IPv6 address and a port to the example TCP endpoint provided by |
341 | | * the `tcp` CLI. Associating the TCP endpoint to an IPv6 |
342 | | * address and port is referred to as "naming the TCP endpoint." This binds the |
343 | | * endpoint for communication. @moreinfo{@tcp}. |
344 | | * @sa otTcpBind |
345 | | */ |
346 | | template <> otError TcpExample::Process<Cmd("bind")>(Arg aArgs[]) |
347 | 1 | { |
348 | 1 | otError error; |
349 | 1 | otSockAddr sockaddr; |
350 | | |
351 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
352 | | |
353 | 0 | SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); |
354 | 0 | SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); |
355 | 0 | VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
356 | | |
357 | 0 | error = otTcpBind(&mEndpoint, &sockaddr); |
358 | |
|
359 | 1 | exit: |
360 | 1 | return error; |
361 | 0 | } |
362 | | |
363 | | /** |
364 | | * @cli tcp connect |
365 | | * @code |
366 | | * tcp connect fe80:0:0:0:a8df:580a:860:ffa4 30000 |
367 | | * Done |
368 | | * TCP: Connection established |
369 | | * @endcode |
370 | | * @code |
371 | | * tcp connect 172.17.0.1 1234 |
372 | | * Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 |
373 | | * Done |
374 | | * @endcode |
375 | | * @cparam tcp connect @ca{ip} @ca{port} [@ca{fastopen}] |
376 | | * * `ip`: IP address of the peer The address can be an IPv4 address, |
377 | | * which gets synthesized to an IPv6 address using the preferred |
378 | | * NAT64 prefix from the network data. The command returns `InvalidState` |
379 | | * when the preferred NAT64 prefix is unavailable. |
380 | | * * `port`: TCP port number of the peer. |
381 | | * * `fastopen`: This parameter is optional. If set to `fast`, TCP Fast Open is enabled |
382 | | * for this connection. Otherwise, if this parameter is set to `slow` or not used, |
383 | | * TCP Fast Open is disabled. |
384 | | * @par |
385 | | * Establishes a connection with the specified peer. |
386 | | * @par |
387 | | * If the connection establishment is successful, the resulting TCP connection |
388 | | * is associated with the example TCP endpoint. @moreinfo{@tcp}. |
389 | | * @sa otTcpConnect |
390 | | */ |
391 | | template <> otError TcpExample::Process<Cmd("connect")>(Arg aArgs[]) |
392 | 1 | { |
393 | 1 | otError error; |
394 | 1 | otSockAddr sockaddr; |
395 | 1 | bool nat64Synth; |
396 | 1 | uint32_t flags; |
397 | | |
398 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
399 | | |
400 | 0 | SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth)); |
401 | | |
402 | 0 | if (nat64Synth) |
403 | 0 | { |
404 | 0 | OutputFormat("Connecting to synthesized IPv6 address: "); |
405 | 0 | OutputIp6AddressLine(sockaddr.mAddress); |
406 | 0 | } |
407 | |
|
408 | 0 | SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); |
409 | | |
410 | 0 | if (aArgs[2].IsEmpty()) |
411 | 0 | { |
412 | 0 | flags = OT_TCP_CONNECT_NO_FAST_OPEN; |
413 | 0 | } |
414 | 0 | else |
415 | 0 | { |
416 | 0 | if (aArgs[2] == "slow") |
417 | 0 | { |
418 | 0 | flags = OT_TCP_CONNECT_NO_FAST_OPEN; |
419 | 0 | } |
420 | 0 | else if (aArgs[2] == "fast") |
421 | 0 | { |
422 | 0 | flags = 0; |
423 | 0 | } |
424 | 0 | else |
425 | 0 | { |
426 | 0 | ExitNow(error = OT_ERROR_INVALID_ARGS); |
427 | 0 | } |
428 | | |
429 | 0 | VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
430 | 0 | } |
431 | | |
432 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
433 | 0 | if (mUseTls) |
434 | 0 | { |
435 | 0 | int rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, |
436 | 0 | MBEDTLS_SSL_PRESET_DEFAULT); |
437 | 0 | if (rv != 0) |
438 | 0 | { |
439 | 0 | OutputLine("mbedtls_ssl_config_defaults returned %d", rv); |
440 | 0 | } |
441 | 0 | } |
442 | 0 | #endif |
443 | |
|
444 | 0 | SuccessOrExit(error = otTcpConnect(&mEndpoint, &sockaddr, flags)); |
445 | 0 | mEndpointConnected = true; |
446 | 0 | mEndpointConnectedFastOpen = ((flags & OT_TCP_CONNECT_NO_FAST_OPEN) == 0); |
447 | |
|
448 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
449 | 0 | if (mUseTls && mEndpointConnectedFastOpen) |
450 | 0 | { |
451 | 0 | PrepareTlsHandshake(); |
452 | 0 | ContinueTlsHandshake(); |
453 | 0 | } |
454 | 0 | #endif |
455 | |
|
456 | 1 | exit: |
457 | 1 | return error; |
458 | 0 | } |
459 | | |
460 | | /** |
461 | | * @cli tcp send |
462 | | * @code |
463 | | * tcp send hello |
464 | | * Done |
465 | | * @endcode |
466 | | * @code |
467 | | * tcp send -x 68656c6c6f |
468 | | * Done |
469 | | * @endcode |
470 | | * @cparam tcp send [@ca{type}] @ca{message} |
471 | | * The `message` parameter contains the message you want to send to the |
472 | | * remote TCP endpoint. |
473 | | * If `type` is `-x`, then |
474 | | * binary data in hexadecimal representation is given in the `message` parameter. |
475 | | * @par |
476 | | * Sends data over the TCP connection associated with the example TCP endpoint |
477 | | * that is provided with the `tcp` CLI. @moreinfo{@tcp}. |
478 | | */ |
479 | | template <> otError TcpExample::Process<Cmd("send")>(Arg aArgs[]) |
480 | 1 | { |
481 | 1 | static constexpr uint16_t kBufferSizeForHexData = 128; |
482 | | |
483 | 1 | otError error; |
484 | 1 | uint16_t dataLen; |
485 | 1 | uint8_t *data; |
486 | 1 | uint8_t buf[kBufferSizeForHexData]; |
487 | | |
488 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
489 | 0 | VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY); |
490 | 0 | VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
491 | | |
492 | 0 | if (aArgs[0] == "-x" && !aArgs[1].IsEmpty()) |
493 | 0 | { |
494 | | // Binary hex data payload |
495 | 0 | dataLen = sizeof(buf); |
496 | 0 | data = &buf[0]; |
497 | 0 | SuccessOrExit(error = aArgs[1].ParseAsHexString(dataLen, buf)); |
498 | 0 | } |
499 | 0 | else |
500 | 0 | { |
501 | 0 | VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
502 | 0 | dataLen = aArgs[0].GetLength(); |
503 | 0 | data = reinterpret_cast<uint8_t *>(aArgs[0].GetCString()); |
504 | 0 | } |
505 | | |
506 | 0 | if (mUseCircularSendBuffer) |
507 | 0 | { |
508 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
509 | 0 | if (mUseTls) |
510 | 0 | { |
511 | 0 | int rv = mbedtls_ssl_write(&mSslContext, data, dataLen); |
512 | |
|
513 | 0 | if (rv < 0 && rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ) |
514 | 0 | { |
515 | 0 | ExitNow(error = OT_ERROR_FAILED); |
516 | 0 | } |
517 | | |
518 | 0 | error = OT_ERROR_NONE; |
519 | 0 | } |
520 | 0 | else |
521 | 0 | #endif |
522 | 0 | { |
523 | 0 | size_t written; |
524 | |
|
525 | 0 | SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, data, dataLen, &written, 0)); |
526 | 0 | } |
527 | 0 | } |
528 | 0 | else |
529 | 0 | { |
530 | 0 | VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); |
531 | | |
532 | 0 | mSendLink.mNext = nullptr; |
533 | 0 | mSendLink.mData = mSendBufferBytes; |
534 | 0 | mSendLink.mLength = OT_MIN(dataLen, sizeof(mSendBufferBytes)); |
535 | 0 | memcpy(mSendBufferBytes, data, mSendLink.mLength); |
536 | |
|
537 | 0 | SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0)); |
538 | 0 | mSendBusy = true; |
539 | 0 | } |
540 | | |
541 | 1 | exit: |
542 | 1 | return error; |
543 | 0 | } |
544 | | |
545 | | template <> otError TcpExample::Process<Cmd("benchmark")>(Arg aArgs[]) |
546 | 83 | { |
547 | 83 | otError error = OT_ERROR_NONE; |
548 | | |
549 | | /** |
550 | | * @cli tcp benchmark result |
551 | | * @code |
552 | | * tcp benchmark result |
553 | | * TCP Benchmark Status: Ongoing |
554 | | * Done |
555 | | * @endcode |
556 | | * @code |
557 | | * tcp benchmark result |
558 | | * TCP Benchmark Status: Completed |
559 | | * TCP Benchmark Complete: Transferred 73728 bytes in 7056 milliseconds |
560 | | * TCP Goodput: 83.592 kb/s |
561 | | * @endcode |
562 | | * @par |
563 | | * Shows the latest result of the TCP benchmark test. Possible status values: |
564 | | * * Ongoing |
565 | | * * Completed |
566 | | * * Untested |
567 | | * @par |
568 | | * This command is primarily intended for creating scripts that automate |
569 | | * the TCP benchmark test. |
570 | | */ |
571 | 83 | if (aArgs[0] == "result") |
572 | 1 | { |
573 | 1 | OutputFormat("TCP Benchmark Status: "); |
574 | | |
575 | 1 | if (mBenchmarkBytesTotal != 0) |
576 | 0 | { |
577 | 0 | OutputLine("Ongoing"); |
578 | 0 | } |
579 | 1 | else if (mBenchmarkTimeUsed != 0) |
580 | 0 | { |
581 | 0 | OutputLine("Completed"); |
582 | 0 | OutputBenchmarkResult(); |
583 | 0 | } |
584 | 1 | else |
585 | 1 | { |
586 | 1 | OutputLine("Untested"); |
587 | 1 | } |
588 | 1 | } |
589 | | /** |
590 | | * @cli tcp benchmark run |
591 | | * @code |
592 | | * tcp benchmark run |
593 | | * Done |
594 | | * TCP Benchmark Complete: Transferred 73728 bytes in 7233 milliseconds |
595 | | * TCP Goodput: 81.546 kb/s |
596 | | * @endcode |
597 | | * @cparam tcp benchmark run [@ca{size}] |
598 | | * Use the `size` parameter to specify the number of bytes to send |
599 | | * for the benchmark. If you do not use the `size` parameter, the default |
600 | | * value (`OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE`) is used. |
601 | | * @par |
602 | | * Transfers the specified number of bytes using the TCP connection |
603 | | * currently associated with the example TCP endpoint provided by the `tcp` CLI. |
604 | | * @note You must establish a TCP connection before you run this command. |
605 | | */ |
606 | 82 | else if (aArgs[0] == "run") |
607 | 81 | { |
608 | 81 | VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); |
609 | 81 | VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY); |
610 | | |
611 | 81 | if (aArgs[1].IsEmpty()) |
612 | 1 | { |
613 | 1 | mBenchmarkBytesTotal = OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE; |
614 | 1 | } |
615 | 80 | else |
616 | 80 | { |
617 | 80 | SuccessOrExit(error = aArgs[1].ParseAsUint32(mBenchmarkBytesTotal)); |
618 | 79 | VerifyOrExit(mBenchmarkBytesTotal != 0, error = OT_ERROR_INVALID_ARGS); |
619 | 79 | } |
620 | | |
621 | 79 | VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
622 | | |
623 | 78 | mBenchmarkStart = TimerMilli::GetNow(); |
624 | 78 | mBenchmarkBytesUnsent = mBenchmarkBytesTotal; |
625 | | |
626 | 78 | if (mUseCircularSendBuffer) |
627 | 78 | { |
628 | 78 | SuccessOrExit(error = ContinueBenchmarkCircularSend()); |
629 | 78 | } |
630 | 0 | else |
631 | 0 | { |
632 | 0 | uint32_t benchmarkLinksLeft = |
633 | 0 | (mBenchmarkBytesTotal + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes); |
634 | 0 | uint32_t toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), benchmarkLinksLeft); |
635 | | |
636 | | /* We could also point the linked buffers directly to sBenchmarkData. */ |
637 | 0 | memset(mSendBufferBytes, 'a', sizeof(mSendBufferBytes)); |
638 | |
|
639 | 0 | for (uint32_t i = 0; i != toSendOut; i++) |
640 | 0 | { |
641 | 0 | mBenchmarkLinks[i].mNext = nullptr; |
642 | 0 | mBenchmarkLinks[i].mData = mSendBufferBytes; |
643 | 0 | mBenchmarkLinks[i].mLength = sizeof(mSendBufferBytes); |
644 | |
|
645 | 0 | if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBufferBytes) != 0) |
646 | 0 | { |
647 | 0 | mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBufferBytes); |
648 | 0 | } |
649 | |
|
650 | 0 | error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i], |
651 | 0 | i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME); |
652 | 0 | VerifyOrExit(error == OT_ERROR_NONE, mBenchmarkBytesTotal = 0); |
653 | 0 | } |
654 | 0 | } |
655 | 78 | } |
656 | 1 | else |
657 | 1 | { |
658 | 1 | error = OT_ERROR_INVALID_ARGS; |
659 | 1 | } |
660 | | |
661 | 83 | exit: |
662 | 83 | return error; |
663 | 83 | } |
664 | | |
665 | | /** |
666 | | * @cli tcp sendend |
667 | | * @code |
668 | | * tcp sendend |
669 | | * Done |
670 | | * @endcode |
671 | | * @par |
672 | | * Sends the "end of stream" signal over the TCP connection |
673 | | * associated with the example TCP endpoint provided by the `tcp` CLI. This |
674 | | * alerts the peer that it will not receive any more data over this TCP connection. |
675 | | * @sa otTcpSendEndOfStream |
676 | | */ |
677 | | template <> otError TcpExample::Process<Cmd("sendend")>(Arg aArgs[]) |
678 | 1 | { |
679 | 1 | otError error; |
680 | | |
681 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
682 | 0 | VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
683 | | |
684 | 0 | error = otTcpSendEndOfStream(&mEndpoint); |
685 | |
|
686 | 1 | exit: |
687 | 1 | return error; |
688 | 0 | } |
689 | | |
690 | | /** |
691 | | * @cli tcp abort |
692 | | * @code |
693 | | * tcp abort |
694 | | * TCP: Connection reset |
695 | | * Done |
696 | | * @endcode |
697 | | * @par |
698 | | * Unceremoniously ends the TCP connection associated with the |
699 | | * example TCP endpoint, transitioning the TCP endpoint to the closed state. |
700 | | * @sa otTcpAbort |
701 | | */ |
702 | | template <> otError TcpExample::Process<Cmd("abort")>(Arg aArgs[]) |
703 | 2 | { |
704 | 2 | otError error; |
705 | | |
706 | 2 | VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
707 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
708 | | |
709 | 0 | SuccessOrExit(error = otTcpAbort(&mEndpoint)); |
710 | 0 | mEndpointConnected = false; |
711 | 0 | mEndpointConnectedFastOpen = false; |
712 | |
|
713 | 2 | exit: |
714 | 2 | return error; |
715 | 0 | } |
716 | | |
717 | | /** |
718 | | * @cli tcp listen |
719 | | * @code |
720 | | * tcp listen :: 30000 |
721 | | * Done |
722 | | * @endcode |
723 | | * @cparam tcp listen @ca{ip} @ca{port} |
724 | | * The following parameters are required: |
725 | | * * `ip`: IPv6 address or the unspecified IPv6 address (`::`) of the example |
726 | | * TCP listener provided by the `tcp` CLI. |
727 | | * * `port`: TCP port of the example TCP listener. |
728 | | * If no TCP connection is associated with the example TCP endpoint, then any |
729 | | * incoming connections matching the specified IPv6 address and port are accepted |
730 | | * and are associated with the example TCP endpoint. |
731 | | * @par |
732 | | * Uses the example TCP listener to listen for incoming connections on the |
733 | | * specified IPv6 address and port. @moreinfo{@tcp}. |
734 | | * @sa otTcpListen |
735 | | */ |
736 | | template <> otError TcpExample::Process<Cmd("listen")>(Arg aArgs[]) |
737 | 1 | { |
738 | 1 | otError error; |
739 | 1 | otSockAddr sockaddr; |
740 | | |
741 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
742 | | |
743 | 0 | SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); |
744 | 0 | SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); |
745 | 0 | VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
746 | | |
747 | 0 | SuccessOrExit(error = otTcpStopListening(&mListener)); |
748 | 0 | error = otTcpListen(&mListener, &sockaddr); |
749 | |
|
750 | 1 | exit: |
751 | 1 | return error; |
752 | 0 | } |
753 | | |
754 | | /** |
755 | | * @cli tcp stoplistening |
756 | | * @code |
757 | | * tcp stoplistening |
758 | | * Done |
759 | | * @endcode |
760 | | * @par |
761 | | * Instructs the example TCP listener to stop listening for incoming TCP connections. |
762 | | * @sa otTcpStopListening |
763 | | */ |
764 | | template <> otError TcpExample::Process<Cmd("stoplistening")>(Arg aArgs[]) |
765 | 2 | { |
766 | 2 | otError error; |
767 | | |
768 | 2 | VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
769 | 1 | VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); |
770 | | |
771 | 0 | error = otTcpStopListening(&mListener); |
772 | |
|
773 | 2 | exit: |
774 | 2 | return error; |
775 | 0 | } |
776 | | |
777 | | otError TcpExample::Process(Arg aArgs[]) |
778 | 152 | { |
779 | 152 | #define CmdEntry(aCommandString) \ |
780 | 1.52k | { \ |
781 | 1.52k | aCommandString, &TcpExample::Process<Cmd(aCommandString)> \ |
782 | 1.52k | } |
783 | | |
784 | 152 | static constexpr Command kCommands[] = { |
785 | 152 | CmdEntry("abort"), CmdEntry("benchmark"), CmdEntry("bind"), CmdEntry("connect"), CmdEntry("deinit"), |
786 | 152 | CmdEntry("init"), CmdEntry("listen"), CmdEntry("send"), CmdEntry("sendend"), CmdEntry("stoplistening"), |
787 | 152 | }; |
788 | | |
789 | 152 | static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); |
790 | | |
791 | 152 | otError error = OT_ERROR_INVALID_COMMAND; |
792 | 152 | const Command *command; |
793 | | |
794 | 152 | if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) |
795 | 2 | { |
796 | 2 | OutputCommandTable(kCommands); |
797 | 2 | ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE); |
798 | 2 | } |
799 | | |
800 | 150 | command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); |
801 | 150 | VerifyOrExit(command != nullptr); |
802 | | |
803 | 148 | error = (this->*command->mHandler)(aArgs + 1); |
804 | | |
805 | 152 | exit: |
806 | 152 | return error; |
807 | 148 | } |
808 | | |
809 | | void TcpExample::HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint) |
810 | 0 | { |
811 | 0 | static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpEstablished(aEndpoint); |
812 | 0 | } |
813 | | |
814 | | void TcpExample::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData) |
815 | 0 | { |
816 | 0 | static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData); |
817 | 0 | } |
818 | | |
819 | | void TcpExample::HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog) |
820 | 0 | { |
821 | 0 | static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint)) |
822 | 0 | ->HandleTcpForwardProgress(aEndpoint, aInSendBuffer, aBacklog); |
823 | 0 | } |
824 | | |
825 | | void TcpExample::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, |
826 | | size_t aBytesAvailable, |
827 | | bool aEndOfStream, |
828 | | size_t aBytesRemaining) |
829 | 0 | { |
830 | 0 | static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint)) |
831 | 0 | ->HandleTcpReceiveAvailable(aEndpoint, aBytesAvailable, aEndOfStream, aBytesRemaining); |
832 | 0 | } |
833 | | |
834 | | void TcpExample::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason) |
835 | 0 | { |
836 | 0 | static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason); |
837 | 0 | } |
838 | | |
839 | | otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReadyCallback(otTcpListener *aListener, |
840 | | const otSockAddr *aPeer, |
841 | | otTcpEndpoint **aAcceptInto) |
842 | 0 | { |
843 | 0 | return static_cast<TcpExample *>(otTcpListenerGetContext(aListener)) |
844 | 0 | ->HandleTcpAcceptReady(aListener, aPeer, aAcceptInto); |
845 | 0 | } |
846 | | |
847 | | void TcpExample::HandleTcpAcceptDoneCallback(otTcpListener *aListener, |
848 | | otTcpEndpoint *aEndpoint, |
849 | | const otSockAddr *aPeer) |
850 | 0 | { |
851 | 0 | static_cast<TcpExample *>(otTcpListenerGetContext(aListener))->HandleTcpAcceptDone(aListener, aEndpoint, aPeer); |
852 | 0 | } |
853 | | |
854 | | void TcpExample::HandleTcpEstablished(otTcpEndpoint *aEndpoint) |
855 | 0 | { |
856 | 0 | OT_UNUSED_VARIABLE(aEndpoint); |
857 | 0 | OutputLine("TCP: Connection established"); |
858 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
859 | 0 | if (mUseTls && !mEndpointConnectedFastOpen) |
860 | 0 | { |
861 | 0 | PrepareTlsHandshake(); |
862 | 0 | ContinueTlsHandshake(); |
863 | 0 | } |
864 | 0 | #endif |
865 | 0 | } |
866 | | |
867 | | void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData) |
868 | 0 | { |
869 | 0 | OT_UNUSED_VARIABLE(aEndpoint); |
870 | 0 | OT_ASSERT(!mUseCircularSendBuffer); // this callback is not used when using the circular send buffer |
871 | | |
872 | 0 | if (mBenchmarkBytesTotal == 0) |
873 | 0 | { |
874 | | // If the benchmark encountered an error, we might end up here. So, |
875 | | // tolerate some benchmark links finishing in this case. |
876 | 0 | if (aData == &mSendLink) |
877 | 0 | { |
878 | 0 | OT_ASSERT(mSendBusy); |
879 | 0 | mSendBusy = false; |
880 | 0 | } |
881 | 0 | } |
882 | 0 | else |
883 | 0 | { |
884 | 0 | OT_ASSERT(aData != &mSendLink); |
885 | 0 | OT_ASSERT(mBenchmarkBytesUnsent >= aData->mLength); |
886 | | |
887 | 0 | mBenchmarkBytesUnsent -= aData->mLength; // could be less than sizeof(mSendBufferBytes) for the first link |
888 | |
|
889 | 0 | if (mBenchmarkBytesUnsent >= OT_ARRAY_LENGTH(mBenchmarkLinks) * sizeof(mSendBufferBytes)) |
890 | 0 | { |
891 | 0 | aData->mLength = sizeof(mSendBufferBytes); |
892 | |
|
893 | 0 | if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE) |
894 | 0 | { |
895 | 0 | OutputLine("TCP Benchmark Failed"); |
896 | 0 | mBenchmarkBytesTotal = 0; |
897 | 0 | } |
898 | 0 | } |
899 | 0 | else if (mBenchmarkBytesUnsent == 0) |
900 | 0 | { |
901 | 0 | CompleteBenchmark(); |
902 | 0 | } |
903 | 0 | } |
904 | 0 | } |
905 | | |
906 | | void TcpExample::HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog) |
907 | 0 | { |
908 | 0 | OT_UNUSED_VARIABLE(aEndpoint); |
909 | 0 | OT_UNUSED_VARIABLE(aBacklog); |
910 | 0 | OT_ASSERT(mUseCircularSendBuffer); // this callback is only used when using the circular send buffer |
911 | | |
912 | 0 | otTcpCircularSendBufferHandleForwardProgress(&mSendBuffer, aInSendBuffer); |
913 | |
|
914 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
915 | 0 | if (mUseTls) |
916 | 0 | { |
917 | 0 | ContinueTlsHandshake(); |
918 | 0 | } |
919 | 0 | #endif |
920 | | |
921 | | /* Handle case where we're in a benchmark. */ |
922 | 0 | if (mBenchmarkBytesTotal != 0) |
923 | 0 | { |
924 | 0 | if (mBenchmarkBytesUnsent != 0) |
925 | 0 | { |
926 | | /* Continue sending out data if there's data we haven't sent. */ |
927 | 0 | IgnoreError(ContinueBenchmarkCircularSend()); |
928 | 0 | } |
929 | 0 | else if (aInSendBuffer == 0) |
930 | 0 | { |
931 | | /* Handle case where all data is sent out and the send buffer has drained. */ |
932 | 0 | CompleteBenchmark(); |
933 | 0 | } |
934 | 0 | } |
935 | 0 | } |
936 | | |
937 | | void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, |
938 | | size_t aBytesAvailable, |
939 | | bool aEndOfStream, |
940 | | size_t aBytesRemaining) |
941 | 0 | { |
942 | 0 | OT_UNUSED_VARIABLE(aBytesRemaining); |
943 | 0 | OT_ASSERT(aEndpoint == &mEndpoint); |
944 | | |
945 | | /* If we get data before the handshake completes, then this is a TFO connection. */ |
946 | 0 | if (!mEndpointConnected) |
947 | 0 | { |
948 | 0 | mEndpointConnected = true; |
949 | 0 | mEndpointConnectedFastOpen = true; |
950 | |
|
951 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
952 | 0 | if (mUseTls) |
953 | 0 | { |
954 | 0 | PrepareTlsHandshake(); |
955 | 0 | } |
956 | 0 | #endif |
957 | 0 | } |
958 | |
|
959 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
960 | 0 | if (mUseTls && ContinueTlsHandshake()) |
961 | 0 | { |
962 | 0 | ExitNow(); |
963 | 0 | } |
964 | 0 | #endif |
965 | | |
966 | 0 | if ((mTlsHandshakeComplete || !mUseTls) && aBytesAvailable > 0) |
967 | 0 | { |
968 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
969 | 0 | if (mUseTls) |
970 | 0 | { |
971 | 0 | uint8_t buffer[500]; |
972 | |
|
973 | 0 | for (;;) |
974 | 0 | { |
975 | 0 | int rv = mbedtls_ssl_read(&mSslContext, buffer, sizeof(buffer)); |
976 | |
|
977 | 0 | if (rv < 0) |
978 | 0 | { |
979 | 0 | if (rv == MBEDTLS_ERR_SSL_WANT_READ) |
980 | 0 | { |
981 | 0 | break; |
982 | 0 | } |
983 | | |
984 | 0 | OutputLine("TLS receive failure: %d", rv); |
985 | 0 | } |
986 | 0 | else |
987 | 0 | { |
988 | 0 | OutputLine("TLS: Received %u bytes: %.*s", static_cast<unsigned>(rv), rv, |
989 | 0 | reinterpret_cast<const char *>(buffer)); |
990 | 0 | } |
991 | 0 | } |
992 | 0 | OutputLine("(TCP: Received %u bytes)", static_cast<unsigned>(aBytesAvailable)); |
993 | 0 | } |
994 | 0 | else |
995 | 0 | #endif // OPENTHREAD_CONFIG_TLS_ENABLE |
996 | 0 | { |
997 | 0 | const otLinkedBuffer *data; |
998 | 0 | size_t totalReceived = 0; |
999 | |
|
1000 | 0 | IgnoreError(otTcpReceiveByReference(aEndpoint, &data)); |
1001 | |
|
1002 | 0 | for (; data != nullptr; data = data->mNext) |
1003 | 0 | { |
1004 | 0 | OutputLine("TCP: Received %u bytes: %.*s", static_cast<unsigned>(data->mLength), |
1005 | 0 | static_cast<unsigned>(data->mLength), reinterpret_cast<const char *>(data->mData)); |
1006 | 0 | totalReceived += data->mLength; |
1007 | 0 | } |
1008 | |
|
1009 | 0 | OT_ASSERT(aBytesAvailable == totalReceived); |
1010 | 0 | IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0)); |
1011 | 0 | } |
1012 | 0 | } |
1013 | | |
1014 | 0 | if (aEndOfStream) |
1015 | 0 | { |
1016 | 0 | OutputLine("TCP: Reached end of stream"); |
1017 | 0 | } |
1018 | |
|
1019 | 0 | ExitNow(); |
1020 | | |
1021 | 0 | exit: |
1022 | 0 | return; |
1023 | 0 | } |
1024 | | |
1025 | | void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason) |
1026 | 0 | { |
1027 | 0 | static const char *const kReasonStrings[] = { |
1028 | 0 | "Disconnected", // (0) OT_TCP_DISCONNECTED_REASON_NORMAL |
1029 | 0 | "Connection refused", // (1) OT_TCP_DISCONNECTED_REASON_REFUSED |
1030 | 0 | "Connection reset", // (2) OT_TCP_DISCONNECTED_REASON_RESET |
1031 | 0 | "Entered TIME-WAIT state", // (3) OT_TCP_DISCONNECTED_REASON_TIME_WAIT |
1032 | 0 | "Connection timed out", // (4) OT_TCP_DISCONNECTED_REASON_TIMED_OUT |
1033 | 0 | }; |
1034 | |
|
1035 | 0 | OT_UNUSED_VARIABLE(aEndpoint); |
1036 | |
|
1037 | 0 | static_assert(0 == OT_TCP_DISCONNECTED_REASON_NORMAL, "OT_TCP_DISCONNECTED_REASON_NORMAL value is incorrect"); |
1038 | 0 | static_assert(1 == OT_TCP_DISCONNECTED_REASON_REFUSED, "OT_TCP_DISCONNECTED_REASON_REFUSED value is incorrect"); |
1039 | 0 | static_assert(2 == OT_TCP_DISCONNECTED_REASON_RESET, "OT_TCP_DISCONNECTED_REASON_RESET value is incorrect"); |
1040 | 0 | static_assert(3 == OT_TCP_DISCONNECTED_REASON_TIME_WAIT, "OT_TCP_DISCONNECTED_REASON_TIME_WAIT value is incorrect"); |
1041 | 0 | static_assert(4 == OT_TCP_DISCONNECTED_REASON_TIMED_OUT, "OT_TCP_DISCONNECTED_REASON_TIMED_OUT value is incorrect"); |
1042 | |
|
1043 | 0 | OutputLine("TCP: %s", Stringify(aReason, kReasonStrings)); |
1044 | |
|
1045 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
1046 | 0 | if (mUseTls) |
1047 | 0 | { |
1048 | 0 | mbedtls_ssl_session_reset(&mSslContext); |
1049 | 0 | } |
1050 | 0 | #endif |
1051 | | |
1052 | | // We set this to false even for the TIME-WAIT state, so that we can reuse |
1053 | | // the active socket if an incoming connection comes in instead of waiting |
1054 | | // for the 2MSL timeout. |
1055 | 0 | mEndpointConnected = false; |
1056 | 0 | mEndpointConnectedFastOpen = false; |
1057 | 0 | mSendBusy = false; |
1058 | | |
1059 | | // Mark the benchmark as inactive if the connection was disconnected. |
1060 | 0 | mBenchmarkBytesTotal = 0; |
1061 | 0 | mBenchmarkBytesUnsent = 0; |
1062 | |
|
1063 | 0 | otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); |
1064 | 0 | } |
1065 | | |
1066 | | otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener *aListener, |
1067 | | const otSockAddr *aPeer, |
1068 | | otTcpEndpoint **aAcceptInto) |
1069 | 0 | { |
1070 | 0 | otTcpIncomingConnectionAction action; |
1071 | |
|
1072 | 0 | OT_UNUSED_VARIABLE(aListener); |
1073 | |
|
1074 | 0 | if (mEndpointConnected) |
1075 | 0 | { |
1076 | 0 | OutputFormat("TCP: Ignoring incoming connection request from "); |
1077 | 0 | OutputSockAddr(*aPeer); |
1078 | 0 | OutputLine(" (active socket is busy)"); |
1079 | |
|
1080 | 0 | ExitNow(action = OT_TCP_INCOMING_CONNECTION_ACTION_DEFER); |
1081 | 0 | } |
1082 | | |
1083 | 0 | *aAcceptInto = &mEndpoint; |
1084 | 0 | action = OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT; |
1085 | |
|
1086 | 0 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
1087 | | /* |
1088 | | * Natural to wait until the AcceptDone callback but with TFO we could get data before that |
1089 | | * so it doesn't make sense to wait until then. |
1090 | | */ |
1091 | 0 | if (mUseTls) |
1092 | 0 | { |
1093 | 0 | int rv; |
1094 | |
|
1095 | 0 | rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, |
1096 | 0 | MBEDTLS_SSL_PRESET_DEFAULT); |
1097 | 0 | if (rv != 0) |
1098 | 0 | { |
1099 | 0 | OutputLine("mbedtls_ssl_config_defaults returned %d", rv); |
1100 | 0 | } |
1101 | |
|
1102 | 0 | mbedtls_ssl_conf_ca_chain(&mSslConfig, mSrvCert.next, nullptr); |
1103 | 0 | rv = mbedtls_ssl_conf_own_cert(&mSslConfig, &mSrvCert, &mPKey); |
1104 | |
|
1105 | 0 | if (rv != 0) |
1106 | 0 | { |
1107 | 0 | OutputLine("mbedtls_ssl_conf_own_cert returned %d", rv); |
1108 | 0 | } |
1109 | 0 | } |
1110 | 0 | #endif // OPENTHREAD_CONFIG_TLS_ENABLE |
1111 | |
|
1112 | 0 | exit: |
1113 | 0 | return action; |
1114 | 0 | } |
1115 | | |
1116 | | void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer) |
1117 | 0 | { |
1118 | 0 | OT_UNUSED_VARIABLE(aListener); |
1119 | 0 | OT_UNUSED_VARIABLE(aEndpoint); |
1120 | |
|
1121 | 0 | mEndpointConnected = true; |
1122 | 0 | OutputFormat("Accepted connection from "); |
1123 | 0 | OutputSockAddrLine(*aPeer); |
1124 | 0 | } |
1125 | | |
1126 | | otError TcpExample::ContinueBenchmarkCircularSend(void) |
1127 | 78 | { |
1128 | 78 | otError error = OT_ERROR_NONE; |
1129 | 78 | size_t freeSpace; |
1130 | | |
1131 | 78 | while (mBenchmarkBytesUnsent != 0 && (freeSpace = otTcpCircularSendBufferGetFreeSpace(&mSendBuffer)) != 0) |
1132 | 78 | { |
1133 | 78 | size_t toSendThisIteration = OT_MIN(mBenchmarkBytesUnsent, sBenchmarkDataLength); |
1134 | 78 | uint32_t flag = (toSendThisIteration < freeSpace && toSendThisIteration < mBenchmarkBytesUnsent) |
1135 | 78 | ? OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME |
1136 | 78 | : 0; |
1137 | 78 | size_t written = 0; |
1138 | | |
1139 | 78 | #if OPENTHREAD_CONFIG_TLS_ENABLE |
1140 | 78 | if (mUseTls) |
1141 | 0 | { |
1142 | 0 | int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<const unsigned char *>(sBenchmarkData), |
1143 | 0 | toSendThisIteration); |
1144 | |
|
1145 | 0 | if (rv > 0) |
1146 | 0 | { |
1147 | 0 | written = static_cast<size_t>(rv); |
1148 | 0 | OT_ASSERT(written <= mBenchmarkBytesUnsent); |
1149 | 0 | } |
1150 | 0 | else if (rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ) |
1151 | 0 | { |
1152 | 0 | ExitNow(error = OT_ERROR_FAILED); |
1153 | 0 | } |
1154 | | |
1155 | 0 | error = OT_ERROR_NONE; |
1156 | 0 | } |
1157 | 78 | else |
1158 | 78 | #endif |
1159 | 78 | { |
1160 | 78 | SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, sBenchmarkData, |
1161 | 78 | toSendThisIteration, &written, flag)); |
1162 | 78 | } |
1163 | 0 | mBenchmarkBytesUnsent -= written; |
1164 | 0 | } |
1165 | | |
1166 | 78 | exit: |
1167 | 78 | if (error != OT_ERROR_NONE) |
1168 | 78 | { |
1169 | 78 | OutputLine("TCP Benchmark Failed"); |
1170 | 78 | mBenchmarkBytesTotal = 0; |
1171 | 78 | mBenchmarkBytesUnsent = 0; |
1172 | 78 | } |
1173 | | |
1174 | 78 | return error; |
1175 | 78 | } |
1176 | | |
1177 | | void TcpExample::OutputBenchmarkResult(void) |
1178 | 0 | { |
1179 | 0 | uint32_t thousandTimesGoodput = |
1180 | 0 | (1000 * (mBenchmarkLastBytesTotal << 3) + (mBenchmarkTimeUsed >> 1)) / mBenchmarkTimeUsed; |
1181 | |
|
1182 | 0 | OutputLine("TCP Benchmark Complete: Transferred %lu bytes in %lu milliseconds", ToUlong(mBenchmarkLastBytesTotal), |
1183 | 0 | ToUlong(mBenchmarkTimeUsed)); |
1184 | 0 | OutputLine("TCP Goodput: %lu.%03u kb/s", ToUlong(thousandTimesGoodput / 1000), |
1185 | 0 | static_cast<uint16_t>(thousandTimesGoodput % 1000)); |
1186 | 0 | } |
1187 | | |
1188 | | void TcpExample::CompleteBenchmark(void) |
1189 | 0 | { |
1190 | 0 | mBenchmarkTimeUsed = TimerMilli::GetNow() - mBenchmarkStart; |
1191 | 0 | mBenchmarkLastBytesTotal = mBenchmarkBytesTotal; |
1192 | |
|
1193 | 0 | OutputBenchmarkResult(); |
1194 | |
|
1195 | 0 | mBenchmarkBytesTotal = 0; |
1196 | 0 | } |
1197 | | |
1198 | | #if OPENTHREAD_CONFIG_TLS_ENABLE |
1199 | | void TcpExample::PrepareTlsHandshake(void) |
1200 | 0 | { |
1201 | 0 | int rv; |
1202 | |
|
1203 | 0 | rv = mbedtls_ssl_set_hostname(&mSslContext, "localhost"); |
1204 | |
|
1205 | 0 | if (rv != 0) |
1206 | 0 | { |
1207 | 0 | OutputLine("mbedtls_ssl_set_hostname returned %d", rv); |
1208 | 0 | } |
1209 | |
|
1210 | 0 | rv = mbedtls_ssl_set_hs_ecjpake_password(&mSslContext, reinterpret_cast<const unsigned char *>(sEcjpakePassword), |
1211 | 0 | sEcjpakePasswordLength); |
1212 | 0 | if (rv != 0) |
1213 | 0 | { |
1214 | 0 | OutputLine("mbedtls_ssl_set_hs_ecjpake_password returned %d", rv); |
1215 | 0 | } |
1216 | |
|
1217 | 0 | mbedtls_ssl_set_bio(&mSslContext, &mEndpointAndCircularSendBuffer, otTcpMbedTlsSslSendCallback, |
1218 | 0 | otTcpMbedTlsSslRecvCallback, nullptr); |
1219 | 0 | mTlsHandshakeComplete = false; |
1220 | 0 | } |
1221 | | |
1222 | | bool TcpExample::ContinueTlsHandshake(void) |
1223 | 0 | { |
1224 | 0 | bool wasNotAlreadyDone = false; |
1225 | 0 | int rv; |
1226 | |
|
1227 | 0 | if (!mTlsHandshakeComplete) |
1228 | 0 | { |
1229 | 0 | rv = mbedtls_ssl_handshake(&mSslContext); |
1230 | |
|
1231 | 0 | if (rv == 0) |
1232 | 0 | { |
1233 | 0 | OutputLine("TLS Handshake Complete"); |
1234 | 0 | mTlsHandshakeComplete = true; |
1235 | 0 | } |
1236 | 0 | else if (rv != MBEDTLS_ERR_SSL_WANT_READ && rv != MBEDTLS_ERR_SSL_WANT_WRITE) |
1237 | 0 | { |
1238 | 0 | OutputLine("TLS Handshake Failed: %d", rv); |
1239 | 0 | } |
1240 | |
|
1241 | 0 | wasNotAlreadyDone = true; |
1242 | 0 | } |
1243 | |
|
1244 | 0 | return wasNotAlreadyDone; |
1245 | 0 | } |
1246 | | #endif // OPENTHREAD_CONFIG_TLS_ENABLE |
1247 | | |
1248 | | } // namespace Cli |
1249 | | } // namespace ot |
1250 | | |
1251 | | #endif // OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE |