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/tcp.h>
43 
44 #include "cli/cli.hpp"
45 #include "common/encoding.hpp"
46 #include "common/timer.hpp"
47 
48 namespace ot {
49 namespace Cli {
50 
51 constexpr TcpExample::Command TcpExample::sCommands[];
52 
TcpExample(Interpreter & aInterpreter)53 TcpExample::TcpExample(Interpreter &aInterpreter)
54     : mInterpreter(aInterpreter)
55     , mInitialized(false)
56     , mEndpointConnected(false)
57     , mSendBusy(false)
58     , mBenchmarkBytesTotal(0)
59     , mBenchmarkLinksLeft(0)
60 {
61 }
62 
ProcessHelp(Arg aArgs[])63 otError TcpExample::ProcessHelp(Arg aArgs[])
64 {
65     OT_UNUSED_VARIABLE(aArgs);
66 
67     for (const Command &command : sCommands)
68     {
69         mInterpreter.OutputLine(command.mName);
70     }
71 
72     return OT_ERROR_NONE;
73 }
74 
ProcessInit(Arg aArgs[])75 otError TcpExample::ProcessInit(Arg aArgs[])
76 {
77     otError error = OT_ERROR_NONE;
78     size_t  receiveBufferSize;
79 
80     VerifyOrExit(!mInitialized, error = OT_ERROR_ALREADY);
81 
82     if (aArgs[0].IsEmpty())
83     {
84         receiveBufferSize = sizeof(mReceiveBuffer);
85     }
86     else
87     {
88         uint32_t windowSize;
89 
90         SuccessOrExit(error = aArgs[0].ParseAsUint32(windowSize));
91         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
92 
93         receiveBufferSize = windowSize + ((windowSize + 7) >> 3);
94         VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBuffer) && receiveBufferSize != 0,
95                      error = OT_ERROR_INVALID_ARGS);
96     }
97 
98     {
99         otTcpEndpointInitializeArgs endpointArgs;
100 
101         memset(&endpointArgs, 0x00, sizeof(endpointArgs));
102         endpointArgs.mEstablishedCallback      = HandleTcpEstablishedCallback;
103         endpointArgs.mSendDoneCallback         = HandleTcpSendDoneCallback;
104         endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback;
105         endpointArgs.mDisconnectedCallback     = HandleTcpDisconnectedCallback;
106         endpointArgs.mContext                  = this;
107         endpointArgs.mReceiveBuffer            = mReceiveBuffer;
108         endpointArgs.mReceiveBufferSize        = receiveBufferSize;
109 
110         SuccessOrExit(error = otTcpEndpointInitialize(mInterpreter.mInstance, &mEndpoint, &endpointArgs));
111     }
112 
113     {
114         otTcpListenerInitializeArgs listenerArgs;
115 
116         memset(&listenerArgs, 0x00, sizeof(listenerArgs));
117         listenerArgs.mAcceptReadyCallback = HandleTcpAcceptReadyCallback;
118         listenerArgs.mAcceptDoneCallback  = HandleTcpAcceptDoneCallback;
119         listenerArgs.mContext             = this;
120 
121         error = otTcpListenerInitialize(mInterpreter.mInstance, &mListener, &listenerArgs);
122         if (error != OT_ERROR_NONE)
123         {
124             IgnoreReturnValue(otTcpEndpointDeinitialize(&mEndpoint));
125             ExitNow();
126         }
127     }
128 
129     mInitialized = true;
130 
131 exit:
132     return error;
133 }
134 
ProcessDeinit(Arg aArgs[])135 otError TcpExample::ProcessDeinit(Arg aArgs[])
136 {
137     otError error = OT_ERROR_NONE;
138     otError endpointError;
139     otError listenerError;
140 
141     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
142     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
143 
144     endpointError = otTcpEndpointDeinitialize(&mEndpoint);
145     mSendBusy     = false;
146 
147     listenerError = otTcpListenerDeinitialize(&mListener);
148     mInitialized  = false;
149 
150     SuccessOrExit(error = endpointError);
151     SuccessOrExit(error = listenerError);
152 
153 exit:
154     return error;
155 }
156 
ProcessBind(Arg aArgs[])157 otError TcpExample::ProcessBind(Arg aArgs[])
158 {
159     otError    error;
160     otSockAddr sockaddr;
161 
162     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
163 
164     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
165     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
166     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
167 
168     error = otTcpBind(&mEndpoint, &sockaddr);
169 
170 exit:
171     return error;
172 }
173 
ProcessConnect(Arg aArgs[])174 otError TcpExample::ProcessConnect(Arg aArgs[])
175 {
176     otError    error;
177     otSockAddr sockaddr;
178 
179     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
180 
181     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
182     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
183     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
184 
185     SuccessOrExit(error = otTcpConnect(&mEndpoint, &sockaddr, OT_TCP_CONNECT_NO_FAST_OPEN));
186     mEndpointConnected = false;
187 
188 exit:
189     return error;
190 }
191 
ProcessSend(Arg aArgs[])192 otError TcpExample::ProcessSend(Arg aArgs[])
193 {
194     otError error;
195 
196     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
197     VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY);
198     VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY);
199 
200     mSendLink.mNext = nullptr;
201     mSendLink.mData = mSendBuffer;
202     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
203     mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBuffer));
204     memcpy(mSendBuffer, aArgs[0].GetCString(), mSendLink.mLength);
205     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
206 
207     SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0));
208     mSendBusy = true;
209 
210 exit:
211     return error;
212 }
213 
ProcessBenchmark(Arg aArgs[])214 otError TcpExample::ProcessBenchmark(Arg aArgs[])
215 {
216     otError  error;
217     uint32_t toSendOut;
218 
219     VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY);
220     VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY);
221 
222     if (aArgs[0].IsEmpty())
223     {
224         mBenchmarkBytesTotal = OPENTHREAD_CONFIG_CLI_TCP_DEFAULT_BENCHMARK_SIZE;
225     }
226     else
227     {
228         SuccessOrExit(error = aArgs[0].ParseAsUint32(mBenchmarkBytesTotal));
229         VerifyOrExit(mBenchmarkBytesTotal != 0, error = OT_ERROR_INVALID_ARGS);
230     }
231     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
232 
233     memset(mSendBuffer, 'a', sizeof(mSendBuffer));
234 
235     mBenchmarkLinksLeft = (mBenchmarkBytesTotal + sizeof(mSendBuffer) - 1) / sizeof(mSendBuffer);
236     toSendOut           = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), mBenchmarkLinksLeft);
237     mBenchmarkStart     = TimerMilli::GetNow();
238     for (uint32_t i = 0; i != toSendOut; i++)
239     {
240         mBenchmarkLinks[i].mNext   = nullptr;
241         mBenchmarkLinks[i].mData   = mSendBuffer;
242         mBenchmarkLinks[i].mLength = sizeof(mSendBuffer);
243         if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBuffer) != 0)
244         {
245             mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBuffer);
246         }
247         SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i],
248                                                    i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME));
249     }
250 
251 exit:
252     if (error != OT_ERROR_NONE)
253     {
254         mBenchmarkBytesTotal = 0;
255         mBenchmarkLinksLeft  = 0;
256     }
257     return error;
258 }
259 
ProcessSendEnd(Arg aArgs[])260 otError TcpExample::ProcessSendEnd(Arg aArgs[])
261 {
262     otError error;
263 
264     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
265     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
266 
267     error = otTcpSendEndOfStream(&mEndpoint);
268 
269 exit:
270     return error;
271 }
272 
ProcessAbort(Arg aArgs[])273 otError TcpExample::ProcessAbort(Arg aArgs[])
274 {
275     otError error;
276 
277     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
278     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
279 
280     SuccessOrExit(error = otTcpAbort(&mEndpoint));
281     mEndpointConnected = false;
282 
283 exit:
284     return error;
285 }
286 
ProcessListen(Arg aArgs[])287 otError TcpExample::ProcessListen(Arg aArgs[])
288 {
289     otError    error;
290     otSockAddr sockaddr;
291 
292     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
293 
294     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
295     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
296     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
297 
298     SuccessOrExit(error = otTcpStopListening(&mListener));
299     error = otTcpListen(&mListener, &sockaddr);
300 
301 exit:
302     return error;
303 }
304 
ProcessStopListening(Arg aArgs[])305 otError TcpExample::ProcessStopListening(Arg aArgs[])
306 {
307     otError error;
308 
309     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
310     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
311 
312     error = otTcpStopListening(&mListener);
313 
314 exit:
315     return error;
316 }
317 
Process(Arg aArgs[])318 otError TcpExample::Process(Arg aArgs[])
319 {
320     otError        error = OT_ERROR_INVALID_ARGS;
321     const Command *command;
322 
323     VerifyOrExit(!aArgs[0].IsEmpty(), IgnoreError(ProcessHelp(nullptr)));
324 
325     command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
326     VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
327 
328     error = (this->*command->mHandler)(aArgs + 1);
329 
330 exit:
331     return error;
332 }
333 
HandleTcpEstablishedCallback(otTcpEndpoint * aEndpoint)334 void TcpExample::HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint)
335 {
336     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpEstablished(aEndpoint);
337 }
338 
HandleTcpSendDoneCallback(otTcpEndpoint * aEndpoint,otLinkedBuffer * aData)339 void TcpExample::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
340 {
341     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData);
342 }
343 
HandleTcpReceiveAvailableCallback(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)344 void TcpExample::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint,
345                                                    size_t         aBytesAvailable,
346                                                    bool           aEndOfStream,
347                                                    size_t         aBytesRemaining)
348 {
349     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))
350         ->HandleTcpReceiveAvailable(aEndpoint, aBytesAvailable, aEndOfStream, aBytesRemaining);
351 }
352 
HandleTcpDisconnectedCallback(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)353 void TcpExample::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
354 {
355     static_cast<TcpExample *>(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason);
356 }
357 
HandleTcpAcceptReadyCallback(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)358 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReadyCallback(otTcpListener *   aListener,
359                                                                        const otSockAddr *aPeer,
360                                                                        otTcpEndpoint **  aAcceptInto)
361 {
362     return static_cast<TcpExample *>(otTcpListenerGetContext(aListener))
363         ->HandleTcpAcceptReady(aListener, aPeer, aAcceptInto);
364 }
365 
HandleTcpAcceptDoneCallback(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)366 void TcpExample::HandleTcpAcceptDoneCallback(otTcpListener *   aListener,
367                                              otTcpEndpoint *   aEndpoint,
368                                              const otSockAddr *aPeer)
369 {
370     static_cast<TcpExample *>(otTcpListenerGetContext(aListener))->HandleTcpAcceptDone(aListener, aEndpoint, aPeer);
371 }
372 
HandleTcpEstablished(otTcpEndpoint * aEndpoint)373 void TcpExample::HandleTcpEstablished(otTcpEndpoint *aEndpoint)
374 {
375     OT_UNUSED_VARIABLE(aEndpoint);
376     mInterpreter.OutputLine("TCP: Connection established");
377 }
378 
HandleTcpSendDone(otTcpEndpoint * aEndpoint,otLinkedBuffer * aData)379 void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
380 {
381     OT_UNUSED_VARIABLE(aEndpoint);
382 
383     if (mBenchmarkBytesTotal == 0)
384     {
385         // If the benchmark encountered an error, we might end up here. So,
386         // tolerate some benchmark links finishing in this case.
387         if (aData == &mSendLink)
388         {
389             OT_ASSERT(mSendBusy);
390             mSendBusy = false;
391         }
392     }
393     else
394     {
395         OT_ASSERT(aData != &mSendLink);
396         mBenchmarkLinksLeft--;
397         if (mBenchmarkLinksLeft >= OT_ARRAY_LENGTH(mBenchmarkLinks))
398         {
399             aData->mLength = sizeof(mSendBuffer);
400             if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE)
401             {
402                 mInterpreter.OutputLine("TCP Benchmark Failed");
403                 mBenchmarkBytesTotal = 0;
404             }
405         }
406         else if (mBenchmarkLinksLeft == 0)
407         {
408             uint32_t milliseconds         = TimerMilli::GetNow() - mBenchmarkStart;
409             uint32_t thousandTimesGoodput = (1000 * (mBenchmarkBytesTotal << 3) + (milliseconds >> 1)) / milliseconds;
410 
411             mInterpreter.OutputLine("TCP Benchmark Complete: Transferred %u bytes in %u milliseconds",
412                                     static_cast<unsigned int>(mBenchmarkBytesTotal),
413                                     static_cast<unsigned int>(milliseconds));
414             mInterpreter.OutputLine("TCP Goodput: %u.%03u kb/s", thousandTimesGoodput / 1000,
415                                     thousandTimesGoodput % 1000);
416             mBenchmarkBytesTotal = 0;
417         }
418     }
419 }
420 
HandleTcpReceiveAvailable(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)421 void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint,
422                                            size_t         aBytesAvailable,
423                                            bool           aEndOfStream,
424                                            size_t         aBytesRemaining)
425 {
426     OT_UNUSED_VARIABLE(aBytesRemaining);
427     OT_ASSERT(aEndpoint == &mEndpoint);
428 
429     if (aBytesAvailable > 0)
430     {
431         const otLinkedBuffer *data;
432         size_t                totalReceived = 0;
433 
434         IgnoreError(otTcpReceiveByReference(aEndpoint, &data));
435         for (; data != nullptr; data = data->mNext)
436         {
437             mInterpreter.OutputLine("TCP: Received %u bytes: %.*s", static_cast<unsigned int>(data->mLength),
438                                     data->mLength, reinterpret_cast<const char *>(data->mData));
439             totalReceived += data->mLength;
440         }
441         OT_ASSERT(aBytesAvailable == totalReceived);
442         IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0));
443     }
444 
445     if (aEndOfStream)
446     {
447         mInterpreter.OutputLine("TCP: Reached end of stream");
448     }
449 }
450 
HandleTcpDisconnected(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)451 void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
452 {
453     OT_UNUSED_VARIABLE(aEndpoint);
454 
455     switch (aReason)
456     {
457     case OT_TCP_DISCONNECTED_REASON_NORMAL:
458         mInterpreter.OutputLine("TCP: Disconnected");
459         break;
460     case OT_TCP_DISCONNECTED_REASON_TIME_WAIT:
461         mInterpreter.OutputLine("TCP: Entered TIME-WAIT state");
462         break;
463     case OT_TCP_DISCONNECTED_REASON_TIMED_OUT:
464         mInterpreter.OutputLine("TCP: Connection timed out");
465         break;
466     case OT_TCP_DISCONNECTED_REASON_REFUSED:
467         mInterpreter.OutputLine("TCP: Connection refused");
468         break;
469     case OT_TCP_DISCONNECTED_REASON_RESET:
470         mInterpreter.OutputLine("TCP: Connection reset");
471         break;
472     }
473 
474     // We set this to false even for the TIME-WAIT state, so that we can reuse
475     // the active socket if an incoming connection comes in instead of waiting
476     // for the 2MSL timeout.
477     mEndpointConnected = false;
478     mSendBusy          = false;
479 
480     // Mark the benchmark as inactive if the connction was disconnected.
481     if (mBenchmarkBytesTotal != 0)
482     {
483         mBenchmarkBytesTotal = 0;
484         mBenchmarkLinksLeft  = 0;
485     }
486 }
487 
HandleTcpAcceptReady(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)488 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener *   aListener,
489                                                                const otSockAddr *aPeer,
490                                                                otTcpEndpoint **  aAcceptInto)
491 {
492     OT_UNUSED_VARIABLE(aListener);
493 
494     if (mEndpointConnected)
495     {
496         mInterpreter.OutputFormat("TCP: Ignoring incoming connection request from [");
497         mInterpreter.OutputIp6Address(aPeer->mAddress);
498         mInterpreter.OutputLine("]:%u (active socket is busy)", static_cast<unsigned int>(aPeer->mPort));
499 
500         return OT_TCP_INCOMING_CONNECTION_ACTION_DEFER;
501     }
502 
503     *aAcceptInto = &mEndpoint;
504     return OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT;
505 }
506 
HandleTcpAcceptDone(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)507 void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer)
508 {
509     OT_UNUSED_VARIABLE(aListener);
510     OT_UNUSED_VARIABLE(aEndpoint);
511 
512     mInterpreter.OutputFormat("Accepted connection from [");
513     mInterpreter.OutputIp6Address(aPeer->mAddress);
514     mInterpreter.OutputLine("]:%u", static_cast<unsigned int>(aPeer->mPort));
515 }
516 
517 } // namespace Cli
518 } // namespace ot
519 
520 #endif // OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
521