1 /* udp.c - UDP specific code for echo server */
2 
3 /*
4  * Copyright (c) 2017 Intel Corporation.
5  * Copyright (c) 2018 Nordic Semiconductor ASA.
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_DECLARE(net_echo_server_sample, LOG_LEVEL_DBG);
12 
13 #include <zephyr/kernel.h>
14 #include <errno.h>
15 #include <stdio.h>
16 
17 #include <zephyr/posix/sys/socket.h>
18 #include <zephyr/posix/unistd.h>
19 
20 #include <zephyr/net/socket.h>
21 #include <zephyr/net/tls_credentials.h>
22 
23 #include "common.h"
24 #include "certificate.h"
25 
26 static void process_udp4(void);
27 static void process_udp6(void);
28 
29 K_THREAD_DEFINE(udp4_thread_id, STACK_SIZE,
30 		process_udp4, NULL, NULL, NULL,
31 		THREAD_PRIORITY,
32 		IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0, -1);
33 
34 K_THREAD_DEFINE(udp6_thread_id, STACK_SIZE,
35 		process_udp6, NULL, NULL, NULL,
36 		THREAD_PRIORITY,
37 		IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0, -1);
38 
start_udp_proto(struct data * data,struct sockaddr * bind_addr,socklen_t bind_addrlen)39 static int start_udp_proto(struct data *data, struct sockaddr *bind_addr,
40 			   socklen_t bind_addrlen)
41 {
42 	int optval;
43 	int ret;
44 
45 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
46 	data->udp.sock = socket(bind_addr->sa_family, SOCK_DGRAM,
47 				IPPROTO_DTLS_1_2);
48 #else
49 	data->udp.sock = socket(bind_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
50 #endif
51 	if (data->udp.sock < 0) {
52 		NET_ERR("Failed to create UDP socket (%s): %d", data->proto,
53 			errno);
54 		return -errno;
55 	}
56 
57 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
58 	sec_tag_t sec_tag_list[] = {
59 		SERVER_CERTIFICATE_TAG,
60 #if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
61 		PSK_TAG,
62 #endif
63 	};
64 	int role = TLS_DTLS_ROLE_SERVER;
65 
66 	ret = setsockopt(data->udp.sock, SOL_TLS, TLS_SEC_TAG_LIST,
67 			 sec_tag_list, sizeof(sec_tag_list));
68 	if (ret < 0) {
69 		NET_ERR("Failed to set UDP secure option (%s): %d", data->proto,
70 			errno);
71 		ret = -errno;
72 	}
73 
74 	/* Set role to DTLS server. */
75 	ret = setsockopt(data->udp.sock, SOL_TLS, TLS_DTLS_ROLE,
76 			 &role, sizeof(role));
77 	if (ret < 0) {
78 		NET_ERR("Failed to set DTLS role secure option (%s): %d",
79 			data->proto, errno);
80 		ret = -errno;
81 	}
82 #endif
83 
84 	if (bind_addr->sa_family == AF_INET6) {
85 		/* Prefer IPv6 temporary addresses */
86 		optval = IPV6_PREFER_SRC_PUBLIC;
87 		(void)setsockopt(data->udp.sock, IPPROTO_IPV6,
88 				 IPV6_ADDR_PREFERENCES,
89 				 &optval, sizeof(optval));
90 
91 		/*
92 		 * Bind only to IPv6 without mapping to IPv4, since we bind to
93 		 * IPv4 using another socket
94 		 */
95 		optval = 1;
96 		(void)setsockopt(data->udp.sock, IPPROTO_IPV6, IPV6_V6ONLY,
97 				 &optval, sizeof(optval));
98 	}
99 
100 	ret = bind(data->udp.sock, bind_addr, bind_addrlen);
101 	if (ret < 0) {
102 		NET_ERR("Failed to bind UDP socket (%s): %d", data->proto,
103 			errno);
104 		ret = -errno;
105 	}
106 
107 	return ret;
108 }
109 
process_udp(struct data * data)110 static int process_udp(struct data *data)
111 {
112 	int ret = 0;
113 	int received;
114 	struct sockaddr client_addr;
115 	socklen_t client_addr_len;
116 
117 	NET_INFO("Waiting for UDP packets on port %d (%s)...",
118 		 MY_PORT, data->proto);
119 
120 	do {
121 		client_addr_len = sizeof(client_addr);
122 		received = recvfrom(data->udp.sock, data->udp.recv_buffer,
123 				    sizeof(data->udp.recv_buffer), 0,
124 				    &client_addr, &client_addr_len);
125 
126 		if (received < 0) {
127 			/* Socket error */
128 			NET_ERR("UDP (%s): Connection error %d", data->proto,
129 				errno);
130 			ret = -errno;
131 			break;
132 		} else if (received) {
133 			atomic_add(&data->udp.bytes_received, received);
134 		}
135 
136 		ret = sendto(data->udp.sock, data->udp.recv_buffer, received, 0,
137 			     &client_addr, client_addr_len);
138 		if (ret < 0) {
139 			NET_ERR("UDP (%s): Failed to send %d", data->proto,
140 				errno);
141 			ret = -errno;
142 			break;
143 		}
144 
145 		if (++data->udp.counter % 1000 == 0U) {
146 			NET_INFO("%s UDP: Sent %u packets", data->proto,
147 				 data->udp.counter);
148 		}
149 
150 		NET_DBG("UDP (%s): Received and replied with %d bytes",
151 			data->proto, received);
152 	} while (true);
153 
154 	return ret;
155 }
156 
process_udp4(void)157 static void process_udp4(void)
158 {
159 	int ret;
160 	struct sockaddr_in addr4;
161 
162 	(void)memset(&addr4, 0, sizeof(addr4));
163 	addr4.sin_family = AF_INET;
164 	addr4.sin_port = htons(MY_PORT);
165 
166 	ret = start_udp_proto(&conf.ipv4, (struct sockaddr *)&addr4,
167 			      sizeof(addr4));
168 	if (ret < 0) {
169 		quit();
170 		return;
171 	}
172 
173 	while (ret == 0) {
174 		ret = process_udp(&conf.ipv4);
175 		if (ret < 0) {
176 			quit();
177 		}
178 	}
179 }
180 
process_udp6(void)181 static void process_udp6(void)
182 {
183 	int ret;
184 	struct sockaddr_in6 addr6;
185 
186 	(void)memset(&addr6, 0, sizeof(addr6));
187 	addr6.sin6_family = AF_INET6;
188 	addr6.sin6_port = htons(MY_PORT);
189 
190 	ret = start_udp_proto(&conf.ipv6, (struct sockaddr *)&addr6,
191 			      sizeof(addr6));
192 	if (ret < 0) {
193 		quit();
194 		return;
195 	}
196 
197 	while (ret == 0) {
198 		ret = process_udp(&conf.ipv6);
199 		if (ret < 0) {
200 			quit();
201 		}
202 	}
203 }
204 
print_stats(struct k_work * work)205 static void print_stats(struct k_work *work)
206 {
207 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
208 	struct data *data = CONTAINER_OF(dwork, struct data, udp.stats_print);
209 	int total_received = atomic_get(&data->udp.bytes_received);
210 
211 	if (total_received) {
212 		if ((total_received / STATS_TIMER) < 1024) {
213 			LOG_INF("%s UDP: Received %d B/sec", data->proto,
214 				total_received / STATS_TIMER);
215 		} else {
216 			LOG_INF("%s UDP: Received %d KiB/sec", data->proto,
217 				total_received / 1024 / STATS_TIMER);
218 		}
219 
220 		atomic_set(&data->udp.bytes_received, 0);
221 	}
222 
223 	k_work_reschedule(&data->udp.stats_print, K_SECONDS(STATS_TIMER));
224 }
225 
start_udp(void)226 void start_udp(void)
227 {
228 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
229 #if defined(CONFIG_USERSPACE)
230 		k_mem_domain_add_thread(&app_domain, udp6_thread_id);
231 #endif
232 
233 		k_work_init_delayable(&conf.ipv6.udp.stats_print, print_stats);
234 		k_thread_name_set(udp6_thread_id, "udp6");
235 		k_thread_start(udp6_thread_id);
236 		k_work_reschedule(&conf.ipv6.udp.stats_print,
237 				  K_SECONDS(STATS_TIMER));
238 	}
239 
240 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
241 #if defined(CONFIG_USERSPACE)
242 		k_mem_domain_add_thread(&app_domain, udp4_thread_id);
243 #endif
244 
245 		k_work_init_delayable(&conf.ipv4.udp.stats_print, print_stats);
246 		k_thread_name_set(udp4_thread_id, "udp4");
247 		k_thread_start(udp4_thread_id);
248 		k_work_reschedule(&conf.ipv4.udp.stats_print,
249 				  K_SECONDS(STATS_TIMER));
250 	}
251 }
252 
stop_udp(void)253 void stop_udp(void)
254 {
255 	/* Not very graceful way to close a thread, but as we may be blocked
256 	 * in recvfrom call it seems to be necessary
257 	 */
258 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
259 		k_thread_abort(udp6_thread_id);
260 		if (conf.ipv6.udp.sock >= 0) {
261 			(void)close(conf.ipv6.udp.sock);
262 		}
263 	}
264 
265 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
266 		k_thread_abort(udp4_thread_id);
267 		if (conf.ipv4.udp.sock >= 0) {
268 			(void)close(conf.ipv4.udp.sock);
269 		}
270 	}
271 }
272