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