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