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