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