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 | |
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>
-rw-r--r-- | tools/bpf/Makefile | 18 | ||||
-rw-r--r-- | tools/bpf/bpftool/Makefile | 80 | ||||
-rw-r--r-- | tools/bpf/bpftool/common.c | 216 | ||||
-rw-r--r-- | tools/bpf/bpftool/jit_disasm.c | 87 | ||||
-rw-r--r-- | tools/bpf/bpftool/main.c | 212 | ||||
-rw-r--r-- | tools/bpf/bpftool/main.h | 99 | ||||
-rw-r--r-- | tools/bpf/bpftool/map.c | 744 | ||||
-rw-r--r-- | tools/bpf/bpftool/prog.c | 456 |
8 files changed, 1909 insertions, 3 deletions
diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile index ddf888010652..325a35e1c28e 100644 --- a/tools/bpf/Makefile +++ b/tools/bpf/Makefile | |||
@@ -3,6 +3,7 @@ prefix = /usr | |||
3 | CC = gcc | 3 | CC = gcc |
4 | LEX = flex | 4 | LEX = flex |
5 | YACC = bison | 5 | YACC = bison |
6 | MAKE = make | ||
6 | 7 | ||
7 | CFLAGS += -Wall -O2 | 8 | CFLAGS += -Wall -O2 |
8 | CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include | 9 | CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include |
@@ -13,7 +14,7 @@ CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include | |||
13 | %.lex.c: %.l | 14 | %.lex.c: %.l |
14 | $(LEX) -o $@ $< | 15 | $(LEX) -o $@ $< |
15 | 16 | ||
16 | all : bpf_jit_disasm bpf_dbg bpf_asm | 17 | all: bpf_jit_disasm bpf_dbg bpf_asm bpftool |
17 | 18 | ||
18 | bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm' | 19 | bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm' |
19 | bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl | 20 | bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl |
@@ -26,10 +27,21 @@ bpf_asm : LDLIBS = | |||
26 | bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o | 27 | bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o |
27 | bpf_exp.lex.o : bpf_exp.yacc.c | 28 | bpf_exp.lex.o : bpf_exp.yacc.c |
28 | 29 | ||
29 | clean : | 30 | clean: bpftool_clean |
30 | rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.* | 31 | rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.* |
31 | 32 | ||
32 | install : | 33 | install: bpftool_install |
33 | install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm | 34 | install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm |
34 | install bpf_dbg $(prefix)/bin/bpf_dbg | 35 | install bpf_dbg $(prefix)/bin/bpf_dbg |
35 | install bpf_asm $(prefix)/bin/bpf_asm | 36 | install bpf_asm $(prefix)/bin/bpf_asm |
37 | |||
38 | bpftool: | ||
39 | $(MAKE) -C bpftool | ||
40 | |||
41 | bpftool_install: | ||
42 | $(MAKE) -C bpftool install | ||
43 | |||
44 | bpftool_clean: | ||
45 | $(MAKE) -C bpftool clean | ||
46 | |||
47 | .PHONY: bpftool FORCE | ||
diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile new file mode 100644 index 000000000000..a7151f47fb40 --- /dev/null +++ b/tools/bpf/bpftool/Makefile | |||
@@ -0,0 +1,80 @@ | |||
1 | include ../../scripts/Makefile.include | ||
2 | |||
3 | include ../../scripts/utilities.mak | ||
4 | |||
5 | ifeq ($(srctree),) | ||
6 | srctree := $(patsubst %/,%,$(dir $(CURDIR))) | ||
7 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
8 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
9 | #$(info Determined 'srctree' to be $(srctree)) | ||
10 | endif | ||
11 | |||
12 | ifneq ($(objtree),) | ||
13 | #$(info Determined 'objtree' to be $(objtree)) | ||
14 | endif | ||
15 | |||
16 | ifneq ($(OUTPUT),) | ||
17 | #$(info Determined 'OUTPUT' to be $(OUTPUT)) | ||
18 | # Adding $(OUTPUT) as a directory to look for source files, | ||
19 | # because use generated output files as sources dependency | ||
20 | # for flex/bison parsers. | ||
21 | VPATH += $(OUTPUT) | ||
22 | export VPATH | ||
23 | endif | ||
24 | |||
25 | ifeq ($(V),1) | ||
26 | Q = | ||
27 | else | ||
28 | Q = @ | ||
29 | endif | ||
30 | |||
31 | BPF_DIR = $(srctree)/tools/lib/bpf/ | ||
32 | |||
33 | ifneq ($(OUTPUT),) | ||
34 | BPF_PATH=$(OUTPUT) | ||
35 | else | ||
36 | BPF_PATH=$(BPF_DIR) | ||
37 | endif | ||
38 | |||
39 | LIBBPF = $(BPF_PATH)libbpf.a | ||
40 | |||
41 | $(LIBBPF): FORCE | ||
42 | $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) | ||
43 | |||
44 | $(LIBBPF)-clean: | ||
45 | $(call QUIET_CLEAN, libbpf) | ||
46 | $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null | ||
47 | |||
48 | prefix = /usr | ||
49 | |||
50 | CC = gcc | ||
51 | |||
52 | CFLAGS += -O2 | ||
53 | CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow | ||
54 | CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf | ||
55 | LIBS = -lelf -lbfd -lopcodes $(LIBBPF) | ||
56 | |||
57 | include $(wildcard *.d) | ||
58 | |||
59 | all: $(OUTPUT)bpftool | ||
60 | |||
61 | SRCS=$(wildcard *.c) | ||
62 | OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) | ||
63 | |||
64 | $(OUTPUT)bpftool: $(OBJS) $(LIBBPF) | ||
65 | $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS) | ||
66 | |||
67 | $(OUTPUT)%.o: %.c | ||
68 | $(QUIET_CC)$(COMPILE.c) -MMD -o $@ $< | ||
69 | |||
70 | clean: $(LIBBPF)-clean | ||
71 | $(call QUIET_CLEAN, bpftool) | ||
72 | $(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d | ||
73 | |||
74 | install: | ||
75 | install $(OUTPUT)bpftool $(prefix)/sbin/bpftool | ||
76 | |||
77 | FORCE: | ||
78 | |||
79 | .PHONY: all clean FORCE | ||
80 | .DEFAULT_GOAL := all | ||
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c new file mode 100644 index 000000000000..df8396a0c400 --- /dev/null +++ b/tools/bpf/bpftool/common.c | |||
@@ -0,0 +1,216 @@ | |||
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 <libgen.h> | ||
38 | #include <stdbool.h> | ||
39 | #include <stdio.h> | ||
40 | #include <stdlib.h> | ||
41 | #include <string.h> | ||
42 | #include <unistd.h> | ||
43 | #include <linux/limits.h> | ||
44 | #include <linux/magic.h> | ||
45 | #include <sys/types.h> | ||
46 | #include <sys/vfs.h> | ||
47 | |||
48 | #include <bpf.h> | ||
49 | |||
50 | #include "main.h" | ||
51 | |||
52 | static bool is_bpffs(char *path) | ||
53 | { | ||
54 | struct statfs st_fs; | ||
55 | |||
56 | if (statfs(path, &st_fs) < 0) | ||
57 | return false; | ||
58 | |||
59 | return (unsigned long)st_fs.f_type == BPF_FS_MAGIC; | ||
60 | } | ||
61 | |||
62 | int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) | ||
63 | { | ||
64 | enum bpf_obj_type type; | ||
65 | int fd; | ||
66 | |||
67 | fd = bpf_obj_get(path); | ||
68 | if (fd < 0) { | ||
69 | err("bpf obj get (%s): %s\n", path, | ||
70 | errno == EACCES && !is_bpffs(dirname(path)) ? | ||
71 | "directory not in bpf file system (bpffs)" : | ||
72 | strerror(errno)); | ||
73 | return -1; | ||
74 | } | ||
75 | |||
76 | type = get_fd_type(fd); | ||
77 | if (type < 0) { | ||
78 | close(fd); | ||
79 | return type; | ||
80 | } | ||
81 | if (type != exp_type) { | ||
82 | err("incorrect object type: %s\n", get_fd_type_name(type)); | ||
83 | close(fd); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | return fd; | ||
88 | } | ||
89 | |||
90 | int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) | ||
91 | { | ||
92 | unsigned int id; | ||
93 | char *endptr; | ||
94 | int err; | ||
95 | int fd; | ||
96 | |||
97 | if (!is_prefix(*argv, "id")) { | ||
98 | err("expected 'id' got %s\n", *argv); | ||
99 | return -1; | ||
100 | } | ||
101 | NEXT_ARG(); | ||
102 | |||
103 | id = strtoul(*argv, &endptr, 0); | ||
104 | if (*endptr) { | ||
105 | err("can't parse %s as ID\n", *argv); | ||
106 | return -1; | ||
107 | } | ||
108 | NEXT_ARG(); | ||
109 | |||
110 | if (argc != 1) | ||
111 | usage(); | ||
112 | |||
113 | fd = get_fd_by_id(id); | ||
114 | if (fd < 0) { | ||
115 | err("can't get prog by id (%u): %s\n", id, strerror(errno)); | ||
116 | return -1; | ||
117 | } | ||
118 | |||
119 | err = bpf_obj_pin(fd, *argv); | ||
120 | close(fd); | ||
121 | if (err) { | ||
122 | err("can't pin the object (%s): %s\n", *argv, | ||
123 | errno == EACCES && !is_bpffs(dirname(*argv)) ? | ||
124 | "directory not in bpf file system (bpffs)" : | ||
125 | strerror(errno)); | ||
126 | return -1; | ||
127 | } | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | const char *get_fd_type_name(enum bpf_obj_type type) | ||
133 | { | ||
134 | static const char * const names[] = { | ||
135 | [BPF_OBJ_UNKNOWN] = "unknown", | ||
136 | [BPF_OBJ_PROG] = "prog", | ||
137 | [BPF_OBJ_MAP] = "map", | ||
138 | }; | ||
139 | |||
140 | if (type < 0 || type >= ARRAY_SIZE(names) || !names[type]) | ||
141 | return names[BPF_OBJ_UNKNOWN]; | ||
142 | |||
143 | return names[type]; | ||
144 | } | ||
145 | |||
146 | int get_fd_type(int fd) | ||
147 | { | ||
148 | char path[PATH_MAX]; | ||
149 | char buf[512]; | ||
150 | ssize_t n; | ||
151 | |||
152 | snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd); | ||
153 | |||
154 | n = readlink(path, buf, sizeof(buf)); | ||
155 | if (n < 0) { | ||
156 | err("can't read link type: %s\n", strerror(errno)); | ||
157 | return -1; | ||
158 | } | ||
159 | if (n == sizeof(path)) { | ||
160 | err("can't read link type: path too long!\n"); | ||
161 | return -1; | ||
162 | } | ||
163 | |||
164 | if (strstr(buf, "bpf-map")) | ||
165 | return BPF_OBJ_MAP; | ||
166 | else if (strstr(buf, "bpf-prog")) | ||
167 | return BPF_OBJ_PROG; | ||
168 | |||
169 | return BPF_OBJ_UNKNOWN; | ||
170 | } | ||
171 | |||
172 | char *get_fdinfo(int fd, const char *key) | ||
173 | { | ||
174 | char path[PATH_MAX]; | ||
175 | char *line = NULL; | ||
176 | size_t line_n = 0; | ||
177 | ssize_t n; | ||
178 | FILE *fdi; | ||
179 | |||
180 | snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd); | ||
181 | |||
182 | fdi = fopen(path, "r"); | ||
183 | if (!fdi) { | ||
184 | err("can't open fdinfo: %s\n", strerror(errno)); | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | while ((n = getline(&line, &line_n, fdi))) { | ||
189 | char *value; | ||
190 | int len; | ||
191 | |||
192 | if (!strstr(line, key)) | ||
193 | continue; | ||
194 | |||
195 | fclose(fdi); | ||
196 | |||
197 | value = strchr(line, '\t'); | ||
198 | if (!value || !value[1]) { | ||
199 | err("malformed fdinfo!?\n"); | ||
200 | free(line); | ||
201 | return NULL; | ||
202 | } | ||
203 | value++; | ||
204 | |||
205 | len = strlen(value); | ||
206 | memmove(line, value, len); | ||
207 | line[len - 1] = '\0'; | ||
208 | |||
209 | return line; | ||
210 | } | ||
211 | |||
212 | err("key '%s' not found in fdinfo\n", key); | ||
213 | free(line); | ||
214 | fclose(fdi); | ||
215 | return NULL; | ||
216 | } | ||
diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c new file mode 100644 index 000000000000..70e480b59e9d --- /dev/null +++ b/tools/bpf/bpftool/jit_disasm.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * Based on: | ||
3 | * | ||
4 | * Minimal BPF JIT image disassembler | ||
5 | * | ||
6 | * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for | ||
7 | * debugging or verification purposes. | ||
8 | * | ||
9 | * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net> | ||
10 | * Licensed under the GNU General Public License, version 2.0 (GPLv2) | ||
11 | */ | ||
12 | |||
13 | #include <stdint.h> | ||
14 | #include <stdio.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <assert.h> | ||
17 | #include <unistd.h> | ||
18 | #include <string.h> | ||
19 | #include <bfd.h> | ||
20 | #include <dis-asm.h> | ||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | |||
24 | static void get_exec_path(char *tpath, size_t size) | ||
25 | { | ||
26 | ssize_t len; | ||
27 | char *path; | ||
28 | |||
29 | snprintf(tpath, size, "/proc/%d/exe", (int) getpid()); | ||
30 | tpath[size - 1] = 0; | ||
31 | |||
32 | path = strdup(tpath); | ||
33 | assert(path); | ||
34 | |||
35 | len = readlink(path, tpath, size - 1); | ||
36 | assert(len > 0); | ||
37 | tpath[len] = 0; | ||
38 | |||
39 | free(path); | ||
40 | } | ||
41 | |||
42 | void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes) | ||
43 | { | ||
44 | disassembler_ftype disassemble; | ||
45 | struct disassemble_info info; | ||
46 | int count, i, pc = 0; | ||
47 | char tpath[256]; | ||
48 | bfd *bfdf; | ||
49 | |||
50 | if (!len) | ||
51 | return; | ||
52 | |||
53 | memset(tpath, 0, sizeof(tpath)); | ||
54 | get_exec_path(tpath, sizeof(tpath)); | ||
55 | |||
56 | bfdf = bfd_openr(tpath, NULL); | ||
57 | assert(bfdf); | ||
58 | assert(bfd_check_format(bfdf, bfd_object)); | ||
59 | |||
60 | init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf); | ||
61 | info.arch = bfd_get_arch(bfdf); | ||
62 | info.mach = bfd_get_mach(bfdf); | ||
63 | info.buffer = image; | ||
64 | info.buffer_length = len; | ||
65 | |||
66 | disassemble_init_for_target(&info); | ||
67 | |||
68 | disassemble = disassembler(bfdf); | ||
69 | assert(disassemble); | ||
70 | |||
71 | do { | ||
72 | printf("%4x:\t", pc); | ||
73 | |||
74 | count = disassemble(pc, &info); | ||
75 | |||
76 | if (opcodes) { | ||
77 | printf("\n\t"); | ||
78 | for (i = 0; i < count; ++i) | ||
79 | printf("%02x ", (uint8_t) image[pc + i]); | ||
80 | } | ||
81 | printf("\n"); | ||
82 | |||
83 | pc += count; | ||
84 | } while (count > 0 && pc < len); | ||
85 | |||
86 | bfd_close(bfdf); | ||
87 | } | ||
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c new file mode 100644 index 000000000000..e02d00d6e00b --- /dev/null +++ b/tools/bpf/bpftool/main.c | |||
@@ -0,0 +1,212 @@ | |||
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 <bfd.h> | ||
37 | #include <ctype.h> | ||
38 | #include <errno.h> | ||
39 | #include <linux/bpf.h> | ||
40 | #include <stdio.h> | ||
41 | #include <stdlib.h> | ||
42 | #include <string.h> | ||
43 | |||
44 | #include <bpf.h> | ||
45 | |||
46 | #include "main.h" | ||
47 | |||
48 | const char *bin_name; | ||
49 | static int last_argc; | ||
50 | static char **last_argv; | ||
51 | static int (*last_do_help)(int argc, char **argv); | ||
52 | |||
53 | void usage(void) | ||
54 | { | ||
55 | last_do_help(last_argc - 1, last_argv + 1); | ||
56 | |||
57 | exit(-1); | ||
58 | } | ||
59 | |||
60 | static int do_help(int argc, char **argv) | ||
61 | { | ||
62 | fprintf(stderr, | ||
63 | "Usage: %s OBJECT { COMMAND | help }\n" | ||
64 | " %s batch file FILE\n" | ||
65 | "\n" | ||
66 | " OBJECT := { prog | map }\n", | ||
67 | bin_name, bin_name); | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | int cmd_select(const struct cmd *cmds, int argc, char **argv, | ||
73 | int (*help)(int argc, char **argv)) | ||
74 | { | ||
75 | unsigned int i; | ||
76 | |||
77 | last_argc = argc; | ||
78 | last_argv = argv; | ||
79 | last_do_help = help; | ||
80 | |||
81 | if (argc < 1 && cmds[0].func) | ||
82 | return cmds[0].func(argc, argv); | ||
83 | |||
84 | for (i = 0; cmds[i].func; i++) | ||
85 | if (is_prefix(*argv, cmds[i].cmd)) | ||
86 | return cmds[i].func(argc - 1, argv + 1); | ||
87 | |||
88 | help(argc - 1, argv + 1); | ||
89 | |||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | bool is_prefix(const char *pfx, const char *str) | ||
94 | { | ||
95 | if (!pfx) | ||
96 | return false; | ||
97 | if (strlen(str) < strlen(pfx)) | ||
98 | return false; | ||
99 | |||
100 | return !memcmp(str, pfx, strlen(pfx)); | ||
101 | } | ||
102 | |||
103 | void print_hex(void *arg, unsigned int n, const char *sep) | ||
104 | { | ||
105 | unsigned char *data = arg; | ||
106 | unsigned int i; | ||
107 | |||
108 | for (i = 0; i < n; i++) { | ||
109 | const char *pfx = ""; | ||
110 | |||
111 | if (!i) | ||
112 | /* nothing */; | ||
113 | else if (!(i % 16)) | ||
114 | printf("\n"); | ||
115 | else if (!(i % 8)) | ||
116 | printf(" "); | ||
117 | else | ||
118 | pfx = sep; | ||
119 | |||
120 | printf("%s%02hhx", i ? pfx : "", data[i]); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static int do_batch(int argc, char **argv); | ||
125 | |||
126 | static const struct cmd cmds[] = { | ||
127 | { "help", do_help }, | ||
128 | { "batch", do_batch }, | ||
129 | { "prog", do_prog }, | ||
130 | { "map", do_map }, | ||
131 | { 0 } | ||
132 | }; | ||
133 | |||
134 | static int do_batch(int argc, char **argv) | ||
135 | { | ||
136 | unsigned int lines = 0; | ||
137 | char *n_argv[4096]; | ||
138 | char buf[65536]; | ||
139 | int n_argc; | ||
140 | FILE *fp; | ||
141 | int err; | ||
142 | |||
143 | if (argc < 2) { | ||
144 | err("too few parameters for batch\n"); | ||
145 | return -1; | ||
146 | } else if (!is_prefix(*argv, "file")) { | ||
147 | err("expected 'file', got: %s\n", *argv); | ||
148 | return -1; | ||
149 | } else if (argc > 2) { | ||
150 | err("too many parameters for batch\n"); | ||
151 | return -1; | ||
152 | } | ||
153 | NEXT_ARG(); | ||
154 | |||
155 | fp = fopen(*argv, "r"); | ||
156 | if (!fp) { | ||
157 | err("Can't open file (%s): %s\n", *argv, strerror(errno)); | ||
158 | return -1; | ||
159 | } | ||
160 | |||
161 | while (fgets(buf, sizeof(buf), fp)) { | ||
162 | if (strlen(buf) == sizeof(buf) - 1) { | ||
163 | errno = E2BIG; | ||
164 | break; | ||
165 | } | ||
166 | |||
167 | n_argc = 0; | ||
168 | n_argv[n_argc] = strtok(buf, " \t\n"); | ||
169 | |||
170 | while (n_argv[n_argc]) { | ||
171 | n_argc++; | ||
172 | if (n_argc == ARRAY_SIZE(n_argv)) { | ||
173 | err("line %d has too many arguments, skip\n", | ||
174 | lines); | ||
175 | n_argc = 0; | ||
176 | break; | ||
177 | } | ||
178 | n_argv[n_argc] = strtok(NULL, " \t\n"); | ||
179 | } | ||
180 | |||
181 | if (!n_argc) | ||
182 | continue; | ||
183 | |||
184 | err = cmd_select(cmds, n_argc, n_argv, do_help); | ||
185 | if (err) | ||
186 | goto err_close; | ||
187 | |||
188 | lines++; | ||
189 | } | ||
190 | |||
191 | if (errno && errno != ENOENT) { | ||
192 | perror("reading batch file failed"); | ||
193 | err = -1; | ||
194 | } else { | ||
195 | info("processed %d lines\n", lines); | ||
196 | err = 0; | ||
197 | } | ||
198 | err_close: | ||
199 | fclose(fp); | ||
200 | |||
201 | return err; | ||
202 | } | ||
203 | |||
204 | int main(int argc, char **argv) | ||
205 | { | ||
206 | bin_name = argv[0]; | ||
207 | NEXT_ARG(); | ||
208 | |||
209 | bfd_init(); | ||
210 | |||
211 | return cmd_select(cmds, argc, argv, do_help); | ||
212 | } | ||
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h new file mode 100644 index 000000000000..85d2d7870a58 --- /dev/null +++ b/tools/bpf/bpftool/main.h | |||
@@ -0,0 +1,99 @@ | |||
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 | #ifndef __BPF_TOOL_H | ||
37 | #define __BPF_TOOL_H | ||
38 | |||
39 | #include <stdbool.h> | ||
40 | #include <stdio.h> | ||
41 | #include <linux/bpf.h> | ||
42 | |||
43 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | ||
44 | |||
45 | #define err(msg...) fprintf(stderr, "Error: " msg) | ||
46 | #define warn(msg...) fprintf(stderr, "Warning: " msg) | ||
47 | #define info(msg...) fprintf(stderr, msg) | ||
48 | |||
49 | #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) | ||
50 | |||
51 | #define min(a, b) \ | ||
52 | ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _b : _a; }) | ||
53 | #define max(a, b) \ | ||
54 | ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _b : _a; }) | ||
55 | |||
56 | #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) | ||
57 | #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) | ||
58 | #define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) | ||
59 | |||
60 | #define BPF_TAG_FMT "%02hhx:%02hhx:%02hhx:%02hhx:" \ | ||
61 | "%02hhx:%02hhx:%02hhx:%02hhx" | ||
62 | |||
63 | #define HELP_SPEC_PROGRAM \ | ||
64 | "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" | ||
65 | |||
66 | enum bpf_obj_type { | ||
67 | BPF_OBJ_UNKNOWN, | ||
68 | BPF_OBJ_PROG, | ||
69 | BPF_OBJ_MAP, | ||
70 | }; | ||
71 | |||
72 | extern const char *bin_name; | ||
73 | |||
74 | bool is_prefix(const char *pfx, const char *str); | ||
75 | void print_hex(void *arg, unsigned int n, const char *sep); | ||
76 | void usage(void) __attribute__((noreturn)); | ||
77 | |||
78 | struct cmd { | ||
79 | const char *cmd; | ||
80 | int (*func)(int argc, char **argv); | ||
81 | }; | ||
82 | |||
83 | int cmd_select(const struct cmd *cmds, int argc, char **argv, | ||
84 | int (*help)(int argc, char **argv)); | ||
85 | |||
86 | int get_fd_type(int fd); | ||
87 | const char *get_fd_type_name(enum bpf_obj_type type); | ||
88 | char *get_fdinfo(int fd, const char *key); | ||
89 | int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type); | ||
90 | int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)); | ||
91 | |||
92 | int do_prog(int argc, char **arg); | ||
93 | int do_map(int argc, char **arg); | ||
94 | |||
95 | int prog_parse_fd(int *argc, char ***argv); | ||
96 | |||
97 | void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); | ||
98 | |||
99 | #endif | ||
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c new file mode 100644 index 000000000000..0528a5379e6c --- /dev/null +++ b/tools/bpf/bpftool/map.c | |||
@@ -0,0 +1,744 @@ | |||
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 <assert.h> | ||
37 | #include <ctype.h> | ||
38 | #include <errno.h> | ||
39 | #include <fcntl.h> | ||
40 | #include <stdbool.h> | ||
41 | #include <stdio.h> | ||
42 | #include <stdlib.h> | ||
43 | #include <string.h> | ||
44 | #include <unistd.h> | ||
45 | #include <sys/types.h> | ||
46 | #include <sys/stat.h> | ||
47 | |||
48 | #include <bpf.h> | ||
49 | |||
50 | #include "main.h" | ||
51 | |||
52 | static const char * const map_type_name[] = { | ||
53 | [BPF_MAP_TYPE_UNSPEC] = "unspec", | ||
54 | [BPF_MAP_TYPE_HASH] = "hash", | ||
55 | [BPF_MAP_TYPE_ARRAY] = "array", | ||
56 | [BPF_MAP_TYPE_PROG_ARRAY] = "prog_array", | ||
57 | [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array", | ||
58 | [BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash", | ||
59 | [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array", | ||
60 | [BPF_MAP_TYPE_STACK_TRACE] = "stack_trace", | ||
61 | [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array", | ||
62 | [BPF_MAP_TYPE_LRU_HASH] = "lru_hash", | ||
63 | [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash", | ||
64 | [BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie", | ||
65 | [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps", | ||
66 | [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps", | ||
67 | [BPF_MAP_TYPE_DEVMAP] = "devmap", | ||
68 | [BPF_MAP_TYPE_SOCKMAP] = "sockmap", | ||
69 | }; | ||
70 | |||
71 | static unsigned int get_possible_cpus(void) | ||
72 | { | ||
73 | static unsigned int result; | ||
74 | char buf[128]; | ||
75 | long int n; | ||
76 | char *ptr; | ||
77 | int fd; | ||
78 | |||
79 | if (result) | ||
80 | return result; | ||
81 | |||
82 | fd = open("/sys/devices/system/cpu/possible", O_RDONLY); | ||
83 | if (fd < 0) { | ||
84 | err("can't open sysfs possible cpus\n"); | ||
85 | exit(-1); | ||
86 | } | ||
87 | |||
88 | n = read(fd, buf, sizeof(buf)); | ||
89 | if (n < 2) { | ||
90 | err("can't read sysfs possible cpus\n"); | ||
91 | exit(-1); | ||
92 | } | ||
93 | close(fd); | ||
94 | |||
95 | if (n == sizeof(buf)) { | ||
96 | err("read sysfs possible cpus overflow\n"); | ||
97 | exit(-1); | ||
98 | } | ||
99 | |||
100 | ptr = buf; | ||
101 | n = 0; | ||
102 | while (*ptr && *ptr != '\n') { | ||
103 | unsigned int a, b; | ||
104 | |||
105 | if (sscanf(ptr, "%u-%u", &a, &b) == 2) { | ||
106 | n += b - a + 1; | ||
107 | |||
108 | ptr = strchr(ptr, '-') + 1; | ||
109 | } else if (sscanf(ptr, "%u", &a) == 1) { | ||
110 | n++; | ||
111 | } else { | ||
112 | assert(0); | ||
113 | } | ||
114 | |||
115 | while (isdigit(*ptr)) | ||
116 | ptr++; | ||
117 | if (*ptr == ',') | ||
118 | ptr++; | ||
119 | } | ||
120 | |||
121 | result = n; | ||
122 | |||
123 | return result; | ||
124 | } | ||
125 | |||
126 | static bool map_is_per_cpu(__u32 type) | ||
127 | { | ||
128 | return type == BPF_MAP_TYPE_PERCPU_HASH || | ||
129 | type == BPF_MAP_TYPE_PERCPU_ARRAY || | ||
130 | type == BPF_MAP_TYPE_LRU_PERCPU_HASH; | ||
131 | } | ||
132 | |||
133 | static bool map_is_map_of_maps(__u32 type) | ||
134 | { | ||
135 | return type == BPF_MAP_TYPE_ARRAY_OF_MAPS || | ||
136 | type == BPF_MAP_TYPE_HASH_OF_MAPS; | ||
137 | } | ||
138 | |||
139 | static bool map_is_map_of_progs(__u32 type) | ||
140 | { | ||
141 | return type == BPF_MAP_TYPE_PROG_ARRAY; | ||
142 | } | ||
143 | |||
144 | static void *alloc_value(struct bpf_map_info *info) | ||
145 | { | ||
146 | if (map_is_per_cpu(info->type)) | ||
147 | return malloc(info->value_size * get_possible_cpus()); | ||
148 | else | ||
149 | return malloc(info->value_size); | ||
150 | } | ||
151 | |||
152 | static int map_parse_fd(int *argc, char ***argv) | ||
153 | { | ||
154 | int fd; | ||
155 | |||
156 | if (is_prefix(**argv, "id")) { | ||
157 | unsigned int id; | ||
158 | char *endptr; | ||
159 | |||
160 | NEXT_ARGP(); | ||
161 | |||
162 | id = strtoul(**argv, &endptr, 0); | ||
163 | if (*endptr) { | ||
164 | err("can't parse %s as ID\n", **argv); | ||
165 | return -1; | ||
166 | } | ||
167 | NEXT_ARGP(); | ||
168 | |||
169 | fd = bpf_map_get_fd_by_id(id); | ||
170 | if (fd < 0) | ||
171 | err("get map by id (%u): %s\n", id, strerror(errno)); | ||
172 | return fd; | ||
173 | } else if (is_prefix(**argv, "pinned")) { | ||
174 | char *path; | ||
175 | |||
176 | NEXT_ARGP(); | ||
177 | |||
178 | path = **argv; | ||
179 | NEXT_ARGP(); | ||
180 | |||
181 | return open_obj_pinned_any(path, BPF_OBJ_MAP); | ||
182 | } | ||
183 | |||
184 | err("expected 'id' or 'pinned', got: '%s'?\n", **argv); | ||
185 | return -1; | ||
186 | } | ||
187 | |||
188 | static int | ||
189 | map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) | ||
190 | { | ||
191 | int err; | ||
192 | int fd; | ||
193 | |||
194 | fd = map_parse_fd(argc, argv); | ||
195 | if (fd < 0) | ||
196 | return -1; | ||
197 | |||
198 | err = bpf_obj_get_info_by_fd(fd, info, info_len); | ||
199 | if (err) { | ||
200 | err("can't get map info: %s\n", strerror(errno)); | ||
201 | close(fd); | ||
202 | return err; | ||
203 | } | ||
204 | |||
205 | return fd; | ||
206 | } | ||
207 | |||
208 | static void print_entry(struct bpf_map_info *info, unsigned char *key, | ||
209 | unsigned char *value) | ||
210 | { | ||
211 | if (!map_is_per_cpu(info->type)) { | ||
212 | bool single_line, break_names; | ||
213 | |||
214 | break_names = info->key_size > 16 || info->value_size > 16; | ||
215 | single_line = info->key_size + info->value_size <= 24 && | ||
216 | !break_names; | ||
217 | |||
218 | printf("key:%c", break_names ? '\n' : ' '); | ||
219 | print_hex(key, info->key_size, " "); | ||
220 | |||
221 | printf(single_line ? " " : "\n"); | ||
222 | |||
223 | printf("value:%c", break_names ? '\n' : ' '); | ||
224 | print_hex(value, info->value_size, " "); | ||
225 | |||
226 | printf("\n"); | ||
227 | } else { | ||
228 | unsigned int i, n; | ||
229 | |||
230 | n = get_possible_cpus(); | ||
231 | |||
232 | printf("key:\n"); | ||
233 | print_hex(key, info->key_size, " "); | ||
234 | printf("\n"); | ||
235 | for (i = 0; i < n; i++) { | ||
236 | printf("value (CPU %02d):%c", | ||
237 | i, info->value_size > 16 ? '\n' : ' '); | ||
238 | print_hex(value + i * info->value_size, | ||
239 | info->value_size, " "); | ||
240 | printf("\n"); | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | static char **parse_bytes(char **argv, const char *name, unsigned char *val, | ||
246 | unsigned int n) | ||
247 | { | ||
248 | unsigned int i = 0; | ||
249 | char *endptr; | ||
250 | |||
251 | while (i < n && argv[i]) { | ||
252 | val[i] = strtoul(argv[i], &endptr, 0); | ||
253 | if (*endptr) { | ||
254 | err("error parsing byte: %s\n", argv[i]); | ||
255 | break; | ||
256 | } | ||
257 | i++; | ||
258 | } | ||
259 | |||
260 | if (i != n) { | ||
261 | err("%s expected %d bytes got %d\n", name, n, i); | ||
262 | return NULL; | ||
263 | } | ||
264 | |||
265 | return argv + i; | ||
266 | } | ||
267 | |||
268 | static int parse_elem(char **argv, struct bpf_map_info *info, | ||
269 | void *key, void *value, __u32 key_size, __u32 value_size, | ||
270 | __u32 *flags, __u32 **value_fd) | ||
271 | { | ||
272 | if (!*argv) { | ||
273 | if (!key && !value) | ||
274 | return 0; | ||
275 | err("did not find %s\n", key ? "key" : "value"); | ||
276 | return -1; | ||
277 | } | ||
278 | |||
279 | if (is_prefix(*argv, "key")) { | ||
280 | if (!key) { | ||
281 | if (key_size) | ||
282 | err("duplicate key\n"); | ||
283 | else | ||
284 | err("unnecessary key\n"); | ||
285 | return -1; | ||
286 | } | ||
287 | |||
288 | argv = parse_bytes(argv + 1, "key", key, key_size); | ||
289 | if (!argv) | ||
290 | return -1; | ||
291 | |||
292 | return parse_elem(argv, info, NULL, value, key_size, value_size, | ||
293 | flags, value_fd); | ||
294 | } else if (is_prefix(*argv, "value")) { | ||
295 | int fd; | ||
296 | |||
297 | if (!value) { | ||
298 | if (value_size) | ||
299 | err("duplicate value\n"); | ||
300 | else | ||
301 | err("unnecessary value\n"); | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | argv++; | ||
306 | |||
307 | if (map_is_map_of_maps(info->type)) { | ||
308 | int argc = 2; | ||
309 | |||
310 | if (value_size != 4) { | ||
311 | err("value smaller than 4B for map in map?\n"); | ||
312 | return -1; | ||
313 | } | ||
314 | if (!argv[0] || !argv[1]) { | ||
315 | err("not enough value arguments for map in map\n"); | ||
316 | return -1; | ||
317 | } | ||
318 | |||
319 | fd = map_parse_fd(&argc, &argv); | ||
320 | if (fd < 0) | ||
321 | return -1; | ||
322 | |||
323 | *value_fd = value; | ||
324 | **value_fd = fd; | ||
325 | } else if (map_is_map_of_progs(info->type)) { | ||
326 | int argc = 2; | ||
327 | |||
328 | if (value_size != 4) { | ||
329 | err("value smaller than 4B for map of progs?\n"); | ||
330 | return -1; | ||
331 | } | ||
332 | if (!argv[0] || !argv[1]) { | ||
333 | err("not enough value arguments for map of progs\n"); | ||
334 | return -1; | ||
335 | } | ||
336 | |||
337 | fd = prog_parse_fd(&argc, &argv); | ||
338 | if (fd < 0) | ||
339 | return -1; | ||
340 | |||
341 | *value_fd = value; | ||
342 | **value_fd = fd; | ||
343 | } else { | ||
344 | argv = parse_bytes(argv, "value", value, value_size); | ||
345 | if (!argv) | ||
346 | return -1; | ||
347 | } | ||
348 | |||
349 | return parse_elem(argv, info, key, NULL, key_size, value_size, | ||
350 | flags, NULL); | ||
351 | } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || | ||
352 | is_prefix(*argv, "exist")) { | ||
353 | if (!flags) { | ||
354 | err("flags specified multiple times: %s\n", *argv); | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | if (is_prefix(*argv, "any")) | ||
359 | *flags = BPF_ANY; | ||
360 | else if (is_prefix(*argv, "noexist")) | ||
361 | *flags = BPF_NOEXIST; | ||
362 | else if (is_prefix(*argv, "exist")) | ||
363 | *flags = BPF_EXIST; | ||
364 | |||
365 | return parse_elem(argv + 1, info, key, value, key_size, | ||
366 | value_size, NULL, value_fd); | ||
367 | } | ||
368 | |||
369 | err("expected key or value, got: %s\n", *argv); | ||
370 | return -1; | ||
371 | } | ||
372 | |||
373 | static int show_map_close(int fd, struct bpf_map_info *info) | ||
374 | { | ||
375 | char *memlock; | ||
376 | |||
377 | memlock = get_fdinfo(fd, "memlock"); | ||
378 | close(fd); | ||
379 | |||
380 | printf("%u: ", info->id); | ||
381 | if (info->type < ARRAY_SIZE(map_type_name)) | ||
382 | printf("%s ", map_type_name[info->type]); | ||
383 | else | ||
384 | printf("type %u ", info->type); | ||
385 | |||
386 | if (*info->name) | ||
387 | printf("name %s ", info->name); | ||
388 | |||
389 | printf("flags 0x%x\n", info->map_flags); | ||
390 | printf("\tkey %uB value %uB max_entries %u", | ||
391 | info->key_size, info->value_size, info->max_entries); | ||
392 | |||
393 | if (memlock) | ||
394 | printf(" memlock %sB", memlock); | ||
395 | free(memlock); | ||
396 | |||
397 | printf("\n"); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int do_show(int argc, char **argv) | ||
403 | { | ||
404 | struct bpf_map_info info = {}; | ||
405 | __u32 len = sizeof(info); | ||
406 | __u32 id = 0; | ||
407 | int err; | ||
408 | int fd; | ||
409 | |||
410 | if (argc == 2) { | ||
411 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | ||
412 | if (fd < 0) | ||
413 | return -1; | ||
414 | |||
415 | return show_map_close(fd, &info); | ||
416 | } | ||
417 | |||
418 | if (argc) | ||
419 | return BAD_ARG(); | ||
420 | |||
421 | while (true) { | ||
422 | err = bpf_map_get_next_id(id, &id); | ||
423 | if (err) { | ||
424 | if (errno == ENOENT) | ||
425 | break; | ||
426 | err("can't get next map: %s\n", strerror(errno)); | ||
427 | if (errno == EINVAL) | ||
428 | err("kernel too old?\n"); | ||
429 | return -1; | ||
430 | } | ||
431 | |||
432 | fd = bpf_map_get_fd_by_id(id); | ||
433 | if (fd < 0) { | ||
434 | err("can't get map by id (%u): %s\n", | ||
435 | id, strerror(errno)); | ||
436 | return -1; | ||
437 | } | ||
438 | |||
439 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
440 | if (err) { | ||
441 | err("can't get map info: %s\n", strerror(errno)); | ||
442 | close(fd); | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | show_map_close(fd, &info); | ||
447 | } | ||
448 | |||
449 | return errno == ENOENT ? 0 : -1; | ||
450 | } | ||
451 | |||
452 | static int do_dump(int argc, char **argv) | ||
453 | { | ||
454 | void *key, *value, *prev_key; | ||
455 | unsigned int num_elems = 0; | ||
456 | struct bpf_map_info info = {}; | ||
457 | __u32 len = sizeof(info); | ||
458 | int err; | ||
459 | int fd; | ||
460 | |||
461 | if (argc != 2) | ||
462 | usage(); | ||
463 | |||
464 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | ||
465 | if (fd < 0) | ||
466 | return -1; | ||
467 | |||
468 | if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) { | ||
469 | err("Dumping maps of maps and program maps not supported\n"); | ||
470 | close(fd); | ||
471 | return -1; | ||
472 | } | ||
473 | |||
474 | key = malloc(info.key_size); | ||
475 | value = alloc_value(&info); | ||
476 | if (!key || !value) { | ||
477 | err("mem alloc failed\n"); | ||
478 | err = -1; | ||
479 | goto exit_free; | ||
480 | } | ||
481 | |||
482 | prev_key = NULL; | ||
483 | while (true) { | ||
484 | err = bpf_map_get_next_key(fd, prev_key, key); | ||
485 | if (err) { | ||
486 | if (errno == ENOENT) | ||
487 | err = 0; | ||
488 | break; | ||
489 | } | ||
490 | |||
491 | if (!bpf_map_lookup_elem(fd, key, value)) { | ||
492 | print_entry(&info, key, value); | ||
493 | } else { | ||
494 | info("can't lookup element with key: "); | ||
495 | print_hex(key, info.key_size, " "); | ||
496 | printf("\n"); | ||
497 | } | ||
498 | |||
499 | prev_key = key; | ||
500 | num_elems++; | ||
501 | } | ||
502 | |||
503 | printf("Found %u element%s\n", num_elems, num_elems != 1 ? "s" : ""); | ||
504 | |||
505 | exit_free: | ||
506 | free(key); | ||
507 | free(value); | ||
508 | close(fd); | ||
509 | |||
510 | return err; | ||
511 | } | ||
512 | |||
513 | static int do_update(int argc, char **argv) | ||
514 | { | ||
515 | struct bpf_map_info info = {}; | ||
516 | __u32 len = sizeof(info); | ||
517 | __u32 *value_fd = NULL; | ||
518 | __u32 flags = BPF_ANY; | ||
519 | void *key, *value; | ||
520 | int fd, err; | ||
521 | |||
522 | if (argc < 2) | ||
523 | usage(); | ||
524 | |||
525 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | ||
526 | if (fd < 0) | ||
527 | return -1; | ||
528 | |||
529 | key = malloc(info.key_size); | ||
530 | value = alloc_value(&info); | ||
531 | if (!key || !value) { | ||
532 | err("mem alloc failed"); | ||
533 | err = -1; | ||
534 | goto exit_free; | ||
535 | } | ||
536 | |||
537 | err = parse_elem(argv, &info, key, value, info.key_size, | ||
538 | info.value_size, &flags, &value_fd); | ||
539 | if (err) | ||
540 | goto exit_free; | ||
541 | |||
542 | err = bpf_map_update_elem(fd, key, value, flags); | ||
543 | if (err) { | ||
544 | err("update failed: %s\n", strerror(errno)); | ||
545 | goto exit_free; | ||
546 | } | ||
547 | |||
548 | exit_free: | ||
549 | if (value_fd) | ||
550 | close(*value_fd); | ||
551 | free(key); | ||
552 | free(value); | ||
553 | close(fd); | ||
554 | |||
555 | return err; | ||
556 | } | ||
557 | |||
558 | static int do_lookup(int argc, char **argv) | ||
559 | { | ||
560 | struct bpf_map_info info = {}; | ||
561 | __u32 len = sizeof(info); | ||
562 | void *key, *value; | ||
563 | int err; | ||
564 | int fd; | ||
565 | |||
566 | if (argc < 2) | ||
567 | usage(); | ||
568 | |||
569 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | ||
570 | if (fd < 0) | ||
571 | return -1; | ||
572 | |||
573 | key = malloc(info.key_size); | ||
574 | value = alloc_value(&info); | ||
575 | if (!key || !value) { | ||
576 | err("mem alloc failed"); | ||
577 | err = -1; | ||
578 | goto exit_free; | ||
579 | } | ||
580 | |||
581 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); | ||
582 | if (err) | ||
583 | goto exit_free; | ||
584 | |||
585 | err = bpf_map_lookup_elem(fd, key, value); | ||
586 | if (!err) { | ||
587 | print_entry(&info, key, value); | ||
588 | } else if (errno == ENOENT) { | ||
589 | printf("key:\n"); | ||
590 | print_hex(key, info.key_size, " "); | ||
591 | printf("\n\nNot found\n"); | ||
592 | } else { | ||
593 | err("lookup failed: %s\n", strerror(errno)); | ||
594 | } | ||
595 | |||
596 | exit_free: | ||
597 | free(key); | ||
598 | free(value); | ||
599 | close(fd); | ||
600 | |||
601 | return err; | ||
602 | } | ||
603 | |||
604 | static int do_getnext(int argc, char **argv) | ||
605 | { | ||
606 | struct bpf_map_info info = {}; | ||
607 | __u32 len = sizeof(info); | ||
608 | void *key, *nextkey; | ||
609 | int err; | ||
610 | int fd; | ||
611 | |||
612 | if (argc < 2) | ||
613 | usage(); | ||
614 | |||
615 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | ||
616 | if (fd < 0) | ||
617 | return -1; | ||
618 | |||
619 | key = malloc(info.key_size); | ||
620 | nextkey = malloc(info.key_size); | ||
621 | if (!key || !nextkey) { | ||
622 | err("mem alloc failed"); | ||
623 | err = -1; | ||
624 | goto exit_free; | ||
625 | } | ||
626 | |||
627 | if (argc) { | ||
628 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, | ||
629 | NULL, NULL); | ||
630 | if (err) | ||
631 | goto exit_free; | ||
632 | } else { | ||
633 | free(key); | ||
634 | key = NULL; | ||
635 | } | ||
636 | |||
637 | err = bpf_map_get_next_key(fd, key, nextkey); | ||
638 | if (err) { | ||
639 | err("can't get next key: %s\n", strerror(errno)); | ||
640 | goto exit_free; | ||
641 | } | ||
642 | |||
643 | if (key) { | ||
644 | printf("key:\n"); | ||
645 | print_hex(key, info.key_size, " "); | ||
646 | printf("\n"); | ||
647 | } else { | ||
648 | printf("key: None\n"); | ||
649 | } | ||
650 | |||
651 | printf("next key:\n"); | ||
652 | print_hex(nextkey, info.key_size, " "); | ||
653 | printf("\n"); | ||
654 | |||
655 | exit_free: | ||
656 | free(nextkey); | ||
657 | free(key); | ||
658 | close(fd); | ||
659 | |||
660 | return err; | ||
661 | } | ||
662 | |||
663 | static int do_delete(int argc, char **argv) | ||
664 | { | ||
665 | struct bpf_map_info info = {}; | ||
666 | __u32 len = sizeof(info); | ||
667 | void *key; | ||
668 | int err; | ||
669 | int fd; | ||
670 | |||
671 | if (argc < 2) | ||
672 | usage(); | ||
673 | |||
674 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | ||
675 | if (fd < 0) | ||
676 | return -1; | ||
677 | |||
678 | key = malloc(info.key_size); | ||
679 | if (!key) { | ||
680 | err("mem alloc failed"); | ||
681 | err = -1; | ||
682 | goto exit_free; | ||
683 | } | ||
684 | |||
685 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); | ||
686 | if (err) | ||
687 | goto exit_free; | ||
688 | |||
689 | err = bpf_map_delete_elem(fd, key); | ||
690 | if (err) | ||
691 | err("delete failed: %s\n", strerror(errno)); | ||
692 | |||
693 | exit_free: | ||
694 | free(key); | ||
695 | close(fd); | ||
696 | |||
697 | return err; | ||
698 | } | ||
699 | |||
700 | static int do_pin(int argc, char **argv) | ||
701 | { | ||
702 | return do_pin_any(argc, argv, bpf_map_get_fd_by_id); | ||
703 | } | ||
704 | |||
705 | static int do_help(int argc, char **argv) | ||
706 | { | ||
707 | fprintf(stderr, | ||
708 | "Usage: %s %s show [MAP]\n" | ||
709 | " %s %s dump MAP\n" | ||
710 | " %s %s update MAP key BYTES value VALUE [UPDATE_FLAGS]\n" | ||
711 | " %s %s lookup MAP key BYTES\n" | ||
712 | " %s %s getnext MAP [key BYTES]\n" | ||
713 | " %s %s delete MAP key BYTES\n" | ||
714 | " %s %s pin MAP FILE\n" | ||
715 | " %s %s help\n" | ||
716 | "\n" | ||
717 | " MAP := { id MAP_ID | pinned FILE }\n" | ||
718 | " " HELP_SPEC_PROGRAM "\n" | ||
719 | " VALUE := { BYTES | MAP | PROG }\n" | ||
720 | " UPDATE_FLAGS := { any | exist | noexist }\n" | ||
721 | "", | ||
722 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | ||
723 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | ||
724 | bin_name, argv[-2], bin_name, argv[-2]); | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static const struct cmd cmds[] = { | ||
730 | { "show", do_show }, | ||
731 | { "help", do_help }, | ||
732 | { "dump", do_dump }, | ||
733 | { "update", do_update }, | ||
734 | { "lookup", do_lookup }, | ||
735 | { "getnext", do_getnext }, | ||
736 | { "delete", do_delete }, | ||
737 | { "pin", do_pin }, | ||
738 | { 0 } | ||
739 | }; | ||
740 | |||
741 | int do_map(int argc, char **argv) | ||
742 | { | ||
743 | return cmd_select(cmds, argc, argv, do_help); | ||
744 | } | ||
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 | } | ||