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 != NULL && 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 	bool should_release_ctx = false;
195 
196 	struct net_if *iface;
197 	struct sockaddr addr;
198 	int addrlen;
199 
200 	if (argc < 4) {
201 		PR_WARNING("Not enough arguments given for udp send command\n");
202 		return -EINVAL;
203 	}
204 
205 	host = argv[1];
206 	port = strtol(argv[2], &endptr, 0);
207 	payload = argv[3];
208 
209 	if (endptr == argv[2]) {
210 		PR_WARNING("Invalid port number\n");
211 		return -EINVAL;
212 	}
213 
214 	memset(&addr, 0, sizeof(addr));
215 	ret = net_ipaddr_parse(host, strlen(host), &addr);
216 	if (ret < 0) {
217 		PR_WARNING("Cannot parse address \"%s\"\n", host);
218 		return ret;
219 	}
220 
221 	/* Re-use already bound context if possible, or allocate temporary one. */
222 	if (udp_ctx == NULL || !net_context_is_used(udp_ctx)) {
223 		ret = net_context_get(addr.sa_family, SOCK_DGRAM, IPPROTO_UDP, &udp_ctx);
224 		if (ret < 0) {
225 			PR_WARNING("Cannot get UDP context (%d)\n", ret);
226 			return ret;
227 		}
228 		should_release_ctx = true;
229 	}
230 
231 	udp_shell = sh;
232 
233 	if (IS_ENABLED(CONFIG_NET_IPV6) && addr.sa_family == AF_INET6) {
234 		net_sin6(&addr)->sin6_port = htons(port);
235 		addrlen = sizeof(struct sockaddr_in6);
236 
237 		iface = net_if_ipv6_select_src_iface(
238 				&net_sin6(&addr)->sin6_addr);
239 	} else if (IS_ENABLED(CONFIG_NET_IPV4) && addr.sa_family == AF_INET) {
240 		net_sin(&addr)->sin_port = htons(port);
241 		addrlen = sizeof(struct sockaddr_in);
242 
243 		iface = net_if_ipv4_select_src_iface(
244 				&net_sin(&addr)->sin_addr);
245 	} else {
246 		PR_WARNING("IPv6 and IPv4 are disabled, cannot %s.\n", "send");
247 		goto release_ctx;
248 	}
249 
250 	if (!iface) {
251 		PR_WARNING("No interface to send to given host\n");
252 		goto release_ctx;
253 	}
254 
255 	net_context_set_iface(udp_ctx, iface);
256 
257 	ret = net_context_recv(udp_ctx, udp_rcvd, K_NO_WAIT, NULL);
258 	if (ret < 0) {
259 		PR_WARNING("Setting rcv callback failed (%d)\n", ret);
260 		goto release_ctx;
261 	}
262 
263 	ret = net_context_sendto(udp_ctx, payload, strlen(payload), &addr,
264 				 addrlen, udp_sent, K_FOREVER, NULL);
265 	if (ret < 0) {
266 		PR_WARNING("Sending packet failed (%d)\n", ret);
267 		goto release_ctx;
268 	}
269 
270 	ret = k_sem_take(&udp_send_wait, K_SECONDS(2));
271 	if (ret == -EAGAIN) {
272 		PR_WARNING("UDP packet sending failed\n");
273 	}
274 
275 release_ctx:
276 	if (should_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 
283 	return 0;
284 #endif
285 }
286 
cmd_net_udp(const struct shell * sh,size_t argc,char * argv[])287 static int cmd_net_udp(const struct shell *sh, size_t argc, char *argv[])
288 {
289 	ARG_UNUSED(sh);
290 	ARG_UNUSED(argc);
291 	ARG_UNUSED(argv);
292 
293 	return 0;
294 }
295 
296 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_udp,
297 	SHELL_CMD(bind, NULL,
298 		  "'net udp bind <addr> <port>' binds to UDP local port.",
299 		  cmd_net_udp_bind),
300 	SHELL_CMD(close, NULL,
301 		  "'net udp close' closes previously bound port.",
302 		  cmd_net_udp_close),
303 	SHELL_CMD(send, NULL,
304 		  "'net udp send <host> <port> <payload>' "
305 		  "sends UDP packet to a network host.",
306 		  cmd_net_udp_send),
307 	SHELL_SUBCMD_SET_END
308 );
309 
310 SHELL_SUBCMD_ADD((net), udp, &net_cmd_udp,
311 		 "Send/recv UDP packet",
312 		 cmd_net_udp, 1, 0);
313