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