1 /*
2 * Copyright (c) 2016 Intel Corporation
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_shell);
10
11 #include <stdlib.h>
12
13 #include "net_shell_private.h"
14
15 #if defined(CONFIG_NET_UDP) && defined(CONFIG_NET_NATIVE_UDP)
16 static struct net_context *udp_ctx;
17 static const struct shell *udp_shell;
18 K_SEM_DEFINE(udp_send_wait, 0, 1);
19
udp_rcvd(struct net_context * context,struct net_pkt * pkt,union net_ip_header * ip_hdr,union net_proto_header * proto_hdr,int status,void * user_data)20 static void udp_rcvd(struct net_context *context, struct net_pkt *pkt,
21 union net_ip_header *ip_hdr,
22 union net_proto_header *proto_hdr, int status,
23 void *user_data)
24 {
25 if (pkt) {
26 size_t len = net_pkt_remaining_data(pkt);
27 uint8_t byte;
28
29 PR_SHELL(udp_shell, "Received UDP packet: ");
30 for (size_t i = 0; i < len; ++i) {
31 if (net_pkt_read_u8(pkt, &byte) < 0) {
32 break;
33 }
34
35 PR_SHELL(udp_shell, "%02x ", byte);
36 }
37 PR_SHELL(udp_shell, "\n");
38
39 net_pkt_unref(pkt);
40 }
41 }
42
udp_sent(struct net_context * context,int status,void * user_data)43 static void udp_sent(struct net_context *context, int status, void *user_data)
44 {
45 ARG_UNUSED(context);
46 ARG_UNUSED(status);
47 ARG_UNUSED(user_data);
48
49 PR_SHELL(udp_shell, "Message sent\n");
50 k_sem_give(&udp_send_wait);
51 }
52 #endif
53
cmd_net_udp_bind(const struct shell * sh,size_t argc,char * argv[])54 static int cmd_net_udp_bind(const struct shell *sh, size_t argc, char *argv[])
55 {
56 #if !defined(CONFIG_NET_UDP) || !defined(CONFIG_NET_NATIVE_UDP)
57 ARG_UNUSED(sh);
58 ARG_UNUSED(argc);
59 ARG_UNUSED(argv);
60
61 return -EOPNOTSUPP;
62 #else
63 char *addr_str = NULL;
64 char *endptr = NULL;
65 uint16_t port;
66 int ret;
67
68 struct net_if *iface;
69 struct sockaddr addr;
70 int addrlen;
71
72 if (argc < 3) {
73 PR_WARNING("Not enough arguments given for udp bind command\n");
74 return -EINVAL;
75 }
76
77 addr_str = argv[1];
78 port = strtol(argv[2], &endptr, 0);
79
80 if (endptr == argv[2]) {
81 PR_WARNING("Invalid port number\n");
82 return -EINVAL;
83 }
84
85 if (udp_ctx && net_context_is_used(udp_ctx)) {
86 PR_WARNING("Network context already in use\n");
87 return -EALREADY;
88 }
89
90 memset(&addr, 0, sizeof(addr));
91
92 ret = net_ipaddr_parse(addr_str, strlen(addr_str), &addr);
93 if (ret < 0) {
94 PR_WARNING("Cannot parse address \"%s\"\n", addr_str);
95 return ret;
96 }
97
98 ret = net_context_get(addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
99 &udp_ctx);
100 if (ret < 0) {
101 PR_WARNING("Cannot get UDP context (%d)\n", ret);
102 return ret;
103 }
104
105 udp_shell = sh;
106
107 if (IS_ENABLED(CONFIG_NET_IPV6) && addr.sa_family == AF_INET6) {
108 net_sin6(&addr)->sin6_port = htons(port);
109 addrlen = sizeof(struct sockaddr_in6);
110
111 iface = net_if_ipv6_select_src_iface(
112 &net_sin6(&addr)->sin6_addr);
113 } else if (IS_ENABLED(CONFIG_NET_IPV4) && addr.sa_family == AF_INET) {
114 net_sin(&addr)->sin_port = htons(port);
115 addrlen = sizeof(struct sockaddr_in);
116
117 iface = net_if_ipv4_select_src_iface(
118 &net_sin(&addr)->sin_addr);
119 } else {
120 PR_WARNING("IPv6 and IPv4 are disabled, cannot %s.\n", "bind");
121 goto release_ctx;
122 }
123
124 if (!iface) {
125 PR_WARNING("No interface to send to given host\n");
126 goto release_ctx;
127 }
128
129 net_context_set_iface(udp_ctx, iface);
130
131 ret = net_context_bind(udp_ctx, &addr, addrlen);
132 if (ret < 0) {
133 PR_WARNING("Binding to UDP port failed (%d)\n", ret);
134 goto release_ctx;
135 }
136
137 ret = net_context_recv(udp_ctx, udp_rcvd, K_NO_WAIT, NULL);
138 if (ret < 0) {
139 PR_WARNING("Receiving from UDP port failed (%d)\n", ret);
140 goto release_ctx;
141 }
142
143 return 0;
144
145 release_ctx:
146 ret = net_context_put(udp_ctx);
147 if (ret < 0) {
148 PR_WARNING("Cannot put UDP context (%d)\n", ret);
149 }
150
151 return 0;
152 #endif
153 }
154
cmd_net_udp_close(const struct shell * sh,size_t argc,char * argv[])155 static int cmd_net_udp_close(const struct shell *sh, size_t argc, char *argv[])
156 {
157 #if !defined(CONFIG_NET_UDP) || !defined(CONFIG_NET_NATIVE_UDP)
158 ARG_UNUSED(sh);
159 ARG_UNUSED(argc);
160 ARG_UNUSED(argv);
161
162 return -EOPNOTSUPP;
163 #else
164 int ret;
165
166 if (!udp_ctx || !net_context_is_used(udp_ctx)) {
167 PR_WARNING("Network context is not used. Cannot close.\n");
168 return -EINVAL;
169 }
170
171 ret = net_context_put(udp_ctx);
172 if (ret < 0) {
173 PR_WARNING("Cannot close UDP port (%d)\n", ret);
174 }
175
176 return 0;
177 #endif
178 }
179
cmd_net_udp_send(const struct shell * sh,size_t argc,char * argv[])180 static int cmd_net_udp_send(const struct shell *sh, size_t argc, char *argv[])
181 {
182 #if !defined(CONFIG_NET_UDP) || !defined(CONFIG_NET_NATIVE_UDP)
183 ARG_UNUSED(sh);
184 ARG_UNUSED(argc);
185 ARG_UNUSED(argv);
186
187 return -EOPNOTSUPP;
188 #else
189 char *host = NULL;
190 char *endptr = NULL;
191 uint16_t port;
192 uint8_t *payload = NULL;
193 int ret;
194
195 struct net_if *iface;
196 struct sockaddr addr;
197 int addrlen;
198
199 if (argc < 4) {
200 PR_WARNING("Not enough arguments given for udp send command\n");
201 return -EINVAL;
202 }
203
204 host = argv[1];
205 port = strtol(argv[2], &endptr, 0);
206 payload = argv[3];
207
208 if (endptr == argv[2]) {
209 PR_WARNING("Invalid port number\n");
210 return -EINVAL;
211 }
212
213 if (udp_ctx && net_context_is_used(udp_ctx)) {
214 PR_WARNING("Network context already in use\n");
215 return -EALREADY;
216 }
217
218 memset(&addr, 0, sizeof(addr));
219 ret = net_ipaddr_parse(host, strlen(host), &addr);
220 if (ret < 0) {
221 PR_WARNING("Cannot parse address \"%s\"\n", host);
222 return ret;
223 }
224
225 ret = net_context_get(addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
226 &udp_ctx);
227 if (ret < 0) {
228 PR_WARNING("Cannot get UDP context (%d)\n", ret);
229 return ret;
230 }
231
232 udp_shell = sh;
233
234 if (IS_ENABLED(CONFIG_NET_IPV6) && addr.sa_family == AF_INET6) {
235 net_sin6(&addr)->sin6_port = htons(port);
236 addrlen = sizeof(struct sockaddr_in6);
237
238 iface = net_if_ipv6_select_src_iface(
239 &net_sin6(&addr)->sin6_addr);
240 } else if (IS_ENABLED(CONFIG_NET_IPV4) && addr.sa_family == AF_INET) {
241 net_sin(&addr)->sin_port = htons(port);
242 addrlen = sizeof(struct sockaddr_in);
243
244 iface = net_if_ipv4_select_src_iface(
245 &net_sin(&addr)->sin_addr);
246 } else {
247 PR_WARNING("IPv6 and IPv4 are disabled, cannot %s.\n", "send");
248 goto release_ctx;
249 }
250
251 if (!iface) {
252 PR_WARNING("No interface to send to given host\n");
253 goto release_ctx;
254 }
255
256 net_context_set_iface(udp_ctx, iface);
257
258 ret = net_context_recv(udp_ctx, udp_rcvd, K_NO_WAIT, NULL);
259 if (ret < 0) {
260 PR_WARNING("Setting rcv callback failed (%d)\n", ret);
261 goto release_ctx;
262 }
263
264 ret = net_context_sendto(udp_ctx, payload, strlen(payload), &addr,
265 addrlen, udp_sent, K_FOREVER, NULL);
266 if (ret < 0) {
267 PR_WARNING("Sending packet failed (%d)\n", ret);
268 goto release_ctx;
269 }
270
271 ret = k_sem_take(&udp_send_wait, K_SECONDS(2));
272 if (ret == -EAGAIN) {
273 PR_WARNING("UDP packet sending failed\n");
274 }
275
276 release_ctx:
277 ret = net_context_put(udp_ctx);
278 if (ret < 0) {
279 PR_WARNING("Cannot put UDP context (%d)\n", ret);
280 }
281
282 return 0;
283 #endif
284 }
285
cmd_net_udp(const struct shell * sh,size_t argc,char * argv[])286 static int cmd_net_udp(const struct shell *sh, size_t argc, char *argv[])
287 {
288 ARG_UNUSED(sh);
289 ARG_UNUSED(argc);
290 ARG_UNUSED(argv);
291
292 return 0;
293 }
294
295 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_udp,
296 SHELL_CMD(bind, NULL,
297 "'net udp bind <addr> <port>' binds to UDP local port.",
298 cmd_net_udp_bind),
299 SHELL_CMD(close, NULL,
300 "'net udp close' closes previously bound port.",
301 cmd_net_udp_close),
302 SHELL_CMD(send, NULL,
303 "'net udp send <host> <port> <payload>' "
304 "sends UDP packet to a network host.",
305 cmd_net_udp_send),
306 SHELL_SUBCMD_SET_END
307 );
308
309 SHELL_SUBCMD_ADD((net), udp, &net_cmd_udp,
310 "Send/recv UDP packet",
311 cmd_net_udp, 1, 0);
312