diff options
author | Jakub Kicinski <jakub.kicinski@netronome.com> | 2017-10-04 23:10:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-10-05 00:45:06 -0400 |
commit | 71bb428fe2c19512ac671d5ee16ef3e73e1b49a8 (patch) | |
tree | 69a3005a095804dcdb3600b177d019fd2fba2c24 /tools/bpf/bpftool/prog.c | |
parent | a92bb546cff0b31d96d5919ebfeda9c678756b42 (diff) |
tools: bpf: add bpftool
Add a simple tool for querying and updating BPF objects on the system.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Simon Horman <simon.horman@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'tools/bpf/bpftool/prog.c')
-rw-r--r-- | tools/bpf/bpftool/prog.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c new file mode 100644 index 000000000000..421ba89ce86a --- /dev/null +++ b/tools/bpf/bpftool/prog.c | |||
@@ -0,0 +1,456 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Netronome Systems, Inc. | ||
3 | * | ||
4 | * This software is dual licensed under the GNU General License Version 2, | ||
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | ||
6 | * source tree or the BSD 2-Clause License provided below. You have the | ||
7 | * option to license this software under the complete terms of either license. | ||
8 | * | ||
9 | * The BSD 2-Clause License: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * 1. Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * 2. Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | /* Author: Jakub Kicinski <kubakici@wp.pl> */ | ||
35 | |||
36 | #include <errno.h> | ||
37 | #include <fcntl.h> | ||
38 | #include <stdio.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <string.h> | ||
41 | #include <time.h> | ||
42 | #include <unistd.h> | ||
43 | #include <sys/types.h> | ||
44 | #include <sys/stat.h> | ||
45 | |||
46 | #include <bpf.h> | ||
47 | |||
48 | #include "main.h" | ||
49 | |||
50 | static const char * const prog_type_name[] = { | ||
51 | [BPF_PROG_TYPE_UNSPEC] = "unspec", | ||
52 | [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", | ||
53 | [BPF_PROG_TYPE_KPROBE] = "kprobe", | ||
54 | [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", | ||
55 | [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", | ||
56 | [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", | ||
57 | [BPF_PROG_TYPE_XDP] = "xdp", | ||
58 | [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", | ||
59 | [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", | ||
60 | [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", | ||
61 | [BPF_PROG_TYPE_LWT_IN] = "lwt_in", | ||
62 | [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", | ||
63 | [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", | ||
64 | [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", | ||
65 | [BPF_PROG_TYPE_SK_SKB] = "sk_skb", | ||
66 | }; | ||
67 | |||
68 | static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) | ||
69 | { | ||
70 | struct timespec real_time_ts, boot_time_ts; | ||
71 | time_t wallclock_secs; | ||
72 | struct tm load_tm; | ||
73 | |||
74 | buf[--size] = '\0'; | ||
75 | |||
76 | if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || | ||
77 | clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { | ||
78 | perror("Can't read clocks"); | ||
79 | snprintf(buf, size, "%llu", nsecs / 1000000000); | ||
80 | return; | ||
81 | } | ||
82 | |||
83 | wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + | ||
84 | nsecs / 1000000000; | ||
85 | |||
86 | if (!localtime_r(&wallclock_secs, &load_tm)) { | ||
87 | snprintf(buf, size, "%llu", nsecs / 1000000000); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | strftime(buf, size, "%b %d/%H:%M", &load_tm); | ||
92 | } | ||
93 | |||
94 | static int prog_fd_by_tag(unsigned char *tag) | ||
95 | { | ||
96 | struct bpf_prog_info info = {}; | ||
97 | __u32 len = sizeof(info); | ||
98 | unsigned int id = 0; | ||
99 | int err; | ||
100 | int fd; | ||
101 | |||
102 | while (true) { | ||
103 | err = bpf_prog_get_next_id(id, &id); | ||
104 | if (err) { | ||
105 | err("%s\n", strerror(errno)); | ||
106 | return -1; | ||
107 | } | ||
108 | |||
109 | fd = bpf_prog_get_fd_by_id(id); | ||
110 | if (fd < 0) { | ||
111 | err("can't get prog by id (%u): %s\n", | ||
112 | id, strerror(errno)); | ||
113 | return -1; | ||
114 | } | ||
115 | |||
116 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
117 | if (err) { | ||
118 | err("can't get prog info (%u): %s\n", | ||
119 | id, strerror(errno)); | ||
120 | close(fd); | ||
121 | return -1; | ||
122 | } | ||
123 | |||
124 | if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) | ||
125 | return fd; | ||
126 | |||
127 | close(fd); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | int prog_parse_fd(int *argc, char ***argv) | ||
132 | { | ||
133 | int fd; | ||
134 | |||
135 | if (is_prefix(**argv, "id")) { | ||
136 | unsigned int id; | ||
137 | char *endptr; | ||
138 | |||
139 | NEXT_ARGP(); | ||
140 | |||
141 | id = strtoul(**argv, &endptr, 0); | ||
142 | if (*endptr) { | ||
143 | err("can't parse %s as ID\n", **argv); | ||
144 | return -1; | ||
145 | } | ||
146 | NEXT_ARGP(); | ||
147 | |||
148 | fd = bpf_prog_get_fd_by_id(id); | ||
149 | if (fd < 0) | ||
150 | err("get by id (%u): %s\n", id, strerror(errno)); | ||
151 | return fd; | ||
152 | } else if (is_prefix(**argv, "tag")) { | ||
153 | unsigned char tag[BPF_TAG_SIZE]; | ||
154 | |||
155 | NEXT_ARGP(); | ||
156 | |||
157 | if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, | ||
158 | tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) | ||
159 | != BPF_TAG_SIZE) { | ||
160 | err("can't parse tag\n"); | ||
161 | return -1; | ||
162 | } | ||
163 | NEXT_ARGP(); | ||
164 | |||
165 | return prog_fd_by_tag(tag); | ||
166 | } else if (is_prefix(**argv, "pinned")) { | ||
167 | char *path; | ||
168 | |||
169 | NEXT_ARGP(); | ||
170 | |||
171 | path = **argv; | ||
172 | NEXT_ARGP(); | ||
173 | |||
174 | return open_obj_pinned_any(path, BPF_OBJ_PROG); | ||
175 | } | ||
176 | |||
177 | err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | static void show_prog_maps(int fd, u32 num_maps) | ||
182 | { | ||
183 | struct bpf_prog_info info = {}; | ||
184 | __u32 len = sizeof(info); | ||
185 | __u32 map_ids[num_maps]; | ||
186 | unsigned int i; | ||
187 | int err; | ||
188 | |||
189 | info.nr_map_ids = num_maps; | ||
190 | info.map_ids = ptr_to_u64(map_ids); | ||
191 | |||
192 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
193 | if (err || !info.nr_map_ids) | ||
194 | return; | ||
195 | |||
196 | printf(" map_ids "); | ||
197 | for (i = 0; i < info.nr_map_ids; i++) | ||
198 | printf("%u%s", map_ids[i], | ||
199 | i == info.nr_map_ids - 1 ? "" : ","); | ||
200 | } | ||
201 | |||
202 | static int show_prog(int fd) | ||
203 | { | ||
204 | struct bpf_prog_info info = {}; | ||
205 | __u32 len = sizeof(info); | ||
206 | char *memlock; | ||
207 | int err; | ||
208 | |||
209 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
210 | if (err) { | ||
211 | err("can't get prog info: %s\n", strerror(errno)); | ||
212 | return -1; | ||
213 | } | ||
214 | |||
215 | printf("%u: ", info.id); | ||
216 | if (info.type < ARRAY_SIZE(prog_type_name)) | ||
217 | printf("%s ", prog_type_name[info.type]); | ||
218 | else | ||
219 | printf("type %u ", info.type); | ||
220 | |||
221 | if (*info.name) | ||
222 | printf("name %s ", info.name); | ||
223 | |||
224 | printf("tag "); | ||
225 | print_hex(info.tag, BPF_TAG_SIZE, ":"); | ||
226 | printf("\n"); | ||
227 | |||
228 | if (info.load_time) { | ||
229 | char buf[32]; | ||
230 | |||
231 | print_boot_time(info.load_time, buf, sizeof(buf)); | ||
232 | |||
233 | /* Piggy back on load_time, since 0 uid is a valid one */ | ||
234 | printf("\tloaded_at %s uid %u\n", buf, info.created_by_uid); | ||
235 | } | ||
236 | |||
237 | printf("\txlated %uB", info.xlated_prog_len); | ||
238 | |||
239 | if (info.jited_prog_len) | ||
240 | printf(" jited %uB", info.jited_prog_len); | ||
241 | else | ||
242 | printf(" not jited"); | ||
243 | |||
244 | memlock = get_fdinfo(fd, "memlock"); | ||
245 | if (memlock) | ||
246 | printf(" memlock %sB", memlock); | ||
247 | free(memlock); | ||
248 | |||
249 | if (info.nr_map_ids) | ||
250 | show_prog_maps(fd, info.nr_map_ids); | ||
251 | |||
252 | printf("\n"); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int do_show(int argc, char **argv) | ||
258 | { __u32 id = 0; | ||
259 | int err; | ||
260 | int fd; | ||
261 | |||
262 | if (argc == 2) { | ||
263 | fd = prog_parse_fd(&argc, &argv); | ||
264 | if (fd < 0) | ||
265 | return -1; | ||
266 | |||
267 | return show_prog(fd); | ||
268 | } | ||
269 | |||
270 | if (argc) | ||
271 | return BAD_ARG(); | ||
272 | |||
273 | while (true) { | ||
274 | err = bpf_prog_get_next_id(id, &id); | ||
275 | if (err) { | ||
276 | if (errno == ENOENT) | ||
277 | break; | ||
278 | err("can't get next program: %s\n", strerror(errno)); | ||
279 | if (errno == EINVAL) | ||
280 | err("kernel too old?\n"); | ||
281 | return -1; | ||
282 | } | ||
283 | |||
284 | fd = bpf_prog_get_fd_by_id(id); | ||
285 | if (fd < 0) { | ||
286 | err("can't get prog by id (%u): %s\n", | ||
287 | id, strerror(errno)); | ||
288 | return -1; | ||
289 | } | ||
290 | |||
291 | err = show_prog(fd); | ||
292 | close(fd); | ||
293 | if (err) | ||
294 | return err; | ||
295 | } | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int do_dump(int argc, char **argv) | ||
301 | { | ||
302 | struct bpf_prog_info info = {}; | ||
303 | __u32 len = sizeof(info); | ||
304 | bool can_disasm = false; | ||
305 | unsigned int buf_size; | ||
306 | char *filepath = NULL; | ||
307 | bool opcodes = false; | ||
308 | unsigned char *buf; | ||
309 | __u32 *member_len; | ||
310 | __u64 *member_ptr; | ||
311 | ssize_t n; | ||
312 | int err; | ||
313 | int fd; | ||
314 | |||
315 | if (is_prefix(*argv, "jited")) { | ||
316 | member_len = &info.jited_prog_len; | ||
317 | member_ptr = &info.jited_prog_insns; | ||
318 | can_disasm = true; | ||
319 | } else if (is_prefix(*argv, "xlated")) { | ||
320 | member_len = &info.xlated_prog_len; | ||
321 | member_ptr = &info.xlated_prog_insns; | ||
322 | } else { | ||
323 | err("expected 'xlated' or 'jited', got: %s\n", *argv); | ||
324 | return -1; | ||
325 | } | ||
326 | NEXT_ARG(); | ||
327 | |||
328 | if (argc < 2) | ||
329 | usage(); | ||
330 | |||
331 | fd = prog_parse_fd(&argc, &argv); | ||
332 | if (fd < 0) | ||
333 | return -1; | ||
334 | |||
335 | if (is_prefix(*argv, "file")) { | ||
336 | NEXT_ARG(); | ||
337 | if (!argc) { | ||
338 | err("expected file path\n"); | ||
339 | return -1; | ||
340 | } | ||
341 | |||
342 | filepath = *argv; | ||
343 | NEXT_ARG(); | ||
344 | } else if (is_prefix(*argv, "opcodes")) { | ||
345 | opcodes = true; | ||
346 | NEXT_ARG(); | ||
347 | } | ||
348 | |||
349 | if (!filepath && !can_disasm) { | ||
350 | err("expected 'file' got %s\n", *argv); | ||
351 | return -1; | ||
352 | } | ||
353 | if (argc) { | ||
354 | usage(); | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
359 | if (err) { | ||
360 | err("can't get prog info: %s\n", strerror(errno)); | ||
361 | return -1; | ||
362 | } | ||
363 | |||
364 | if (!*member_len) { | ||
365 | info("no instructions returned\n"); | ||
366 | close(fd); | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | buf_size = *member_len; | ||
371 | |||
372 | buf = malloc(buf_size); | ||
373 | if (!buf) { | ||
374 | err("mem alloc failed\n"); | ||
375 | close(fd); | ||
376 | return -1; | ||
377 | } | ||
378 | |||
379 | memset(&info, 0, sizeof(info)); | ||
380 | |||
381 | *member_ptr = ptr_to_u64(buf); | ||
382 | *member_len = buf_size; | ||
383 | |||
384 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
385 | close(fd); | ||
386 | if (err) { | ||
387 | err("can't get prog info: %s\n", strerror(errno)); | ||
388 | goto err_free; | ||
389 | } | ||
390 | |||
391 | if (*member_len > buf_size) { | ||
392 | info("too many instructions returned\n"); | ||
393 | goto err_free; | ||
394 | } | ||
395 | |||
396 | if (filepath) { | ||
397 | fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); | ||
398 | if (fd < 0) { | ||
399 | err("can't open file %s: %s\n", filepath, | ||
400 | strerror(errno)); | ||
401 | goto err_free; | ||
402 | } | ||
403 | |||
404 | n = write(fd, buf, *member_len); | ||
405 | close(fd); | ||
406 | if (n != *member_len) { | ||
407 | err("error writing output file: %s\n", | ||
408 | n < 0 ? strerror(errno) : "short write"); | ||
409 | goto err_free; | ||
410 | } | ||
411 | } else { | ||
412 | disasm_print_insn(buf, *member_len, opcodes); | ||
413 | } | ||
414 | |||
415 | free(buf); | ||
416 | |||
417 | return 0; | ||
418 | |||
419 | err_free: | ||
420 | free(buf); | ||
421 | return -1; | ||
422 | } | ||
423 | |||
424 | static int do_pin(int argc, char **argv) | ||
425 | { | ||
426 | return do_pin_any(argc, argv, bpf_prog_get_fd_by_id); | ||
427 | } | ||
428 | |||
429 | static int do_help(int argc, char **argv) | ||
430 | { | ||
431 | fprintf(stderr, | ||
432 | "Usage: %s %s show [PROG]\n" | ||
433 | " %s %s dump xlated PROG file FILE\n" | ||
434 | " %s %s dump jited PROG [file FILE] [opcodes]\n" | ||
435 | " %s %s pin PROG FILE\n" | ||
436 | " %s %s help\n" | ||
437 | "\n" | ||
438 | " " HELP_SPEC_PROGRAM "\n" | ||
439 | "", | ||
440 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | ||
441 | bin_name, argv[-2], bin_name, argv[-2]); | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static const struct cmd cmds[] = { | ||
447 | { "show", do_show }, | ||
448 | { "dump", do_dump }, | ||
449 | { "pin", do_pin }, | ||
450 | { 0 } | ||
451 | }; | ||
452 | |||
453 | int do_prog(int argc, char **argv) | ||
454 | { | ||
455 | return cmd_select(cmds, argc, argv, do_help); | ||
456 | } | ||