1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Facebook */
3 #include <test_progs.h>
4 #include <linux/in6.h>
5 #include <sys/socket.h>
6 #include <sched.h>
7 #include <unistd.h>
8 #include "cgroup_helpers.h"
9 #include "testing_helpers.h"
10 #include "cgroup_tcp_skb.skel.h"
11 #include "cgroup_tcp_skb.h"
12 
13 #define CGROUP_TCP_SKB_PATH "/test_cgroup_tcp_skb"
14 
15 static int install_filters(int cgroup_fd,
16 			   struct bpf_link **egress_link,
17 			   struct bpf_link **ingress_link,
18 			   struct bpf_program *egress_prog,
19 			   struct bpf_program *ingress_prog,
20 			   struct cgroup_tcp_skb *skel)
21 {
22 	/* Prepare filters */
23 	skel->bss->g_sock_state = 0;
24 	skel->bss->g_unexpected = 0;
25 	*egress_link =
26 		bpf_program__attach_cgroup(egress_prog,
27 					   cgroup_fd);
28 	if (!ASSERT_OK_PTR(egress_link, "egress_link"))
29 		return -1;
30 	*ingress_link =
31 		bpf_program__attach_cgroup(ingress_prog,
32 					   cgroup_fd);
33 	if (!ASSERT_OK_PTR(ingress_link, "ingress_link"))
34 		return -1;
35 
36 	return 0;
37 }
38 
39 static void uninstall_filters(struct bpf_link **egress_link,
40 			      struct bpf_link **ingress_link)
41 {
42 	bpf_link__destroy(*egress_link);
43 	*egress_link = NULL;
44 	bpf_link__destroy(*ingress_link);
45 	*ingress_link = NULL;
46 }
47 
48 static int create_client_sock_v6(void)
49 {
50 	int fd;
51 
52 	fd = socket(AF_INET6, SOCK_STREAM, 0);
53 	if (fd < 0) {
54 		perror("socket");
55 		return -1;
56 	}
57 
58 	return fd;
59 }
60 
61 static int create_server_sock_v6(void)
62 {
63 	struct sockaddr_in6 addr = {
64 		.sin6_family = AF_INET6,
65 		.sin6_port = htons(0),
66 		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
67 	};
68 	int fd, err;
69 
70 	fd = socket(AF_INET6, SOCK_STREAM, 0);
71 	if (fd < 0) {
72 		perror("socket");
73 		return -1;
74 	}
75 
76 	err = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
77 	if (err < 0) {
78 		perror("bind");
79 		return -1;
80 	}
81 
82 	err = listen(fd, 1);
83 	if (err < 0) {
84 		perror("listen");
85 		return -1;
86 	}
87 
88 	return fd;
89 }
90 
91 static int get_sock_port_v6(int fd)
92 {
93 	struct sockaddr_in6 addr;
94 	socklen_t len;
95 	int err;
96 
97 	len = sizeof(addr);
98 	err = getsockname(fd, (struct sockaddr *)&addr, &len);
99 	if (err < 0) {
100 		perror("getsockname");
101 		return -1;
102 	}
103 
104 	return ntohs(addr.sin6_port);
105 }
106 
107 static int connect_client_server_v6(int client_fd, int listen_fd)
108 {
109 	struct sockaddr_in6 addr = {
110 		.sin6_family = AF_INET6,
111 		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
112 	};
113 	int err;
114 
115 	addr.sin6_port = htons(get_sock_port_v6(listen_fd));
116 	if (addr.sin6_port < 0)
117 		return -1;
118 
119 	err = connect(client_fd, (struct sockaddr *)&addr, sizeof(addr));
120 	if (err < 0) {
121 		perror("connect");
122 		return -1;
123 	}
124 
125 	return 0;
126 }
127 
128 /* Connect to the server in a cgroup from the outside of the cgroup. */
129 static int talk_to_cgroup(int *client_fd, int *listen_fd, int *service_fd,
130 			  struct cgroup_tcp_skb *skel)
131 {
132 	int err, cp;
133 	char buf[5];
134 
135 	/* Create client & server socket */
136 	err = join_root_cgroup();
137 	if (!ASSERT_OK(err, "join_root_cgroup"))
138 		return -1;
139 	*client_fd = create_client_sock_v6();
140 	if (!ASSERT_GE(*client_fd, 0, "client_fd"))
141 		return -1;
142 	err = join_cgroup(CGROUP_TCP_SKB_PATH);
143 	if (!ASSERT_OK(err, "join_cgroup"))
144 		return -1;
145 	*listen_fd = create_server_sock_v6();
146 	if (!ASSERT_GE(*listen_fd, 0, "listen_fd"))
147 		return -1;
148 	skel->bss->g_sock_port = get_sock_port_v6(*listen_fd);
149 
150 	/* Connect client to server */
151 	err = connect_client_server_v6(*client_fd, *listen_fd);
152 	if (!ASSERT_OK(err, "connect_client_server_v6"))
153 		return -1;
154 	*service_fd = accept(*listen_fd, NULL, NULL);
155 	if (!ASSERT_GE(*service_fd, 0, "service_fd"))
156 		return -1;
157 	err = join_root_cgroup();
158 	if (!ASSERT_OK(err, "join_root_cgroup"))
159 		return -1;
160 	cp = write(*client_fd, "hello", 5);
161 	if (!ASSERT_EQ(cp, 5, "write"))
162 		return -1;
163 	cp = read(*service_fd, buf, 5);
164 	if (!ASSERT_EQ(cp, 5, "read"))
165 		return -1;
166 
167 	return 0;
168 }
169 
170 /* Connect to the server out of a cgroup from inside the cgroup. */
171 static int talk_to_outside(int *client_fd, int *listen_fd, int *service_fd,
172 			   struct cgroup_tcp_skb *skel)
173 
174 {
175 	int err, cp;
176 	char buf[5];
177 
178 	/* Create client & server socket */
179 	err = join_root_cgroup();
180 	if (!ASSERT_OK(err, "join_root_cgroup"))
181 		return -1;
182 	*listen_fd = create_server_sock_v6();
183 	if (!ASSERT_GE(*listen_fd, 0, "listen_fd"))
184 		return -1;
185 	err = join_cgroup(CGROUP_TCP_SKB_PATH);
186 	if (!ASSERT_OK(err, "join_cgroup"))
187 		return -1;
188 	*client_fd = create_client_sock_v6();
189 	if (!ASSERT_GE(*client_fd, 0, "client_fd"))
190 		return -1;
191 	err = join_root_cgroup();
192 	if (!ASSERT_OK(err, "join_root_cgroup"))
193 		return -1;
194 	skel->bss->g_sock_port = get_sock_port_v6(*listen_fd);
195 
196 	/* Connect client to server */
197 	err = connect_client_server_v6(*client_fd, *listen_fd);
198 	if (!ASSERT_OK(err, "connect_client_server_v6"))
199 		return -1;
200 	*service_fd = accept(*listen_fd, NULL, NULL);
201 	if (!ASSERT_GE(*service_fd, 0, "service_fd"))
202 		return -1;
203 	cp = write(*client_fd, "hello", 5);
204 	if (!ASSERT_EQ(cp, 5, "write"))
205 		return -1;
206 	cp = read(*service_fd, buf, 5);
207 	if (!ASSERT_EQ(cp, 5, "read"))
208 		return -1;
209 
210 	return 0;
211 }
212 
213 static int close_connection(int *closing_fd, int *peer_fd, int *listen_fd,
214 			    struct cgroup_tcp_skb *skel)
215 {
216 	__u32 saved_packet_count = 0;
217 	int err;
218 	int i;
219 
220 	/* Wait for ACKs to be sent */
221 	saved_packet_count = skel->bss->g_packet_count;
222 	usleep(100000);		/* 0.1s */
223 	for (i = 0;
224 	     skel->bss->g_packet_count != saved_packet_count && i < 10;
225 	     i++) {
226 		saved_packet_count = skel->bss->g_packet_count;
227 		usleep(100000);	/* 0.1s */
228 	}
229 	if (!ASSERT_EQ(skel->bss->g_packet_count, saved_packet_count,
230 		       "packet_count"))
231 		return -1;
232 
233 	skel->bss->g_packet_count = 0;
234 	saved_packet_count = 0;
235 
236 	/* Half shutdown to make sure the closing socket having a chance to
237 	 * receive a FIN from the peer.
238 	 */
239 	err = shutdown(*closing_fd, SHUT_WR);
240 	if (!ASSERT_OK(err, "shutdown closing_fd"))
241 		return -1;
242 
243 	/* Wait for FIN and the ACK of the FIN to be observed */
244 	for (i = 0;
245 	     skel->bss->g_packet_count < saved_packet_count + 2 && i < 10;
246 	     i++)
247 		usleep(100000);	/* 0.1s */
248 	if (!ASSERT_GE(skel->bss->g_packet_count, saved_packet_count + 2,
249 		       "packet_count"))
250 		return -1;
251 
252 	saved_packet_count = skel->bss->g_packet_count;
253 
254 	/* Fully shutdown the connection */
255 	err = close(*peer_fd);
256 	if (!ASSERT_OK(err, "close peer_fd"))
257 		return -1;
258 	*peer_fd = -1;
259 
260 	/* Wait for FIN and the ACK of the FIN to be observed */
261 	for (i = 0;
262 	     skel->bss->g_packet_count < saved_packet_count + 2 && i < 10;
263 	     i++)
264 		usleep(100000);	/* 0.1s */
265 	if (!ASSERT_GE(skel->bss->g_packet_count, saved_packet_count + 2,
266 		       "packet_count"))
267 		return -1;
268 
269 	err = close(*closing_fd);
270 	if (!ASSERT_OK(err, "close closing_fd"))
271 		return -1;
272 	*closing_fd = -1;
273 
274 	close(*listen_fd);
275 	*listen_fd = -1;
276 
277 	return 0;
278 }
279 
280 /* This test case includes four scenarios:
281  * 1. Connect to the server from outside the cgroup and close the connection
282  *    from outside the cgroup.
283  * 2. Connect to the server from outside the cgroup and close the connection
284  *    from inside the cgroup.
285  * 3. Connect to the server from inside the cgroup and close the connection
286  *    from outside the cgroup.
287  * 4. Connect to the server from inside the cgroup and close the connection
288  *    from inside the cgroup.
289  *
290  * The test case is to verify that cgroup_skb/{egress,ingress} filters
291  * receive expected packets including SYN, SYN/ACK, ACK, FIN, and FIN/ACK.
292  */
293 void test_cgroup_tcp_skb(void)
294 {
295 	struct bpf_link *ingress_link = NULL;
296 	struct bpf_link *egress_link = NULL;
297 	int client_fd = -1, listen_fd = -1;
298 	struct cgroup_tcp_skb *skel;
299 	int service_fd = -1;
300 	int cgroup_fd = -1;
301 	int err;
302 
303 	skel = cgroup_tcp_skb__open_and_load();
304 	if (!ASSERT_OK(!skel, "skel_open_load"))
305 		return;
306 
307 	err = setup_cgroup_environment();
308 	if (!ASSERT_OK(err, "setup_cgroup_environment"))
309 		goto cleanup;
310 
311 	cgroup_fd = create_and_get_cgroup(CGROUP_TCP_SKB_PATH);
312 	if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd"))
313 		goto cleanup;
314 
315 	/* Scenario 1 */
316 	err = install_filters(cgroup_fd, &egress_link, &ingress_link,
317 			      skel->progs.server_egress,
318 			      skel->progs.server_ingress,
319 			      skel);
320 	if (!ASSERT_OK(err, "install_filters"))
321 		goto cleanup;
322 
323 	err = talk_to_cgroup(&client_fd, &listen_fd, &service_fd, skel);
324 	if (!ASSERT_OK(err, "talk_to_cgroup"))
325 		goto cleanup;
326 
327 	err = close_connection(&client_fd, &service_fd, &listen_fd, skel);
328 	if (!ASSERT_OK(err, "close_connection"))
329 		goto cleanup;
330 
331 	ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
332 	ASSERT_EQ(skel->bss->g_sock_state, CLOSED, "g_sock_state");
333 
334 	uninstall_filters(&egress_link, &ingress_link);
335 
336 	/* Scenario 2 */
337 	err = install_filters(cgroup_fd, &egress_link, &ingress_link,
338 			      skel->progs.server_egress_srv,
339 			      skel->progs.server_ingress_srv,
340 			      skel);
341 
342 	err = talk_to_cgroup(&client_fd, &listen_fd, &service_fd, skel);
343 	if (!ASSERT_OK(err, "talk_to_cgroup"))
344 		goto cleanup;
345 
346 	err = close_connection(&service_fd, &client_fd, &listen_fd, skel);
347 	if (!ASSERT_OK(err, "close_connection"))
348 		goto cleanup;
349 
350 	ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
351 	ASSERT_EQ(skel->bss->g_sock_state, TIME_WAIT, "g_sock_state");
352 
353 	uninstall_filters(&egress_link, &ingress_link);
354 
355 	/* Scenario 3 */
356 	err = install_filters(cgroup_fd, &egress_link, &ingress_link,
357 			      skel->progs.client_egress_srv,
358 			      skel->progs.client_ingress_srv,
359 			      skel);
360 
361 	err = talk_to_outside(&client_fd, &listen_fd, &service_fd, skel);
362 	if (!ASSERT_OK(err, "talk_to_outside"))
363 		goto cleanup;
364 
365 	err = close_connection(&service_fd, &client_fd, &listen_fd, skel);
366 	if (!ASSERT_OK(err, "close_connection"))
367 		goto cleanup;
368 
369 	ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
370 	ASSERT_EQ(skel->bss->g_sock_state, CLOSED, "g_sock_state");
371 
372 	uninstall_filters(&egress_link, &ingress_link);
373 
374 	/* Scenario 4 */
375 	err = install_filters(cgroup_fd, &egress_link, &ingress_link,
376 			      skel->progs.client_egress,
377 			      skel->progs.client_ingress,
378 			      skel);
379 
380 	err = talk_to_outside(&client_fd, &listen_fd, &service_fd, skel);
381 	if (!ASSERT_OK(err, "talk_to_outside"))
382 		goto cleanup;
383 
384 	err = close_connection(&client_fd, &service_fd, &listen_fd, skel);
385 	if (!ASSERT_OK(err, "close_connection"))
386 		goto cleanup;
387 
388 	ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
389 	ASSERT_EQ(skel->bss->g_sock_state, TIME_WAIT, "g_sock_state");
390 
391 	uninstall_filters(&egress_link, &ingress_link);
392 
393 cleanup:
394 	close(client_fd);
395 	close(listen_fd);
396 	close(service_fd);
397 	close(cgroup_fd);
398 	bpf_link__destroy(egress_link);
399 	bpf_link__destroy(ingress_link);
400 	cleanup_cgroup_environment();
401 	cgroup_tcp_skb__destroy(skel);
402 }
403