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