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