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_