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