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