1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2021 Facebook */ 3 #define _GNU_SOURCE 4 #include <sched.h> 5 #include <test_progs.h> 6 #include "network_helpers.h" 7 #include "bpf_dctcp.skel.h" 8 #include "bpf_cubic.skel.h" 9 #include "bpf_iter_setsockopt.skel.h" 10 11 static int create_netns(void) 12 { 13 if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns")) 14 return -1; 15 16 if (!ASSERT_OK(system("ip link set dev lo up"), "bring up lo")) 17 return -1; 18 19 return 0; 20 } 21 22 static unsigned int set_bpf_cubic(int *fds, unsigned int nr_fds) 23 { 24 unsigned int i; 25 26 for (i = 0; i < nr_fds; i++) { 27 if (setsockopt(fds[i], SOL_TCP, TCP_CONGESTION, "bpf_cubic", 28 sizeof("bpf_cubic"))) 29 return i; 30 } 31 32 return nr_fds; 33 } 34 35 static unsigned int check_bpf_dctcp(int *fds, unsigned int nr_fds) 36 { 37 char tcp_cc[16]; 38 socklen_t optlen = sizeof(tcp_cc); 39 unsigned int i; 40 41 for (i = 0; i < nr_fds; i++) { 42 if (getsockopt(fds[i], SOL_TCP, TCP_CONGESTION, 43 tcp_cc, &optlen) || 44 strcmp(tcp_cc, "bpf_dctcp")) 45 return i; 46 } 47 48 return nr_fds; 49 } 50 51 static int *make_established(int listen_fd, unsigned int nr_est, 52 int **paccepted_fds) 53 { 54 int *est_fds, *accepted_fds; 55 unsigned int i; 56 57 est_fds = malloc(sizeof(*est_fds) * nr_est); 58 if (!est_fds) 59 return NULL; 60 61 accepted_fds = malloc(sizeof(*accepted_fds) * nr_est); 62 if (!accepted_fds) { 63 free(est_fds); 64 return NULL; 65 } 66 67 for (i = 0; i < nr_est; i++) { 68 est_fds[i] = connect_to_fd(listen_fd, 0); 69 if (est_fds[i] == -1) 70 break; 71 if (set_bpf_cubic(&est_fds[i], 1) != 1) { 72 close(est_fds[i]); 73 break; 74 } 75 76 accepted_fds[i] = accept(listen_fd, NULL, 0); 77 if (accepted_fds[i] == -1) { 78 close(est_fds[i]); 79 break; 80 } 81 } 82 83 if (!ASSERT_EQ(i, nr_est, "create established fds")) { 84 free_fds(accepted_fds, i); 85 free_fds(est_fds, i); 86 return NULL; 87 } 88 89 *paccepted_fds = accepted_fds; 90 return est_fds; 91 } 92 93 static unsigned short get_local_port(int fd) 94 { 95 struct sockaddr_in6 addr; 96 socklen_t addrlen = sizeof(addr); 97 98 if (!getsockname(fd, &addr, &addrlen)) 99 return ntohs(addr.sin6_port); 100 101 return 0; 102 } 103 104 static void do_bpf_iter_setsockopt(struct bpf_iter_setsockopt *iter_skel, 105 bool random_retry) 106 { 107 int *reuse_listen_fds = NULL, *accepted_fds = NULL, *est_fds = NULL; 108 unsigned int nr_reuse_listens = 256, nr_est = 256; 109 int err, iter_fd = -1, listen_fd = -1; 110 char buf; 111 112 /* Prepare non-reuseport listen_fd */ 113 listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); 114 if (!ASSERT_GE(listen_fd, 0, "start_server")) 115 return; 116 if (!ASSERT_EQ(set_bpf_cubic(&listen_fd, 1), 1, 117 "set listen_fd to cubic")) 118 goto done; 119 iter_skel->bss->listen_hport = get_local_port(listen_fd); 120 if (!ASSERT_NEQ(iter_skel->bss->listen_hport, 0, 121 "get_local_port(listen_fd)")) 122 goto done; 123 124 /* Connect to non-reuseport listen_fd */ 125 est_fds = make_established(listen_fd, nr_est, &accepted_fds); 126 if (!ASSERT_OK_PTR(est_fds, "create established")) 127 goto done; 128 129 /* Prepare reuseport listen fds */ 130 reuse_listen_fds = start_reuseport_server(AF_INET6, SOCK_STREAM, 131 "::1", 0, 0, 132 nr_reuse_listens); 133 if (!ASSERT_OK_PTR(reuse_listen_fds, "start_reuseport_server")) 134 goto done; 135 if (!ASSERT_EQ(set_bpf_cubic(reuse_listen_fds, nr_reuse_listens), 136 nr_reuse_listens, "set reuse_listen_fds to cubic")) 137 goto done; 138 iter_skel->bss->reuse_listen_hport = get_local_port(reuse_listen_fds[0]); 139 if (!ASSERT_NEQ(iter_skel->bss->reuse_listen_hport, 0, 140 "get_local_port(reuse_listen_fds[0])")) 141 goto done; 142 143 /* Run bpf tcp iter to switch from bpf_cubic to bpf_dctcp */ 144 iter_skel->bss->random_retry = random_retry; 145 iter_fd = bpf_iter_create(bpf_link__fd(iter_skel->links.change_tcp_cc)); 146 if (!ASSERT_GE(iter_fd, 0, "create iter_fd")) 147 goto done; 148 149 while ((err = read(iter_fd, &buf, sizeof(buf))) == -1 && 150 errno == EAGAIN) 151 ; 152 if (!ASSERT_OK(err, "read iter error")) 153 goto done; 154 155 /* Check reuseport listen fds for dctcp */ 156 ASSERT_EQ(check_bpf_dctcp(reuse_listen_fds, nr_reuse_listens), 157 nr_reuse_listens, 158 "check reuse_listen_fds dctcp"); 159 160 /* Check non reuseport listen fd for dctcp */ 161 ASSERT_EQ(check_bpf_dctcp(&listen_fd, 1), 1, 162 "check listen_fd dctcp"); 163 164 /* Check established fds for dctcp */ 165 ASSERT_EQ(check_bpf_dctcp(est_fds, nr_est), nr_est, 166 "check est_fds dctcp"); 167 168 /* Check accepted fds for dctcp */ 169 ASSERT_EQ(check_bpf_dctcp(accepted_fds, nr_est), nr_est, 170 "check accepted_fds dctcp"); 171 172 done: 173 if (iter_fd != -1) 174 close(iter_fd); 175 if (listen_fd != -1) 176 close(listen_fd); 177 free_fds(reuse_listen_fds, nr_reuse_listens); 178 free_fds(accepted_fds, nr_est); 179 free_fds(est_fds, nr_est); 180 } 181 182 void serial_test_bpf_iter_setsockopt(void) 183 { 184 struct bpf_iter_setsockopt *iter_skel = NULL; 185 struct bpf_cubic *cubic_skel = NULL; 186 struct bpf_dctcp *dctcp_skel = NULL; 187 struct bpf_link *cubic_link = NULL; 188 struct bpf_link *dctcp_link = NULL; 189 190 if (create_netns()) 191 return; 192 193 /* Load iter_skel */ 194 iter_skel = bpf_iter_setsockopt__open_and_load(); 195 if (!ASSERT_OK_PTR(iter_skel, "iter_skel")) 196 return; 197 iter_skel->links.change_tcp_cc = bpf_program__attach_iter(iter_skel->progs.change_tcp_cc, NULL); 198 if (!ASSERT_OK_PTR(iter_skel->links.change_tcp_cc, "attach iter")) 199 goto done; 200 201 /* Load bpf_cubic */ 202 cubic_skel = bpf_cubic__open_and_load(); 203 if (!ASSERT_OK_PTR(cubic_skel, "cubic_skel")) 204 goto done; 205 cubic_link = bpf_map__attach_struct_ops(cubic_skel->maps.cubic); 206 if (!ASSERT_OK_PTR(cubic_link, "cubic_link")) 207 goto done; 208 209 /* Load bpf_dctcp */ 210 dctcp_skel = bpf_dctcp__open_and_load(); 211 if (!ASSERT_OK_PTR(dctcp_skel, "dctcp_skel")) 212 goto done; 213 dctcp_link = bpf_map__attach_struct_ops(dctcp_skel->maps.dctcp); 214 if (!ASSERT_OK_PTR(dctcp_link, "dctcp_link")) 215 goto done; 216 217 do_bpf_iter_setsockopt(iter_skel, true); 218 do_bpf_iter_setsockopt(iter_skel, false); 219 220 done: 221 bpf_link__destroy(cubic_link); 222 bpf_link__destroy(dctcp_link); 223 bpf_cubic__destroy(cubic_skel); 224 bpf_dctcp__destroy(dctcp_skel); 225 bpf_iter_setsockopt__destroy(iter_skel); 226 } 227