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