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`: IPv6 address to bind to. If you wish to have the UDP/IPv6 stack assign the binding
74 * IPv6 address, then you can use the following value to use the unspecified
75 * 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 = Interpreter::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 ClearAllBytes(messageInfo);
260
261 // Possible argument formats:
262 //
263 // send <text>
264 // send <type> <value>
265 // send <ip> <port> <text>
266 // send <ip> <port> <type> <value>
267
268 if (!aArgs[2].IsEmpty())
269 {
270 bool nat64Synth;
271
272 SuccessOrExit(
273 error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr, nat64Synth));
274
275 if (nat64Synth)
276 {
277 OutputFormat("Sending to synthesized IPv6 address: ");
278 OutputIp6AddressLine(messageInfo.mPeerAddr);
279 }
280
281 SuccessOrExit(error = aArgs[1].ParseAsUint16(messageInfo.mPeerPort));
282 aArgs += 2;
283 }
284
285 message = otUdpNewMessage(GetInstancePtr(), &messageSettings);
286 VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
287
288 if (aArgs[0] == "-s")
289 {
290 // Auto-generated payload with a given length
291
292 uint16_t payloadLength;
293
294 SuccessOrExit(error = aArgs[1].ParseAsUint16(payloadLength));
295 SuccessOrExit(error = PrepareAutoGeneratedPayload(*message, payloadLength));
296 }
297 else if (aArgs[0] == "-x")
298 {
299 // Binary hex data payload
300
301 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
302 SuccessOrExit(error = PrepareHexStringPayload(*message, aArgs[1].GetCString()));
303 }
304 else
305 {
306 // Text payload (same as without specifying the type)
307
308 if (aArgs[0] == "-t")
309 {
310 aArgs++;
311 }
312
313 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
314 SuccessOrExit(error = otMessageAppend(message, aArgs[0].GetCString(), aArgs[0].GetLength()));
315 }
316
317 SuccessOrExit(error = otUdpSend(GetInstancePtr(), &mSocket, message, &messageInfo));
318
319 message = nullptr;
320
321 exit:
322 if (message != nullptr)
323 {
324 otMessageFree(message);
325 }
326
327 return error;
328 }
329
Process(Arg aArgs[])330 template <> otError UdpExample::Process<Cmd("linksecurity")>(Arg aArgs[])
331 {
332 otError error = OT_ERROR_NONE;
333
334 /**
335 * @cli udp linksecurity
336 * @code
337 * udp linksecurity
338 * Enabled
339 * Done
340 * @endcode
341 * @par
342 * Indicates whether link security is enabled or disabled.
343 */
344 if (aArgs[0].IsEmpty())
345 {
346 OutputEnabledDisabledStatus(mLinkSecurityEnabled);
347 }
348 /**
349 * @cli udp linksecurity (enable,disable)
350 * @code
351 * udp linksecurity enable
352 * Done
353 * @endcode
354 * @code
355 * udp linksecurity disable
356 * Done
357 * @endcode
358 * @par
359 * Enables or disables link security.
360 */
361 else
362 {
363 error = Interpreter::ParseEnableOrDisable(aArgs[0], mLinkSecurityEnabled);
364 }
365
366 return error;
367 }
368
PrepareAutoGeneratedPayload(otMessage & aMessage,uint16_t aPayloadLength)369 otError UdpExample::PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength)
370 {
371 otError error = OT_ERROR_NONE;
372 uint8_t character = '0';
373
374 for (; aPayloadLength != 0; aPayloadLength--)
375 {
376 SuccessOrExit(error = otMessageAppend(&aMessage, &character, sizeof(character)));
377
378 switch (character)
379 {
380 case '9':
381 character = 'A';
382 break;
383 case 'Z':
384 character = 'a';
385 break;
386 case 'z':
387 character = '0';
388 break;
389 default:
390 character++;
391 break;
392 }
393 }
394
395 exit:
396 return error;
397 }
398
PrepareHexStringPayload(otMessage & aMessage,const char * aHexString)399 otError UdpExample::PrepareHexStringPayload(otMessage &aMessage, const char *aHexString)
400 {
401 static constexpr uint16_t kBufferSize = 50;
402
403 otError error;
404 uint8_t buf[kBufferSize];
405 uint16_t length;
406 bool done = false;
407
408 while (!done)
409 {
410 length = sizeof(buf);
411 error = ot::Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
412
413 VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING));
414 done = (error == OT_ERROR_NONE);
415
416 SuccessOrExit(error = otMessageAppend(&aMessage, buf, length));
417 }
418
419 exit:
420 return error;
421 }
422
Process(Arg aArgs[])423 otError UdpExample::Process(Arg aArgs[])
424 {
425 #define CmdEntry(aCommandString) \
426 { \
427 aCommandString, &UdpExample::Process<Cmd(aCommandString)> \
428 }
429
430 static constexpr Command kCommands[] = {
431 CmdEntry("bind"), CmdEntry("close"), CmdEntry("connect"),
432 CmdEntry("linksecurity"), CmdEntry("open"), CmdEntry("send"),
433 };
434
435 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
436
437 otError error = OT_ERROR_INVALID_COMMAND;
438 const Command *command;
439
440 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
441 {
442 OutputCommandTable(kCommands);
443 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
444 }
445
446 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
447 VerifyOrExit(command != nullptr);
448
449 error = (this->*command->mHandler)(aArgs + 1);
450
451 exit:
452 return error;
453 }
454
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)455 void UdpExample::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
456 {
457 static_cast<UdpExample *>(aContext)->HandleUdpReceive(aMessage, aMessageInfo);
458 }
459
HandleUdpReceive(otMessage * aMessage,const otMessageInfo * aMessageInfo)460 void UdpExample::HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo)
461 {
462 char buf[1500];
463 int length;
464
465 OutputFormat("%d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
466 OutputIp6Address(aMessageInfo->mPeerAddr);
467 OutputFormat(" %d ", aMessageInfo->mPeerPort);
468
469 length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
470 buf[length] = '\0';
471
472 OutputLine("%s", buf);
473 }
474
475 } // namespace Cli
476 } // namespace ot
477