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 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 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 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. */ 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. */ 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 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 */ 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.