xref: /linux/tools/testing/selftests/bpf/prog_tests/icmp_send_kfunc.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
139b337a3SMahe Tardy // SPDX-License-Identifier: GPL-2.0
239b337a3SMahe Tardy #include <test_progs.h>
339b337a3SMahe Tardy #include <network_helpers.h>
449d07ba6SMahe Tardy #include <cgroup_helpers.h>
539b337a3SMahe Tardy #include <linux/errqueue.h>
639b337a3SMahe Tardy #include <poll.h>
749d07ba6SMahe Tardy #include <unistd.h>
839b337a3SMahe Tardy #include "icmp_send.skel.h"
939b337a3SMahe Tardy 
1039b337a3SMahe Tardy #define TIMEOUT_MS 1000
1139b337a3SMahe Tardy 
1239b337a3SMahe Tardy #define ICMP_DEST_UNREACH 3
13340a40dfSMahe Tardy #define ICMPV6_DEST_UNREACH 1
1439b337a3SMahe Tardy 
1549d07ba6SMahe Tardy #define ICMP_HOST_UNREACH 1
1639b337a3SMahe Tardy #define ICMP_FRAG_NEEDED 4
1739b337a3SMahe Tardy #define NR_ICMP_UNREACH 15
18340a40dfSMahe Tardy #define ICMPV6_REJECT_ROUTE 6
1939b337a3SMahe Tardy 
2039b337a3SMahe Tardy #define KFUNC_RET_UNSET -1
2139b337a3SMahe Tardy 
connect_to_fd_nonblock(int server_fd)2239b337a3SMahe Tardy static int connect_to_fd_nonblock(int server_fd)
2339b337a3SMahe Tardy {
2439b337a3SMahe Tardy 	struct sockaddr_storage addr;
2539b337a3SMahe Tardy 	socklen_t len = sizeof(addr);
26340a40dfSMahe Tardy 	int fd, err, on = 1;
2739b337a3SMahe Tardy 
2839b337a3SMahe Tardy 	if (getsockname(server_fd, (struct sockaddr *)&addr, &len))
2939b337a3SMahe Tardy 		return -1;
3039b337a3SMahe Tardy 
3139b337a3SMahe Tardy 	fd = socket(addr.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
3239b337a3SMahe Tardy 	if (fd < 0)
3339b337a3SMahe Tardy 		return -1;
3439b337a3SMahe Tardy 
35340a40dfSMahe Tardy 	if (addr.ss_family == AF_INET6 &&
36340a40dfSMahe Tardy 	    setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &on, sizeof(on)) < 0) {
37340a40dfSMahe Tardy 		close(fd);
38340a40dfSMahe Tardy 		return -1;
39340a40dfSMahe Tardy 	}
40340a40dfSMahe Tardy 
4139b337a3SMahe Tardy 	err = connect(fd, (struct sockaddr *)&addr, len);
4239b337a3SMahe Tardy 	if (err < 0 && errno != EINPROGRESS) {
4339b337a3SMahe Tardy 		close(fd);
4439b337a3SMahe Tardy 		return -1;
4539b337a3SMahe Tardy 	}
4639b337a3SMahe Tardy 
4739b337a3SMahe Tardy 	return fd;
4839b337a3SMahe Tardy }
4939b337a3SMahe Tardy 
read_icmp_errqueue(int sockfd,int expected_code,int af)50340a40dfSMahe Tardy static void read_icmp_errqueue(int sockfd, int expected_code, int af)
5139b337a3SMahe Tardy {
52340a40dfSMahe Tardy 	int expected_ee_type = (af == AF_INET) ? ICMP_DEST_UNREACH :
53340a40dfSMahe Tardy 						 ICMPV6_DEST_UNREACH;
54340a40dfSMahe Tardy 	int expected_origin = (af == AF_INET) ? SO_EE_ORIGIN_ICMP :
55340a40dfSMahe Tardy 						SO_EE_ORIGIN_ICMP6;
56340a40dfSMahe Tardy 	int expected_level = (af == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;
57340a40dfSMahe Tardy 	int expected_type = (af == AF_INET) ? IP_RECVERR : IPV6_RECVERR;
5839b337a3SMahe Tardy 	struct sock_extended_err *sock_err;
5939b337a3SMahe Tardy 	char ctrl_buf[512];
6039b337a3SMahe Tardy 	struct msghdr msg = {
6139b337a3SMahe Tardy 		.msg_control = ctrl_buf,
6239b337a3SMahe Tardy 		.msg_controllen = sizeof(ctrl_buf),
6339b337a3SMahe Tardy 	};
6439b337a3SMahe Tardy 	struct pollfd pfd = {
6539b337a3SMahe Tardy 		.fd = sockfd,
6639b337a3SMahe Tardy 		.events = POLLERR,
6739b337a3SMahe Tardy 	};
6839b337a3SMahe Tardy 	struct cmsghdr *cm;
6939b337a3SMahe Tardy 	ssize_t n;
7039b337a3SMahe Tardy 
7139b337a3SMahe Tardy 	if (!ASSERT_GE(poll(&pfd, 1, TIMEOUT_MS), 1, "poll_errqueue"))
7239b337a3SMahe Tardy 		return;
7339b337a3SMahe Tardy 
7439b337a3SMahe Tardy 	n = recvmsg(sockfd, &msg, MSG_ERRQUEUE);
7539b337a3SMahe Tardy 	if (!ASSERT_GE(n, 0, "recvmsg_errqueue"))
7639b337a3SMahe Tardy 		return;
7739b337a3SMahe Tardy 
7839b337a3SMahe Tardy 	cm = CMSG_FIRSTHDR(&msg);
7939b337a3SMahe Tardy 	if (!ASSERT_NEQ(cm, NULL, "cm_firsthdr_null"))
8039b337a3SMahe Tardy 		return;
8139b337a3SMahe Tardy 
8239b337a3SMahe Tardy 	for (; cm; cm = CMSG_NXTHDR(&msg, cm)) {
83340a40dfSMahe Tardy 		if (cm->cmsg_level != expected_level ||
84340a40dfSMahe Tardy 		    cm->cmsg_type != expected_type)
8539b337a3SMahe Tardy 			continue;
8639b337a3SMahe Tardy 
8739b337a3SMahe Tardy 		sock_err = (struct sock_extended_err *)CMSG_DATA(cm);
8839b337a3SMahe Tardy 
89340a40dfSMahe Tardy 		if (!ASSERT_EQ(sock_err->ee_origin, expected_origin,
90340a40dfSMahe Tardy 			       "sock_err_origin"))
9139b337a3SMahe Tardy 			return;
92340a40dfSMahe Tardy 		if (!ASSERT_EQ(sock_err->ee_type, expected_ee_type,
9339b337a3SMahe Tardy 			       "sock_err_type_dest_unreach"))
9439b337a3SMahe Tardy 			return;
9539b337a3SMahe Tardy 		ASSERT_EQ(sock_err->ee_code, expected_code, "sock_err_code");
9639b337a3SMahe Tardy 		return;
9739b337a3SMahe Tardy 	}
9839b337a3SMahe Tardy 
99340a40dfSMahe Tardy 	ASSERT_FAIL("no IP_RECVERR/IPV6_RECVERR control message found");
10039b337a3SMahe Tardy }
10139b337a3SMahe Tardy 
valid_unreach_code(int code,int af)102340a40dfSMahe Tardy static bool valid_unreach_code(int code, int af)
10339b337a3SMahe Tardy {
10439b337a3SMahe Tardy 	if (code < 0)
10539b337a3SMahe Tardy 		return false;
10639b337a3SMahe Tardy 
107340a40dfSMahe Tardy 	if (af == AF_INET)
10839b337a3SMahe Tardy 		return code <= NR_ICMP_UNREACH && code != ICMP_FRAG_NEEDED;
109340a40dfSMahe Tardy 
110340a40dfSMahe Tardy 	return code <= ICMPV6_REJECT_ROUTE;
11139b337a3SMahe Tardy }
11239b337a3SMahe Tardy 
trigger_prog_read_icmp_errqueue(struct icmp_send * skel,int code,int af,const char * ip)113340a40dfSMahe Tardy static void trigger_prog_read_icmp_errqueue(struct icmp_send *skel, int code,
114340a40dfSMahe Tardy 					    int af, const char *ip)
11539b337a3SMahe Tardy {
11639b337a3SMahe Tardy 	int srv_fd = -1, client_fd = -1;
11739b337a3SMahe Tardy 	int port;
11839b337a3SMahe Tardy 
119340a40dfSMahe Tardy 	srv_fd = start_server(af, SOCK_STREAM, ip, 0, TIMEOUT_MS);
12039b337a3SMahe Tardy 	if (!ASSERT_OK_FD(srv_fd, "start_server"))
12139b337a3SMahe Tardy 		return;
12239b337a3SMahe Tardy 
12339b337a3SMahe Tardy 	port = get_socket_local_port(srv_fd);
12439b337a3SMahe Tardy 	if (!ASSERT_GE(port, 0, "get_socket_local_port")) {
12539b337a3SMahe Tardy 		close(srv_fd);
12639b337a3SMahe Tardy 		return;
12739b337a3SMahe Tardy 	}
12839b337a3SMahe Tardy 
12939b337a3SMahe Tardy 	skel->bss->server_port = ntohs(port);
130340a40dfSMahe Tardy 	skel->bss->unreach_type = (af == AF_INET) ? ICMP_DEST_UNREACH :
131340a40dfSMahe Tardy 						    ICMPV6_DEST_UNREACH;
13239b337a3SMahe Tardy 	skel->bss->unreach_code = code;
13339b337a3SMahe Tardy 	skel->data->kfunc_ret = KFUNC_RET_UNSET;
13439b337a3SMahe Tardy 
13539b337a3SMahe Tardy 	client_fd = connect_to_fd_nonblock(srv_fd);
13639b337a3SMahe Tardy 	if (!ASSERT_OK_FD(client_fd, "client_connect_nonblock")) {
13739b337a3SMahe Tardy 		close(srv_fd);
13839b337a3SMahe Tardy 		return;
13939b337a3SMahe Tardy 	}
14039b337a3SMahe Tardy 
141340a40dfSMahe Tardy 	if (valid_unreach_code(code, af))
142340a40dfSMahe Tardy 		read_icmp_errqueue(client_fd, code, af);
14339b337a3SMahe Tardy 
14439b337a3SMahe Tardy 	close(client_fd);
14539b337a3SMahe Tardy 	close(srv_fd);
14639b337a3SMahe Tardy }
14739b337a3SMahe Tardy 
run_icmp_test(struct icmp_send * skel,int af,const char * ip,int max_code)148340a40dfSMahe Tardy static void run_icmp_test(struct icmp_send *skel, int af, const char *ip,
149340a40dfSMahe Tardy 			  int max_code)
150340a40dfSMahe Tardy {
151340a40dfSMahe Tardy 	for (int code = 0; code <= max_code; code++) {
152340a40dfSMahe Tardy 		if (af == AF_INET && code == ICMP_FRAG_NEEDED)
153340a40dfSMahe Tardy 			continue;
154340a40dfSMahe Tardy 
155340a40dfSMahe Tardy 		trigger_prog_read_icmp_errqueue(skel, code, af, ip);
156340a40dfSMahe Tardy 		ASSERT_EQ(skel->data->kfunc_ret, 0, "kfunc_ret");
157340a40dfSMahe Tardy 	}
158340a40dfSMahe Tardy 
159340a40dfSMahe Tardy 	/* Test invalid codes */
160340a40dfSMahe Tardy 	trigger_prog_read_icmp_errqueue(skel, -1, af, ip);
161340a40dfSMahe Tardy 	ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret");
162340a40dfSMahe Tardy 
163340a40dfSMahe Tardy 	trigger_prog_read_icmp_errqueue(skel, max_code + 1, af, ip);
164340a40dfSMahe Tardy 	ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret");
165340a40dfSMahe Tardy 
166340a40dfSMahe Tardy 	if (af == AF_INET) {
167340a40dfSMahe Tardy 		trigger_prog_read_icmp_errqueue(skel, ICMP_FRAG_NEEDED, af, ip);
168340a40dfSMahe Tardy 		ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret");
169340a40dfSMahe Tardy 	}
170340a40dfSMahe Tardy }
171340a40dfSMahe Tardy 
run_icmp_no_route_test(struct icmp_send * skel,int af)172*b1d4514fSMahe Tardy static void run_icmp_no_route_test(struct icmp_send *skel, int af)
173*b1d4514fSMahe Tardy {
174*b1d4514fSMahe Tardy 	union {
175*b1d4514fSMahe Tardy 		struct ipv4_packet v4;
176*b1d4514fSMahe Tardy 		struct ipv6_packet v6;
177*b1d4514fSMahe Tardy 	} pkt;
178*b1d4514fSMahe Tardy 	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
179*b1d4514fSMahe Tardy 		.data_in = &pkt,
180*b1d4514fSMahe Tardy 	);
181*b1d4514fSMahe Tardy 	int err;
182*b1d4514fSMahe Tardy 
183*b1d4514fSMahe Tardy 	switch (af) {
184*b1d4514fSMahe Tardy 	case AF_INET:
185*b1d4514fSMahe Tardy 		pkt.v4 = pkt_v4;
186*b1d4514fSMahe Tardy 		pkt.v4.iph.version = 4;
187*b1d4514fSMahe Tardy 		pkt.v4.iph.daddr = htonl(INADDR_LOOPBACK);
188*b1d4514fSMahe Tardy 		pkt.v4.tcp.dest = htons(80);
189*b1d4514fSMahe Tardy 		opts.data_size_in = sizeof(pkt.v4);
190*b1d4514fSMahe Tardy 		skel->bss->unreach_type = ICMP_DEST_UNREACH;
191*b1d4514fSMahe Tardy 		break;
192*b1d4514fSMahe Tardy 	case AF_INET6:
193*b1d4514fSMahe Tardy 		pkt.v6 = pkt_v6;
194*b1d4514fSMahe Tardy 		pkt.v6.iph.version = 6;
195*b1d4514fSMahe Tardy 		pkt.v6.iph.daddr = in6addr_loopback;
196*b1d4514fSMahe Tardy 		pkt.v6.tcp.dest = htons(80);
197*b1d4514fSMahe Tardy 		opts.data_size_in = sizeof(pkt.v6);
198*b1d4514fSMahe Tardy 		skel->bss->unreach_type = ICMPV6_DEST_UNREACH;
199*b1d4514fSMahe Tardy 		break;
200*b1d4514fSMahe Tardy 	default:
201*b1d4514fSMahe Tardy 		ASSERT_FAIL("af_not_supported");
202*b1d4514fSMahe Tardy 		return;
203*b1d4514fSMahe Tardy 	}
204*b1d4514fSMahe Tardy 
205*b1d4514fSMahe Tardy 	skel->bss->server_port = 80;
206*b1d4514fSMahe Tardy 	skel->data->kfunc_ret = KFUNC_RET_UNSET;
207*b1d4514fSMahe Tardy 
208*b1d4514fSMahe Tardy 	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.egress), &opts);
209*b1d4514fSMahe Tardy 	if (!ASSERT_OK(err, "test_run"))
210*b1d4514fSMahe Tardy 		return;
211*b1d4514fSMahe Tardy 
212*b1d4514fSMahe Tardy 	ASSERT_EQ(skel->data->kfunc_ret, -ENETUNREACH, "kfunc_ret_no_route");
213*b1d4514fSMahe Tardy }
214*b1d4514fSMahe Tardy 
test_icmp_send_unreach_cgroup(void)21539b337a3SMahe Tardy void test_icmp_send_unreach_cgroup(void)
21639b337a3SMahe Tardy {
21739b337a3SMahe Tardy 	struct icmp_send *skel;
21839b337a3SMahe Tardy 	int cgroup_fd = -1;
21939b337a3SMahe Tardy 
22039b337a3SMahe Tardy 	skel = icmp_send__open_and_load();
22139b337a3SMahe Tardy 	if (!ASSERT_OK_PTR(skel, "skel_open"))
22239b337a3SMahe Tardy 		goto cleanup;
22339b337a3SMahe Tardy 
22439b337a3SMahe Tardy 	cgroup_fd = test__join_cgroup("/icmp_send_unreach_cgroup");
22539b337a3SMahe Tardy 	if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup"))
22639b337a3SMahe Tardy 		goto cleanup;
22739b337a3SMahe Tardy 
22839b337a3SMahe Tardy 	skel->links.egress =
22939b337a3SMahe Tardy 		bpf_program__attach_cgroup(skel->progs.egress, cgroup_fd);
23039b337a3SMahe Tardy 	if (!ASSERT_OK_PTR(skel->links.egress, "prog_attach_cgroup"))
23139b337a3SMahe Tardy 		goto cleanup;
23239b337a3SMahe Tardy 
233340a40dfSMahe Tardy 	if (test__start_subtest("ipv4"))
234340a40dfSMahe Tardy 		run_icmp_test(skel, AF_INET, "127.0.0.1", NR_ICMP_UNREACH);
23539b337a3SMahe Tardy 
236340a40dfSMahe Tardy 	if (test__start_subtest("ipv6"))
237340a40dfSMahe Tardy 		run_icmp_test(skel, AF_INET6, "::1", ICMPV6_REJECT_ROUTE);
23839b337a3SMahe Tardy 
239*b1d4514fSMahe Tardy 	if (test__start_subtest("no_route_ipv4"))
240*b1d4514fSMahe Tardy 		run_icmp_no_route_test(skel, AF_INET);
241*b1d4514fSMahe Tardy 
242*b1d4514fSMahe Tardy 	if (test__start_subtest("no_route_ipv6"))
243*b1d4514fSMahe Tardy 		run_icmp_no_route_test(skel, AF_INET6);
244*b1d4514fSMahe Tardy 
24539b337a3SMahe Tardy cleanup:
24639b337a3SMahe Tardy 	icmp_send__destroy(skel);
24739b337a3SMahe Tardy 	if (cgroup_fd >= 0)
24839b337a3SMahe Tardy 		close(cgroup_fd);
24939b337a3SMahe Tardy }
25049d07ba6SMahe Tardy 
test_icmp_send_unreach_recursion(void)25149d07ba6SMahe Tardy void test_icmp_send_unreach_recursion(void)
25249d07ba6SMahe Tardy {
25349d07ba6SMahe Tardy 	struct icmp_send *skel;
25449d07ba6SMahe Tardy 	int cgroup_fd = -1;
25549d07ba6SMahe Tardy 	int err;
25649d07ba6SMahe Tardy 
25749d07ba6SMahe Tardy 	err = setup_cgroup_environment();
25849d07ba6SMahe Tardy 	if (!ASSERT_OK(err, "setup_cgroup_environment"))
25949d07ba6SMahe Tardy 		return;
26049d07ba6SMahe Tardy 
26149d07ba6SMahe Tardy 	skel = icmp_send__open_and_load();
26249d07ba6SMahe Tardy 	if (!ASSERT_OK_PTR(skel, "skel_open"))
26349d07ba6SMahe Tardy 		goto cleanup;
26449d07ba6SMahe Tardy 
26549d07ba6SMahe Tardy 	cgroup_fd = get_root_cgroup();
26649d07ba6SMahe Tardy 	if (!ASSERT_OK_FD(cgroup_fd, "get_root_cgroup"))
26749d07ba6SMahe Tardy 		goto cleanup;
26849d07ba6SMahe Tardy 
26949d07ba6SMahe Tardy 	skel->data->target_pid = getpid();
27049d07ba6SMahe Tardy 	skel->links.recursion =
27149d07ba6SMahe Tardy 		bpf_program__attach_cgroup(skel->progs.recursion, cgroup_fd);
27249d07ba6SMahe Tardy 	if (!ASSERT_OK_PTR(skel->links.recursion, "prog_attach_cgroup"))
27349d07ba6SMahe Tardy 		goto cleanup;
27449d07ba6SMahe Tardy 
27549d07ba6SMahe Tardy 	trigger_prog_read_icmp_errqueue(skel, ICMP_HOST_UNREACH, AF_INET,
27649d07ba6SMahe Tardy 					"127.0.0.1");
27749d07ba6SMahe Tardy 
27849d07ba6SMahe Tardy 	/*
27949d07ba6SMahe Tardy 	 * Because there's recursion involved, the first call will return at
28049d07ba6SMahe Tardy 	 * index 1 since it will return the second, and the second call will
28149d07ba6SMahe Tardy 	 * return at index 0 since it will return the first.
28249d07ba6SMahe Tardy 	 */
28349d07ba6SMahe Tardy 	ASSERT_EQ(skel->bss->rec_count, 2, "rec_count");
28449d07ba6SMahe Tardy 	ASSERT_EQ(skel->data->rec_kfunc_rets[0], -EBUSY, "kfunc_rets[0]");
28549d07ba6SMahe Tardy 	ASSERT_EQ(skel->data->rec_kfunc_rets[1], 0, "kfunc_rets[1]");
28649d07ba6SMahe Tardy 
28749d07ba6SMahe Tardy cleanup:
28849d07ba6SMahe Tardy 	icmp_send__destroy(skel);
28949d07ba6SMahe Tardy 	if (cgroup_fd >= 0)
29049d07ba6SMahe Tardy 		close(cgroup_fd);
29149d07ba6SMahe Tardy 	cleanup_cgroup_environment();
29249d07ba6SMahe Tardy }
293