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