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