• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2021, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements a TCP CLI tool.
32  */
33 
34 #include "openthread-core-config.h"
35 
36 #include "cli_config.h"
37 
38 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
39 
40 #include "cli_tcp.hpp"
41 
42 #include <openthread/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(Output & aOutput)53 TcpExample::TcpExample(Output &aOutput)
54     : OutputWrapper(aOutput)
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         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(GetInstancePtr(), &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(GetInstancePtr(), &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 = OT_ERROR_NONE;
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 = BinarySearch::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     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                 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             OutputLine("TCP Benchmark Complete: Transferred %u bytes in %u milliseconds",
412                        static_cast<unsigned int>(mBenchmarkBytesTotal), static_cast<unsigned int>(milliseconds));
413             OutputLine("TCP Goodput: %u.%03u kb/s", thousandTimesGoodput / 1000, thousandTimesGoodput % 1000);
414             mBenchmarkBytesTotal = 0;
415         }
416     }
417 }
418 
HandleTcpReceiveAvailable(otTcpEndpoint * aEndpoint,size_t aBytesAvailable,bool aEndOfStream,size_t aBytesRemaining)419 void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint,
420                                            size_t         aBytesAvailable,
421                                            bool           aEndOfStream,
422                                            size_t         aBytesRemaining)
423 {
424     OT_UNUSED_VARIABLE(aBytesRemaining);
425     OT_ASSERT(aEndpoint == &mEndpoint);
426 
427     if (aBytesAvailable > 0)
428     {
429         const otLinkedBuffer *data;
430         size_t                totalReceived = 0;
431 
432         IgnoreError(otTcpReceiveByReference(aEndpoint, &data));
433         for (; data != nullptr; data = data->mNext)
434         {
435             OutputLine("TCP: Received %u bytes: %.*s", static_cast<unsigned int>(data->mLength), data->mLength,
436                        reinterpret_cast<const char *>(data->mData));
437             totalReceived += data->mLength;
438         }
439         OT_ASSERT(aBytesAvailable == totalReceived);
440         IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0));
441     }
442 
443     if (aEndOfStream)
444     {
445         OutputLine("TCP: Reached end of stream");
446     }
447 }
448 
HandleTcpDisconnected(otTcpEndpoint * aEndpoint,otTcpDisconnectedReason aReason)449 void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
450 {
451     static const char *const kReasonStrings[] = {
452         "Disconnected",            // (0) OT_TCP_DISCONNECTED_REASON_NORMAL
453         "Connection refused",      // (1) OT_TCP_DISCONNECTED_REASON_REFUSED
454         "Connection reset",        // (2) OT_TCP_DISCONNECTED_REASON_RESET
455         "Entered TIME-WAIT state", // (3) OT_TCP_DISCONNECTED_REASON_TIME_WAIT
456         "Connection timed out",    // (4) OT_TCP_DISCONNECTED_REASON_TIMED_OUT
457     };
458 
459     OT_UNUSED_VARIABLE(aEndpoint);
460 
461     static_assert(0 == OT_TCP_DISCONNECTED_REASON_NORMAL, "OT_TCP_DISCONNECTED_REASON_NORMAL value is incorrect");
462     static_assert(1 == OT_TCP_DISCONNECTED_REASON_REFUSED, "OT_TCP_DISCONNECTED_REASON_REFUSED value is incorrect");
463     static_assert(2 == OT_TCP_DISCONNECTED_REASON_RESET, "OT_TCP_DISCONNECTED_REASON_RESET value is incorrect");
464     static_assert(3 == OT_TCP_DISCONNECTED_REASON_TIME_WAIT, "OT_TCP_DISCONNECTED_REASON_TIME_WAIT value is incorrect");
465     static_assert(4 == OT_TCP_DISCONNECTED_REASON_TIMED_OUT, "OT_TCP_DISCONNECTED_REASON_TIMED_OUT value is incorrect");
466 
467     OutputLine("TCP: %s", Stringify(aReason, kReasonStrings));
468 
469     // We set this to false even for the TIME-WAIT state, so that we can reuse
470     // the active socket if an incoming connection comes in instead of waiting
471     // for the 2MSL timeout.
472     mEndpointConnected = false;
473     mSendBusy          = false;
474 
475     // Mark the benchmark as inactive if the connection was disconnected.
476     if (mBenchmarkBytesTotal != 0)
477     {
478         mBenchmarkBytesTotal = 0;
479         mBenchmarkLinksLeft  = 0;
480     }
481 }
482 
HandleTcpAcceptReady(otTcpListener * aListener,const otSockAddr * aPeer,otTcpEndpoint ** aAcceptInto)483 otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener *   aListener,
484                                                                const otSockAddr *aPeer,
485                                                                otTcpEndpoint **  aAcceptInto)
486 {
487     OT_UNUSED_VARIABLE(aListener);
488 
489     if (mEndpointConnected)
490     {
491         OutputFormat("TCP: Ignoring incoming connection request from ");
492         OutputSockAddr(*aPeer);
493         OutputLine(" (active socket is busy)");
494 
495         return OT_TCP_INCOMING_CONNECTION_ACTION_DEFER;
496     }
497 
498     *aAcceptInto = &mEndpoint;
499     return OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT;
500 }
501 
HandleTcpAcceptDone(otTcpListener * aListener,otTcpEndpoint * aEndpoint,const otSockAddr * aPeer)502 void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer)
503 {
504     OT_UNUSED_VARIABLE(aListener);
505     OT_UNUSED_VARIABLE(aEndpoint);
506 
507     OutputFormat("Accepted connection from ");
508     OutputSockAddrLine(*aPeer);
509 }
510 
511 } // namespace Cli
512 } // namespace ot
513 
514 #endif // OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
515