diff --git a/src/cli/README.md b/src/cli/README.md index 7a8be33f2..cbe631cf3 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -63,6 +63,7 @@ Done - [neighbor](#neighbor-list) - [netdataregister](#netdataregister) - [netdatashow](#netdatashow) +- [netstat](#netstat) - [networkdiagnostic](#networkdiagnostic-get-addr-type-) - [networkidtimeout](#networkidtimeout) - [networkname](#networkname) @@ -1042,6 +1043,21 @@ Show Thread Leader network data. Done ``` +### netstat + +List all UDP sockets. + +```bash +> netstat +| Local Address | Peer Address | ++-----------------------------------------------+-----------------------------------------------+ +| 0:0:0:0:0:0:0:0:49153 | 0:0:0:0:0:0:0:0:* | +| 0:0:0:0:0:0:0:0:49152 | 0:0:0:0:0:0:0:0:* | +| 0:0:0:0:0:0:0:0:61631 | 0:0:0:0:0:0:0:0:* | +| 0:0:0:0:0:0:0:0:19788 | 0:0:0:0:0:0:0:0:* | +Done +``` + ### networkdiagnostic get \ \ .. Send network diagnostic request to retrieve tlv of \s. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 86160593b..0d916bc8d 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -193,6 +193,7 @@ const struct Command Interpreter::sCommands[] = { #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE {"netif", &Interpreter::ProcessNetif}, #endif + {"netstat", &Interpreter::ProcessNetstat}, #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE {"networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic}, #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE @@ -376,9 +377,9 @@ void Interpreter::OutputBytes(const uint8_t *aBytes, uint8_t aLength) const } } -void Interpreter::OutputIp6Address(const otIp6Address &aAddress) const +int Interpreter::OutputIp6Address(const otIp6Address &aAddress) const { - mServer->OutputFormat( + return mServer->OutputFormat( "%x:%x:%x:%x:%x:%x:%x:%x", HostSwap16(aAddress.mFields.m16[0]), HostSwap16(aAddress.mFields.m16[1]), HostSwap16(aAddress.mFields.m16[2]), HostSwap16(aAddress.mFields.m16[3]), HostSwap16(aAddress.mFields.m16[4]), HostSwap16(aAddress.mFields.m16[5]), HostSwap16(aAddress.mFields.m16[6]), HostSwap16(aAddress.mFields.m16[7])); @@ -2062,6 +2063,70 @@ exit: } #endif +void Interpreter::ProcessNetstat(uint8_t aArgsLength, char *aArgs[]) +{ + otUdpSocket *socket = otUdpGetSockets(mInstance); + + OT_UNUSED_VARIABLE(aArgsLength); + OT_UNUSED_VARIABLE(aArgs); + + mServer->OutputFormat( + "| Local Address | Peer Address |\n"); + mServer->OutputFormat( + "+-----------------------------------------------+-----------------------------------------------+\n"); + + while (socket) + { + constexpr int kMaxOutputLength = 45; + int outputLength; + + mServer->OutputFormat("| "); + + outputLength = OutputSocketAddress(socket->mSockName); + for (int i = outputLength; 0 <= i && i < kMaxOutputLength; ++i) + { + mServer->OutputFormat(" "); + } + mServer->OutputFormat(" | "); + + outputLength = OutputSocketAddress(socket->mPeerName); + for (int i = outputLength; 0 <= i && i < kMaxOutputLength; ++i) + { + mServer->OutputFormat(" "); + } + mServer->OutputFormat(" |\n"); + + socket = socket->mNext; + } + + AppendResult(OT_ERROR_NONE); +} + +int Interpreter::OutputSocketAddress(const otSockAddr &aAddress) +{ + int outputLength; + int result = 0; + + VerifyOrExit((outputLength = OutputIp6Address(aAddress.mAddress)) >= 0, result = -1); + result += outputLength; + + VerifyOrExit((outputLength = mServer->OutputFormat(":")) >= 0, result = -1); + result += outputLength; + if (aAddress.mPort == 0) + { + VerifyOrExit((outputLength = mServer->OutputFormat("*")) >= 0, result = -1); + result += outputLength; + } + else + { + VerifyOrExit((outputLength = mServer->OutputFormat("%d", aAddress.mPort)) >= 0, result = -1); + result += outputLength; + } + +exit: + return result; +} + #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE void Interpreter::ProcessService(uint8_t aArgsLength, char *aArgs[]) { diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 0ef44e019..59eadd33c 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -177,8 +177,13 @@ public: * Write an IPv6 address to the CLI console. * * @param[in] aAddress A reference to the IPv6 address. + * + * @returns The number of bytes placed in the output queue. + * + * @retval -1 Driver is broken. + * */ - void OutputIp6Address(const otIp6Address &aAddress) const; + int OutputIp6Address(const otIp6Address &aAddress) const; /** * Set a user command table. @@ -293,6 +298,8 @@ private: #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE void ProcessNetif(uint8_t aArgsLength, char *aArgs[]); #endif + void ProcessNetstat(uint8_t aArgsLength, char *aArgs[]); + int OutputSocketAddress(const otSockAddr &aAddress); #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE void ProcessService(uint8_t aArgsLength, char *aArgs[]); #endif diff --git a/src/core/api/udp_api.cpp b/src/core/api/udp_api.cpp index 40d9e4f55..2cd17a7c3 100644 --- a/src/core/api/udp_api.cpp +++ b/src/core/api/udp_api.cpp @@ -119,14 +119,12 @@ void otUdpForwardReceive(otInstance * aInstance, } #endif // OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE -#if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE otUdpSocket *otUdpGetSockets(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); return instance.Get().GetUdpSockets(); } -#endif otError otUdpAddReceiver(otInstance *aInstance, otUdpReceiver *aUdpReceiver) { diff --git a/src/core/net/udp6.cpp b/src/core/net/udp6.cpp index 30bd14439..9174eecfe 100644 --- a/src/core/net/udp6.cpp +++ b/src/core/net/udp6.cpp @@ -124,12 +124,19 @@ otError Udp::Socket::Connect(const SockAddr &aSockAddr) mPeerName = aSockAddr; + if (!IsBound()) + { + SuccessOrExit(error = Bind(GetSockName())); + } + #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE if (!IsMle(GetInstance(), mSockName.mPort)) { error = otPlatUdpConnect(this); } #endif + +exit: return error; } diff --git a/src/core/net/udp6.hpp b/src/core/net/udp6.hpp index ddb6e456f..35abc5169 100644 --- a/src/core/net/udp6.hpp +++ b/src/core/net/udp6.hpp @@ -425,7 +425,6 @@ public: */ void UpdateChecksum(Message &aMessage, uint16_t aChecksum); -#if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE /** * This method returns the head of UDP Sockets list. * @@ -433,7 +432,6 @@ public: * */ Socket *GetUdpSockets(void) { return mSockets.GetHead(); } -#endif #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE /** diff --git a/tests/scripts/expect/tun-netstat.exp b/tests/scripts/expect/tun-netstat.exp new file mode 100755 index 000000000..3010529e1 --- /dev/null +++ b/tests/scripts/expect/tun-netstat.exp @@ -0,0 +1,63 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2020, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +source "tests/scripts/expect/_common.exp" + +set timeout 3 +set spawn_id [spawn_node 1] + +send "ifconfig up\n" +expect "Done" +send "udp open\n" +expect "Done" +send "netstat\n" +expect "| Local Address | Peer Address |" +expect "+-----------------------------------------------+-----------------------------------------------+" +expect "| 0:0:0:0:0:0:0:0:* | 0:0:0:0:0:0:0:0:* |" +expect "Done" +send "udp bind :: 10001\n" +expect "Done" +send "netstat\n" +expect "| Local Address | Peer Address |" +expect "+-----------------------------------------------+-----------------------------------------------+" +expect "| 0:0:0:0:0:0:0:0:10001 | 0:0:0:0:0:0:0:0:* |" +expect "Done" +send "ipaddr mleid\n" +expect "ipaddr mleid" +expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})} +set addr $expect_out(1,string) +send "udp connect $addr 10001\n" +expect "Done" +send "netstat\n" +expect "| Local Address | Peer Address |" +expect "+-----------------------------------------------+-----------------------------------------------+" +expect -re "\\| 0:0:0:0:0:0:0:0:10001 +\\| $addr:10001 +\\|" +expect "Done" + +dispose