aboutsummaryrefslogtreecommitdiffstats
path: root/tools/bpf/bpftool/prog.c
diff options
context:
space:
mode:
authorJakub Kicinski <jakub.kicinski@netronome.com>2017-10-04 23:10:04 -0400
committerDavid S. Miller <davem@davemloft.net>2017-10-05 00:45:06 -0400
commit71bb428fe2c19512ac671d5ee16ef3e73e1b49a8 (patch)
tree69a3005a095804dcdb3600b177d019fd2fba2c24 /tools/bpf/bpftool/prog.c
parenta92bb546cff0b31d96d5919ebfeda9c678756b42 (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.c456
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
50static 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
68static 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
94static 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
131int 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
181static 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
202static 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
257static 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
300static 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
419err_free:
420 free(buf);
421 return -1;
422}
423
424static int do_pin(int argc, char **argv)
425{
426 return do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
427}
428
429static 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
446static const struct cmd cmds[] = {
447 { "show", do_show },
448 { "dump", do_dump },
449 { "pin", do_pin },
450 { 0 }
451};
452
453int do_prog(int argc, char **argv)
454{
455 return cmd_select(cmds, argc, argv, do_help);
456}