1 /** @file
2 * @brief ICMP related functions
3 */
4
5 /*
6 * Copyright (c) 2023 Nordic Semiconductor ASA
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 /* Use highest log level if both IPv4 and IPv6 are defined */
12 #if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6)
13
14 #if CONFIG_NET_ICMPV4_LOG_LEVEL > CONFIG_NET_ICMPV6_LOG_LEVEL
15 #define ICMP_LOG_LEVEL CONFIG_NET_ICMPV4_LOG_LEVEL
16 #else
17 #define ICMP_LOG_LEVEL CONFIG_NET_ICMPV6_LOG_LEVEL
18 #endif
19
20 #elif defined(CONFIG_NET_IPV4)
21 #define ICMP_LOG_LEVEL CONFIG_NET_ICMPV4_LOG_LEVEL
22 #elif defined(CONFIG_NET_IPV6)
23 #define ICMP_LOG_LEVEL CONFIG_NET_ICMPV6_LOG_LEVEL
24 #else
25 #define ICMP_LOG_LEVEL LOG_LEVEL_INF
26 #endif
27
28 #include <zephyr/logging/log.h>
29 LOG_MODULE_REGISTER(net_icmp, ICMP_LOG_LEVEL);
30
31 #include <errno.h>
32 #include <zephyr/random/random.h>
33 #include <zephyr/sys/slist.h>
34 #include <zephyr/net/net_pkt.h>
35 #include <zephyr/net/icmp.h>
36
37 #include "net_private.h"
38 #include "icmpv6.h"
39 #include "icmpv4.h"
40 #include "ipv4.h"
41 #include "ipv6.h"
42 #include "net_stats.h"
43
44 static K_MUTEX_DEFINE(lock);
45 static sys_slist_t handlers = SYS_SLIST_STATIC_INIT(&handlers);
46
47 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
48 static sys_slist_t offload_handlers = SYS_SLIST_STATIC_INIT(&offload_handlers);
49 #endif
50
51 #define PKT_WAIT_TIME K_SECONDS(1)
52
net_icmp_init_ctx(struct net_icmp_ctx * ctx,uint8_t type,uint8_t code,net_icmp_handler_t handler)53 int net_icmp_init_ctx(struct net_icmp_ctx *ctx, uint8_t type, uint8_t code,
54 net_icmp_handler_t handler)
55 {
56 if (ctx == NULL || handler == NULL) {
57 return -EINVAL;
58 }
59
60 memset(ctx, 0, sizeof(struct net_icmp_ctx));
61
62 ctx->handler = handler;
63 ctx->type = type;
64 ctx->code = code;
65
66 k_mutex_lock(&lock, K_FOREVER);
67
68 sys_slist_prepend(&handlers, &ctx->node);
69
70 k_mutex_unlock(&lock);
71
72 return 0;
73 }
74
set_offload_handler(struct net_if * iface,net_icmp_handler_t handler)75 static void set_offload_handler(struct net_if *iface,
76 net_icmp_handler_t handler)
77 {
78 struct net_icmp_offload *offload;
79
80 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
81 return;
82 }
83
84 k_mutex_lock(&lock, K_FOREVER);
85
86 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
87 SYS_SLIST_FOR_EACH_CONTAINER(&offload_handlers, offload, node) {
88 if (offload->iface == iface) {
89 offload->handler = handler;
90 break;
91 }
92 }
93 #else
94 ARG_UNUSED(offload);
95 #endif
96
97 k_mutex_unlock(&lock);
98 }
99
net_icmp_cleanup_ctx(struct net_icmp_ctx * ctx)100 int net_icmp_cleanup_ctx(struct net_icmp_ctx *ctx)
101 {
102 if (ctx == NULL) {
103 return -EINVAL;
104 }
105
106 k_mutex_lock(&lock, K_FOREVER);
107
108 sys_slist_find_and_remove(&handlers, &ctx->node);
109
110 k_mutex_unlock(&lock);
111
112 set_offload_handler(ctx->iface, NULL);
113
114 memset(ctx, 0, sizeof(struct net_icmp_ctx));
115
116 return 0;
117 }
118
119 #if defined(CONFIG_NET_IPV4)
send_icmpv4_echo_request(struct net_icmp_ctx * ctx,struct net_if * iface,struct in_addr * dst,struct net_icmp_ping_params * params,void * user_data,k_timeout_t timeout)120 static int send_icmpv4_echo_request(struct net_icmp_ctx *ctx,
121 struct net_if *iface,
122 struct in_addr *dst,
123 struct net_icmp_ping_params *params,
124 void *user_data,
125 k_timeout_t timeout)
126 {
127 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access,
128 struct net_icmpv4_echo_req);
129 int ret = -ENOBUFS;
130 struct net_icmpv4_echo_req *echo_req;
131 const struct in_addr *src;
132 struct net_pkt *pkt;
133
134 if (!iface->config.ip.ipv4) {
135 return -ENETUNREACH;
136 }
137
138 src = net_if_ipv4_select_src_addr(iface, dst);
139
140 pkt = net_pkt_alloc_with_buffer(iface,
141 sizeof(struct net_icmpv4_echo_req)
142 + params->data_size,
143 AF_INET, IPPROTO_ICMP,
144 timeout);
145 if (!pkt) {
146 return -ENOMEM;
147 }
148
149 if (!IS_ENABLED(CONFIG_NET_ALLOW_ANY_PRIORITY) &&
150 params->priority >= NET_MAX_PRIORITIES) {
151 NET_ERR("Priority %d is too large, maximum allowed is %d",
152 params->priority, NET_MAX_PRIORITIES - 1);
153 ret = -EINVAL;
154 goto drop;
155 }
156
157 if (params->priority < 0) {
158 net_pkt_set_ip_dscp(pkt, net_ipv4_get_dscp(params->tc_tos));
159 net_pkt_set_ip_ecn(pkt, net_ipv4_get_ecn(params->tc_tos));
160 } else {
161 net_pkt_set_priority(pkt, params->priority);
162 }
163
164 if (net_ipv4_create(pkt, src, dst) ||
165 net_icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0)) {
166 goto drop;
167 }
168
169 echo_req = (struct net_icmpv4_echo_req *)net_pkt_get_data(
170 pkt, &icmpv4_access);
171 if (!echo_req) {
172 goto drop;
173 }
174
175 echo_req->identifier = htons(params->identifier);
176 echo_req->sequence = htons(params->sequence);
177
178 net_pkt_set_data(pkt, &icmpv4_access);
179
180 if (params->data != NULL && params->data_size > 0) {
181 net_pkt_write(pkt, params->data, params->data_size);
182 } else if (params->data == NULL && params->data_size > 0) {
183 /* Generate payload. */
184 if (params->data_size >= sizeof(uint32_t)) {
185 uint32_t time_stamp = htonl(k_cycle_get_32());
186
187 net_pkt_write(pkt, &time_stamp, sizeof(time_stamp));
188 params->data_size -= sizeof(time_stamp);
189 }
190
191 for (size_t i = 0; i < params->data_size; i++) {
192 net_pkt_write_u8(pkt, (uint8_t)i);
193 }
194 } else {
195 /* No payload. */
196 }
197
198 net_pkt_cursor_init(pkt);
199
200 net_ipv4_finalize(pkt, IPPROTO_ICMP);
201
202 NET_DBG("Sending ICMPv4 Echo Request type %d from %s to %s",
203 NET_ICMPV4_ECHO_REQUEST,
204 net_sprint_ipv4_addr(src),
205 net_sprint_ipv4_addr(dst));
206
207 ctx->user_data = user_data;
208 ctx->iface = iface;
209
210 if (net_send_data(pkt) >= 0) {
211 net_stats_update_icmp_sent(iface);
212 return 0;
213 }
214
215 net_stats_update_icmp_drop(iface);
216
217 ret = -EIO;
218
219 drop:
220 net_pkt_unref(pkt);
221
222 return ret;
223
224 }
225 #else
send_icmpv4_echo_request(struct net_icmp_ctx * ctx,struct net_if * iface,struct in_addr * dst,struct net_icmp_ping_params * params,void * user_data,k_timeout_t timeout)226 static int send_icmpv4_echo_request(struct net_icmp_ctx *ctx,
227 struct net_if *iface,
228 struct in_addr *dst,
229 struct net_icmp_ping_params *params,
230 void *user_data,
231 k_timeout_t timeout)
232 {
233 ARG_UNUSED(ctx);
234 ARG_UNUSED(iface);
235 ARG_UNUSED(dst);
236 ARG_UNUSED(params);
237 ARG_UNUSED(timeout);
238
239 return -ENOTSUP;
240 }
241 #endif
242
243 #if defined(CONFIG_NET_IPV6)
send_icmpv6_echo_request(struct net_icmp_ctx * ctx,struct net_if * iface,struct in6_addr * dst,struct net_icmp_ping_params * params,void * user_data,k_timeout_t timeout)244 static int send_icmpv6_echo_request(struct net_icmp_ctx *ctx,
245 struct net_if *iface,
246 struct in6_addr *dst,
247 struct net_icmp_ping_params *params,
248 void *user_data,
249 k_timeout_t timeout)
250 {
251 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv6_access,
252 struct net_icmpv6_echo_req);
253 int ret = -ENOBUFS;
254 struct net_icmpv6_echo_req *echo_req;
255 const struct in6_addr *src;
256 struct net_pkt *pkt;
257
258 if (!iface->config.ip.ipv6) {
259 return -ENETUNREACH;
260 }
261
262 src = net_if_ipv6_select_src_addr(iface, dst);
263
264 pkt = net_pkt_alloc_with_buffer(iface,
265 sizeof(struct net_icmpv6_echo_req)
266 + params->data_size,
267 AF_INET6, IPPROTO_ICMPV6,
268 timeout);
269 if (!pkt) {
270 return -ENOMEM;
271 }
272
273 if (!IS_ENABLED(CONFIG_NET_ALLOW_ANY_PRIORITY) &&
274 params->priority >= NET_MAX_PRIORITIES) {
275 NET_ERR("Priority %d is too large, maximum allowed is %d",
276 params->priority, NET_MAX_PRIORITIES - 1);
277 ret = -EINVAL;
278 goto drop;
279 }
280
281 if (params->priority < 0) {
282 net_pkt_set_ip_dscp(pkt, net_ipv6_get_dscp(params->tc_tos));
283 net_pkt_set_ip_ecn(pkt, net_ipv6_get_ecn(params->tc_tos));
284 } else {
285 net_pkt_set_priority(pkt, params->priority);
286 }
287
288 if (net_ipv6_create(pkt, src, dst) ||
289 net_icmpv6_create(pkt, NET_ICMPV6_ECHO_REQUEST, 0)) {
290 goto drop;
291 }
292
293 echo_req = (struct net_icmpv6_echo_req *)net_pkt_get_data(
294 pkt, &icmpv6_access);
295 if (!echo_req) {
296 goto drop;
297 }
298
299 echo_req->identifier = htons(params->identifier);
300 echo_req->sequence = htons(params->sequence);
301
302 net_pkt_set_data(pkt, &icmpv6_access);
303
304 if (params->data != NULL && params->data_size > 0) {
305 net_pkt_write(pkt, params->data, params->data_size);
306 } else if (params->data == NULL && params->data_size > 0) {
307 /* Generate payload. */
308 if (params->data_size >= sizeof(uint32_t)) {
309 uint32_t time_stamp = htonl(k_cycle_get_32());
310
311 net_pkt_write(pkt, &time_stamp, sizeof(time_stamp));
312 params->data_size -= sizeof(time_stamp);
313 }
314
315 for (size_t i = 0; i < params->data_size; i++) {
316 net_pkt_write_u8(pkt, (uint8_t)i);
317 }
318 } else {
319 /* No payload. */
320 }
321
322 net_pkt_cursor_init(pkt);
323 net_ipv6_finalize(pkt, IPPROTO_ICMPV6);
324
325 NET_DBG("Sending ICMPv6 Echo Request type %d from %s to %s",
326 NET_ICMPV6_ECHO_REQUEST,
327 net_sprint_ipv6_addr(src),
328 net_sprint_ipv6_addr(dst));
329
330 ctx->user_data = user_data;
331 ctx->iface = iface;
332
333 if (net_send_data(pkt) >= 0) {
334 net_stats_update_icmp_sent(iface);
335 return 0;
336 }
337
338 net_stats_update_icmp_drop(iface);
339
340 ret = -EIO;
341
342 drop:
343 net_pkt_unref(pkt);
344
345 return ret;
346 }
347 #else
send_icmpv6_echo_request(struct net_icmp_ctx * ctx,struct net_if * iface,struct in6_addr * dst,struct net_icmp_ping_params * params,void * user_data,k_timeout_t timeout)348 static int send_icmpv6_echo_request(struct net_icmp_ctx *ctx,
349 struct net_if *iface,
350 struct in6_addr *dst,
351 struct net_icmp_ping_params *params,
352 void *user_data,
353 k_timeout_t timeout)
354 {
355 ARG_UNUSED(ctx);
356 ARG_UNUSED(iface);
357 ARG_UNUSED(dst);
358 ARG_UNUSED(params);
359 ARG_UNUSED(timeout);
360
361 return -ENOTSUP;
362 }
363 #endif
364
get_default_params(void)365 static struct net_icmp_ping_params *get_default_params(void)
366 {
367 static struct net_icmp_ping_params params = { 0 };
368
369 params.identifier = sys_rand16_get();
370
371 return ¶ms;
372 }
373
get_offloaded_ping_handler(struct net_if * iface,net_icmp_offload_ping_handler_t * ping_handler)374 static int get_offloaded_ping_handler(struct net_if *iface,
375 net_icmp_offload_ping_handler_t *ping_handler)
376 {
377 struct net_icmp_offload *offload;
378 int ret;
379
380 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
381 return -ENOTSUP;
382 }
383
384 if (iface == NULL) {
385 return -EINVAL;
386 }
387
388 if (!net_if_is_offloaded(iface)) {
389 return -ENOENT;
390 }
391
392 ret = -ENOENT;
393
394 k_mutex_lock(&lock, K_FOREVER);
395
396 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
397 SYS_SLIST_FOR_EACH_CONTAINER(&offload_handlers, offload, node) {
398 if (offload->iface == iface) {
399 *ping_handler = offload->ping_handler;
400 ret = 0;
401 break;
402 }
403 }
404 #else
405 ARG_UNUSED(offload);
406 #endif
407
408 k_mutex_unlock(&lock);
409
410 return ret;
411 }
412
net_icmp_send_echo_request_timeout(struct net_icmp_ctx * ctx,struct net_if * iface,struct sockaddr * dst,struct net_icmp_ping_params * params,void * user_data,k_timeout_t timeout)413 static int net_icmp_send_echo_request_timeout(struct net_icmp_ctx *ctx,
414 struct net_if *iface,
415 struct sockaddr *dst,
416 struct net_icmp_ping_params *params,
417 void *user_data,
418 k_timeout_t timeout)
419 {
420 if (ctx == NULL || dst == NULL) {
421 return -EINVAL;
422 }
423
424 if (iface == NULL) {
425 if (IS_ENABLED(CONFIG_NET_IPV4) && dst->sa_family == AF_INET) {
426 iface = net_if_ipv4_select_src_iface(&net_sin(dst)->sin_addr);
427 } else if (IS_ENABLED(CONFIG_NET_IPV6) && dst->sa_family == AF_INET6) {
428 iface = net_if_ipv6_select_src_iface(&net_sin6(dst)->sin6_addr);
429 }
430
431 if (iface == NULL) {
432 return -ENOENT;
433 }
434 }
435
436 if (IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT) && net_if_is_offloaded(iface)) {
437 net_icmp_offload_ping_handler_t ping_handler = NULL;
438 int ret;
439
440 ret = get_offloaded_ping_handler(iface, &ping_handler);
441 if (ret < 0) {
442 return ret;
443 }
444
445 if (ping_handler == NULL) {
446 NET_ERR("No ping handler set");
447 return -ENOENT;
448 }
449
450 set_offload_handler(iface, ctx->handler);
451
452 return ping_handler(ctx, iface, dst, params, user_data);
453 }
454
455 if (IS_ENABLED(CONFIG_NET_IPV4) && dst->sa_family == AF_INET) {
456 if (params == NULL) {
457 params = get_default_params();
458 }
459
460 return send_icmpv4_echo_request(ctx, iface, &net_sin(dst)->sin_addr,
461 params, user_data, timeout);
462 }
463
464 if (IS_ENABLED(CONFIG_NET_IPV6) && dst->sa_family == AF_INET6) {
465 if (params == NULL) {
466 params = get_default_params();
467 }
468
469 return send_icmpv6_echo_request(ctx, iface, &net_sin6(dst)->sin6_addr,
470 params, user_data, timeout);
471 }
472
473 return -ENOENT;
474 }
475
net_icmp_send_echo_request(struct net_icmp_ctx * ctx,struct net_if * iface,struct sockaddr * dst,struct net_icmp_ping_params * params,void * user_data)476 int net_icmp_send_echo_request(struct net_icmp_ctx *ctx,
477 struct net_if *iface,
478 struct sockaddr *dst,
479 struct net_icmp_ping_params *params,
480 void *user_data)
481 {
482 return net_icmp_send_echo_request_timeout(ctx,
483 iface,
484 dst,
485 params,
486 user_data,
487 PKT_WAIT_TIME);
488 }
489
net_icmp_send_echo_request_no_wait(struct net_icmp_ctx * ctx,struct net_if * iface,struct sockaddr * dst,struct net_icmp_ping_params * params,void * user_data)490 int net_icmp_send_echo_request_no_wait(struct net_icmp_ctx *ctx,
491 struct net_if *iface,
492 struct sockaddr *dst,
493 struct net_icmp_ping_params *params,
494 void *user_data)
495 {
496 return net_icmp_send_echo_request_timeout(ctx,
497 iface,
498 dst,
499 params,
500 user_data,
501 K_NO_WAIT);
502 }
503
icmp_call_handlers(struct net_pkt * pkt,struct net_icmp_ip_hdr * ip_hdr,struct net_icmp_hdr * icmp_hdr)504 static int icmp_call_handlers(struct net_pkt *pkt,
505 struct net_icmp_ip_hdr *ip_hdr,
506 struct net_icmp_hdr *icmp_hdr)
507 {
508 struct net_icmp_ctx *ctx;
509 int ret = -ENOENT;
510
511 k_mutex_lock(&lock, K_FOREVER);
512
513 SYS_SLIST_FOR_EACH_CONTAINER(&handlers, ctx, node) {
514 if (ctx->type == icmp_hdr->type &&
515 (ctx->code == icmp_hdr->code || ctx->code == 0U)) {
516 /* Do not use a handler that is expecting data from different
517 * network interface we sent the request.
518 */
519 if (ctx->iface != NULL && ctx->iface != net_pkt_iface(pkt)) {
520 continue;
521 }
522
523 ret = ctx->handler(ctx, pkt, ip_hdr, icmp_hdr, ctx->user_data);
524 if (ret < 0) {
525 goto out;
526 }
527 }
528 }
529
530 out:
531 k_mutex_unlock(&lock);
532
533 return ret;
534 }
535
536
net_icmp_call_ipv4_handlers(struct net_pkt * pkt,struct net_ipv4_hdr * ipv4_hdr,struct net_icmp_hdr * icmp_hdr)537 int net_icmp_call_ipv4_handlers(struct net_pkt *pkt,
538 struct net_ipv4_hdr *ipv4_hdr,
539 struct net_icmp_hdr *icmp_hdr)
540 {
541 struct net_icmp_ip_hdr ip_hdr;
542
543 ip_hdr.ipv4 = ipv4_hdr;
544 ip_hdr.family = AF_INET;
545
546 return icmp_call_handlers(pkt, &ip_hdr, icmp_hdr);
547 }
548
net_icmp_call_ipv6_handlers(struct net_pkt * pkt,struct net_ipv6_hdr * ipv6_hdr,struct net_icmp_hdr * icmp_hdr)549 int net_icmp_call_ipv6_handlers(struct net_pkt *pkt,
550 struct net_ipv6_hdr *ipv6_hdr,
551 struct net_icmp_hdr *icmp_hdr)
552 {
553 struct net_icmp_ip_hdr ip_hdr;
554
555 ip_hdr.ipv6 = ipv6_hdr;
556 ip_hdr.family = AF_INET6;
557
558 return icmp_call_handlers(pkt, &ip_hdr, icmp_hdr);
559 }
560
net_icmp_register_offload_ping(struct net_icmp_offload * ctx,struct net_if * iface,net_icmp_offload_ping_handler_t ping_handler)561 int net_icmp_register_offload_ping(struct net_icmp_offload *ctx,
562 struct net_if *iface,
563 net_icmp_offload_ping_handler_t ping_handler)
564 {
565 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
566 return -ENOTSUP;
567 }
568
569 if (iface == NULL) {
570 return -EINVAL;
571 }
572
573 if (!net_if_is_offloaded(iface)) {
574 return -ENOENT;
575 }
576
577 memset(ctx, 0, sizeof(struct net_icmp_offload));
578
579 ctx->ping_handler = ping_handler;
580 ctx->iface = iface;
581
582 k_mutex_lock(&lock, K_FOREVER);
583
584 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
585 sys_slist_prepend(&offload_handlers, &ctx->node);
586 #endif
587
588 k_mutex_unlock(&lock);
589
590 return 0;
591 }
592
net_icmp_unregister_offload_ping(struct net_icmp_offload * ctx)593 int net_icmp_unregister_offload_ping(struct net_icmp_offload *ctx)
594 {
595 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
596 return -ENOTSUP;
597 }
598
599 if (ctx == NULL) {
600 return -EINVAL;
601 }
602
603 k_mutex_lock(&lock, K_FOREVER);
604
605 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
606 sys_slist_find_and_remove(&offload_handlers, &ctx->node);
607 #endif
608
609 k_mutex_unlock(&lock);
610
611 memset(ctx, 0, sizeof(struct net_icmp_offload));
612
613 return 0;
614 }
615
net_icmp_get_offload_rsp_handler(struct net_icmp_offload * ctx,net_icmp_handler_t * resp_handler)616 int net_icmp_get_offload_rsp_handler(struct net_icmp_offload *ctx,
617 net_icmp_handler_t *resp_handler)
618 {
619 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
620 return -ENOTSUP;
621 }
622
623 if (ctx == NULL) {
624 return -EINVAL;
625 }
626
627 *resp_handler = ctx->handler;
628
629 return 0;
630 }
631