1 /* echo-client.c - Networking echo client */
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 /*
11 * The echo-client application is acting as a client that is run in Zephyr OS,
12 * and echo-server is run in the host acting as a server. The client will send
13 * either unicast or multicast packets to the server which will reply the packet
14 * back to the originator.
15 *
16 * In this sample application we create four threads that start to send data.
17 * This might not be what you want to do in your app so caveat emptor.
18 */
19
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(net_echo_client_sample, LOG_LEVEL_DBG);
22
23 #include <zephyr/kernel.h>
24 #include <errno.h>
25 #include <stdio.h>
26
27 #include <zephyr/posix/sys/eventfd.h>
28 #include <zephyr/posix/arpa/inet.h>
29 #include <zephyr/posix/poll.h>
30
31 #include <zephyr/misc/lorem_ipsum.h>
32 #include <zephyr/net/socket.h>
33 #include <zephyr/net/tls_credentials.h>
34 #include <zephyr/net/net_if.h>
35 #include <zephyr/net/net_mgmt.h>
36 #include <zephyr/net/net_event.h>
37 #include <zephyr/net/conn_mgr_monitor.h>
38
39 #if defined(CONFIG_USERSPACE)
40 #include <zephyr/app_memory/app_memdomain.h>
41 K_APPMEM_PARTITION_DEFINE(app_partition);
42 struct k_mem_domain app_domain;
43 #endif
44
45 #include "common.h"
46 #include "ca_certificate.h"
47
48 #define APP_BANNER "Run echo client"
49
50 #define INVALID_SOCK (-1)
51
52 #define EVENT_MASK (NET_EVENT_L4_CONNECTED | \
53 NET_EVENT_L4_DISCONNECTED)
54 #define IPV6_EVENT_MASK (NET_EVENT_IPV6_ADDR_ADD | \
55 NET_EVENT_IPV6_ADDR_DEPRECATED)
56
57 const char lorem_ipsum[] = LOREM_IPSUM;
58
59 const int ipsum_len = sizeof(lorem_ipsum) - 1;
60
61 APP_DMEM struct configs conf = {
62 .ipv4 = {
63 .proto = "IPv4",
64 .udp.sock = INVALID_SOCK,
65 .tcp.sock = INVALID_SOCK,
66 },
67 .ipv6 = {
68 .proto = "IPv6",
69 .udp.sock = INVALID_SOCK,
70 .tcp.sock = INVALID_SOCK,
71 },
72 };
73
74 static APP_BMEM struct pollfd fds[1 + 4];
75 static APP_BMEM int nfds;
76
77 static APP_BMEM bool connected;
78 static APP_BMEM bool need_restart;
79
80 K_SEM_DEFINE(run_app, 0, 1);
81
82 static struct net_mgmt_event_callback mgmt_cb;
83 static struct net_mgmt_event_callback ipv6_mgmt_cb;
84
prepare_fds(void)85 static void prepare_fds(void)
86 {
87 nfds = 0;
88
89 /* eventfd is used to trigger restart */
90 fds[nfds].fd = eventfd(0, 0);
91 fds[nfds].events = POLLIN;
92 nfds++;
93
94 if (conf.ipv4.udp.sock >= 0) {
95 fds[nfds].fd = conf.ipv4.udp.sock;
96 fds[nfds].events = POLLIN;
97 nfds++;
98 }
99
100 if (conf.ipv4.tcp.sock >= 0) {
101 fds[nfds].fd = conf.ipv4.tcp.sock;
102 fds[nfds].events = POLLIN;
103 nfds++;
104 }
105
106 if (conf.ipv6.udp.sock >= 0) {
107 fds[nfds].fd = conf.ipv6.udp.sock;
108 fds[nfds].events = POLLIN;
109 nfds++;
110 }
111
112 if (conf.ipv6.tcp.sock >= 0) {
113 fds[nfds].fd = conf.ipv6.tcp.sock;
114 fds[nfds].events = POLLIN;
115 nfds++;
116 }
117 }
118
wait(void)119 static void wait(void)
120 {
121 int ret;
122
123 /* Wait for event on any socket used. Once event occurs,
124 * we'll check them all.
125 */
126 ret = poll(fds, nfds, -1);
127 if (ret < 0) {
128 static bool once;
129
130 if (!once) {
131 once = true;
132 LOG_ERR("Error in poll:%d", errno);
133 }
134
135 return;
136 }
137
138 if (ret > 0 && fds[0].revents) {
139 eventfd_t value;
140
141 eventfd_read(fds[0].fd, &value);
142 LOG_DBG("Received restart event.");
143 return;
144 }
145 }
146
start_udp_and_tcp(void)147 static int start_udp_and_tcp(void)
148 {
149 int ret;
150
151 LOG_INF("Starting...");
152
153 if (IS_ENABLED(CONFIG_NET_TCP)) {
154 ret = start_tcp();
155 if (ret < 0) {
156 return ret;
157 }
158 }
159
160 if (IS_ENABLED(CONFIG_NET_UDP)) {
161 ret = start_udp();
162 if (ret < 0) {
163 return ret;
164 }
165 }
166
167 prepare_fds();
168
169 return 0;
170 }
171
run_udp_and_tcp(void)172 static int run_udp_and_tcp(void)
173 {
174 int ret;
175
176 wait();
177
178 if (IS_ENABLED(CONFIG_NET_TCP)) {
179 ret = process_tcp();
180 if (ret < 0) {
181 return ret;
182 }
183 }
184
185 if (IS_ENABLED(CONFIG_NET_UDP)) {
186 ret = process_udp();
187 if (ret < 0) {
188 return ret;
189 }
190 }
191
192 return 0;
193 }
194
stop_udp_and_tcp(void)195 static void stop_udp_and_tcp(void)
196 {
197 LOG_INF("Stopping...");
198
199 if (IS_ENABLED(CONFIG_NET_UDP)) {
200 stop_udp();
201 }
202
203 if (IS_ENABLED(CONFIG_NET_TCP)) {
204 stop_tcp();
205 }
206 }
207
check_our_ipv6_sockets(int sock,struct in6_addr * deprecated_addr)208 static int check_our_ipv6_sockets(int sock,
209 struct in6_addr *deprecated_addr)
210 {
211 struct sockaddr_in6 addr = { 0 };
212 socklen_t addrlen = sizeof(addr);
213 int ret;
214
215 if (sock < 0) {
216 return -EINVAL;
217 }
218
219 ret = getsockname(sock, (struct sockaddr *)&addr, &addrlen);
220 if (ret != 0) {
221 return -errno;
222 }
223
224 if (!net_ipv6_addr_cmp(deprecated_addr, &addr.sin6_addr)) {
225 return -ENOENT;
226 }
227
228 need_restart = true;
229
230 return 0;
231 }
232
ipv6_event_handler(struct net_mgmt_event_callback * cb,uint64_t mgmt_event,struct net_if * iface)233 static void ipv6_event_handler(struct net_mgmt_event_callback *cb,
234 uint64_t mgmt_event, struct net_if *iface)
235 {
236 static char addr_str[INET6_ADDRSTRLEN];
237
238 if (!IS_ENABLED(CONFIG_NET_IPV6_PE)) {
239 return;
240 }
241
242 if ((mgmt_event & IPV6_EVENT_MASK) != mgmt_event) {
243 return;
244 }
245
246 if (cb->info == NULL ||
247 cb->info_length != sizeof(struct in6_addr)) {
248 return;
249 }
250
251 if (mgmt_event == NET_EVENT_IPV6_ADDR_ADD) {
252 struct net_if_addr *ifaddr;
253 struct in6_addr added_addr;
254
255 memcpy(&added_addr, cb->info, sizeof(struct in6_addr));
256
257 ifaddr = net_if_ipv6_addr_lookup(&added_addr, &iface);
258 if (ifaddr == NULL) {
259 return;
260 }
261
262 /* Wait until we get a temporary address before continuing after
263 * boot.
264 */
265 if (ifaddr->is_temporary) {
266 static bool once;
267
268 LOG_INF("Temporary IPv6 address %s added",
269 inet_ntop(AF_INET6, &added_addr, addr_str,
270 sizeof(addr_str) - 1));
271
272 if (!once) {
273 k_sem_give(&run_app);
274 once = true;
275 }
276 }
277 }
278
279 if (mgmt_event == NET_EVENT_IPV6_ADDR_DEPRECATED) {
280 struct in6_addr deprecated_addr;
281
282 memcpy(&deprecated_addr, cb->info, sizeof(struct in6_addr));
283
284 LOG_INF("IPv6 address %s deprecated",
285 inet_ntop(AF_INET6, &deprecated_addr, addr_str,
286 sizeof(addr_str) - 1));
287
288 (void)check_our_ipv6_sockets(conf.ipv6.tcp.sock,
289 &deprecated_addr);
290 (void)check_our_ipv6_sockets(conf.ipv6.udp.sock,
291 &deprecated_addr);
292
293 if (need_restart) {
294 eventfd_write(fds[0].fd, 1);
295 }
296
297 return;
298 }
299 }
300
event_handler(struct net_mgmt_event_callback * cb,uint64_t mgmt_event,struct net_if * iface)301 static void event_handler(struct net_mgmt_event_callback *cb,
302 uint64_t mgmt_event, struct net_if *iface)
303 {
304 if ((mgmt_event & EVENT_MASK) != mgmt_event) {
305 return;
306 }
307
308 if (mgmt_event == NET_EVENT_L4_CONNECTED) {
309 LOG_INF("Network connected");
310
311 connected = true;
312 conf.ipv4.udp.mtu = net_if_get_mtu(iface);
313 conf.ipv6.udp.mtu = conf.ipv4.udp.mtu;
314
315 if (!IS_ENABLED(CONFIG_NET_IPV6_PE)) {
316 k_sem_give(&run_app);
317 }
318
319 return;
320 }
321
322 if (mgmt_event == NET_EVENT_L4_DISCONNECTED) {
323 LOG_INF("Network disconnected");
324
325 connected = false;
326 k_sem_reset(&run_app);
327
328 return;
329 }
330 }
331
init_app(void)332 static void init_app(void)
333 {
334 LOG_INF(APP_BANNER);
335
336 #if defined(CONFIG_USERSPACE)
337 struct k_mem_partition *parts[] = {
338 #if Z_LIBC_PARTITION_EXISTS
339 &z_libc_partition,
340 #endif
341 &app_partition
342 };
343
344 int ret = k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
345
346 __ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
347 ARG_UNUSED(ret);
348 #endif
349
350 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
351 int err = tls_credential_add(CA_CERTIFICATE_TAG,
352 TLS_CREDENTIAL_CA_CERTIFICATE,
353 ca_certificate,
354 sizeof(ca_certificate));
355 if (err < 0) {
356 LOG_ERR("Failed to register public certificate: %d", err);
357 }
358
359 #if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
360 err = tls_credential_add(PSK_TAG,
361 TLS_CREDENTIAL_PSK,
362 psk,
363 sizeof(psk));
364 if (err < 0) {
365 LOG_ERR("Failed to register PSK: %d", err);
366 }
367 err = tls_credential_add(PSK_TAG,
368 TLS_CREDENTIAL_PSK_ID,
369 psk_id,
370 sizeof(psk_id) - 1);
371 if (err < 0) {
372 LOG_ERR("Failed to register PSK ID: %d", err);
373 }
374 #endif /* defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) */
375 #endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
376
377
378 if (IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) {
379 net_mgmt_init_event_callback(&mgmt_cb,
380 event_handler, EVENT_MASK);
381 net_mgmt_add_event_callback(&mgmt_cb);
382
383 conn_mgr_mon_resend_status();
384 }
385
386 net_mgmt_init_event_callback(&ipv6_mgmt_cb,
387 ipv6_event_handler, IPV6_EVENT_MASK);
388 net_mgmt_add_event_callback(&ipv6_mgmt_cb);
389
390 init_vlan();
391 init_udp();
392 }
393
start_client(void * p1,void * p2,void * p3)394 static void start_client(void *p1, void *p2, void *p3)
395 {
396 ARG_UNUSED(p1);
397 ARG_UNUSED(p2);
398 ARG_UNUSED(p3);
399
400 int iterations = CONFIG_NET_SAMPLE_SEND_ITERATIONS;
401 int i = 0;
402 int ret;
403
404 while (iterations == 0 || i < iterations) {
405 /* Wait for the connection. */
406 k_sem_take(&run_app, K_FOREVER);
407
408 if (IS_ENABLED(CONFIG_NET_IPV6_PE)) {
409 /* Make sure that we have a temporary address */
410 k_sleep(K_SECONDS(1));
411 }
412
413 do {
414 if (need_restart) {
415 /* Close all sockets and get a fresh restart */
416 stop_udp_and_tcp();
417 need_restart = false;
418 }
419
420 ret = start_udp_and_tcp();
421
422 while (connected && (ret == 0)) {
423 ret = run_udp_and_tcp();
424
425 if (iterations > 0) {
426 i++;
427 if (i >= iterations) {
428 break;
429 }
430 }
431
432 if (need_restart) {
433 break;
434 }
435 }
436 } while (need_restart);
437
438 stop_udp_and_tcp();
439 }
440 }
441
main(void)442 int main(void)
443 {
444 init_app();
445
446 if (!IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) {
447 /* If the config library has not been configured to start the
448 * app only after we have a connection, then we can start
449 * it right away.
450 */
451 connected = true;
452 k_sem_give(&run_app);
453 }
454
455 k_thread_priority_set(k_current_get(), THREAD_PRIORITY);
456
457 #if defined(CONFIG_USERSPACE)
458 k_thread_access_grant(k_current_get(), &run_app);
459 k_mem_domain_add_thread(&app_domain, k_current_get());
460
461 k_thread_user_mode_enter(start_client, NULL, NULL, NULL);
462 #else
463 start_client(NULL, NULL, NULL);
464 #endif
465 return 0;
466 }
467