xref: /linux/tools/testing/selftests/bpf/prog_tests/icmp_send_kfunc.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <network_helpers.h>
4 #include <cgroup_helpers.h>
5 #include <linux/errqueue.h>
6 #include <poll.h>
7 #include <unistd.h>
8 #include "icmp_send.skel.h"
9 
10 #define TIMEOUT_MS 1000
11 
12 #define ICMP_DEST_UNREACH 3
13 #define ICMPV6_DEST_UNREACH 1
14 
15 #define ICMP_HOST_UNREACH 1
16 #define ICMP_FRAG_NEEDED 4
17 #define NR_ICMP_UNREACH 15
18 #define ICMPV6_REJECT_ROUTE 6
19 
20 #define KFUNC_RET_UNSET -1
21 
connect_to_fd_nonblock(int server_fd)22 static int connect_to_fd_nonblock(int server_fd)
23 {
24 	struct sockaddr_storage addr;
25 	socklen_t len = sizeof(addr);
26 	int fd, err, on = 1;
27 
28 	if (getsockname(server_fd, (struct sockaddr *)&addr, &len))
29 		return -1;
30 
31 	fd = socket(addr.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
32 	if (fd < 0)
33 		return -1;
34 
35 	if (addr.ss_family == AF_INET6 &&
36 	    setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &on, sizeof(on)) < 0) {
37 		close(fd);
38 		return -1;
39 	}
40 
41 	err = connect(fd, (struct sockaddr *)&addr, len);
42 	if (err < 0 && errno != EINPROGRESS) {
43 		close(fd);
44 		return -1;
45 	}
46 
47 	return fd;
48 }
49 
read_icmp_errqueue(int sockfd,int expected_code,int af)50 static void read_icmp_errqueue(int sockfd, int expected_code, int af)
51 {
52 	int expected_ee_type = (af == AF_INET) ? ICMP_DEST_UNREACH :
53 						 ICMPV6_DEST_UNREACH;
54 	int expected_origin = (af == AF_INET) ? SO_EE_ORIGIN_ICMP :
55 						SO_EE_ORIGIN_ICMP6;
56 	int expected_level = (af == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;
57 	int expected_type = (af == AF_INET) ? IP_RECVERR : IPV6_RECVERR;
58 	struct sock_extended_err *sock_err;
59 	char ctrl_buf[512];
60 	struct msghdr msg = {
61 		.msg_control = ctrl_buf,
62 		.msg_controllen = sizeof(ctrl_buf),
63 	};
64 	struct pollfd pfd = {
65 		.fd = sockfd,
66 		.events = POLLERR,
67 	};
68 	struct cmsghdr *cm;
69 	ssize_t n;
70 
71 	if (!ASSERT_GE(poll(&pfd, 1, TIMEOUT_MS), 1, "poll_errqueue"))
72 		return;
73 
74 	n = recvmsg(sockfd, &msg, MSG_ERRQUEUE);
75 	if (!ASSERT_GE(n, 0, "recvmsg_errqueue"))
76 		return;
77 
78 	cm = CMSG_FIRSTHDR(&msg);
79 	if (!ASSERT_NEQ(cm, NULL, "cm_firsthdr_null"))
80 		return;
81 
82 	for (; cm; cm = CMSG_NXTHDR(&msg, cm)) {
83 		if (cm->cmsg_level != expected_level ||
84 		    cm->cmsg_type != expected_type)
85 			continue;
86 
87 		sock_err = (struct sock_extended_err *)CMSG_DATA(cm);
88 
89 		if (!ASSERT_EQ(sock_err->ee_origin, expected_origin,
90 			       "sock_err_origin"))
91 			return;
92 		if (!ASSERT_EQ(sock_err->ee_type, expected_ee_type,
93 			       "sock_err_type_dest_unreach"))
94 			return;
95 		ASSERT_EQ(sock_err->ee_code, expected_code, "sock_err_code");
96 		return;
97 	}
98 
99 	ASSERT_FAIL("no IP_RECVERR/IPV6_RECVERR control message found");
100 }
101 
valid_unreach_code(int code,int af)102 static bool valid_unreach_code(int code, int af)
103 {
104 	if (code < 0)
105 		return false;
106 
107 	if (af == AF_INET)
108 		return code <= NR_ICMP_UNREACH && code != ICMP_FRAG_NEEDED;
109 
110 	return code <= ICMPV6_REJECT_ROUTE;
111 }
112 
trigger_prog_read_icmp_errqueue(struct icmp_send * skel,int code,int af,const char * ip)113 static void trigger_prog_read_icmp_errqueue(struct icmp_send *skel, int code,
114 					    int af, const char *ip)
115 {
116 	int srv_fd = -1, client_fd = -1;
117 	int port;
118 
119 	srv_fd = start_server(af, SOCK_STREAM, ip, 0, TIMEOUT_MS);
120 	if (!ASSERT_OK_FD(srv_fd, "start_server"))
121 		return;
122 
123 	port = get_socket_local_port(srv_fd);
124 	if (!ASSERT_GE(port, 0, "get_socket_local_port")) {
125 		close(srv_fd);
126 		return;
127 	}
128 
129 	skel->bss->server_port = ntohs(port);
130 	skel->bss->unreach_type = (af == AF_INET) ? ICMP_DEST_UNREACH :
131 						    ICMPV6_DEST_UNREACH;
132 	skel->bss->unreach_code = code;
133 	skel->data->kfunc_ret = KFUNC_RET_UNSET;
134 
135 	client_fd = connect_to_fd_nonblock(srv_fd);
136 	if (!ASSERT_OK_FD(client_fd, "client_connect_nonblock")) {
137 		close(srv_fd);
138 		return;
139 	}
140 
141 	if (valid_unreach_code(code, af))
142 		read_icmp_errqueue(client_fd, code, af);
143 
144 	close(client_fd);
145 	close(srv_fd);
146 }
147 
run_icmp_test(struct icmp_send * skel,int af,const char * ip,int max_code)148 static void run_icmp_test(struct icmp_send *skel, int af, const char *ip,
149 			  int max_code)
150 {
151 	for (int code = 0; code <= max_code; code++) {
152 		if (af == AF_INET && code == ICMP_FRAG_NEEDED)
153 			continue;
154 
155 		trigger_prog_read_icmp_errqueue(skel, code, af, ip);
156 		ASSERT_EQ(skel->data->kfunc_ret, 0, "kfunc_ret");
157 	}
158 
159 	/* Test invalid codes */
160 	trigger_prog_read_icmp_errqueue(skel, -1, af, ip);
161 	ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret");
162 
163 	trigger_prog_read_icmp_errqueue(skel, max_code + 1, af, ip);
164 	ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret");
165 
166 	if (af == AF_INET) {
167 		trigger_prog_read_icmp_errqueue(skel, ICMP_FRAG_NEEDED, af, ip);
168 		ASSERT_EQ(skel->data->kfunc_ret, -EINVAL, "kfunc_ret");
169 	}
170 }
171 
run_icmp_no_route_test(struct icmp_send * skel,int af)172 static void run_icmp_no_route_test(struct icmp_send *skel, int af)
173 {
174 	union {
175 		struct ipv4_packet v4;
176 		struct ipv6_packet v6;
177 	} pkt;
178 	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
179 		.data_in = &pkt,
180 	);
181 	int err;
182 
183 	switch (af) {
184 	case AF_INET:
185 		pkt.v4 = pkt_v4;
186 		pkt.v4.iph.version = 4;
187 		pkt.v4.iph.daddr = htonl(INADDR_LOOPBACK);
188 		pkt.v4.tcp.dest = htons(80);
189 		opts.data_size_in = sizeof(pkt.v4);
190 		skel->bss->unreach_type = ICMP_DEST_UNREACH;
191 		break;
192 	case AF_INET6:
193 		pkt.v6 = pkt_v6;
194 		pkt.v6.iph.version = 6;
195 		pkt.v6.iph.daddr = in6addr_loopback;
196 		pkt.v6.tcp.dest = htons(80);
197 		opts.data_size_in = sizeof(pkt.v6);
198 		skel->bss->unreach_type = ICMPV6_DEST_UNREACH;
199 		break;
200 	default:
201 		ASSERT_FAIL("af_not_supported");
202 		return;
203 	}
204 
205 	skel->bss->server_port = 80;
206 	skel->data->kfunc_ret = KFUNC_RET_UNSET;
207 
208 	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.egress), &opts);
209 	if (!ASSERT_OK(err, "test_run"))
210 		return;
211 
212 	ASSERT_EQ(skel->data->kfunc_ret, -ENETUNREACH, "kfunc_ret_no_route");
213 }
214 
test_icmp_send_unreach_cgroup(void)215 void test_icmp_send_unreach_cgroup(void)
216 {
217 	struct icmp_send *skel;
218 	int cgroup_fd = -1;
219 
220 	skel = icmp_send__open_and_load();
221 	if (!ASSERT_OK_PTR(skel, "skel_open"))
222 		goto cleanup;
223 
224 	cgroup_fd = test__join_cgroup("/icmp_send_unreach_cgroup");
225 	if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup"))
226 		goto cleanup;
227 
228 	skel->links.egress =
229 		bpf_program__attach_cgroup(skel->progs.egress, cgroup_fd);
230 	if (!ASSERT_OK_PTR(skel->links.egress, "prog_attach_cgroup"))
231 		goto cleanup;
232 
233 	if (test__start_subtest("ipv4"))
234 		run_icmp_test(skel, AF_INET, "127.0.0.1", NR_ICMP_UNREACH);
235 
236 	if (test__start_subtest("ipv6"))
237 		run_icmp_test(skel, AF_INET6, "::1", ICMPV6_REJECT_ROUTE);
238 
239 	if (test__start_subtest("no_route_ipv4"))
240 		run_icmp_no_route_test(skel, AF_INET);
241 
242 	if (test__start_subtest("no_route_ipv6"))
243 		run_icmp_no_route_test(skel, AF_INET6);
244 
245 cleanup:
246 	icmp_send__destroy(skel);
247 	if (cgroup_fd >= 0)
248 		close(cgroup_fd);
249 }
250 
test_icmp_send_unreach_recursion(void)251 void test_icmp_send_unreach_recursion(void)
252 {
253 	struct icmp_send *skel;
254 	int cgroup_fd = -1;
255 	int err;
256 
257 	err = setup_cgroup_environment();
258 	if (!ASSERT_OK(err, "setup_cgroup_environment"))
259 		return;
260 
261 	skel = icmp_send__open_and_load();
262 	if (!ASSERT_OK_PTR(skel, "skel_open"))
263 		goto cleanup;
264 
265 	cgroup_fd = get_root_cgroup();
266 	if (!ASSERT_OK_FD(cgroup_fd, "get_root_cgroup"))
267 		goto cleanup;
268 
269 	skel->data->target_pid = getpid();
270 	skel->links.recursion =
271 		bpf_program__attach_cgroup(skel->progs.recursion, cgroup_fd);
272 	if (!ASSERT_OK_PTR(skel->links.recursion, "prog_attach_cgroup"))
273 		goto cleanup;
274 
275 	trigger_prog_read_icmp_errqueue(skel, ICMP_HOST_UNREACH, AF_INET,
276 					"127.0.0.1");
277 
278 	/*
279 	 * Because there's recursion involved, the first call will return at
280 	 * index 1 since it will return the second, and the second call will
281 	 * return at index 0 since it will return the first.
282 	 */
283 	ASSERT_EQ(skel->bss->rec_count, 2, "rec_count");
284 	ASSERT_EQ(skel->data->rec_kfunc_rets[0], -EBUSY, "kfunc_rets[0]");
285 	ASSERT_EQ(skel->data->rec_kfunc_rets[1], 0, "kfunc_rets[1]");
286 
287 cleanup:
288 	icmp_send__destroy(skel);
289 	if (cgroup_fd >= 0)
290 		close(cgroup_fd);
291 	cleanup_cgroup_environment();
292 }
293