1 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause 2 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ 3 4 #include <stdnoreturn.h> 5 #include <stdlib.h> 6 #include <stdio.h> 7 #include <string.h> 8 #include <errno.h> 9 #include <unistd.h> 10 #include <getopt.h> 11 #include <signal.h> 12 #include <sys/types.h> 13 #include <bpf/bpf.h> 14 #include <bpf/libbpf.h> 15 #include <net/if.h> 16 #include <linux/if_link.h> 17 #include <linux/limits.h> 18 19 static unsigned int ifindex; 20 static __u32 attached_prog_id; 21 static bool attached_tc; 22 23 static void noreturn cleanup(int sig) 24 { 25 LIBBPF_OPTS(bpf_xdp_attach_opts, opts); 26 int prog_fd; 27 int err; 28 29 if (attached_prog_id == 0) 30 exit(0); 31 32 if (attached_tc) { 33 LIBBPF_OPTS(bpf_tc_hook, hook, 34 .ifindex = ifindex, 35 .attach_point = BPF_TC_INGRESS); 36 37 err = bpf_tc_hook_destroy(&hook); 38 if (err < 0) { 39 fprintf(stderr, "Error: bpf_tc_hook_destroy: %s\n", strerror(-err)); 40 fprintf(stderr, "Failed to destroy the TC hook\n"); 41 exit(1); 42 } 43 exit(0); 44 } 45 46 prog_fd = bpf_prog_get_fd_by_id(attached_prog_id); 47 if (prog_fd < 0) { 48 fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd)); 49 err = bpf_xdp_attach(ifindex, -1, 0, NULL); 50 if (err < 0) { 51 fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n", strerror(-err)); 52 fprintf(stderr, "Failed to detach XDP program\n"); 53 exit(1); 54 } 55 } else { 56 opts.old_prog_fd = prog_fd; 57 err = bpf_xdp_attach(ifindex, -1, XDP_FLAGS_REPLACE, &opts); 58 close(prog_fd); 59 if (err < 0) { 60 fprintf(stderr, "Error: bpf_set_link_xdp_fd_opts: %s\n", strerror(-err)); 61 /* Not an error if already replaced by someone else. */ 62 if (err != -EEXIST) { 63 fprintf(stderr, "Failed to detach XDP program\n"); 64 exit(1); 65 } 66 } 67 } 68 exit(0); 69 } 70 71 static noreturn void usage(const char *progname) 72 { 73 fprintf(stderr, "Usage: %s [--iface <iface>|--prog <prog_id>] [--mss4 <mss ipv4> --mss6 <mss ipv6> --wscale <wscale> --ttl <ttl>] [--ports <port1>,<port2>,...] [--single] [--tc]\n", 74 progname); 75 exit(1); 76 } 77 78 static unsigned long parse_arg_ul(const char *progname, const char *arg, unsigned long limit) 79 { 80 unsigned long res; 81 char *endptr; 82 83 errno = 0; 84 res = strtoul(arg, &endptr, 10); 85 if (errno != 0 || *endptr != '\0' || arg[0] == '\0' || res > limit) 86 usage(progname); 87 88 return res; 89 } 90 91 static void parse_options(int argc, char *argv[], unsigned int *ifindex, __u32 *prog_id, 92 __u64 *tcpipopts, char **ports, bool *single, bool *tc) 93 { 94 static struct option long_options[] = { 95 { "help", no_argument, NULL, 'h' }, 96 { "iface", required_argument, NULL, 'i' }, 97 { "prog", required_argument, NULL, 'x' }, 98 { "mss4", required_argument, NULL, 4 }, 99 { "mss6", required_argument, NULL, 6 }, 100 { "wscale", required_argument, NULL, 'w' }, 101 { "ttl", required_argument, NULL, 't' }, 102 { "ports", required_argument, NULL, 'p' }, 103 { "single", no_argument, NULL, 's' }, 104 { "tc", no_argument, NULL, 'c' }, 105 { NULL, 0, NULL, 0 }, 106 }; 107 unsigned long mss4, mss6, wscale, ttl; 108 unsigned int tcpipopts_mask = 0; 109 110 if (argc < 2) 111 usage(argv[0]); 112 113 *ifindex = 0; 114 *prog_id = 0; 115 *tcpipopts = 0; 116 *ports = NULL; 117 *single = false; 118 119 while (true) { 120 int opt; 121 122 opt = getopt_long(argc, argv, "", long_options, NULL); 123 if (opt == -1) 124 break; 125 126 switch (opt) { 127 case 'h': 128 usage(argv[0]); 129 break; 130 case 'i': 131 *ifindex = if_nametoindex(optarg); 132 if (*ifindex == 0) 133 usage(argv[0]); 134 break; 135 case 'x': 136 *prog_id = parse_arg_ul(argv[0], optarg, UINT32_MAX); 137 if (*prog_id == 0) 138 usage(argv[0]); 139 break; 140 case 4: 141 mss4 = parse_arg_ul(argv[0], optarg, UINT16_MAX); 142 tcpipopts_mask |= 1 << 0; 143 break; 144 case 6: 145 mss6 = parse_arg_ul(argv[0], optarg, UINT16_MAX); 146 tcpipopts_mask |= 1 << 1; 147 break; 148 case 'w': 149 wscale = parse_arg_ul(argv[0], optarg, 14); 150 tcpipopts_mask |= 1 << 2; 151 break; 152 case 't': 153 ttl = parse_arg_ul(argv[0], optarg, UINT8_MAX); 154 tcpipopts_mask |= 1 << 3; 155 break; 156 case 'p': 157 *ports = optarg; 158 break; 159 case 's': 160 *single = true; 161 break; 162 case 'c': 163 *tc = true; 164 break; 165 default: 166 usage(argv[0]); 167 } 168 } 169 if (optind < argc) 170 usage(argv[0]); 171 172 if (tcpipopts_mask == 0xf) { 173 if (mss4 == 0 || mss6 == 0 || wscale == 0 || ttl == 0) 174 usage(argv[0]); 175 *tcpipopts = (mss6 << 32) | (ttl << 24) | (wscale << 16) | mss4; 176 } else if (tcpipopts_mask != 0) { 177 usage(argv[0]); 178 } 179 180 if (*ifindex != 0 && *prog_id != 0) 181 usage(argv[0]); 182 if (*ifindex == 0 && *prog_id == 0) 183 usage(argv[0]); 184 } 185 186 static int syncookie_attach(const char *argv0, unsigned int ifindex, bool tc) 187 { 188 struct bpf_prog_info info = {}; 189 __u32 info_len = sizeof(info); 190 char xdp_filename[PATH_MAX]; 191 struct bpf_program *prog; 192 struct bpf_object *obj; 193 int prog_fd; 194 int err; 195 196 snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.bpf.o", argv0); 197 obj = bpf_object__open_file(xdp_filename, NULL); 198 err = libbpf_get_error(obj); 199 if (err < 0) { 200 fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err)); 201 return err; 202 } 203 204 err = bpf_object__load(obj); 205 if (err < 0) { 206 fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err)); 207 return err; 208 } 209 210 prog = bpf_object__find_program_by_name(obj, tc ? "syncookie_tc" : "syncookie_xdp"); 211 if (!prog) { 212 fprintf(stderr, "Error: bpf_object__find_program_by_name: program was not found\n"); 213 return -ENOENT; 214 } 215 216 prog_fd = bpf_program__fd(prog); 217 218 err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); 219 if (err < 0) { 220 fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err)); 221 goto out; 222 } 223 attached_tc = tc; 224 attached_prog_id = info.id; 225 signal(SIGINT, cleanup); 226 signal(SIGTERM, cleanup); 227 if (tc) { 228 LIBBPF_OPTS(bpf_tc_hook, hook, 229 .ifindex = ifindex, 230 .attach_point = BPF_TC_INGRESS); 231 LIBBPF_OPTS(bpf_tc_opts, opts, 232 .handle = 1, 233 .priority = 1, 234 .prog_fd = prog_fd); 235 236 err = bpf_tc_hook_create(&hook); 237 if (err < 0) { 238 fprintf(stderr, "Error: bpf_tc_hook_create: %s\n", 239 strerror(-err)); 240 goto fail; 241 } 242 err = bpf_tc_attach(&hook, &opts); 243 if (err < 0) { 244 fprintf(stderr, "Error: bpf_tc_attach: %s\n", 245 strerror(-err)); 246 goto fail; 247 } 248 249 } else { 250 err = bpf_xdp_attach(ifindex, prog_fd, 251 XDP_FLAGS_UPDATE_IF_NOEXIST, NULL); 252 if (err < 0) { 253 fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n", 254 strerror(-err)); 255 goto fail; 256 } 257 } 258 err = 0; 259 out: 260 bpf_object__close(obj); 261 return err; 262 fail: 263 signal(SIGINT, SIG_DFL); 264 signal(SIGTERM, SIG_DFL); 265 attached_prog_id = 0; 266 goto out; 267 } 268 269 static int syncookie_open_bpf_maps(__u32 prog_id, int *values_map_fd, int *ports_map_fd) 270 { 271 struct bpf_prog_info prog_info; 272 __u32 map_ids[8]; 273 __u32 info_len; 274 int prog_fd; 275 int err; 276 int i; 277 278 *values_map_fd = -1; 279 *ports_map_fd = -1; 280 281 prog_fd = bpf_prog_get_fd_by_id(prog_id); 282 if (prog_fd < 0) { 283 fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd)); 284 return prog_fd; 285 } 286 287 prog_info = (struct bpf_prog_info) { 288 .nr_map_ids = 8, 289 .map_ids = (__u64)map_ids, 290 }; 291 info_len = sizeof(prog_info); 292 293 err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len); 294 if (err != 0) { 295 fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err)); 296 goto out; 297 } 298 299 if (prog_info.nr_map_ids < 2) { 300 fprintf(stderr, "Error: Found %u BPF maps, expected at least 2\n", 301 prog_info.nr_map_ids); 302 err = -ENOENT; 303 goto out; 304 } 305 306 for (i = 0; i < prog_info.nr_map_ids; i++) { 307 struct bpf_map_info map_info = {}; 308 int map_fd; 309 310 err = bpf_map_get_fd_by_id(map_ids[i]); 311 if (err < 0) { 312 fprintf(stderr, "Error: bpf_map_get_fd_by_id: %s\n", strerror(-err)); 313 goto err_close_map_fds; 314 } 315 map_fd = err; 316 317 info_len = sizeof(map_info); 318 err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len); 319 if (err != 0) { 320 fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err)); 321 close(map_fd); 322 goto err_close_map_fds; 323 } 324 if (strcmp(map_info.name, "values") == 0) { 325 *values_map_fd = map_fd; 326 continue; 327 } 328 if (strcmp(map_info.name, "allowed_ports") == 0) { 329 *ports_map_fd = map_fd; 330 continue; 331 } 332 close(map_fd); 333 } 334 335 if (*values_map_fd != -1 && *ports_map_fd != -1) { 336 err = 0; 337 goto out; 338 } 339 340 err = -ENOENT; 341 342 err_close_map_fds: 343 if (*values_map_fd != -1) 344 close(*values_map_fd); 345 if (*ports_map_fd != -1) 346 close(*ports_map_fd); 347 *values_map_fd = -1; 348 *ports_map_fd = -1; 349 350 out: 351 close(prog_fd); 352 return err; 353 } 354 355 int main(int argc, char *argv[]) 356 { 357 int values_map_fd, ports_map_fd; 358 __u64 tcpipopts; 359 bool firstiter; 360 __u64 prevcnt; 361 __u32 prog_id; 362 char *ports; 363 bool single; 364 int err = 0; 365 bool tc; 366 367 parse_options(argc, argv, &ifindex, &prog_id, &tcpipopts, &ports, 368 &single, &tc); 369 370 if (prog_id == 0) { 371 if (!tc) { 372 err = bpf_xdp_query_id(ifindex, 0, &prog_id); 373 if (err < 0) { 374 fprintf(stderr, "Error: bpf_get_link_xdp_id: %s\n", 375 strerror(-err)); 376 goto out; 377 } 378 } 379 if (prog_id == 0) { 380 err = syncookie_attach(argv[0], ifindex, tc); 381 if (err < 0) 382 goto out; 383 prog_id = attached_prog_id; 384 } 385 } 386 387 err = syncookie_open_bpf_maps(prog_id, &values_map_fd, &ports_map_fd); 388 if (err < 0) 389 goto out; 390 391 if (ports) { 392 __u16 port_last = 0; 393 __u32 port_idx = 0; 394 char *p = ports; 395 396 fprintf(stderr, "Replacing allowed ports\n"); 397 398 while (p && *p != '\0') { 399 char *token = strsep(&p, ","); 400 __u16 port; 401 402 port = parse_arg_ul(argv[0], token, UINT16_MAX); 403 err = bpf_map_update_elem(ports_map_fd, &port_idx, &port, BPF_ANY); 404 if (err != 0) { 405 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err)); 406 fprintf(stderr, "Failed to add port %u (index %u)\n", 407 port, port_idx); 408 goto out_close_maps; 409 } 410 fprintf(stderr, "Added port %u\n", port); 411 port_idx++; 412 } 413 err = bpf_map_update_elem(ports_map_fd, &port_idx, &port_last, BPF_ANY); 414 if (err != 0) { 415 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err)); 416 fprintf(stderr, "Failed to add the terminator value 0 (index %u)\n", 417 port_idx); 418 goto out_close_maps; 419 } 420 } 421 422 if (tcpipopts) { 423 __u32 key = 0; 424 425 fprintf(stderr, "Replacing TCP/IP options\n"); 426 427 err = bpf_map_update_elem(values_map_fd, &key, &tcpipopts, BPF_ANY); 428 if (err != 0) { 429 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err)); 430 goto out_close_maps; 431 } 432 } 433 434 if ((ports || tcpipopts) && attached_prog_id == 0 && !single) 435 goto out_close_maps; 436 437 prevcnt = 0; 438 firstiter = true; 439 while (true) { 440 __u32 key = 1; 441 __u64 value; 442 443 err = bpf_map_lookup_elem(values_map_fd, &key, &value); 444 if (err != 0) { 445 fprintf(stderr, "Error: bpf_map_lookup_elem: %s\n", strerror(-err)); 446 goto out_close_maps; 447 } 448 if (firstiter) { 449 prevcnt = value; 450 firstiter = false; 451 } 452 if (single) { 453 printf("Total SYNACKs generated: %llu\n", value); 454 break; 455 } 456 printf("SYNACKs generated: %llu (total %llu)\n", value - prevcnt, value); 457 prevcnt = value; 458 sleep(1); 459 } 460 461 out_close_maps: 462 close(values_map_fd); 463 close(ports_map_fd); 464 out: 465 return err == 0 ? 0 : 1; 466 } 467