1 // SPDX-License-Identifier: GPL-2.0
2 #include <poll.h>
3 #include <test_progs.h>
4 #include <bpf/bpf_endian.h>
5
6 #include "sock_destroy_prog.skel.h"
7 #include "sock_destroy_prog_fail.skel.h"
8 #include "network_helpers.h"
9
10 #define TEST_NS "sock_destroy_netns"
11
start_iter_sockets(struct bpf_program * prog)12 static void start_iter_sockets(struct bpf_program *prog)
13 {
14 struct bpf_link *link;
15 char buf[50] = {};
16 int iter_fd, len;
17
18 link = bpf_program__attach_iter(prog, NULL);
19 if (!ASSERT_OK_PTR(link, "attach_iter"))
20 return;
21
22 iter_fd = bpf_iter_create(bpf_link__fd(link));
23 if (!ASSERT_GE(iter_fd, 0, "create_iter"))
24 goto free_link;
25
26 while ((len = read(iter_fd, buf, sizeof(buf))) > 0)
27 ;
28 ASSERT_GE(len, 0, "read");
29
30 close(iter_fd);
31
32 free_link:
33 bpf_link__destroy(link);
34 }
35
test_tcp_client(struct sock_destroy_prog * skel)36 static void test_tcp_client(struct sock_destroy_prog *skel)
37 {
38 int serv = -1, clien = -1, accept_serv = -1, n;
39
40 serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
41 if (!ASSERT_GE(serv, 0, "start_server"))
42 goto cleanup;
43
44 clien = connect_to_fd(serv, 0);
45 if (!ASSERT_GE(clien, 0, "connect_to_fd"))
46 goto cleanup;
47
48 accept_serv = accept(serv, NULL, NULL);
49 if (!ASSERT_GE(accept_serv, 0, "serv accept"))
50 goto cleanup;
51
52 n = send(clien, "t", 1, 0);
53 if (!ASSERT_EQ(n, 1, "client send"))
54 goto cleanup;
55
56 /* Run iterator program that destroys connected client sockets. */
57 start_iter_sockets(skel->progs.iter_tcp6_client);
58
59 n = send(clien, "t", 1, 0);
60 if (!ASSERT_LT(n, 0, "client_send on destroyed socket"))
61 goto cleanup;
62 ASSERT_EQ(errno, ECONNABORTED, "error code on destroyed socket");
63
64 cleanup:
65 if (clien != -1)
66 close(clien);
67 if (accept_serv != -1)
68 close(accept_serv);
69 if (serv != -1)
70 close(serv);
71 }
72
test_tcp_server(struct sock_destroy_prog * skel)73 static void test_tcp_server(struct sock_destroy_prog *skel)
74 {
75 int serv = -1, clien = -1, accept_serv = -1, n, serv_port;
76
77 serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
78 if (!ASSERT_GE(serv, 0, "start_server"))
79 goto cleanup;
80 serv_port = get_socket_local_port(serv);
81 if (!ASSERT_GE(serv_port, 0, "get_sock_local_port"))
82 goto cleanup;
83 skel->bss->serv_port = (__be16) serv_port;
84
85 clien = connect_to_fd(serv, 0);
86 if (!ASSERT_GE(clien, 0, "connect_to_fd"))
87 goto cleanup;
88
89 accept_serv = accept(serv, NULL, NULL);
90 if (!ASSERT_GE(accept_serv, 0, "serv accept"))
91 goto cleanup;
92
93 n = send(clien, "t", 1, 0);
94 if (!ASSERT_EQ(n, 1, "client send"))
95 goto cleanup;
96
97 /* Run iterator program that destroys server sockets. */
98 start_iter_sockets(skel->progs.iter_tcp6_server);
99
100 n = send(clien, "t", 1, 0);
101 if (!ASSERT_LT(n, 0, "client_send on destroyed socket"))
102 goto cleanup;
103 ASSERT_EQ(errno, ECONNRESET, "error code on destroyed socket");
104
105 cleanup:
106 if (clien != -1)
107 close(clien);
108 if (accept_serv != -1)
109 close(accept_serv);
110 if (serv != -1)
111 close(serv);
112 }
113
test_tcp_listen_pending(struct sock_destroy_prog * skel)114 static void test_tcp_listen_pending(struct sock_destroy_prog *skel)
115 {
116 int serv = -1, clien = -1, accept_serv = -1, n, serv_port;
117 struct pollfd pfd = { .events = POLLIN };
118 char buf[1];
119
120 serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
121 if (!ASSERT_GE(serv, 0, "start_server"))
122 goto cleanup;
123 serv_port = get_socket_local_port(serv);
124 if (!ASSERT_GE(serv_port, 0, "get_sock_local_port"))
125 goto cleanup;
126 skel->bss->serv_port = (__be16)serv_port;
127
128 /*
129 * Connect but never accept, so the child sits in the accept queue
130 * of the listener. Wait until it's actually there.
131 */
132 clien = connect_to_fd(serv, 0);
133 if (!ASSERT_GE(clien, 0, "connect_to_fd"))
134 goto cleanup;
135 pfd.fd = serv;
136 if (!ASSERT_EQ(poll(&pfd, 1, -1), 1, "poll listener"))
137 goto cleanup;
138
139 /* Run iterator program that destroys server sockets. */
140 start_iter_sockets(skel->progs.iter_tcp6_server);
141
142 accept_serv = accept(serv, NULL, NULL);
143 if (!ASSERT_LT(accept_serv, 0, "accept on destroyed listener"))
144 goto cleanup;
145 ASSERT_EQ(errno, EINVAL, "error code on destroyed listener");
146
147 /* The unaccepted child was reset along with the listener. */
148 n = recv(clien, buf, sizeof(buf), 0);
149 if (!ASSERT_LT(n, 0, "client recv on reset child"))
150 goto cleanup;
151 ASSERT_EQ(errno, ECONNRESET, "error code on reset child");
152
153 cleanup:
154 if (clien != -1)
155 close(clien);
156 if (accept_serv != -1)
157 close(accept_serv);
158 if (serv != -1)
159 close(serv);
160 }
161
test_tcp_timewait(struct sock_destroy_prog * skel)162 static void test_tcp_timewait(struct sock_destroy_prog *skel)
163 {
164 int serv = -1, clien = -1, accept_serv = -1, n;
165 struct timeval tv = {};
166 char buf[1];
167
168 serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
169 if (!ASSERT_GE(serv, 0, "start_server"))
170 goto cleanup;
171
172 clien = connect_to_fd(serv, 0);
173 if (!ASSERT_GE(clien, 0, "connect_to_fd"))
174 goto cleanup;
175
176 accept_serv = accept(serv, NULL, NULL);
177 if (!ASSERT_GE(accept_serv, 0, "serv accept"))
178 goto cleanup;
179
180 /*
181 * Active close from the client, then close the server side. Once
182 * recv() sees EOF the server FIN has been processed and the client
183 * sock is in TIME_WAIT. Block without timeout so a loaded CI box
184 * can't race us.
185 */
186 if (!ASSERT_OK(setsockopt(clien, SOL_SOCKET, SO_RCVTIMEO, &tv,
187 sizeof(tv)), "clear rcvtimeo"))
188 goto cleanup;
189 if (!ASSERT_OK(shutdown(clien, SHUT_WR), "client shutdown"))
190 goto cleanup;
191
192 /*
193 * Make sure the server has seen the client FIN before it closes,
194 * so the two FINs never cross.
195 */
196 n = recv(accept_serv, buf, sizeof(buf), 0);
197 if (!ASSERT_EQ(n, 0, "server recv EOF"))
198 goto cleanup;
199
200 close(accept_serv);
201 accept_serv = -1;
202
203 /* block until return EOF */
204 n = recv(clien, buf, sizeof(buf), 0);
205 if (!ASSERT_EQ(n, 0, "client recv EOF"))
206 goto cleanup;
207
208 /* Run iterator program that destroys the timewait client sock. */
209 skel->bss->tw_found = 0;
210 start_iter_sockets(skel->progs.iter_tcp6_timewait);
211 if (!ASSERT_EQ(skel->bss->tw_found, 1, "timewait sock found"))
212 goto cleanup;
213
214 ASSERT_OK(skel->bss->tw_destroy_err, "destroy timewait sock");
215
216 /* The destroyed timewait sock must be gone. */
217 skel->bss->tw_found = 0;
218 start_iter_sockets(skel->progs.iter_tcp6_timewait);
219 ASSERT_EQ(skel->bss->tw_found, 0, "timewait sock destroyed");
220
221 cleanup:
222 if (clien != -1)
223 close(clien);
224 if (accept_serv != -1)
225 close(accept_serv);
226 if (serv != -1)
227 close(serv);
228 }
229
test_udp_client(struct sock_destroy_prog * skel)230 static void test_udp_client(struct sock_destroy_prog *skel)
231 {
232 int serv = -1, clien = -1, n = 0;
233
234 serv = start_server(AF_INET6, SOCK_DGRAM, NULL, 0, 0);
235 if (!ASSERT_GE(serv, 0, "start_server"))
236 goto cleanup;
237
238 clien = connect_to_fd(serv, 0);
239 if (!ASSERT_GE(clien, 0, "connect_to_fd"))
240 goto cleanup;
241
242 n = send(clien, "t", 1, 0);
243 if (!ASSERT_EQ(n, 1, "client send"))
244 goto cleanup;
245
246 /* Run iterator program that destroys sockets. */
247 start_iter_sockets(skel->progs.iter_udp6_client);
248
249 n = send(clien, "t", 1, 0);
250 if (!ASSERT_LT(n, 0, "client_send on destroyed socket"))
251 goto cleanup;
252 /* UDP sockets have an overriding error code after they are disconnected,
253 * so we don't check for ECONNABORTED error code.
254 */
255
256 cleanup:
257 if (clien != -1)
258 close(clien);
259 if (serv != -1)
260 close(serv);
261 }
262
test_udp_server(struct sock_destroy_prog * skel)263 static void test_udp_server(struct sock_destroy_prog *skel)
264 {
265 int *listen_fds = NULL, n, i, serv_port;
266 unsigned int num_listens = 5;
267 char buf[1];
268
269 /* Start reuseport servers. */
270 listen_fds = start_reuseport_server(AF_INET6, SOCK_DGRAM,
271 "::1", 0, 0, num_listens);
272 if (!ASSERT_OK_PTR(listen_fds, "start_reuseport_server"))
273 goto cleanup;
274 serv_port = get_socket_local_port(listen_fds[0]);
275 if (!ASSERT_GE(serv_port, 0, "get_sock_local_port"))
276 goto cleanup;
277 skel->bss->serv_port = (__be16) serv_port;
278
279 /* Run iterator program that destroys server sockets. */
280 start_iter_sockets(skel->progs.iter_udp6_server);
281
282 for (i = 0; i < num_listens; ++i) {
283 n = read(listen_fds[i], buf, sizeof(buf));
284 if (!ASSERT_EQ(n, -1, "read") ||
285 !ASSERT_EQ(errno, ECONNABORTED, "error code on destroyed socket"))
286 break;
287 }
288 ASSERT_EQ(i, num_listens, "server socket");
289
290 cleanup:
291 free_fds(listen_fds, num_listens);
292 }
293
test_sock_destroy(void)294 void test_sock_destroy(void)
295 {
296 struct sock_destroy_prog *skel;
297 struct nstoken *nstoken = NULL;
298 int cgroup_fd;
299
300 skel = sock_destroy_prog__open_and_load();
301 if (!ASSERT_OK_PTR(skel, "skel_open"))
302 return;
303
304 cgroup_fd = test__join_cgroup("/sock_destroy");
305 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
306 goto cleanup;
307
308 skel->links.sock_connect = bpf_program__attach_cgroup(
309 skel->progs.sock_connect, cgroup_fd);
310 if (!ASSERT_OK_PTR(skel->links.sock_connect, "prog_attach"))
311 goto cleanup;
312
313 SYS(cleanup, "ip netns add %s", TEST_NS);
314 SYS(cleanup, "ip -net %s link set dev lo up", TEST_NS);
315
316 nstoken = open_netns(TEST_NS);
317 if (!ASSERT_OK_PTR(nstoken, "open_netns"))
318 goto cleanup;
319
320 if (test__start_subtest("tcp_client"))
321 test_tcp_client(skel);
322 if (test__start_subtest("tcp_server"))
323 test_tcp_server(skel);
324 if (test__start_subtest("tcp_listen_pending"))
325 test_tcp_listen_pending(skel);
326 if (test__start_subtest("tcp_timewait"))
327 test_tcp_timewait(skel);
328 if (test__start_subtest("udp_client"))
329 test_udp_client(skel);
330 if (test__start_subtest("udp_server"))
331 test_udp_server(skel);
332
333 RUN_TESTS(sock_destroy_prog_fail);
334
335 cleanup:
336 if (nstoken)
337 close_netns(nstoken);
338 SYS_NOFAIL("ip netns del " TEST_NS);
339 if (cgroup_fd >= 0)
340 close(cgroup_fd);
341 sock_destroy_prog__destroy(skel);
342 }
343