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