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)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 {
126 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv4_access,
127 struct net_icmpv4_echo_req);
128 int ret = -ENOBUFS;
129 struct net_icmpv4_echo_req *echo_req;
130 const struct in_addr *src;
131 struct net_pkt *pkt;
132
133 if (!iface->config.ip.ipv4) {
134 return -ENETUNREACH;
135 }
136
137 src = net_if_ipv4_select_src_addr(iface, dst);
138
139 pkt = net_pkt_alloc_with_buffer(iface,
140 sizeof(struct net_icmpv4_echo_req)
141 + params->data_size,
142 AF_INET, IPPROTO_ICMP,
143 PKT_WAIT_TIME);
144 if (!pkt) {
145 return -ENOMEM;
146 }
147
148 if (!IS_ENABLED(CONFIG_NET_ALLOW_ANY_PRIORITY) &&
149 params->priority >= NET_MAX_PRIORITIES) {
150 NET_ERR("Priority %d is too large, maximum allowed is %d",
151 params->priority, NET_MAX_PRIORITIES - 1);
152 return -EINVAL;
153 }
154
155 if (params->priority < 0) {
156 net_pkt_set_ip_dscp(pkt, net_ipv4_get_dscp(params->tc_tos));
157 net_pkt_set_ip_ecn(pkt, net_ipv4_get_ecn(params->tc_tos));
158 } else {
159 net_pkt_set_priority(pkt, params->priority);
160 }
161
162 if (net_ipv4_create(pkt, src, dst) ||
163 net_icmpv4_create(pkt, NET_ICMPV4_ECHO_REQUEST, 0)) {
164 goto drop;
165 }
166
167 echo_req = (struct net_icmpv4_echo_req *)net_pkt_get_data(
168 pkt, &icmpv4_access);
169 if (!echo_req) {
170 goto drop;
171 }
172
173 echo_req->identifier = htons(params->identifier);
174 echo_req->sequence = htons(params->sequence);
175
176 net_pkt_set_data(pkt, &icmpv4_access);
177
178 if (params->data != NULL && params->data_size > 0) {
179 net_pkt_write(pkt, params->data, params->data_size);
180 } else if (params->data == NULL && params->data_size > 0) {
181 /* Generate payload. */
182 if (params->data_size >= sizeof(uint32_t)) {
183 uint32_t time_stamp = htonl(k_cycle_get_32());
184
185 net_pkt_write(pkt, &time_stamp, sizeof(time_stamp));
186 params->data_size -= sizeof(time_stamp);
187 }
188
189 for (size_t i = 0; i < params->data_size; i++) {
190 net_pkt_write_u8(pkt, (uint8_t)i);
191 }
192 } else {
193 /* No payload. */
194 }
195
196 net_pkt_cursor_init(pkt);
197
198 net_ipv4_finalize(pkt, IPPROTO_ICMP);
199
200 NET_DBG("Sending ICMPv4 Echo Request type %d from %s to %s",
201 NET_ICMPV4_ECHO_REQUEST,
202 net_sprint_ipv4_addr(src),
203 net_sprint_ipv4_addr(dst));
204
205 ctx->user_data = user_data;
206 ctx->iface = iface;
207
208 if (net_send_data(pkt) >= 0) {
209 net_stats_update_icmp_sent(iface);
210 return 0;
211 }
212
213 net_stats_update_icmp_drop(iface);
214
215 ret = -EIO;
216
217 drop:
218 net_pkt_unref(pkt);
219
220 return ret;
221
222 }
223 #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)224 static int send_icmpv4_echo_request(struct net_icmp_ctx *ctx,
225 struct net_if *iface,
226 struct in_addr *dst,
227 struct net_icmp_ping_params *params,
228 void *user_data)
229 {
230 ARG_UNUSED(ctx);
231 ARG_UNUSED(iface);
232 ARG_UNUSED(dst);
233 ARG_UNUSED(params);
234
235 return -ENOTSUP;
236 }
237 #endif
238
239 #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)240 static int send_icmpv6_echo_request(struct net_icmp_ctx *ctx,
241 struct net_if *iface,
242 struct in6_addr *dst,
243 struct net_icmp_ping_params *params,
244 void *user_data)
245 {
246 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmpv6_access,
247 struct net_icmpv6_echo_req);
248 int ret = -ENOBUFS;
249 struct net_icmpv6_echo_req *echo_req;
250 const struct in6_addr *src;
251 struct net_pkt *pkt;
252
253 if (!iface->config.ip.ipv6) {
254 return -ENETUNREACH;
255 }
256
257 src = net_if_ipv6_select_src_addr(iface, dst);
258
259 pkt = net_pkt_alloc_with_buffer(iface,
260 sizeof(struct net_icmpv6_echo_req)
261 + params->data_size,
262 AF_INET6, IPPROTO_ICMPV6,
263 PKT_WAIT_TIME);
264 if (!pkt) {
265 return -ENOMEM;
266 }
267
268 if (!IS_ENABLED(CONFIG_NET_ALLOW_ANY_PRIORITY) &&
269 params->priority >= NET_MAX_PRIORITIES) {
270 NET_ERR("Priority %d is too large, maximum allowed is %d",
271 params->priority, NET_MAX_PRIORITIES - 1);
272 return -EINVAL;
273 }
274
275 if (params->priority < 0) {
276 net_pkt_set_ip_dscp(pkt, net_ipv6_get_dscp(params->tc_tos));
277 net_pkt_set_ip_ecn(pkt, net_ipv6_get_ecn(params->tc_tos));
278 } else {
279 net_pkt_set_priority(pkt, params->priority);
280 }
281
282 if (net_ipv6_create(pkt, src, dst) ||
283 net_icmpv6_create(pkt, NET_ICMPV6_ECHO_REQUEST, 0)) {
284 goto drop;
285 }
286
287 echo_req = (struct net_icmpv6_echo_req *)net_pkt_get_data(
288 pkt, &icmpv6_access);
289 if (!echo_req) {
290 goto drop;
291 }
292
293 echo_req->identifier = htons(params->identifier);
294 echo_req->sequence = htons(params->sequence);
295
296 net_pkt_set_data(pkt, &icmpv6_access);
297
298 if (params->data != NULL && params->data_size > 0) {
299 net_pkt_write(pkt, params->data, params->data_size);
300 } else if (params->data == NULL && params->data_size > 0) {
301 /* Generate payload. */
302 if (params->data_size >= sizeof(uint32_t)) {
303 uint32_t time_stamp = htonl(k_cycle_get_32());
304
305 net_pkt_write(pkt, &time_stamp, sizeof(time_stamp));
306 params->data_size -= sizeof(time_stamp);
307 }
308
309 for (size_t i = 0; i < params->data_size; i++) {
310 net_pkt_write_u8(pkt, (uint8_t)i);
311 }
312 } else {
313 /* No payload. */
314 }
315
316 net_pkt_cursor_init(pkt);
317 net_ipv6_finalize(pkt, IPPROTO_ICMPV6);
318
319 NET_DBG("Sending ICMPv6 Echo Request type %d from %s to %s",
320 NET_ICMPV6_ECHO_REQUEST,
321 net_sprint_ipv6_addr(src),
322 net_sprint_ipv6_addr(dst));
323
324 ctx->user_data = user_data;
325 ctx->iface = iface;
326
327 if (net_send_data(pkt) >= 0) {
328 net_stats_update_icmp_sent(iface);
329 return 0;
330 }
331
332 net_stats_update_icmp_drop(iface);
333
334 ret = -EIO;
335
336 drop:
337 net_pkt_unref(pkt);
338
339 return ret;
340 }
341 #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)342 static int send_icmpv6_echo_request(struct net_icmp_ctx *ctx,
343 struct net_if *iface,
344 struct in6_addr *dst,
345 struct net_icmp_ping_params *params,
346 void *user_data)
347 {
348 ARG_UNUSED(ctx);
349 ARG_UNUSED(iface);
350 ARG_UNUSED(dst);
351 ARG_UNUSED(params);
352
353 return -ENOTSUP;
354 }
355 #endif
356
get_default_params(void)357 static struct net_icmp_ping_params *get_default_params(void)
358 {
359 static struct net_icmp_ping_params params = { 0 };
360
361 params.identifier = sys_rand32_get();
362
363 return ¶ms;
364 }
365
get_offloaded_ping_handler(struct net_if * iface,net_icmp_offload_ping_handler_t * ping_handler)366 static int get_offloaded_ping_handler(struct net_if *iface,
367 net_icmp_offload_ping_handler_t *ping_handler)
368 {
369 struct net_icmp_offload *offload;
370 int ret;
371
372 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
373 return -ENOTSUP;
374 }
375
376 if (iface == NULL) {
377 return -EINVAL;
378 }
379
380 if (!net_if_is_offloaded(iface)) {
381 return -ENOENT;
382 }
383
384 ret = -ENOENT;
385
386 k_mutex_lock(&lock, K_FOREVER);
387
388 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
389 SYS_SLIST_FOR_EACH_CONTAINER(&offload_handlers, offload, node) {
390 if (offload->iface == iface) {
391 *ping_handler = offload->ping_handler;
392 ret = 0;
393 break;
394 }
395 }
396 #else
397 ARG_UNUSED(offload);
398 #endif
399
400 k_mutex_unlock(&lock);
401
402 return ret;
403 }
404
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)405 int net_icmp_send_echo_request(struct net_icmp_ctx *ctx,
406 struct net_if *iface,
407 struct sockaddr *dst,
408 struct net_icmp_ping_params *params,
409 void *user_data)
410 {
411 if (ctx == NULL || dst == NULL) {
412 return -EINVAL;
413 }
414
415 if (iface == NULL) {
416 if (IS_ENABLED(CONFIG_NET_IPV4) && dst->sa_family == AF_INET) {
417 iface = net_if_ipv4_select_src_iface(&net_sin(dst)->sin_addr);
418 } else if (IS_ENABLED(CONFIG_NET_IPV6) && dst->sa_family == AF_INET6) {
419 iface = net_if_ipv6_select_src_iface(&net_sin6(dst)->sin6_addr);
420 }
421
422 if (iface == NULL) {
423 return -ENOENT;
424 }
425 }
426
427 if (IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT) && net_if_is_offloaded(iface)) {
428 net_icmp_offload_ping_handler_t ping_handler = NULL;
429 int ret;
430
431 ret = get_offloaded_ping_handler(iface, &ping_handler);
432 if (ret < 0) {
433 return ret;
434 }
435
436 if (ping_handler == NULL) {
437 NET_ERR("No ping handler set");
438 return -ENOENT;
439 }
440
441 set_offload_handler(iface, ctx->handler);
442
443 return ping_handler(ctx, iface, dst, params, user_data);
444 }
445
446 if (IS_ENABLED(CONFIG_NET_IPV4) && dst->sa_family == AF_INET) {
447 if (params == NULL) {
448 params = get_default_params();
449 }
450
451 return send_icmpv4_echo_request(ctx, iface, &net_sin(dst)->sin_addr,
452 params, user_data);
453 }
454
455 if (IS_ENABLED(CONFIG_NET_IPV6) && dst->sa_family == AF_INET6) {
456 if (params == NULL) {
457 params = get_default_params();
458 }
459
460 return send_icmpv6_echo_request(ctx, iface, &net_sin6(dst)->sin6_addr,
461 params, user_data);
462 }
463
464 return -ENOENT;
465 }
466
icmp_call_handlers(struct net_pkt * pkt,struct net_icmp_ip_hdr * ip_hdr,struct net_icmp_hdr * icmp_hdr)467 static int icmp_call_handlers(struct net_pkt *pkt,
468 struct net_icmp_ip_hdr *ip_hdr,
469 struct net_icmp_hdr *icmp_hdr)
470 {
471 struct net_icmp_ctx *ctx;
472 int ret = -ENOENT;
473
474 k_mutex_lock(&lock, K_FOREVER);
475
476 SYS_SLIST_FOR_EACH_CONTAINER(&handlers, ctx, node) {
477 if (ctx->type == icmp_hdr->type &&
478 (ctx->code == icmp_hdr->code || ctx->code == 0U)) {
479 /* Do not use a handler that is expecting data from different
480 * network interface we sent the request.
481 */
482 if (ctx->iface != NULL && ctx->iface != net_pkt_iface(pkt)) {
483 continue;
484 }
485
486 ret = ctx->handler(ctx, pkt, ip_hdr, icmp_hdr, ctx->user_data);
487 if (ret < 0) {
488 goto out;
489 }
490 }
491 }
492
493 out:
494 k_mutex_unlock(&lock);
495
496 return ret;
497 }
498
499
net_icmp_call_ipv4_handlers(struct net_pkt * pkt,struct net_ipv4_hdr * ipv4_hdr,struct net_icmp_hdr * icmp_hdr)500 int net_icmp_call_ipv4_handlers(struct net_pkt *pkt,
501 struct net_ipv4_hdr *ipv4_hdr,
502 struct net_icmp_hdr *icmp_hdr)
503 {
504 struct net_icmp_ip_hdr ip_hdr;
505
506 ip_hdr.ipv4 = ipv4_hdr;
507 ip_hdr.family = AF_INET;
508
509 return icmp_call_handlers(pkt, &ip_hdr, icmp_hdr);
510 }
511
net_icmp_call_ipv6_handlers(struct net_pkt * pkt,struct net_ipv6_hdr * ipv6_hdr,struct net_icmp_hdr * icmp_hdr)512 int net_icmp_call_ipv6_handlers(struct net_pkt *pkt,
513 struct net_ipv6_hdr *ipv6_hdr,
514 struct net_icmp_hdr *icmp_hdr)
515 {
516 struct net_icmp_ip_hdr ip_hdr;
517
518 ip_hdr.ipv6 = ipv6_hdr;
519 ip_hdr.family = AF_INET6;
520
521 return icmp_call_handlers(pkt, &ip_hdr, icmp_hdr);
522 }
523
net_icmp_register_offload_ping(struct net_icmp_offload * ctx,struct net_if * iface,net_icmp_offload_ping_handler_t ping_handler)524 int net_icmp_register_offload_ping(struct net_icmp_offload *ctx,
525 struct net_if *iface,
526 net_icmp_offload_ping_handler_t ping_handler)
527 {
528 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
529 return -ENOTSUP;
530 }
531
532 if (iface == NULL) {
533 return -EINVAL;
534 }
535
536 if (!net_if_is_offloaded(iface)) {
537 return -ENOENT;
538 }
539
540 memset(ctx, 0, sizeof(struct net_icmp_offload));
541
542 ctx->ping_handler = ping_handler;
543 ctx->iface = iface;
544
545 k_mutex_lock(&lock, K_FOREVER);
546
547 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
548 sys_slist_prepend(&offload_handlers, &ctx->node);
549 #endif
550
551 k_mutex_unlock(&lock);
552
553 return 0;
554 }
555
net_icmp_unregister_offload_ping(struct net_icmp_offload * ctx)556 int net_icmp_unregister_offload_ping(struct net_icmp_offload *ctx)
557 {
558 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
559 return -ENOTSUP;
560 }
561
562 if (ctx == NULL) {
563 return -EINVAL;
564 }
565
566 k_mutex_lock(&lock, K_FOREVER);
567
568 #if defined(CONFIG_NET_OFFLOADING_SUPPORT)
569 sys_slist_find_and_remove(&offload_handlers, &ctx->node);
570 #endif
571
572 k_mutex_unlock(&lock);
573
574 memset(ctx, 0, sizeof(struct net_icmp_offload));
575
576 return 0;
577 }
578
net_icmp_get_offload_rsp_handler(struct net_icmp_offload * ctx,net_icmp_handler_t * resp_handler)579 int net_icmp_get_offload_rsp_handler(struct net_icmp_offload *ctx,
580 net_icmp_handler_t *resp_handler)
581 {
582 if (!IS_ENABLED(CONFIG_NET_OFFLOADING_SUPPORT)) {
583 return -ENOTSUP;
584 }
585
586 if (ctx == NULL) {
587 return -EINVAL;
588 }
589
590 *resp_handler = ctx->handler;
591
592 return 0;
593 }
594