xref: /openbmc/linux/samples/bpf/xdp_fwd_user.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1fe616055SDavid Ahern // SPDX-License-Identifier: GPL-2.0
2fe616055SDavid Ahern /* Copyright (c) 2017-18 David Ahern <dsahern@gmail.com>
3fe616055SDavid Ahern  *
4fe616055SDavid Ahern  * This program is free software; you can redistribute it and/or
5fe616055SDavid Ahern  * modify it under the terms of version 2 of the GNU General Public
6fe616055SDavid Ahern  * License as published by the Free Software Foundation.
7fe616055SDavid Ahern  *
8fe616055SDavid Ahern  * This program is distributed in the hope that it will be useful, but
9fe616055SDavid Ahern  * WITHOUT ANY WARRANTY; without even the implied warranty of
10fe616055SDavid Ahern  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11fe616055SDavid Ahern  * General Public License for more details.
12fe616055SDavid Ahern  */
13fe616055SDavid Ahern 
14fe616055SDavid Ahern #include <linux/bpf.h>
15fe616055SDavid Ahern #include <linux/if_link.h>
16fe616055SDavid Ahern #include <linux/limits.h>
17fe616055SDavid Ahern #include <net/if.h>
18fe616055SDavid Ahern #include <errno.h>
19fe616055SDavid Ahern #include <stdio.h>
20fe616055SDavid Ahern #include <stdlib.h>
21fe616055SDavid Ahern #include <stdbool.h>
22fe616055SDavid Ahern #include <string.h>
23fe616055SDavid Ahern #include <unistd.h>
24fe616055SDavid Ahern #include <fcntl.h>
25fe616055SDavid Ahern #include <libgen.h>
26fe616055SDavid Ahern 
277cf245a3SToke Høiland-Jørgensen #include <bpf/libbpf.h>
282bf3e2efSJakub Kicinski #include <bpf/bpf.h>
29fe616055SDavid Ahern 
30d50ecc46SToke Høiland-Jørgensen static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
31d50ecc46SToke Høiland-Jørgensen 
do_attach(int idx,int prog_fd,int map_fd,const char * name)32a32a32cbSJesper Dangaard Brouer static int do_attach(int idx, int prog_fd, int map_fd, const char *name)
33fe616055SDavid Ahern {
34fe616055SDavid Ahern 	int err;
35fe616055SDavid Ahern 
36d4e34bfcSAndrii Nakryiko 	err = bpf_xdp_attach(idx, prog_fd, xdp_flags, NULL);
37a32a32cbSJesper Dangaard Brouer 	if (err < 0) {
38fe616055SDavid Ahern 		printf("ERROR: failed to attach program to %s\n", name);
39a32a32cbSJesper Dangaard Brouer 		return err;
40a32a32cbSJesper Dangaard Brouer 	}
41a32a32cbSJesper Dangaard Brouer 
42a32a32cbSJesper Dangaard Brouer 	/* Adding ifindex as a possible egress TX port */
43a32a32cbSJesper Dangaard Brouer 	err = bpf_map_update_elem(map_fd, &idx, &idx, 0);
44a32a32cbSJesper Dangaard Brouer 	if (err)
45a32a32cbSJesper Dangaard Brouer 		printf("ERROR: failed using device %s as TX-port\n", name);
46fe616055SDavid Ahern 
47fe616055SDavid Ahern 	return err;
48fe616055SDavid Ahern }
49fe616055SDavid Ahern 
do_detach(int ifindex,const char * ifname,const char * app_name)50de5bb438SZhengchao Shao static int do_detach(int ifindex, const char *ifname, const char *app_name)
51fe616055SDavid Ahern {
52de5bb438SZhengchao Shao 	LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
53de5bb438SZhengchao Shao 	struct bpf_prog_info prog_info = {};
54de5bb438SZhengchao Shao 	char prog_name[BPF_OBJ_NAME_LEN];
55de5bb438SZhengchao Shao 	__u32 info_len, curr_prog_id;
56de5bb438SZhengchao Shao 	int prog_fd;
57de5bb438SZhengchao Shao 	int err = 1;
58fe616055SDavid Ahern 
59de5bb438SZhengchao Shao 	if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) {
60de5bb438SZhengchao Shao 		printf("ERROR: bpf_xdp_query_id failed (%s)\n",
61de5bb438SZhengchao Shao 		       strerror(errno));
62de5bb438SZhengchao Shao 		return err;
63de5bb438SZhengchao Shao 	}
64de5bb438SZhengchao Shao 
65de5bb438SZhengchao Shao 	if (!curr_prog_id) {
66de5bb438SZhengchao Shao 		printf("ERROR: flags(0x%x) xdp prog is not attached to %s\n",
67de5bb438SZhengchao Shao 		       xdp_flags, ifname);
68de5bb438SZhengchao Shao 		return err;
69de5bb438SZhengchao Shao 	}
70de5bb438SZhengchao Shao 
71de5bb438SZhengchao Shao 	info_len = sizeof(prog_info);
72de5bb438SZhengchao Shao 	prog_fd = bpf_prog_get_fd_by_id(curr_prog_id);
73de5bb438SZhengchao Shao 	if (prog_fd < 0) {
74de5bb438SZhengchao Shao 		printf("ERROR: bpf_prog_get_fd_by_id failed (%s)\n",
75de5bb438SZhengchao Shao 		       strerror(errno));
76de5bb438SZhengchao Shao 		return prog_fd;
77de5bb438SZhengchao Shao 	}
78de5bb438SZhengchao Shao 
79*c0ca277bSIlya Leoshkevich 	err = bpf_prog_get_info_by_fd(prog_fd, &prog_info, &info_len);
80de5bb438SZhengchao Shao 	if (err) {
81*c0ca277bSIlya Leoshkevich 		printf("ERROR: bpf_prog_get_info_by_fd failed (%s)\n",
82de5bb438SZhengchao Shao 		       strerror(errno));
83de5bb438SZhengchao Shao 		goto close_out;
84de5bb438SZhengchao Shao 	}
85de5bb438SZhengchao Shao 	snprintf(prog_name, sizeof(prog_name), "%s_prog", app_name);
86de5bb438SZhengchao Shao 	prog_name[BPF_OBJ_NAME_LEN - 1] = '\0';
87de5bb438SZhengchao Shao 
88de5bb438SZhengchao Shao 	if (strcmp(prog_info.name, prog_name)) {
89de5bb438SZhengchao Shao 		printf("ERROR: %s isn't attached to %s\n", app_name, ifname);
90de5bb438SZhengchao Shao 		err = 1;
91de5bb438SZhengchao Shao 		goto close_out;
92de5bb438SZhengchao Shao 	}
93de5bb438SZhengchao Shao 
94de5bb438SZhengchao Shao 	opts.old_prog_fd = prog_fd;
95de5bb438SZhengchao Shao 	err = bpf_xdp_detach(ifindex, xdp_flags, &opts);
96fe616055SDavid Ahern 	if (err < 0)
97de5bb438SZhengchao Shao 		printf("ERROR: failed to detach program from %s (%s)\n",
98de5bb438SZhengchao Shao 		       ifname, strerror(errno));
99a32a32cbSJesper Dangaard Brouer 	/* TODO: Remember to cleanup map, when adding use of shared map
100a32a32cbSJesper Dangaard Brouer 	 *  bpf_map_delete_elem((map_fd, &idx);
101a32a32cbSJesper Dangaard Brouer 	 */
102de5bb438SZhengchao Shao close_out:
103de5bb438SZhengchao Shao 	close(prog_fd);
104fe616055SDavid Ahern 	return err;
105fe616055SDavid Ahern }
106fe616055SDavid Ahern 
usage(const char * prog)107fe616055SDavid Ahern static void usage(const char *prog)
108fe616055SDavid Ahern {
109fe616055SDavid Ahern 	fprintf(stderr,
110fe616055SDavid Ahern 		"usage: %s [OPTS] interface-list\n"
111fe616055SDavid Ahern 		"\nOPTS:\n"
112fe616055SDavid Ahern 		"    -d    detach program\n"
113bf067f1cSWang Hai 		"    -S    use skb-mode\n"
114bf067f1cSWang Hai 		"    -F    force loading prog\n"
115fe616055SDavid Ahern 		"    -D    direct table lookups (skip fib rules)\n",
116fe616055SDavid Ahern 		prog);
117fe616055SDavid Ahern }
118fe616055SDavid Ahern 
main(int argc,char ** argv)119fe616055SDavid Ahern int main(int argc, char **argv)
120fe616055SDavid Ahern {
121e1a40ef4SJakub Kicinski 	const char *prog_name = "xdp_fwd";
1227490d592SKui-Feng Lee 	struct bpf_program *prog = NULL;
1237490d592SKui-Feng Lee 	struct bpf_program *pos;
1247490d592SKui-Feng Lee 	const char *sec_name;
1251e4edb6dSAndrii Nakryiko 	int prog_fd = -1, map_fd = -1;
126fe616055SDavid Ahern 	char filename[PATH_MAX];
127e1a40ef4SJakub Kicinski 	struct bpf_object *obj;
128fe616055SDavid Ahern 	int opt, i, idx, err;
129fe616055SDavid Ahern 	int attach = 1;
130fe616055SDavid Ahern 	int ret = 0;
131fe616055SDavid Ahern 
132d50ecc46SToke Høiland-Jørgensen 	while ((opt = getopt(argc, argv, ":dDSF")) != -1) {
133fe616055SDavid Ahern 		switch (opt) {
134fe616055SDavid Ahern 		case 'd':
135fe616055SDavid Ahern 			attach = 0;
136fe616055SDavid Ahern 			break;
137d50ecc46SToke Høiland-Jørgensen 		case 'S':
138d50ecc46SToke Høiland-Jørgensen 			xdp_flags |= XDP_FLAGS_SKB_MODE;
139d50ecc46SToke Høiland-Jørgensen 			break;
140d50ecc46SToke Høiland-Jørgensen 		case 'F':
141d50ecc46SToke Høiland-Jørgensen 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
142d50ecc46SToke Høiland-Jørgensen 			break;
143fe616055SDavid Ahern 		case 'D':
144e1a40ef4SJakub Kicinski 			prog_name = "xdp_fwd_direct";
145fe616055SDavid Ahern 			break;
146fe616055SDavid Ahern 		default:
147fe616055SDavid Ahern 			usage(basename(argv[0]));
148fe616055SDavid Ahern 			return 1;
149fe616055SDavid Ahern 		}
150fe616055SDavid Ahern 	}
151fe616055SDavid Ahern 
152d50ecc46SToke Høiland-Jørgensen 	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
153d50ecc46SToke Høiland-Jørgensen 		xdp_flags |= XDP_FLAGS_DRV_MODE;
154d50ecc46SToke Høiland-Jørgensen 
155fe616055SDavid Ahern 	if (optind == argc) {
156fe616055SDavid Ahern 		usage(basename(argv[0]));
157fe616055SDavid Ahern 		return 1;
158fe616055SDavid Ahern 	}
159fe616055SDavid Ahern 
160fe616055SDavid Ahern 	if (attach) {
161fe616055SDavid Ahern 		snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
162fe616055SDavid Ahern 
163fe616055SDavid Ahern 		if (access(filename, O_RDONLY) < 0) {
164fe616055SDavid Ahern 			printf("error accessing file %s: %s\n",
165fe616055SDavid Ahern 				filename, strerror(errno));
166fe616055SDavid Ahern 			return 1;
167fe616055SDavid Ahern 		}
168fe616055SDavid Ahern 
1691e4edb6dSAndrii Nakryiko 		obj = bpf_object__open_file(filename, NULL);
1701e4edb6dSAndrii Nakryiko 		if (libbpf_get_error(obj))
1711e4edb6dSAndrii Nakryiko 			return 1;
1721e4edb6dSAndrii Nakryiko 
1731e4edb6dSAndrii Nakryiko 		prog = bpf_object__next_program(obj, NULL);
1741e4edb6dSAndrii Nakryiko 		bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
1751e4edb6dSAndrii Nakryiko 
1761e4edb6dSAndrii Nakryiko 		err = bpf_object__load(obj);
177a32a32cbSJesper Dangaard Brouer 		if (err) {
178a32a32cbSJesper Dangaard Brouer 			printf("Does kernel support devmap lookup?\n");
179a32a32cbSJesper Dangaard Brouer 			/* If not, the error message will be:
180a32a32cbSJesper Dangaard Brouer 			 *  "cannot pass map_type 14 into func bpf_map_lookup_elem#1"
181a32a32cbSJesper Dangaard Brouer 			 */
182e1a40ef4SJakub Kicinski 			return 1;
183a32a32cbSJesper Dangaard Brouer 		}
184e1a40ef4SJakub Kicinski 
1857490d592SKui-Feng Lee 		bpf_object__for_each_program(pos, obj) {
1867490d592SKui-Feng Lee 			sec_name = bpf_program__section_name(pos);
1877490d592SKui-Feng Lee 			if (sec_name && !strcmp(sec_name, prog_name)) {
1887490d592SKui-Feng Lee 				prog = pos;
1897490d592SKui-Feng Lee 				break;
1907490d592SKui-Feng Lee 			}
1917490d592SKui-Feng Lee 		}
192e1a40ef4SJakub Kicinski 		prog_fd = bpf_program__fd(prog);
193e1a40ef4SJakub Kicinski 		if (prog_fd < 0) {
194e1a40ef4SJakub Kicinski 			printf("program not found: %s\n", strerror(prog_fd));
195fe616055SDavid Ahern 			return 1;
196fe616055SDavid Ahern 		}
197e1a40ef4SJakub Kicinski 		map_fd = bpf_map__fd(bpf_object__find_map_by_name(obj,
1983783d437SJesper Dangaard Brouer 							"xdp_tx_ports"));
199e1a40ef4SJakub Kicinski 		if (map_fd < 0) {
200e1a40ef4SJakub Kicinski 			printf("map not found: %s\n", strerror(map_fd));
201fe616055SDavid Ahern 			return 1;
202fe616055SDavid Ahern 		}
203fe616055SDavid Ahern 	}
204fe616055SDavid Ahern 
205fe616055SDavid Ahern 	for (i = optind; i < argc; ++i) {
206fe616055SDavid Ahern 		idx = if_nametoindex(argv[i]);
207fe616055SDavid Ahern 		if (!idx)
208fe616055SDavid Ahern 			idx = strtoul(argv[i], NULL, 0);
209fe616055SDavid Ahern 
210fe616055SDavid Ahern 		if (!idx) {
211fe616055SDavid Ahern 			fprintf(stderr, "Invalid arg\n");
212fe616055SDavid Ahern 			return 1;
213fe616055SDavid Ahern 		}
214fe616055SDavid Ahern 		if (!attach) {
215de5bb438SZhengchao Shao 			err = do_detach(idx, argv[i], prog_name);
216fe616055SDavid Ahern 			if (err)
217fe616055SDavid Ahern 				ret = err;
218fe616055SDavid Ahern 		} else {
219a32a32cbSJesper Dangaard Brouer 			err = do_attach(idx, prog_fd, map_fd, argv[i]);
220fe616055SDavid Ahern 			if (err)
221fe616055SDavid Ahern 				ret = err;
222fe616055SDavid Ahern 		}
223fe616055SDavid Ahern 	}
224fe616055SDavid Ahern 
225fe616055SDavid Ahern 	return ret;
226fe616055SDavid Ahern }
227