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 &params;
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