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