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, wscale, ttl; 108 unsigned long long mss6; 109 unsigned int tcpipopts_mask = 0; 110 111 if (argc < 2) 112 usage(argv[0]); 113 114 *ifindex = 0; 115 *prog_id = 0; 116 *tcpipopts = 0; 117 *ports = NULL; 118 *single = false; 119 *tc = false; 120 121 while (true) { 122 int opt; 123 124 opt = getopt_long(argc, argv, "", long_options, NULL); 125 if (opt == -1) 126 break; 127 128 switch (opt) { 129 case 'h': 130 usage(argv[0]); 131 break; 132 case 'i': 133 *ifindex = if_nametoindex(optarg); 134 if (*ifindex == 0) 135 usage(argv[0]); 136 break; 137 case 'x': 138 *prog_id = parse_arg_ul(argv[0], optarg, UINT32_MAX); 139 if (*prog_id == 0) 140 usage(argv[0]); 141 break; 142 case 4: 143 mss4 = parse_arg_ul(argv[0], optarg, UINT16_MAX); 144 tcpipopts_mask |= 1 << 0; 145 break; 146 case 6: 147 mss6 = parse_arg_ul(argv[0], optarg, UINT16_MAX); 148 tcpipopts_mask |= 1 << 1; 149 break; 150 case 'w': 151 wscale = parse_arg_ul(argv[0], optarg, 14); 152 tcpipopts_mask |= 1 << 2; 153 break; 154 case 't': 155 ttl = parse_arg_ul(argv[0], optarg, UINT8_MAX); 156 tcpipopts_mask |= 1 << 3; 157 break; 158 case 'p': 159 *ports = optarg; 160 break; 161 case 's': 162 *single = true; 163 break; 164 case 'c': 165 *tc = true; 166 break; 167 default: 168 usage(argv[0]); 169 } 170 } 171 if (optind < argc) 172 usage(argv[0]); 173 174 if (tcpipopts_mask == 0xf) { 175 if (mss4 == 0 || mss6 == 0 || wscale == 0 || ttl == 0) 176 usage(argv[0]); 177 *tcpipopts = (mss6 << 32) | (ttl << 24) | (wscale << 16) | mss4; 178 } else if (tcpipopts_mask != 0) { 179 usage(argv[0]); 180 } 181 182 if (*ifindex != 0 && *prog_id != 0) 183 usage(argv[0]); 184 if (*ifindex == 0 && *prog_id == 0) 185 usage(argv[0]); 186 } 187 188 static int syncookie_attach(const char *argv0, unsigned int ifindex, bool tc) 189 { 190 struct bpf_prog_info info = {}; 191 __u32 info_len = sizeof(info); 192 char xdp_filename[PATH_MAX]; 193 struct bpf_program *prog; 194 struct bpf_object *obj; 195 int prog_fd; 196 int err; 197 198 snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.bpf.o", argv0); 199 obj = bpf_object__open_file(xdp_filename, NULL); 200 err = libbpf_get_error(obj); 201 if (err < 0) { 202 fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err)); 203 return err; 204 } 205 206 err = bpf_object__load(obj); 207 if (err < 0) { 208 fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err)); 209 return err; 210 } 211 212 prog = bpf_object__find_program_by_name(obj, tc ? "syncookie_tc" : "syncookie_xdp"); 213 if (!prog) { 214 fprintf(stderr, "Error: bpf_object__find_program_by_name: program was not found\n"); 215 return -ENOENT; 216 } 217 218 prog_fd = bpf_program__fd(prog); 219 220 err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); 221 if (err < 0) { 222 fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err)); 223 goto out; 224 } 225 attached_tc = tc; 226 attached_prog_id = info.id; 227 signal(SIGINT, cleanup); 228 signal(SIGTERM, cleanup); 229 if (tc) { 230 LIBBPF_OPTS(bpf_tc_hook, hook, 231 .ifindex = ifindex, 232 .attach_point = BPF_TC_INGRESS); 233 LIBBPF_OPTS(bpf_tc_opts, opts, 234 .handle = 1, 235 .priority = 1, 236 .prog_fd = prog_fd); 237 238 err = bpf_tc_hook_create(&hook); 239 if (err < 0) { 240 fprintf(stderr, "Error: bpf_tc_hook_create: %s\n", 241 strerror(-err)); 242 goto fail; 243 } 244 err = bpf_tc_attach(&hook, &opts); 245 if (err < 0) { 246 fprintf(stderr, "Error: bpf_tc_attach: %s\n", 247 strerror(-err)); 248 goto fail; 249 } 250 251 } else { 252 err = bpf_xdp_attach(ifindex, prog_fd, 253 XDP_FLAGS_UPDATE_IF_NOEXIST, NULL); 254 if (err < 0) { 255 fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n", 256 strerror(-err)); 257 goto fail; 258 } 259 } 260 err = 0; 261 out: 262 bpf_object__close(obj); 263 return err; 264 fail: 265 signal(SIGINT, SIG_DFL); 266 signal(SIGTERM, SIG_DFL); 267 attached_prog_id = 0; 268 goto out; 269 } 270 271 static int syncookie_open_bpf_maps(__u32 prog_id, int *values_map_fd, int *ports_map_fd) 272 { 273 struct bpf_prog_info prog_info; 274 __u32 map_ids[8]; 275 __u32 info_len; 276 int prog_fd; 277 int err; 278 int i; 279 280 *values_map_fd = -1; 281 *ports_map_fd = -1; 282 283 prog_fd = bpf_prog_get_fd_by_id(prog_id); 284 if (prog_fd < 0) { 285 fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd)); 286 return prog_fd; 287 } 288 289 prog_info = (struct bpf_prog_info) { 290 .nr_map_ids = 8, 291 .map_ids = (__u64)(unsigned long)map_ids, 292 }; 293 info_len = sizeof(prog_info); 294 295 err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len); 296 if (err != 0) { 297 fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err)); 298 goto out; 299 } 300 301 if (prog_info.nr_map_ids < 2) { 302 fprintf(stderr, "Error: Found %u BPF maps, expected at least 2\n", 303 prog_info.nr_map_ids); 304 err = -ENOENT; 305 goto out; 306 } 307 308 for (i = 0; i < prog_info.nr_map_ids; i++) { 309 struct bpf_map_info map_info = {}; 310 int map_fd; 311 312 err = bpf_map_get_fd_by_id(map_ids[i]); 313 if (err < 0) { 314 fprintf(stderr, "Error: bpf_map_get_fd_by_id: %s\n", strerror(-err)); 315 goto err_close_map_fds; 316 } 317 map_fd = err; 318 319 info_len = sizeof(map_info); 320 err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len); 321 if (err != 0) { 322 fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err)); 323 close(map_fd); 324 goto err_close_map_fds; 325 } 326 if (strcmp(map_info.name, "values") == 0) { 327 *values_map_fd = map_fd; 328 continue; 329 } 330 if (strcmp(map_info.name, "allowed_ports") == 0) { 331 *ports_map_fd = map_fd; 332 continue; 333 } 334 close(map_fd); 335 } 336 337 if (*values_map_fd != -1 && *ports_map_fd != -1) { 338 err = 0; 339 goto out; 340 } 341 342 err = -ENOENT; 343 344 err_close_map_fds: 345 if (*values_map_fd != -1) 346 close(*values_map_fd); 347 if (*ports_map_fd != -1) 348 close(*ports_map_fd); 349 *values_map_fd = -1; 350 *ports_map_fd = -1; 351 352 out: 353 close(prog_fd); 354 return err; 355 } 356 357 int main(int argc, char *argv[]) 358 { 359 int values_map_fd, ports_map_fd; 360 __u64 tcpipopts; 361 bool firstiter; 362 __u64 prevcnt; 363 __u32 prog_id; 364 char *ports; 365 bool single; 366 int err = 0; 367 bool tc; 368 369 parse_options(argc, argv, &ifindex, &prog_id, &tcpipopts, &ports, 370 &single, &tc); 371 372 if (prog_id == 0) { 373 if (!tc) { 374 err = bpf_xdp_query_id(ifindex, 0, &prog_id); 375 if (err < 0) { 376 fprintf(stderr, "Error: bpf_get_link_xdp_id: %s\n", 377 strerror(-err)); 378 goto out; 379 } 380 } 381 if (prog_id == 0) { 382 err = syncookie_attach(argv[0], ifindex, tc); 383 if (err < 0) 384 goto out; 385 prog_id = attached_prog_id; 386 } 387 } 388 389 err = syncookie_open_bpf_maps(prog_id, &values_map_fd, &ports_map_fd); 390 if (err < 0) 391 goto out; 392 393 if (ports) { 394 __u16 port_last = 0; 395 __u32 port_idx = 0; 396 char *p = ports; 397 398 fprintf(stderr, "Replacing allowed ports\n"); 399 400 while (p && *p != '\0') { 401 char *token = strsep(&p, ","); 402 __u16 port; 403 404 port = parse_arg_ul(argv[0], token, UINT16_MAX); 405 err = bpf_map_update_elem(ports_map_fd, &port_idx, &port, BPF_ANY); 406 if (err != 0) { 407 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err)); 408 fprintf(stderr, "Failed to add port %u (index %u)\n", 409 port, port_idx); 410 goto out_close_maps; 411 } 412 fprintf(stderr, "Added port %u\n", port); 413 port_idx++; 414 } 415 err = bpf_map_update_elem(ports_map_fd, &port_idx, &port_last, BPF_ANY); 416 if (err != 0) { 417 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err)); 418 fprintf(stderr, "Failed to add the terminator value 0 (index %u)\n", 419 port_idx); 420 goto out_close_maps; 421 } 422 } 423 424 if (tcpipopts) { 425 __u32 key = 0; 426 427 fprintf(stderr, "Replacing TCP/IP options\n"); 428 429 err = bpf_map_update_elem(values_map_fd, &key, &tcpipopts, BPF_ANY); 430 if (err != 0) { 431 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err)); 432 goto out_close_maps; 433 } 434 } 435 436 if ((ports || tcpipopts) && attached_prog_id == 0 && !single) 437 goto out_close_maps; 438 439 prevcnt = 0; 440 firstiter = true; 441 while (true) { 442 __u32 key = 1; 443 __u64 value; 444 445 err = bpf_map_lookup_elem(values_map_fd, &key, &value); 446 if (err != 0) { 447 fprintf(stderr, "Error: bpf_map_lookup_elem: %s\n", strerror(-err)); 448 goto out_close_maps; 449 } 450 if (firstiter) { 451 prevcnt = value; 452 firstiter = false; 453 } 454 if (single) { 455 printf("Total SYNACKs generated: %llu\n", value); 456 break; 457 } 458 printf("SYNACKs generated: %llu (total %llu)\n", value - prevcnt, value); 459 prevcnt = value; 460 sleep(1); 461 } 462 463 out_close_maps: 464 close(values_map_fd); 465 close(ports_map_fd); 466 out: 467 return err == 0 ? 0 : 1; 468 } 469