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 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 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 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 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 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 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 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 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