345 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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 16static 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 40static 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 49static 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. */63static 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. */109static 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 155static 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 to179 * 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 connection224 * from outside the cgroup.225 * 2. Connect to the server from outside the cgroup and close the connection226 * from inside the cgroup.227 * 3. Connect to the server from inside the cgroup and close the connection228 * from outside the cgroup.229 * 4. Connect to the server from inside the cgroup and close the connection230 * from inside the cgroup.231 *232 * The test case is to verify that cgroup_skb/{egress,ingress} filters233 * receive expected packets including SYN, SYN/ACK, ACK, FIN, and FIN/ACK.234 */235void 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 335cleanup: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