0001
0002
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
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 }