aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--tools/bpf/Makefile18
-rw-r--r--tools/bpf/bpftool/Makefile80
-rw-r--r--tools/bpf/bpftool/common.c216
-rw-r--r--tools/bpf/bpftool/jit_disasm.c87
-rw-r--r--tools/bpf/bpftool/main.c212
-rw-r--r--tools/bpf/bpftool/main.h99
-rw-r--r--tools/bpf/bpftool/map.c744
-rw-r--r--tools/bpf/bpftool/prog.c456
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
3CC = gcc 3CC = gcc
4LEX = flex 4LEX = flex
5YACC = bison 5YACC = bison
6MAKE = make
6 7
7CFLAGS += -Wall -O2 8CFLAGS += -Wall -O2
8CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include 9CFLAGS += -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
16all : bpf_jit_disasm bpf_dbg bpf_asm 17all: bpf_jit_disasm bpf_dbg bpf_asm bpftool
17 18
18bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm' 19bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'
19bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl 20bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
@@ -26,10 +27,21 @@ bpf_asm : LDLIBS =
26bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o 27bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
27bpf_exp.lex.o : bpf_exp.yacc.c 28bpf_exp.lex.o : bpf_exp.yacc.c
28 29
29clean : 30clean: 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
32install : 33install: 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
38bpftool:
39 $(MAKE) -C bpftool
40
41bpftool_install:
42 $(MAKE) -C bpftool install
43
44bpftool_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 @@
1include ../../scripts/Makefile.include
2
3include ../../scripts/utilities.mak
4
5ifeq ($(srctree),)
6srctree := $(patsubst %/,%,$(dir $(CURDIR)))
7srctree := $(patsubst %/,%,$(dir $(srctree)))
8srctree := $(patsubst %/,%,$(dir $(srctree)))
9#$(info Determined 'srctree' to be $(srctree))
10endif
11
12ifneq ($(objtree),)
13#$(info Determined 'objtree' to be $(objtree))
14endif
15
16ifneq ($(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.
21VPATH += $(OUTPUT)
22export VPATH
23endif
24
25ifeq ($(V),1)
26 Q =
27else
28 Q = @
29endif
30
31BPF_DIR = $(srctree)/tools/lib/bpf/
32
33ifneq ($(OUTPUT),)
34 BPF_PATH=$(OUTPUT)
35else
36 BPF_PATH=$(BPF_DIR)
37endif
38
39LIBBPF = $(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
48prefix = /usr
49
50CC = gcc
51
52CFLAGS += -O2
53CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow
54CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf
55LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
56
57include $(wildcard *.d)
58
59all: $(OUTPUT)bpftool
60
61SRCS=$(wildcard *.c)
62OBJS=$(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
70clean: $(LIBBPF)-clean
71 $(call QUIET_CLEAN, bpftool)
72 $(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
73
74install:
75 install $(OUTPUT)bpftool $(prefix)/sbin/bpftool
76
77FORCE:
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
52static 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
62int 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
90int 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
132const 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
146int 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
172char *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
24static 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
42void 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
48const char *bin_name;
49static int last_argc;
50static char **last_argv;
51static int (*last_do_help)(int argc, char **argv);
52
53void usage(void)
54{
55 last_do_help(last_argc - 1, last_argv + 1);
56
57 exit(-1);
58}
59
60static 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
72int 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
93bool 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
103void 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
124static int do_batch(int argc, char **argv);
125
126static const struct cmd cmds[] = {
127 { "help", do_help },
128 { "batch", do_batch },
129 { "prog", do_prog },
130 { "map", do_map },
131 { 0 }
132};
133
134static 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 }
198err_close:
199 fclose(fp);
200
201 return err;
202}
203
204int 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
66enum bpf_obj_type {
67 BPF_OBJ_UNKNOWN,
68 BPF_OBJ_PROG,
69 BPF_OBJ_MAP,
70};
71
72extern const char *bin_name;
73
74bool is_prefix(const char *pfx, const char *str);
75void print_hex(void *arg, unsigned int n, const char *sep);
76void usage(void) __attribute__((noreturn));
77
78struct cmd {
79 const char *cmd;
80 int (*func)(int argc, char **argv);
81};
82
83int cmd_select(const struct cmd *cmds, int argc, char **argv,
84 int (*help)(int argc, char **argv));
85
86int get_fd_type(int fd);
87const char *get_fd_type_name(enum bpf_obj_type type);
88char *get_fdinfo(int fd, const char *key);
89int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);
90int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));
91
92int do_prog(int argc, char **arg);
93int do_map(int argc, char **arg);
94
95int prog_parse_fd(int *argc, char ***argv);
96
97void 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
52static 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
71static 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
126static 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
133static 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
139static bool map_is_map_of_progs(__u32 type)
140{
141 return type == BPF_MAP_TYPE_PROG_ARRAY;
142}
143
144static 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
152static 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
188static int
189map_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
208static 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
245static 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
268static 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
373static 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
402static 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
452static 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
505exit_free:
506 free(key);
507 free(value);
508 close(fd);
509
510 return err;
511}
512
513static 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
548exit_free:
549 if (value_fd)
550 close(*value_fd);
551 free(key);
552 free(value);
553 close(fd);
554
555 return err;
556}
557
558static 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
596exit_free:
597 free(key);
598 free(value);
599 close(fd);
600
601 return err;
602}
603
604static 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
655exit_free:
656 free(nextkey);
657 free(key);
658 close(fd);
659
660 return err;
661}
662
663static 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
693exit_free:
694 free(key);
695 close(fd);
696
697 return err;
698}
699
700static int do_pin(int argc, char **argv)
701{
702 return do_pin_any(argc, argv, bpf_map_get_fd_by_id);
703}
704
705static 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
729static 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
741int 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
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}