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