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