From f88c537c6c6056c51d76f6e8f0c52e5e9aeb3d5f Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 26 Jul 2017 12:14:44 -0700 Subject: [PATCH] [udp] add cli-based example for UDP APIs (#2027) --- .../libopenthread-cli-windows.vcxproj.filters | 6 +- etc/visual-studio/libopenthread-cli.vcxproj | 2 +- .../libopenthread-cli.vcxproj.filters | 8 +- etc/visual-studio/libopenthread.vcxproj | 1 + .../libopenthread.vcxproj.filters | 3 + etc/visual-studio/libopenthread_k.vcxproj | 1 + .../libopenthread_k.vcxproj.filters | 3 + src/cli/Makefile.am | 4 +- src/cli/README_UDP.md | 126 ++++++++++ src/cli/cli.cpp | 13 + src/cli/cli.hpp | 8 + src/cli/cli_udp_example.cpp | 224 ++++++++++++++++++ src/cli/cli_udp_example.hpp | 95 ++++++++ 13 files changed, 484 insertions(+), 10 deletions(-) create mode 100644 src/cli/README_UDP.md create mode 100644 src/cli/cli_udp_example.cpp create mode 100644 src/cli/cli_udp_example.hpp diff --git a/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters b/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters index d0ba0e979..a5869353a 100644 --- a/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters +++ b/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters @@ -10,14 +10,14 @@ Source Files - - Source Files - Source Files Source Files + + Source Files + \ No newline at end of file diff --git a/etc/visual-studio/libopenthread-cli.vcxproj b/etc/visual-studio/libopenthread-cli.vcxproj index 8120e9fc8..1f135ab98 100644 --- a/etc/visual-studio/libopenthread-cli.vcxproj +++ b/etc/visual-studio/libopenthread-cli.vcxproj @@ -59,7 +59,7 @@ - + diff --git a/etc/visual-studio/libopenthread-cli.vcxproj.filters b/etc/visual-studio/libopenthread-cli.vcxproj.filters index cf6c8a37b..a3855ac05 100644 --- a/etc/visual-studio/libopenthread-cli.vcxproj.filters +++ b/etc/visual-studio/libopenthread-cli.vcxproj.filters @@ -10,14 +10,14 @@ Source Files - + Source Files - + Source Files - + Source Files - + \ No newline at end of file diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index f2257092b..9386127cf 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -72,6 +72,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index c7a15d55f..8cee5ee62 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -117,6 +117,9 @@ Source Files\api + + Source Files\api + Source Files\coap diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index ea389c490..e9ee136f7 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -81,6 +81,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 981bf68aa..7a245e0e0 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -117,6 +117,9 @@ Source Files\api + + Source Files\api + Source Files\coap diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index 8331e4ce6..80b36eb69 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -137,7 +137,7 @@ SOURCES_COMMON = \ cli_dataset.cpp \ cli_uart.cpp \ cli_coap.cpp \ - cli_udp.cpp \ + cli_udp_example.cpp \ $(NULL) libopenthread_cli_ftd_a_SOURCES = \ @@ -155,7 +155,7 @@ noinst_HEADERS = \ cli_uart.hpp \ cli_server.hpp \ cli_coap.hpp \ - cli_udp.hpp \ + cli_udp_example.hpp \ $(NULL) if OPENTHREAD_BUILD_COVERAGE diff --git a/src/cli/README_UDP.md b/src/cli/README_UDP.md new file mode 100644 index 000000000..74217cc33 --- /dev/null +++ b/src/cli/README_UDP.md @@ -0,0 +1,126 @@ +# OpenThread CLI - UDP Example + +The OpenThread UDP APIs may be invoked via the OpenThread CLI. + +## Quick Start + +### Form Network + +Form a network with at least two devices. + +### Node 1 + +On node 1, open and bind the example UDP socket. + +```bash +> udp open +> udp bind :: 1234 +``` + +The `::` specifies the IPv6 Unspecified Address. + +### Node 2 + +On node 2, open the example UDP socket and send a simple message. + +```bash +> udp open +> udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 hello +``` + +### Result + +On node 1, you should see a print out similar to below: + +```bash +5 bytes from fdde:ad00:beef:0:dac3:6792:e2e:90d8: hello +``` + +## Command List + +* [help](#help) +* [bind](#bind-ip-port) +* [close](#close) +* [connect](#connect-ip-port) +* [open](#open) +* [send](#send-ip-port-message) + +## Command Details + +### help + +List the UDP CLI commands. + +```bash +> udp help +help +bind +close +connect +open +send +Done +``` + +### bind \ \ + +Assigns a name (i.e. IPv6 address and port) to the example socket. +* ip: the IPv6 address or the unspecified IPv6 address (`::`). +* port: the UDP port + +```bash +> udp bind :: 1234 +Done +``` + +### close + +Closes the example socket. + +```bash +> udp close +Done +``` + +### connect \ \ + +Specifies the peer with which the socket is to be associated. + +* ip: the peer's IPv6 address. +* port: the peer's UDP port. + +```bash +> udp connect fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 +Done +``` + +### open + +Opens the example socket. + +```bash +> udp open +Done +``` + +### send \ \ \ + +Send a UDP message. + +* ip: the IPv6 destination address. +* port: the UDP destination port. +* message: the message to send. + +```bash +> udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 hello +``` + +### send \ + +Send a UDP message on a connected socket. + +* message: the message to send. + +```bash +> udp send hello +``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index a9f0b5a70..d6443a231 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -201,6 +201,9 @@ const struct Command Interpreter::sCommands[] = { "state", &Interpreter::ProcessState }, { "thread", &Interpreter::ProcessThread }, { "txpowermax", &Interpreter::ProcessTxPowerMax }, +#ifndef OTDLL + { "udp", &Interpreter::ProcessUdp }, +#endif { "version", &Interpreter::ProcessVersion }, }; @@ -248,6 +251,7 @@ Interpreter::Interpreter(otInstance *aInstance): #if OPENTHREAD_ENABLE_DNS_CLIENT mResolvingInProgress(0), #endif + mUdp(*this), #endif mInstance(aInstance) { @@ -2487,6 +2491,15 @@ void Interpreter::ProcessVersion(int argc, char *argv[]) OT_UNUSED_VARIABLE(argv); } +#ifndef OTDLL +void Interpreter::ProcessUdp(int argc, char *argv[]) +{ + otError error; + error = mUdp.Process(argc, argv); + AppendResult(error); +} +#endif + #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD void Interpreter::ProcessCommissioner(int argc, char *argv[]) diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 9568273bd..be5c5758a 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -43,6 +43,7 @@ #include #include "cli/cli_server.hpp" +#include "cli/cli_udp_example.hpp" #if OPENTHREAD_ENABLE_APPLICATION_COAP #include @@ -93,6 +94,7 @@ struct Command class Interpreter { friend class Coap; + friend class Udp; public: @@ -287,6 +289,10 @@ private: void ProcessInstance(int argc, char *argv[]); #endif +#ifndef OTDLL + void ProcessUdp(int argc, char *argv[]); +#endif + #ifndef OTDLL static void s_HandleIcmpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader); @@ -383,6 +389,8 @@ private: char mResolvingHostname[OT_DNS_MAX_HOSTNAME_LENGTH]; #endif + Udp mUdp; + #endif otInstance *mInstance; diff --git a/src/cli/cli_udp_example.cpp b/src/cli/cli_udp_example.cpp new file mode 100644 index 000000000..b4b4da7ed --- /dev/null +++ b/src/cli/cli_udp_example.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017, 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. + */ + +/** + * @file + * This file implements a simple CLI for the CoAP service. + */ + +#include "cli_udp_example.hpp" + +#include +#include +#include + +#include "cli/cli.hpp" +#include "common/encoding.hpp" + +using ot::Encoding::BigEndian::HostSwap16; + +namespace ot { +namespace Cli { + +const struct Udp::Command Udp::sCommands[] = +{ + { "help", &Udp::ProcessHelp }, + { "bind", &Udp::ProcessBind }, + { "close", &Udp::ProcessClose }, + { "connect", &Udp::ProcessConnect }, + { "open", &Udp::ProcessOpen }, + { "send", &Udp::ProcessSend } +}; + +otError Udp::ProcessHelp(int argc, char *argv[]) +{ + for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) + { + mInterpreter.mServer->OutputFormat("%s\r\n", sCommands[i].mName); + } + + OT_UNUSED_VARIABLE(argc); + OT_UNUSED_VARIABLE(argv); + + return OT_ERROR_NONE; +} + +otError Udp::ProcessBind(int argc, char *argv[]) +{ + otError error; + otSockAddr sockaddr; + long value; + + memset(&sockaddr, 0, sizeof(sockaddr)); + + VerifyOrExit(argc == 2, error = OT_ERROR_PARSE); + + error = otIp6AddressFromString(argv[1], &sockaddr.mAddress); + SuccessOrExit(error); + + error = Interpreter::ParseLong(argv[2], value); + SuccessOrExit(error); + + sockaddr.mPort = static_cast(value); + + error = otUdpBind(&mSocket, &sockaddr); + +exit: + return error; +} + +otError Udp::ProcessConnect(int argc, char *argv[]) +{ + otError error; + otSockAddr sockaddr; + long value; + + memset(&sockaddr, 0, sizeof(sockaddr)); + + VerifyOrExit(argc == 2, error = OT_ERROR_PARSE); + + error = otIp6AddressFromString(argv[1], &sockaddr.mAddress); + SuccessOrExit(error); + + error = Interpreter::ParseLong(argv[2], value); + SuccessOrExit(error); + + sockaddr.mPort = static_cast(value); + + error = otUdpConnect(&mSocket, &sockaddr); + +exit: + return error; +} + +otError Udp::ProcessClose(int argc, char *argv[]) +{ + OT_UNUSED_VARIABLE(argc); + OT_UNUSED_VARIABLE(argv); + + return otUdpClose(&mSocket); +} + +otError Udp::ProcessOpen(int argc, char *argv[]) +{ + OT_UNUSED_VARIABLE(argc); + OT_UNUSED_VARIABLE(argv); + + return otUdpOpen(mInterpreter.mInstance, &mSocket, HandleUdpReceive, this); +} + +otError Udp::ProcessSend(int argc, char *argv[]) +{ + otError error; + otMessageInfo messageInfo; + otMessage *message = NULL; + int curArg = 1; + + memset(&messageInfo, 0, sizeof(messageInfo)); + + VerifyOrExit(argc == 1 || argc == 3, error = OT_ERROR_PARSE); + + if (argc == 3) + { + long value; + + error = otIp6AddressFromString(argv[curArg++], &messageInfo.mPeerAddr); + SuccessOrExit(error); + + error = Interpreter::ParseLong(argv[curArg++], value); + SuccessOrExit(error); + + messageInfo.mPeerPort = static_cast(value); + } + + message = otUdpNewMessage(mInterpreter.mInstance, true); + VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); + + error = otMessageAppend(message, argv[curArg], static_cast(strlen(argv[3]))); + SuccessOrExit(error); + + error = otUdpSend(&mSocket, message, &messageInfo); + +exit: + + if (error != OT_ERROR_NONE && message != NULL) + { + otMessageFree(message); + } + + return error; +} + +otError Udp::Process(int argc, char *argv[]) +{ + otError error = OT_ERROR_NONE; + + for (size_t i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) + { + if (strcmp(argv[0], sCommands[i].mName) == 0) + { + (this->*sCommands[i].mCommand)(argc, argv); + ExitNow(); + } + } + + error = OT_ERROR_PARSE; + +exit: + return error; +} + +void Udp::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + static_cast(aContext)->HandleUdpReceive(aMessage, aMessageInfo); +} + +void Udp::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + uint8_t buf[1500]; + int length; + + mInterpreter.mServer->OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage)); + mInterpreter.mServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x: ", + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[0]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[1]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[2]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[3]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[4]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[5]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[6]), + HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[7])); + + length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1); + buf[length] = '\0'; + + mInterpreter.mServer->OutputFormat("%s\r\n", buf); +} + +} // namespace Cli +} // namespace ot diff --git a/src/cli/cli_udp_example.hpp b/src/cli/cli_udp_example.hpp new file mode 100644 index 000000000..44570a997 --- /dev/null +++ b/src/cli/cli_udp_example.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017, 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. + */ + +/** + * @file + * This file contains definitions for a simple CLI CoAP server and client. + */ + +#ifndef CLI_UDP_EXAMPLE_HPP_ +#define CLI_UDP_EXAMPLE_HPP_ + +#include +#include + +namespace ot { +namespace Cli { + +class Interpreter; + +/** + * This class implements a CLI-based UDP example. + * + */ +class Udp +{ +public: + /** + * Constructor + * + * @param[in] aInterpreter The CLI interpreter. + * + */ + Udp(Interpreter &aInterpreter): mInterpreter(aInterpreter) { } + + /** + * This method interprets a list of CLI arguments. + * + * @param[in] argc The number of elements in argv. + * @param[in] argv A pointer to an array of command line arguments. + * + */ + otError Process(int argc, char *argv[]); + +private: + struct Command + { + const char *mName; + otError(Udp::*mCommand)(int argc, char *argv[]); + }; + + otError ProcessHelp(int argc, char *argv[]); + otError ProcessBind(int argc, char *argv[]); + otError ProcessClose(int argc, char *argv[]); + otError ProcessConnect(int argc, char *argv[]); + otError ProcessOpen(int argc, char *argv[]); + otError ProcessSend(int argc, char *argv[]); + + static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); + void HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo); + + static const Command sCommands[]; + Interpreter &mInterpreter; + + otUdpSocket mSocket; +}; + +} // namespace Cli +} // namespace ot + +#endif // CLI_UDP_EXAMPLE_HPP_