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