Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
0002 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
0003 
0004 #include <stdnoreturn.h>
0005 #include <stdlib.h>
0006 #include <stdio.h>
0007 #include <string.h>
0008 #include <errno.h>
0009 #include <unistd.h>
0010 #include <getopt.h>
0011 #include <signal.h>
0012 #include <sys/types.h>
0013 #include <bpf/bpf.h>
0014 #include <bpf/libbpf.h>
0015 #include <net/if.h>
0016 #include <linux/if_link.h>
0017 #include <linux/limits.h>
0018 
0019 static unsigned int ifindex;
0020 static __u32 attached_prog_id;
0021 static bool attached_tc;
0022 
0023 static void noreturn cleanup(int sig)
0024 {
0025     LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
0026     int prog_fd;
0027     int err;
0028 
0029     if (attached_prog_id == 0)
0030         exit(0);
0031 
0032     if (attached_tc) {
0033         LIBBPF_OPTS(bpf_tc_hook, hook,
0034                 .ifindex = ifindex,
0035                 .attach_point = BPF_TC_INGRESS);
0036 
0037         err = bpf_tc_hook_destroy(&hook);
0038         if (err < 0) {
0039             fprintf(stderr, "Error: bpf_tc_hook_destroy: %s\n", strerror(-err));
0040             fprintf(stderr, "Failed to destroy the TC hook\n");
0041             exit(1);
0042         }
0043         exit(0);
0044     }
0045 
0046     prog_fd = bpf_prog_get_fd_by_id(attached_prog_id);
0047     if (prog_fd < 0) {
0048         fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd));
0049         err = bpf_xdp_attach(ifindex, -1, 0, NULL);
0050         if (err < 0) {
0051             fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n", strerror(-err));
0052             fprintf(stderr, "Failed to detach XDP program\n");
0053             exit(1);
0054         }
0055     } else {
0056         opts.old_prog_fd = prog_fd;
0057         err = bpf_xdp_attach(ifindex, -1, XDP_FLAGS_REPLACE, &opts);
0058         close(prog_fd);
0059         if (err < 0) {
0060             fprintf(stderr, "Error: bpf_set_link_xdp_fd_opts: %s\n", strerror(-err));
0061             /* Not an error if already replaced by someone else. */
0062             if (err != -EEXIST) {
0063                 fprintf(stderr, "Failed to detach XDP program\n");
0064                 exit(1);
0065             }
0066         }
0067     }
0068     exit(0);
0069 }
0070 
0071 static noreturn void usage(const char *progname)
0072 {
0073     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",
0074         progname);
0075     exit(1);
0076 }
0077 
0078 static unsigned long parse_arg_ul(const char *progname, const char *arg, unsigned long limit)
0079 {
0080     unsigned long res;
0081     char *endptr;
0082 
0083     errno = 0;
0084     res = strtoul(arg, &endptr, 10);
0085     if (errno != 0 || *endptr != '\0' || arg[0] == '\0' || res > limit)
0086         usage(progname);
0087 
0088     return res;
0089 }
0090 
0091 static void parse_options(int argc, char *argv[], unsigned int *ifindex, __u32 *prog_id,
0092               __u64 *tcpipopts, char **ports, bool *single, bool *tc)
0093 {
0094     static struct option long_options[] = {
0095         { "help", no_argument, NULL, 'h' },
0096         { "iface", required_argument, NULL, 'i' },
0097         { "prog", required_argument, NULL, 'x' },
0098         { "mss4", required_argument, NULL, 4 },
0099         { "mss6", required_argument, NULL, 6 },
0100         { "wscale", required_argument, NULL, 'w' },
0101         { "ttl", required_argument, NULL, 't' },
0102         { "ports", required_argument, NULL, 'p' },
0103         { "single", no_argument, NULL, 's' },
0104         { "tc", no_argument, NULL, 'c' },
0105         { NULL, 0, NULL, 0 },
0106     };
0107     unsigned long mss4, mss6, wscale, ttl;
0108     unsigned int tcpipopts_mask = 0;
0109 
0110     if (argc < 2)
0111         usage(argv[0]);
0112 
0113     *ifindex = 0;
0114     *prog_id = 0;
0115     *tcpipopts = 0;
0116     *ports = NULL;
0117     *single = false;
0118 
0119     while (true) {
0120         int opt;
0121 
0122         opt = getopt_long(argc, argv, "", long_options, NULL);
0123         if (opt == -1)
0124             break;
0125 
0126         switch (opt) {
0127         case 'h':
0128             usage(argv[0]);
0129             break;
0130         case 'i':
0131             *ifindex = if_nametoindex(optarg);
0132             if (*ifindex == 0)
0133                 usage(argv[0]);
0134             break;
0135         case 'x':
0136             *prog_id = parse_arg_ul(argv[0], optarg, UINT32_MAX);
0137             if (*prog_id == 0)
0138                 usage(argv[0]);
0139             break;
0140         case 4:
0141             mss4 = parse_arg_ul(argv[0], optarg, UINT16_MAX);
0142             tcpipopts_mask |= 1 << 0;
0143             break;
0144         case 6:
0145             mss6 = parse_arg_ul(argv[0], optarg, UINT16_MAX);
0146             tcpipopts_mask |= 1 << 1;
0147             break;
0148         case 'w':
0149             wscale = parse_arg_ul(argv[0], optarg, 14);
0150             tcpipopts_mask |= 1 << 2;
0151             break;
0152         case 't':
0153             ttl = parse_arg_ul(argv[0], optarg, UINT8_MAX);
0154             tcpipopts_mask |= 1 << 3;
0155             break;
0156         case 'p':
0157             *ports = optarg;
0158             break;
0159         case 's':
0160             *single = true;
0161             break;
0162         case 'c':
0163             *tc = true;
0164             break;
0165         default:
0166             usage(argv[0]);
0167         }
0168     }
0169     if (optind < argc)
0170         usage(argv[0]);
0171 
0172     if (tcpipopts_mask == 0xf) {
0173         if (mss4 == 0 || mss6 == 0 || wscale == 0 || ttl == 0)
0174             usage(argv[0]);
0175         *tcpipopts = (mss6 << 32) | (ttl << 24) | (wscale << 16) | mss4;
0176     } else if (tcpipopts_mask != 0) {
0177         usage(argv[0]);
0178     }
0179 
0180     if (*ifindex != 0 && *prog_id != 0)
0181         usage(argv[0]);
0182     if (*ifindex == 0 && *prog_id == 0)
0183         usage(argv[0]);
0184 }
0185 
0186 static int syncookie_attach(const char *argv0, unsigned int ifindex, bool tc)
0187 {
0188     struct bpf_prog_info info = {};
0189     __u32 info_len = sizeof(info);
0190     char xdp_filename[PATH_MAX];
0191     struct bpf_program *prog;
0192     struct bpf_object *obj;
0193     int prog_fd;
0194     int err;
0195 
0196     snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.o", argv0);
0197     obj = bpf_object__open_file(xdp_filename, NULL);
0198     err = libbpf_get_error(obj);
0199     if (err < 0) {
0200         fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err));
0201         return err;
0202     }
0203 
0204     err = bpf_object__load(obj);
0205     if (err < 0) {
0206         fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err));
0207         return err;
0208     }
0209 
0210     prog = bpf_object__find_program_by_name(obj, tc ? "syncookie_tc" : "syncookie_xdp");
0211     if (!prog) {
0212         fprintf(stderr, "Error: bpf_object__find_program_by_name: program was not found\n");
0213         return -ENOENT;
0214     }
0215 
0216     prog_fd = bpf_program__fd(prog);
0217 
0218     err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
0219     if (err < 0) {
0220         fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err));
0221         goto out;
0222     }
0223     attached_tc = tc;
0224     attached_prog_id = info.id;
0225     signal(SIGINT, cleanup);
0226     signal(SIGTERM, cleanup);
0227     if (tc) {
0228         LIBBPF_OPTS(bpf_tc_hook, hook,
0229                 .ifindex = ifindex,
0230                 .attach_point = BPF_TC_INGRESS);
0231         LIBBPF_OPTS(bpf_tc_opts, opts,
0232                 .handle = 1,
0233                 .priority = 1,
0234                 .prog_fd = prog_fd);
0235 
0236         err = bpf_tc_hook_create(&hook);
0237         if (err < 0) {
0238             fprintf(stderr, "Error: bpf_tc_hook_create: %s\n",
0239                 strerror(-err));
0240             goto fail;
0241         }
0242         err = bpf_tc_attach(&hook, &opts);
0243         if (err < 0) {
0244             fprintf(stderr, "Error: bpf_tc_attach: %s\n",
0245                 strerror(-err));
0246             goto fail;
0247         }
0248 
0249     } else {
0250         err = bpf_xdp_attach(ifindex, prog_fd,
0251                      XDP_FLAGS_UPDATE_IF_NOEXIST, NULL);
0252         if (err < 0) {
0253             fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n",
0254                 strerror(-err));
0255             goto fail;
0256         }
0257     }
0258     err = 0;
0259 out:
0260     bpf_object__close(obj);
0261     return err;
0262 fail:
0263     signal(SIGINT, SIG_DFL);
0264     signal(SIGTERM, SIG_DFL);
0265     attached_prog_id = 0;
0266     goto out;
0267 }
0268 
0269 static int syncookie_open_bpf_maps(__u32 prog_id, int *values_map_fd, int *ports_map_fd)
0270 {
0271     struct bpf_prog_info prog_info;
0272     __u32 map_ids[8];
0273     __u32 info_len;
0274     int prog_fd;
0275     int err;
0276     int i;
0277 
0278     *values_map_fd = -1;
0279     *ports_map_fd = -1;
0280 
0281     prog_fd = bpf_prog_get_fd_by_id(prog_id);
0282     if (prog_fd < 0) {
0283         fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd));
0284         return prog_fd;
0285     }
0286 
0287     prog_info = (struct bpf_prog_info) {
0288         .nr_map_ids = 8,
0289         .map_ids = (__u64)map_ids,
0290     };
0291     info_len = sizeof(prog_info);
0292 
0293     err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
0294     if (err != 0) {
0295         fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err));
0296         goto out;
0297     }
0298 
0299     if (prog_info.nr_map_ids < 2) {
0300         fprintf(stderr, "Error: Found %u BPF maps, expected at least 2\n",
0301             prog_info.nr_map_ids);
0302         err = -ENOENT;
0303         goto out;
0304     }
0305 
0306     for (i = 0; i < prog_info.nr_map_ids; i++) {
0307         struct bpf_map_info map_info = {};
0308         int map_fd;
0309 
0310         err = bpf_map_get_fd_by_id(map_ids[i]);
0311         if (err < 0) {
0312             fprintf(stderr, "Error: bpf_map_get_fd_by_id: %s\n", strerror(-err));
0313             goto err_close_map_fds;
0314         }
0315         map_fd = err;
0316 
0317         info_len = sizeof(map_info);
0318         err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
0319         if (err != 0) {
0320             fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err));
0321             close(map_fd);
0322             goto err_close_map_fds;
0323         }
0324         if (strcmp(map_info.name, "values") == 0) {
0325             *values_map_fd = map_fd;
0326             continue;
0327         }
0328         if (strcmp(map_info.name, "allowed_ports") == 0) {
0329             *ports_map_fd = map_fd;
0330             continue;
0331         }
0332         close(map_fd);
0333     }
0334 
0335     if (*values_map_fd != -1 && *ports_map_fd != -1) {
0336         err = 0;
0337         goto out;
0338     }
0339 
0340     err = -ENOENT;
0341 
0342 err_close_map_fds:
0343     if (*values_map_fd != -1)
0344         close(*values_map_fd);
0345     if (*ports_map_fd != -1)
0346         close(*ports_map_fd);
0347     *values_map_fd = -1;
0348     *ports_map_fd = -1;
0349 
0350 out:
0351     close(prog_fd);
0352     return err;
0353 }
0354 
0355 int main(int argc, char *argv[])
0356 {
0357     int values_map_fd, ports_map_fd;
0358     __u64 tcpipopts;
0359     bool firstiter;
0360     __u64 prevcnt;
0361     __u32 prog_id;
0362     char *ports;
0363     bool single;
0364     int err = 0;
0365     bool tc;
0366 
0367     parse_options(argc, argv, &ifindex, &prog_id, &tcpipopts, &ports,
0368               &single, &tc);
0369 
0370     if (prog_id == 0) {
0371         if (!tc) {
0372             err = bpf_xdp_query_id(ifindex, 0, &prog_id);
0373             if (err < 0) {
0374                 fprintf(stderr, "Error: bpf_get_link_xdp_id: %s\n",
0375                     strerror(-err));
0376                 goto out;
0377             }
0378         }
0379         if (prog_id == 0) {
0380             err = syncookie_attach(argv[0], ifindex, tc);
0381             if (err < 0)
0382                 goto out;
0383             prog_id = attached_prog_id;
0384         }
0385     }
0386 
0387     err = syncookie_open_bpf_maps(prog_id, &values_map_fd, &ports_map_fd);
0388     if (err < 0)
0389         goto out;
0390 
0391     if (ports) {
0392         __u16 port_last = 0;
0393         __u32 port_idx = 0;
0394         char *p = ports;
0395 
0396         fprintf(stderr, "Replacing allowed ports\n");
0397 
0398         while (p && *p != '\0') {
0399             char *token = strsep(&p, ",");
0400             __u16 port;
0401 
0402             port = parse_arg_ul(argv[0], token, UINT16_MAX);
0403             err = bpf_map_update_elem(ports_map_fd, &port_idx, &port, BPF_ANY);
0404             if (err != 0) {
0405                 fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err));
0406                 fprintf(stderr, "Failed to add port %u (index %u)\n",
0407                     port, port_idx);
0408                 goto out_close_maps;
0409             }
0410             fprintf(stderr, "Added port %u\n", port);
0411             port_idx++;
0412         }
0413         err = bpf_map_update_elem(ports_map_fd, &port_idx, &port_last, BPF_ANY);
0414         if (err != 0) {
0415             fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err));
0416             fprintf(stderr, "Failed to add the terminator value 0 (index %u)\n",
0417                 port_idx);
0418             goto out_close_maps;
0419         }
0420     }
0421 
0422     if (tcpipopts) {
0423         __u32 key = 0;
0424 
0425         fprintf(stderr, "Replacing TCP/IP options\n");
0426 
0427         err = bpf_map_update_elem(values_map_fd, &key, &tcpipopts, BPF_ANY);
0428         if (err != 0) {
0429             fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err));
0430             goto out_close_maps;
0431         }
0432     }
0433 
0434     if ((ports || tcpipopts) && attached_prog_id == 0 && !single)
0435         goto out_close_maps;
0436 
0437     prevcnt = 0;
0438     firstiter = true;
0439     while (true) {
0440         __u32 key = 1;
0441         __u64 value;
0442 
0443         err = bpf_map_lookup_elem(values_map_fd, &key, &value);
0444         if (err != 0) {
0445             fprintf(stderr, "Error: bpf_map_lookup_elem: %s\n", strerror(-err));
0446             goto out_close_maps;
0447         }
0448         if (firstiter) {
0449             prevcnt = value;
0450             firstiter = false;
0451         }
0452         if (single) {
0453             printf("Total SYNACKs generated: %llu\n", value);
0454             break;
0455         }
0456         printf("SYNACKs generated: %llu (total %llu)\n", value - prevcnt, value);
0457         prevcnt = value;
0458         sleep(1);
0459     }
0460 
0461 out_close_maps:
0462     close(values_map_fd);
0463     close(ports_map_fd);
0464 out:
0465     return err == 0 ? 0 : 1;
0466 }