• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2017, 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 simple CLI for the CoAP service.
32  */
33 
34 #include "cli_udp.hpp"
35 
36 #include <openthread/message.h>
37 #include <openthread/nat64.h>
38 #include <openthread/udp.h>
39 
40 #include "cli/cli.hpp"
41 #include "common/encoding.hpp"
42 
43 namespace ot {
44 namespace Cli {
45 
UdpExample(otInstance * aInstance,OutputImplementer & aOutputImplementer)46 UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
47     : Utils(aInstance, aOutputImplementer)
48     , mLinkSecurityEnabled(true)
49 {
50     ClearAllBytes(mSocket);
51 }
52 
53 /**
54  * @cli udp bind
55  * @code
56  * udp bind :: 1234
57  * Done
58  * @endcode
59  * @code
60  * udp bind -u :: 1234
61  * Done
62  * @endcode
63  * @code
64  * udp bind -b :: 1234
65  * Done
66  * @endcode
67  * @code
68  * udp bind -h :: 1234
69  * Done
70  * @endcode
71  * @cparam udp bind [@ca{netif}] @ca{ip} @ca{port}
72  * - `netif`: The binding network interface, which is determined as follows:
73  *   - No value (leaving out this parameter from the command): Thread stack network interface is used.
74  *   - `-u`: Unspecified network interface, which means that the Host UDP/IPv6 stack determines which
75  *   network interface to bind the socket to. Valid if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE is set.
76  *   - `-b`: Backbone network interface is used. Valid if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE is set.
77  *   - `-h`: Host Thread network interface is used. Valid if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE is set.
78  * - `ip`: Unicast IPv6 address to bind to. If you wish to have the UDP/IPv6 stack assign the binding
79  *   IPv6 address, or if you wish to bind to multicast IPv6 addresses, then you can use the following
80  *   value to use the unspecified IPv6 address: `::`. Each example uses the unspecified IPv6 address.
81  * - `port`: UDP port number to bind to. Each of the examples is using port number 1234.
82  * @par
83  * Assigns an IPv6 address and a port to an open socket, which binds the socket for communication.
84  * Assigning the IPv6 address and port is referred to as naming the socket. @moreinfo{@udp}.
85  * @sa otUdpBind
86  */
Process(Arg aArgs[])87 template <> otError UdpExample::Process<Cmd("bind")>(Arg aArgs[])
88 {
89     otError           error;
90     otSockAddr        sockaddr;
91     otNetifIdentifier netif = OT_NETIF_THREAD_INTERNAL;
92 
93     if (aArgs[0] == "-u")
94     {
95         netif = OT_NETIF_UNSPECIFIED;
96         aArgs++;
97     }
98     else if (aArgs[0] == "-b")
99     {
100         netif = OT_NETIF_BACKBONE;
101         aArgs++;
102     }
103     else if (aArgs[0] == "-h")
104     {
105         netif = OT_NETIF_THREAD_HOST;
106         aArgs++;
107     }
108 
109     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
110     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
111     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
112 
113     error = otUdpBind(GetInstancePtr(), &mSocket, &sockaddr, netif);
114 
115 exit:
116     return error;
117 }
118 
119 /**
120  * @cli udp connect
121  * @code
122  * udp connect fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234
123  * Done
124  * @endcode
125  * @code
126  * udp connect 172.17.0.1 1234
127  * Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
128  * Done
129  * @endcode
130  * @cparam udp connect @ca{ip} @ca{port}
131  * The following parameters are required:
132  * - `ip`: IP address of the peer.
133  * - `port`: UDP port number of the peer.
134  * The address can be an IPv4 address, which gets synthesized to an IPv6 address
135  * using the preferred NAT64 prefix from the network data. The command returns
136  * `InvalidState` when the preferred NAT64 prefix is unavailable.
137  * @par api_copy
138  * #otUdpConnect
139  * @moreinfo{@udp}.
140  */
Process(Arg aArgs[])141 template <> otError UdpExample::Process<Cmd("connect")>(Arg aArgs[])
142 {
143     otError    error;
144     otSockAddr sockaddr;
145     bool       nat64Synth;
146 
147     SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth));
148 
149     if (nat64Synth)
150     {
151         OutputFormat("Connecting to synthesized IPv6 address: ");
152         OutputIp6AddressLine(sockaddr.mAddress);
153     }
154 
155     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
156     VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
157 
158     error = otUdpConnect(GetInstancePtr(), &mSocket, &sockaddr);
159 
160 exit:
161     return error;
162 }
163 
164 /**
165  * @cli udp close
166  * @code
167  * udp close
168  * Done
169  * @endcode
170  * @par api_copy
171  * #otUdpClose
172  */
Process(Arg aArgs[])173 template <> otError UdpExample::Process<Cmd("close")>(Arg aArgs[])
174 {
175     OT_UNUSED_VARIABLE(aArgs);
176 
177     return otUdpClose(GetInstancePtr(), &mSocket);
178 }
179 
180 /**
181  * @cli udp open
182  * @code
183  * udp open
184  * Done
185  * @endcode
186  * @par api_copy
187  * #otUdpOpen
188  */
Process(Arg aArgs[])189 template <> otError UdpExample::Process<Cmd("open")>(Arg aArgs[])
190 {
191     OT_UNUSED_VARIABLE(aArgs);
192 
193     otError error;
194 
195     VerifyOrExit(!otUdpIsOpen(GetInstancePtr(), &mSocket), error = OT_ERROR_ALREADY);
196     error = otUdpOpen(GetInstancePtr(), &mSocket, HandleUdpReceive, this);
197 
198 exit:
199     return error;
200 }
201 
202 /**
203  * @cli udp send
204  * @code
205  * udp send hello
206  * Done
207  * @endcode
208  * @code
209  * udp send -t hello
210  * Done
211  * @endcode
212  * @code
213  * udp send -x 68656c6c6f
214  * Done
215  * @endcode
216  * @code
217  * udp send -s 800
218  * Done
219  * @endcode
220  * @code
221  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 hello
222  * Done
223  * @endcode
224  * @code
225  * udp send 172.17.0.1 1234 hello
226  * Sending to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
227  * Done
228  * @endcode
229  * @code
230  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 -t hello
231  * Done
232  * @endcode
233  * @code
234  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 -x 68656c6c6f
235  * Done
236  * @endcode
237  * @code
238  * udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 -s 800
239  * Done
240  * @endcode
241  * @cparam udp send [@ca{ip} @ca{port}] [@ca{type}] @ca{value}
242  * The `ip` and `port` are optional as a pair, but if you specify one you must
243  * specify the other. If `ip` and `port` are not specified, the socket peer address
244  * is used from `udp connect`.
245  * - `ip`: Destination address. This address can be either an IPv4 or IPv6 address,
246  *   An IPv4 address gets synthesized to an IPv6 address with the preferred
247  *   NAT64 prefix from the network data. (If the preferred NAT64 prefix
248  *   is unavailable, the command returns `InvalidState`).
249  * - `port`: UDP destination port.
250  * - `type`/`value` combinations:
251  *   - `-t`: The payload in the `value` parameter is treated as text. If no `type` value
252  *   is entered, the payload in the `value` parameter is also treated as text.
253  *   - `-s`: Auto-generated payload with the specified length given in the `value` parameter.
254  *   - `-x`: Binary data in hexadecimal representation given in the `value` parameter.
255  * @par
256  * Sends a UDP message using the socket. @moreinfo{@udp}.
257  * @csa{udp open}
258  * @csa{udp bind}
259  * @csa{udp connect}
260  * @sa otUdpSend
261  */
Process(Arg aArgs[])262 template <> otError UdpExample::Process<Cmd("send")>(Arg aArgs[])
263 {
264     otError           error   = OT_ERROR_NONE;
265     otMessage        *message = nullptr;
266     otMessageInfo     messageInfo;
267     otMessageSettings messageSettings = {mLinkSecurityEnabled, OT_MESSAGE_PRIORITY_NORMAL};
268 
269     VerifyOrExit(otUdpIsOpen(GetInstancePtr(), &mSocket), error = OT_ERROR_INVALID_STATE);
270 
271     ClearAllBytes(messageInfo);
272 
273     // Possible argument formats:
274     //
275     // send             <text>
276     // send             <type> <value>
277     // send <ip> <port> <text>
278     // send <ip> <port> <type> <value>
279 
280     if (!aArgs[2].IsEmpty())
281     {
282         bool nat64Synth;
283 
284         SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr, nat64Synth));
285 
286         if (nat64Synth)
287         {
288             OutputFormat("Sending to synthesized IPv6 address: ");
289             OutputIp6AddressLine(messageInfo.mPeerAddr);
290         }
291 
292         SuccessOrExit(error = aArgs[1].ParseAsUint16(messageInfo.mPeerPort));
293         aArgs += 2;
294     }
295 
296     message = otUdpNewMessage(GetInstancePtr(), &messageSettings);
297     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
298 
299     if (aArgs[0] == "-s")
300     {
301         // Auto-generated payload with a given length
302 
303         uint16_t payloadLength;
304 
305         SuccessOrExit(error = aArgs[1].ParseAsUint16(payloadLength));
306         SuccessOrExit(error = PrepareAutoGeneratedPayload(*message, payloadLength));
307     }
308     else if (aArgs[0] == "-x")
309     {
310         // Binary hex data payload
311 
312         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
313         SuccessOrExit(error = PrepareHexStringPayload(*message, aArgs[1].GetCString()));
314     }
315     else
316     {
317         // Text payload (same as without specifying the type)
318 
319         if (aArgs[0] == "-t")
320         {
321             aArgs++;
322         }
323 
324         VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
325         SuccessOrExit(error = otMessageAppend(message, aArgs[0].GetCString(), aArgs[0].GetLength()));
326     }
327 
328     SuccessOrExit(error = otUdpSend(GetInstancePtr(), &mSocket, message, &messageInfo));
329 
330     message = nullptr;
331 
332 exit:
333     if (message != nullptr)
334     {
335         otMessageFree(message);
336     }
337 
338     return error;
339 }
340 
Process(Arg aArgs[])341 template <> otError UdpExample::Process<Cmd("linksecurity")>(Arg aArgs[])
342 {
343     otError error = OT_ERROR_NONE;
344 
345     /**
346      * @cli udp linksecurity
347      * @code
348      * udp linksecurity
349      * Enabled
350      * Done
351      * @endcode
352      * @par
353      * Indicates whether link security is enabled or disabled.
354      */
355     if (aArgs[0].IsEmpty())
356     {
357         OutputEnabledDisabledStatus(mLinkSecurityEnabled);
358     }
359     /**
360      * @cli udp linksecurity (enable,disable)
361      * @code
362      * udp linksecurity enable
363      * Done
364      * @endcode
365      * @code
366      * udp linksecurity disable
367      * Done
368      * @endcode
369      * @par
370      * Enables or disables link security.
371      */
372     else
373     {
374         error = ParseEnableOrDisable(aArgs[0], mLinkSecurityEnabled);
375     }
376 
377     return error;
378 }
379 
PrepareAutoGeneratedPayload(otMessage & aMessage,uint16_t aPayloadLength)380 otError UdpExample::PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength)
381 {
382     otError error     = OT_ERROR_NONE;
383     uint8_t character = '0';
384 
385     for (; aPayloadLength != 0; aPayloadLength--)
386     {
387         SuccessOrExit(error = otMessageAppend(&aMessage, &character, sizeof(character)));
388 
389         switch (character)
390         {
391         case '9':
392             character = 'A';
393             break;
394         case 'Z':
395             character = 'a';
396             break;
397         case 'z':
398             character = '0';
399             break;
400         default:
401             character++;
402             break;
403         }
404     }
405 
406 exit:
407     return error;
408 }
409 
PrepareHexStringPayload(otMessage & aMessage,const char * aHexString)410 otError UdpExample::PrepareHexStringPayload(otMessage &aMessage, const char *aHexString)
411 {
412     static constexpr uint16_t kBufferSize = 50;
413 
414     otError  error;
415     uint8_t  buf[kBufferSize];
416     uint16_t length;
417     bool     done = false;
418 
419     while (!done)
420     {
421         length = sizeof(buf);
422         error  = ot::Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
423 
424         VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING));
425         done = (error == OT_ERROR_NONE);
426 
427         SuccessOrExit(error = otMessageAppend(&aMessage, buf, length));
428     }
429 
430 exit:
431     return error;
432 }
433 
Process(Arg aArgs[])434 otError UdpExample::Process(Arg aArgs[])
435 {
436 #define CmdEntry(aCommandString)                                  \
437     {                                                             \
438         aCommandString, &UdpExample::Process<Cmd(aCommandString)> \
439     }
440 
441     static constexpr Command kCommands[] = {
442         CmdEntry("bind"),         CmdEntry("close"), CmdEntry("connect"),
443         CmdEntry("linksecurity"), CmdEntry("open"),  CmdEntry("send"),
444     };
445 
446     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
447 
448     otError        error = OT_ERROR_INVALID_COMMAND;
449     const Command *command;
450 
451     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
452     {
453         OutputCommandTable(kCommands);
454         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
455     }
456 
457     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
458     VerifyOrExit(command != nullptr);
459 
460     error = (this->*command->mHandler)(aArgs + 1);
461 
462 exit:
463     return error;
464 }
465 
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)466 void UdpExample::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
467 {
468     static_cast<UdpExample *>(aContext)->HandleUdpReceive(aMessage, aMessageInfo);
469 }
470 
HandleUdpReceive(otMessage * aMessage,const otMessageInfo * aMessageInfo)471 void UdpExample::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo)
472 {
473     char buf[1500];
474     int  length;
475 
476     OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
477     OutputIp6Address(aMessageInfo->mPeerAddr);
478     OutputFormat(" %d ", aMessageInfo->mPeerPort);
479 
480     length      = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
481     buf[length] = '\0';
482 
483     OutputLine("%s", buf);
484 }
485 
486 } // namespace Cli
487 } // namespace ot
488