1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2018 Facebook 3 // Copyright (c) 2019 Cloudflare 4 5 #include <limits.h> 6 #include <string.h> 7 #include <stdlib.h> 8 #include <unistd.h> 9 10 #include <arpa/inet.h> 11 #include <netinet/in.h> 12 #include <sys/types.h> 13 #include <sys/socket.h> 14 15 #include <bpf/bpf.h> 16 #include <bpf/libbpf.h> 17 18 #include "bpf_rlimit.h" 19 #include "cgroup_helpers.h" 20 21 static int start_server(const struct sockaddr *addr, socklen_t len, bool dual) 22 { 23 int mode = !dual; 24 int fd; 25 26 fd = socket(addr->sa_family, SOCK_STREAM, 0); 27 if (fd == -1) { 28 log_err("Failed to create server socket"); 29 goto out; 30 } 31 32 if (addr->sa_family == AF_INET6) { 33 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&mode, 34 sizeof(mode)) == -1) { 35 log_err("Failed to set the dual-stack mode"); 36 goto close_out; 37 } 38 } 39 40 if (bind(fd, addr, len) == -1) { 41 log_err("Failed to bind server socket"); 42 goto close_out; 43 } 44 45 if (listen(fd, 128) == -1) { 46 log_err("Failed to listen on server socket"); 47 goto close_out; 48 } 49 50 goto out; 51 52 close_out: 53 close(fd); 54 fd = -1; 55 out: 56 return fd; 57 } 58 59 static int connect_to_server(const struct sockaddr *addr, socklen_t len) 60 { 61 int fd = -1; 62 63 fd = socket(addr->sa_family, SOCK_STREAM, 0); 64 if (fd == -1) { 65 log_err("Failed to create client socket"); 66 goto out; 67 } 68 69 if (connect(fd, (const struct sockaddr *)addr, len) == -1) { 70 log_err("Fail to connect to server"); 71 goto close_out; 72 } 73 74 goto out; 75 76 close_out: 77 close(fd); 78 fd = -1; 79 out: 80 return fd; 81 } 82 83 static int get_map_fd_by_prog_id(int prog_id, bool *xdp) 84 { 85 struct bpf_prog_info info = {}; 86 __u32 info_len = sizeof(info); 87 __u32 map_ids[1]; 88 int prog_fd = -1; 89 int map_fd = -1; 90 91 prog_fd = bpf_prog_get_fd_by_id(prog_id); 92 if (prog_fd < 0) { 93 log_err("Failed to get fd by prog id %d", prog_id); 94 goto err; 95 } 96 97 info.nr_map_ids = 1; 98 info.map_ids = (__u64)(unsigned long)map_ids; 99 100 if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) { 101 log_err("Failed to get info by prog fd %d", prog_fd); 102 goto err; 103 } 104 105 if (!info.nr_map_ids) { 106 log_err("No maps found for prog fd %d", prog_fd); 107 goto err; 108 } 109 110 *xdp = info.type == BPF_PROG_TYPE_XDP; 111 112 map_fd = bpf_map_get_fd_by_id(map_ids[0]); 113 if (map_fd < 0) 114 log_err("Failed to get fd by map id %d", map_ids[0]); 115 err: 116 if (prog_fd >= 0) 117 close(prog_fd); 118 return map_fd; 119 } 120 121 static int run_test(int server_fd, int results_fd, bool xdp, 122 const struct sockaddr *addr, socklen_t len) 123 { 124 int client = -1, srv_client = -1; 125 int ret = 0; 126 __u32 key = 0; 127 __u32 key_gen = 1; 128 __u32 key_mss = 2; 129 __u32 value = 0; 130 __u32 value_gen = 0; 131 __u32 value_mss = 0; 132 133 if (bpf_map_update_elem(results_fd, &key, &value, 0) < 0) { 134 log_err("Can't clear results"); 135 goto err; 136 } 137 138 if (bpf_map_update_elem(results_fd, &key_gen, &value_gen, 0) < 0) { 139 log_err("Can't clear results"); 140 goto err; 141 } 142 143 if (bpf_map_update_elem(results_fd, &key_mss, &value_mss, 0) < 0) { 144 log_err("Can't clear results"); 145 goto err; 146 } 147 148 client = connect_to_server(addr, len); 149 if (client == -1) 150 goto err; 151 152 srv_client = accept(server_fd, NULL, 0); 153 if (srv_client == -1) { 154 log_err("Can't accept connection"); 155 goto err; 156 } 157 158 if (bpf_map_lookup_elem(results_fd, &key, &value) < 0) { 159 log_err("Can't lookup result"); 160 goto err; 161 } 162 163 if (value == 0) { 164 log_err("Didn't match syncookie: %u", value); 165 goto err; 166 } 167 168 if (bpf_map_lookup_elem(results_fd, &key_gen, &value_gen) < 0) { 169 log_err("Can't lookup result"); 170 goto err; 171 } 172 173 if (xdp && value_gen == 0) { 174 // SYN packets do not get passed through generic XDP, skip the 175 // rest of the test. 176 printf("Skipping XDP cookie check\n"); 177 goto out; 178 } 179 180 if (bpf_map_lookup_elem(results_fd, &key_mss, &value_mss) < 0) { 181 log_err("Can't lookup result"); 182 goto err; 183 } 184 185 if (value != value_gen) { 186 log_err("BPF generated cookie does not match kernel one"); 187 goto err; 188 } 189 190 if (value_mss < 536 || value_mss > USHRT_MAX) { 191 log_err("Unexpected MSS retrieved"); 192 goto err; 193 } 194 195 goto out; 196 197 err: 198 ret = 1; 199 out: 200 close(client); 201 close(srv_client); 202 return ret; 203 } 204 205 static bool get_port(int server_fd, in_port_t *port) 206 { 207 struct sockaddr_in addr; 208 socklen_t len = sizeof(addr); 209 210 if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) { 211 log_err("Failed to get server addr"); 212 return false; 213 } 214 215 /* sin_port and sin6_port are located at the same offset. */ 216 *port = addr.sin_port; 217 return true; 218 } 219 220 int main(int argc, char **argv) 221 { 222 struct sockaddr_in addr4; 223 struct sockaddr_in6 addr6; 224 struct sockaddr_in addr4dual; 225 struct sockaddr_in6 addr6dual; 226 int server = -1; 227 int server_v6 = -1; 228 int server_dual = -1; 229 int results = -1; 230 int err = 0; 231 bool xdp; 232 233 if (argc < 2) { 234 fprintf(stderr, "Usage: %s prog_id\n", argv[0]); 235 exit(1); 236 } 237 238 results = get_map_fd_by_prog_id(atoi(argv[1]), &xdp); 239 if (results < 0) { 240 log_err("Can't get map"); 241 goto err; 242 } 243 244 memset(&addr4, 0, sizeof(addr4)); 245 addr4.sin_family = AF_INET; 246 addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 247 addr4.sin_port = 0; 248 memcpy(&addr4dual, &addr4, sizeof(addr4dual)); 249 250 memset(&addr6, 0, sizeof(addr6)); 251 addr6.sin6_family = AF_INET6; 252 addr6.sin6_addr = in6addr_loopback; 253 addr6.sin6_port = 0; 254 255 memset(&addr6dual, 0, sizeof(addr6dual)); 256 addr6dual.sin6_family = AF_INET6; 257 addr6dual.sin6_addr = in6addr_any; 258 addr6dual.sin6_port = 0; 259 260 server = start_server((const struct sockaddr *)&addr4, sizeof(addr4), 261 false); 262 if (server == -1 || !get_port(server, &addr4.sin_port)) 263 goto err; 264 265 server_v6 = start_server((const struct sockaddr *)&addr6, 266 sizeof(addr6), false); 267 if (server_v6 == -1 || !get_port(server_v6, &addr6.sin6_port)) 268 goto err; 269 270 server_dual = start_server((const struct sockaddr *)&addr6dual, 271 sizeof(addr6dual), true); 272 if (server_dual == -1 || !get_port(server_dual, &addr4dual.sin_port)) 273 goto err; 274 275 if (run_test(server, results, xdp, 276 (const struct sockaddr *)&addr4, sizeof(addr4))) 277 goto err; 278 279 if (run_test(server_v6, results, xdp, 280 (const struct sockaddr *)&addr6, sizeof(addr6))) 281 goto err; 282 283 if (run_test(server_dual, results, xdp, 284 (const struct sockaddr *)&addr4dual, sizeof(addr4dual))) 285 goto err; 286 287 printf("ok\n"); 288 goto out; 289 err: 290 err = 1; 291 out: 292 close(server); 293 close(server_v6); 294 close(server_dual); 295 close(results); 296 return err; 297 } 298