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