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