From 71bb428fe2c19512ac671d5ee16ef3e73e1b49a8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 4 Oct 2017 20:10:04 -0700 Subject: tools: bpf: add bpftool Add a simple tool for querying and updating BPF objects on the system. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 216 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 tools/bpf/bpftool/common.c (limited to 'tools/bpf/bpftool/common.c') 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 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Author: Jakub Kicinski */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" + +static bool is_bpffs(char *path) +{ + struct statfs st_fs; + + if (statfs(path, &st_fs) < 0) + return false; + + return (unsigned long)st_fs.f_type == BPF_FS_MAGIC; +} + +int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) +{ + enum bpf_obj_type type; + int fd; + + fd = bpf_obj_get(path); + if (fd < 0) { + err("bpf obj get (%s): %s\n", path, + errno == EACCES && !is_bpffs(dirname(path)) ? + "directory not in bpf file system (bpffs)" : + strerror(errno)); + return -1; + } + + type = get_fd_type(fd); + if (type < 0) { + close(fd); + return type; + } + if (type != exp_type) { + err("incorrect object type: %s\n", get_fd_type_name(type)); + close(fd); + return -1; + } + + return fd; +} + +int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) +{ + unsigned int id; + char *endptr; + int err; + int fd; + + if (!is_prefix(*argv, "id")) { + err("expected 'id' got %s\n", *argv); + return -1; + } + NEXT_ARG(); + + id = strtoul(*argv, &endptr, 0); + if (*endptr) { + err("can't parse %s as ID\n", *argv); + return -1; + } + NEXT_ARG(); + + if (argc != 1) + usage(); + + fd = get_fd_by_id(id); + if (fd < 0) { + err("can't get prog by id (%u): %s\n", id, strerror(errno)); + return -1; + } + + err = bpf_obj_pin(fd, *argv); + close(fd); + if (err) { + err("can't pin the object (%s): %s\n", *argv, + errno == EACCES && !is_bpffs(dirname(*argv)) ? + "directory not in bpf file system (bpffs)" : + strerror(errno)); + return -1; + } + + return 0; +} + +const char *get_fd_type_name(enum bpf_obj_type type) +{ + static const char * const names[] = { + [BPF_OBJ_UNKNOWN] = "unknown", + [BPF_OBJ_PROG] = "prog", + [BPF_OBJ_MAP] = "map", + }; + + if (type < 0 || type >= ARRAY_SIZE(names) || !names[type]) + return names[BPF_OBJ_UNKNOWN]; + + return names[type]; +} + +int get_fd_type(int fd) +{ + char path[PATH_MAX]; + char buf[512]; + ssize_t n; + + snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd); + + n = readlink(path, buf, sizeof(buf)); + if (n < 0) { + err("can't read link type: %s\n", strerror(errno)); + return -1; + } + if (n == sizeof(path)) { + err("can't read link type: path too long!\n"); + return -1; + } + + if (strstr(buf, "bpf-map")) + return BPF_OBJ_MAP; + else if (strstr(buf, "bpf-prog")) + return BPF_OBJ_PROG; + + return BPF_OBJ_UNKNOWN; +} + +char *get_fdinfo(int fd, const char *key) +{ + char path[PATH_MAX]; + char *line = NULL; + size_t line_n = 0; + ssize_t n; + FILE *fdi; + + snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd); + + fdi = fopen(path, "r"); + if (!fdi) { + err("can't open fdinfo: %s\n", strerror(errno)); + return NULL; + } + + while ((n = getline(&line, &line_n, fdi))) { + char *value; + int len; + + if (!strstr(line, key)) + continue; + + fclose(fdi); + + value = strchr(line, '\t'); + if (!value || !value[1]) { + err("malformed fdinfo!?\n"); + free(line); + return NULL; + } + value++; + + len = strlen(value); + memmove(line, value, len); + line[len - 1] = '\0'; + + return line; + } + + err("key '%s' not found in fdinfo\n", key); + free(line); + fclose(fdi); + return NULL; +} -- cgit v1.2.3 From f05e2c32f715985f54265b1e237b5cce1b576c71 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:10 -0700 Subject: tools: bpftool: add JSON output for `bpftool prog dump xlated *` command Add a new printing function to dump translated eBPF instructions as JSON. As for plain output, opcodes are printed only on request (when `opcodes` is provided on the command line). The disassembled output is generated by the same code that is used by the kernel verifier. Example output: $ bpftool --json --pretty prog dump xlated id 1 [{ "disasm": "(bf) r6 = r1" },{ "disasm": "(61) r7 = *(u32 *)(r6 +16)" },{ "disasm": "(95) exit" } ] $ bpftool --json --pretty prog dump xlated id 1 opcodes [{ "disasm": "(bf) r6 = r1", "opcodes": { "code": "0xbf", "src_reg": "0x1", "dst_reg": "0x6", "off": ["0x00","0x00" ], "imm": ["0x00","0x00","0x00","0x00" ] } },{ "disasm": "(61) r7 = *(u32 *)(r6 +16)", "opcodes": { "code": "0x61", "src_reg": "0x6", "dst_reg": "0x7", "off": ["0x10","0x00" ], "imm": ["0x00","0x00","0x00","0x00" ] } },{ "disasm": "(95) exit", "opcodes": { "code": "0x95", "src_reg": "0x0", "dst_reg": "0x0", "off": ["0x00","0x00" ], "imm": ["0x00","0x00","0x00","0x00" ] } } ] Signed-off-by: Quentin Monnet Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 10 ++++++ tools/bpf/bpftool/json_writer.c | 8 +++++ tools/bpf/bpftool/json_writer.h | 2 ++ tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/prog.c | 70 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 2 deletions(-) (limited to 'tools/bpf/bpftool/common.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index df8396a0c400..e7a756b8ee21 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -214,3 +214,13 @@ char *get_fdinfo(int fd, const char *key) fclose(fdi); return NULL; } + +void print_hex_data_json(uint8_t *data, size_t len) +{ + unsigned int i; + + jsonw_start_array(json_wtr); + for (i = 0; i < len; i++) + jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]); + jsonw_end_array(json_wtr); +} diff --git a/tools/bpf/bpftool/json_writer.c b/tools/bpf/bpftool/json_writer.c index 6b77d288cce2..c6eef76322ae 100644 --- a/tools/bpf/bpftool/json_writer.c +++ b/tools/bpf/bpftool/json_writer.c @@ -156,6 +156,14 @@ void jsonw_name(json_writer_t *self, const char *name) putc(' ', self->out); } +void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap) +{ + jsonw_eor(self); + putc('"', self->out); + vfprintf(self->out, fmt, ap); + putc('"', self->out); +} + void jsonw_printf(json_writer_t *self, const char *fmt, ...) { va_list ap; diff --git a/tools/bpf/bpftool/json_writer.h b/tools/bpf/bpftool/json_writer.h index 1516aafba59d..0fa2fb1b6351 100644 --- a/tools/bpf/bpftool/json_writer.h +++ b/tools/bpf/bpftool/json_writer.h @@ -17,6 +17,7 @@ #include #include +#include /* Opaque class structure */ typedef struct json_writer json_writer_t; @@ -33,6 +34,7 @@ void jsonw_pretty(json_writer_t *self, bool on); void jsonw_name(json_writer_t *self, const char *name); /* Add value */ +void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap); void jsonw_printf(json_writer_t *self, const char *fmt, ...); void jsonw_string(json_writer_t *self, const char *value); void jsonw_bool(json_writer_t *self, bool value); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 15927fc9fb31..693fc9710be1 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -95,5 +95,6 @@ int do_map(int argc, char **arg); int prog_parse_fd(int *argc, char ***argv); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); +void print_hex_data_json(uint8_t *data, size_t len); #endif diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index f373f2baef5a..43e49799a624 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -385,7 +385,7 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) va_end(args); } -static void dump_xlated(void *buf, unsigned int len, bool opcodes) +static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) { struct bpf_insn *insn = buf; bool double_insn = false; @@ -414,6 +414,69 @@ static void dump_xlated(void *buf, unsigned int len, bool opcodes) } } +static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) +{ + unsigned int l = strlen(fmt); + char chomped_fmt[l]; + va_list args; + + va_start(args, fmt); + if (l > 0) { + strncpy(chomped_fmt, fmt, l - 1); + chomped_fmt[l - 1] = '\0'; + } + jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); + va_end(args); +} + +static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) +{ + struct bpf_insn *insn = buf; + bool double_insn = false; + unsigned int i; + + jsonw_start_array(json_wtr); + for (i = 0; i < len / sizeof(*insn); i++) { + if (double_insn) { + double_insn = false; + continue; + } + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); + + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "disasm"); + print_bpf_insn(print_insn_json, NULL, insn + i, true); + + if (opcodes) { + jsonw_name(json_wtr, "opcodes"); + jsonw_start_object(json_wtr); + + jsonw_name(json_wtr, "code"); + jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); + + jsonw_name(json_wtr, "src_reg"); + jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); + + jsonw_name(json_wtr, "dst_reg"); + jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); + + jsonw_name(json_wtr, "off"); + print_hex_data_json((uint8_t *)(&insn[i].off), 2); + + jsonw_name(json_wtr, "imm"); + if (double_insn && i < len - 1) + print_hex_data_json((uint8_t *)(&insn[i].imm), + 12); + else + print_hex_data_json((uint8_t *)(&insn[i].imm), + 4); + jsonw_end_object(json_wtr); + } + jsonw_end_object(json_wtr); + } + jsonw_end_array(json_wtr); +} + static int do_dump(int argc, char **argv) { struct bpf_prog_info info = {}; @@ -523,7 +586,10 @@ static int do_dump(int argc, char **argv) if (member_len == &info.jited_prog_len) disasm_print_insn(buf, *member_len, opcodes); else - dump_xlated(buf, *member_len, opcodes); + if (json_output) + dump_xlated_json(buf, *member_len, opcodes); + else + dump_xlated_plain(buf, *member_len, opcodes); } free(buf); -- cgit v1.2.3 From 9a5ab8bf1d6d16ef47fdf55dba1683ec00d751ad Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:13 -0700 Subject: tools: bpftool: turn err() and info() macros into functions Turn err() and info() macros into functions. In order to avoid naming conflicts with variables in the code, rename them as p_err() and p_info() respectively. The behavior of these functions is similar to the one of the macros for plain output. However, when JSON output is requested, these macros return a JSON-formatted "error" object instead of printing a message to stderr. To handle error messages correctly with JSON, a modification was brought to their behavior nonetheless: the functions now append a end-of-line character at the end of the message. This way, we can remove end-of-line characters at the end of the argument strings, and not have them in the JSON output. All error messages are formatted to hold in a single call to p_err(), in order to produce a single JSON field. Signed-off-by: Quentin Monnet Acked-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 26 +++++++------- tools/bpf/bpftool/main.c | 16 ++++----- tools/bpf/bpftool/main.h | 37 +++++++++++++++++--- tools/bpf/bpftool/map.c | 87 +++++++++++++++++++++++++--------------------- tools/bpf/bpftool/prog.c | 51 +++++++++++++-------------- 5 files changed, 126 insertions(+), 91 deletions(-) (limited to 'tools/bpf/bpftool/common.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index e7a756b8ee21..b2533f1cae3e 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -66,8 +66,8 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) fd = bpf_obj_get(path); if (fd < 0) { - err("bpf obj get (%s): %s\n", path, - errno == EACCES && !is_bpffs(dirname(path)) ? + p_err("bpf obj get (%s): %s", path, + errno == EACCES && !is_bpffs(dirname(path)) ? "directory not in bpf file system (bpffs)" : strerror(errno)); return -1; @@ -79,7 +79,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) return type; } if (type != exp_type) { - err("incorrect object type: %s\n", get_fd_type_name(type)); + p_err("incorrect object type: %s", get_fd_type_name(type)); close(fd); return -1; } @@ -95,14 +95,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) int fd; if (!is_prefix(*argv, "id")) { - err("expected 'id' got %s\n", *argv); + p_err("expected 'id' got %s", *argv); return -1; } NEXT_ARG(); id = strtoul(*argv, &endptr, 0); if (*endptr) { - err("can't parse %s as ID\n", *argv); + p_err("can't parse %s as ID", *argv); return -1; } NEXT_ARG(); @@ -112,15 +112,15 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) fd = get_fd_by_id(id); if (fd < 0) { - err("can't get prog by id (%u): %s\n", id, strerror(errno)); + p_err("can't get prog by id (%u): %s", id, strerror(errno)); return -1; } err = bpf_obj_pin(fd, *argv); close(fd); if (err) { - err("can't pin the object (%s): %s\n", *argv, - errno == EACCES && !is_bpffs(dirname(*argv)) ? + p_err("can't pin the object (%s): %s", *argv, + errno == EACCES && !is_bpffs(dirname(*argv)) ? "directory not in bpf file system (bpffs)" : strerror(errno)); return -1; @@ -153,11 +153,11 @@ int get_fd_type(int fd) n = readlink(path, buf, sizeof(buf)); if (n < 0) { - err("can't read link type: %s\n", strerror(errno)); + p_err("can't read link type: %s", strerror(errno)); return -1; } if (n == sizeof(path)) { - err("can't read link type: path too long!\n"); + p_err("can't read link type: path too long!"); return -1; } @@ -181,7 +181,7 @@ char *get_fdinfo(int fd, const char *key) fdi = fopen(path, "r"); if (!fdi) { - err("can't open fdinfo: %s\n", strerror(errno)); + p_err("can't open fdinfo: %s", strerror(errno)); return NULL; } @@ -196,7 +196,7 @@ char *get_fdinfo(int fd, const char *key) value = strchr(line, '\t'); if (!value || !value[1]) { - err("malformed fdinfo!?\n"); + p_err("malformed fdinfo!?"); free(line); return NULL; } @@ -209,7 +209,7 @@ char *get_fdinfo(int fd, const char *key) return line; } - err("key '%s' not found in fdinfo\n", key); + p_err("key '%s' not found in fdinfo", key); free(line); fclose(fdi); return NULL; diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 71b01bf73912..9989a77fdc4a 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -158,20 +158,20 @@ static int do_batch(int argc, char **argv) int i; if (argc < 2) { - err("too few parameters for batch\n"); + p_err("too few parameters for batch"); return -1; } else if (!is_prefix(*argv, "file")) { - err("expected 'file', got: %s\n", *argv); + p_err("expected 'file', got: %s", *argv); return -1; } else if (argc > 2) { - err("too many parameters for batch\n"); + p_err("too many parameters for batch"); return -1; } NEXT_ARG(); fp = fopen(*argv, "r"); if (!fp) { - err("Can't open file (%s): %s\n", *argv, strerror(errno)); + p_err("Can't open file (%s): %s", *argv, strerror(errno)); return -1; } @@ -189,8 +189,8 @@ static int do_batch(int argc, char **argv) while (n_argv[n_argc]) { n_argc++; if (n_argc == ARRAY_SIZE(n_argv)) { - err("line %d has too many arguments, skip\n", - lines); + p_err("line %d has too many arguments, skip", + lines); n_argc = 0; break; } @@ -225,7 +225,7 @@ static int do_batch(int argc, char **argv) perror("reading batch file failed"); err = -1; } else { - info("processed %d lines\n", lines); + p_info("processed %d lines", lines); err = 0; } err_close: @@ -279,7 +279,7 @@ int main(int argc, char **argv) if (json_output) { json_wtr = jsonw_new(stdout); if (!json_wtr) { - err("failed to create JSON writer\n"); + p_err("failed to create JSON writer"); return -1; } jsonw_pretty(json_wtr, pretty_output); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 693fc9710be1..04c88b55d8c7 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -45,15 +45,11 @@ #include "json_writer.h" -#define err(msg...) fprintf(stderr, "Error: " msg) -#define warn(msg...) fprintf(stderr, "Warning: " msg) -#define info(msg...) fprintf(stderr, msg) - #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) -#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) +#define BAD_ARG() ({ p_err("what is '%s'?\n", *argv); -1; }) #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" @@ -97,4 +93,35 @@ int prog_parse_fd(int *argc, char ***argv); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); void print_hex_data_json(uint8_t *data, size_t len); +static inline void p_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (json_output) { + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "error"); + jsonw_vprintf_enquote(json_wtr, fmt, ap); + jsonw_end_object(json_wtr); + } else { + fprintf(stderr, "Error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } + va_end(ap); +} + +static inline void p_info(const char *fmt, ...) +{ + va_list ap; + + if (json_output) + return; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + #endif diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 14d89bfabc66..86c128c433ba 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -81,19 +81,19 @@ static unsigned int get_possible_cpus(void) fd = open("/sys/devices/system/cpu/possible", O_RDONLY); if (fd < 0) { - err("can't open sysfs possible cpus\n"); + p_err("can't open sysfs possible cpus"); exit(-1); } n = read(fd, buf, sizeof(buf)); if (n < 2) { - err("can't read sysfs possible cpus\n"); + p_err("can't read sysfs possible cpus"); exit(-1); } close(fd); if (n == sizeof(buf)) { - err("read sysfs possible cpus overflow\n"); + p_err("read sysfs possible cpus overflow"); exit(-1); } @@ -161,14 +161,14 @@ static int map_parse_fd(int *argc, char ***argv) id = strtoul(**argv, &endptr, 0); if (*endptr) { - err("can't parse %s as ID\n", **argv); + p_err("can't parse %s as ID", **argv); return -1; } NEXT_ARGP(); fd = bpf_map_get_fd_by_id(id); if (fd < 0) - err("get map by id (%u): %s\n", id, strerror(errno)); + p_err("get map by id (%u): %s", id, strerror(errno)); return fd; } else if (is_prefix(**argv, "pinned")) { char *path; @@ -181,7 +181,7 @@ static int map_parse_fd(int *argc, char ***argv) return open_obj_pinned_any(path, BPF_OBJ_MAP); } - err("expected 'id' or 'pinned', got: '%s'?\n", **argv); + p_err("expected 'id' or 'pinned', got: '%s'?", **argv); return -1; } @@ -197,7 +197,7 @@ map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) err = bpf_obj_get_info_by_fd(fd, info, info_len); if (err) { - err("can't get map info: %s\n", strerror(errno)); + p_err("can't get map info: %s", strerror(errno)); close(fd); return err; } @@ -288,14 +288,14 @@ static char **parse_bytes(char **argv, const char *name, unsigned char *val, while (i < n && argv[i]) { val[i] = strtoul(argv[i], &endptr, 0); if (*endptr) { - err("error parsing byte: %s\n", argv[i]); + p_err("error parsing byte: %s", argv[i]); return NULL; } i++; } if (i != n) { - err("%s expected %d bytes got %d\n", name, n, i); + p_err("%s expected %d bytes got %d", name, n, i); return NULL; } @@ -309,16 +309,16 @@ static int parse_elem(char **argv, struct bpf_map_info *info, if (!*argv) { if (!key && !value) return 0; - err("did not find %s\n", key ? "key" : "value"); + p_err("did not find %s", key ? "key" : "value"); return -1; } if (is_prefix(*argv, "key")) { if (!key) { if (key_size) - err("duplicate key\n"); + p_err("duplicate key"); else - err("unnecessary key\n"); + p_err("unnecessary key"); return -1; } @@ -333,9 +333,9 @@ static int parse_elem(char **argv, struct bpf_map_info *info, if (!value) { if (value_size) - err("duplicate value\n"); + p_err("duplicate value"); else - err("unnecessary value\n"); + p_err("unnecessary value"); return -1; } @@ -345,11 +345,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info, int argc = 2; if (value_size != 4) { - err("value smaller than 4B for map in map?\n"); + p_err("value smaller than 4B for map in map?"); return -1; } if (!argv[0] || !argv[1]) { - err("not enough value arguments for map in map\n"); + p_err("not enough value arguments for map in map"); return -1; } @@ -363,11 +363,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info, int argc = 2; if (value_size != 4) { - err("value smaller than 4B for map of progs?\n"); + p_err("value smaller than 4B for map of progs?"); return -1; } if (!argv[0] || !argv[1]) { - err("not enough value arguments for map of progs\n"); + p_err("not enough value arguments for map of progs"); return -1; } @@ -388,7 +388,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || is_prefix(*argv, "exist")) { if (!flags) { - err("flags specified multiple times: %s\n", *argv); + p_err("flags specified multiple times: %s", *argv); return -1; } @@ -403,7 +403,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, value_size, NULL, value_fd); } - err("expected key or value, got: %s\n", *argv); + p_err("expected key or value, got: %s", *argv); return -1; } @@ -499,22 +499,21 @@ static int do_show(int argc, char **argv) if (err) { if (errno == ENOENT) break; - err("can't get next map: %s\n", strerror(errno)); - if (errno == EINVAL) - err("kernel too old?\n"); + p_err("can't get next map: %s%s", strerror(errno), + errno == EINVAL ? " -- kernel too old?" : ""); return -1; } fd = bpf_map_get_fd_by_id(id); if (fd < 0) { - err("can't get map by id (%u): %s\n", - id, strerror(errno)); + p_err("can't get map by id (%u): %s", + id, strerror(errno)); return -1; } err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get map info: %s\n", strerror(errno)); + p_err("can't get map info: %s", strerror(errno)); close(fd); return -1; } @@ -547,7 +546,7 @@ static int do_dump(int argc, char **argv) return -1; if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) { - err("Dumping maps of maps and program maps not supported\n"); + p_err("Dumping maps of maps and program maps not supported"); close(fd); return -1; } @@ -555,7 +554,7 @@ static int do_dump(int argc, char **argv) key = malloc(info.key_size); value = alloc_value(&info); if (!key || !value) { - err("mem alloc failed\n"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -577,9 +576,19 @@ static int do_dump(int argc, char **argv) else print_entry_plain(&info, key, value); } else { - info("can't lookup element with key: "); - fprint_hex(stderr, key, info.key_size, " "); - fprintf(stderr, "\n"); + if (json_output) { + jsonw_name(json_wtr, "key"); + print_hex_data_json(key, info.key_size); + jsonw_name(json_wtr, "value"); + jsonw_start_object(json_wtr); + jsonw_string_field(json_wtr, "error", + "can't lookup element"); + jsonw_end_object(json_wtr); + } else { + p_info("can't lookup element with key: "); + fprint_hex(stderr, key, info.key_size, " "); + fprintf(stderr, "\n"); + } } prev_key = key; @@ -619,7 +628,7 @@ static int do_update(int argc, char **argv) key = malloc(info.key_size); value = alloc_value(&info); if (!key || !value) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -631,7 +640,7 @@ static int do_update(int argc, char **argv) err = bpf_map_update_elem(fd, key, value, flags); if (err) { - err("update failed: %s\n", strerror(errno)); + p_err("update failed: %s", strerror(errno)); goto exit_free; } @@ -663,7 +672,7 @@ static int do_lookup(int argc, char **argv) key = malloc(info.key_size); value = alloc_value(&info); if (!key || !value) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -687,7 +696,7 @@ static int do_lookup(int argc, char **argv) printf("\n\nNot found\n"); } } else { - err("lookup failed: %s\n", strerror(errno)); + p_err("lookup failed: %s", strerror(errno)); } exit_free: @@ -716,7 +725,7 @@ static int do_getnext(int argc, char **argv) key = malloc(info.key_size); nextkey = malloc(info.key_size); if (!key || !nextkey) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -733,7 +742,7 @@ static int do_getnext(int argc, char **argv) err = bpf_map_get_next_key(fd, key, nextkey); if (err) { - err("can't get next key: %s\n", strerror(errno)); + p_err("can't get next key: %s", strerror(errno)); goto exit_free; } @@ -786,7 +795,7 @@ static int do_delete(int argc, char **argv) key = malloc(info.key_size); if (!key) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -797,7 +806,7 @@ static int do_delete(int argc, char **argv) err = bpf_map_delete_elem(fd, key); if (err) - err("delete failed: %s\n", strerror(errno)); + p_err("delete failed: %s", strerror(errno)); exit_free: free(key); diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 43e49799a624..41bd5390b4fc 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -104,21 +104,21 @@ static int prog_fd_by_tag(unsigned char *tag) while (true) { err = bpf_prog_get_next_id(id, &id); if (err) { - err("%s\n", strerror(errno)); + p_err("%s", strerror(errno)); return -1; } fd = bpf_prog_get_fd_by_id(id); if (fd < 0) { - err("can't get prog by id (%u): %s\n", - id, strerror(errno)); + p_err("can't get prog by id (%u): %s", + id, strerror(errno)); return -1; } err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get prog info (%u): %s\n", - id, strerror(errno)); + p_err("can't get prog info (%u): %s", + id, strerror(errno)); close(fd); return -1; } @@ -142,14 +142,14 @@ int prog_parse_fd(int *argc, char ***argv) id = strtoul(**argv, &endptr, 0); if (*endptr) { - err("can't parse %s as ID\n", **argv); + p_err("can't parse %s as ID", **argv); return -1; } NEXT_ARGP(); fd = bpf_prog_get_fd_by_id(id); if (fd < 0) - err("get by id (%u): %s\n", id, strerror(errno)); + p_err("get by id (%u): %s", id, strerror(errno)); return fd; } else if (is_prefix(**argv, "tag")) { unsigned char tag[BPF_TAG_SIZE]; @@ -159,7 +159,7 @@ int prog_parse_fd(int *argc, char ***argv) if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) != BPF_TAG_SIZE) { - err("can't parse tag\n"); + p_err("can't parse tag"); return -1; } NEXT_ARGP(); @@ -176,7 +176,7 @@ int prog_parse_fd(int *argc, char ***argv) return open_obj_pinned_any(path, BPF_OBJ_PROG); } - err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv); + p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); return -1; } @@ -311,7 +311,7 @@ static int show_prog(int fd) err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get prog info: %s\n", strerror(errno)); + p_err("can't get prog info: %s", strerror(errno)); return -1; } @@ -349,17 +349,16 @@ static int do_show(int argc, char **argv) err = 0; break; } - err("can't get next program: %s\n", strerror(errno)); - if (errno == EINVAL) - err("kernel too old?\n"); + p_err("can't get next program: %s%s", strerror(errno), + errno == EINVAL ? " -- kernel too old?" : ""); err = -1; break; } fd = bpf_prog_get_fd_by_id(id); if (fd < 0) { - err("can't get prog by id (%u): %s\n", - id, strerror(errno)); + p_err("can't get prog by id (%u): %s", + id, strerror(errno)); err = -1; break; } @@ -498,7 +497,7 @@ static int do_dump(int argc, char **argv) member_len = &info.xlated_prog_len; member_ptr = &info.xlated_prog_insns; } else { - err("expected 'xlated' or 'jited', got: %s\n", *argv); + p_err("expected 'xlated' or 'jited', got: %s", *argv); return -1; } NEXT_ARG(); @@ -513,7 +512,7 @@ static int do_dump(int argc, char **argv) if (is_prefix(*argv, "file")) { NEXT_ARG(); if (!argc) { - err("expected file path\n"); + p_err("expected file path"); return -1; } @@ -531,12 +530,12 @@ static int do_dump(int argc, char **argv) err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get prog info: %s\n", strerror(errno)); + p_err("can't get prog info: %s", strerror(errno)); return -1; } if (!*member_len) { - info("no instructions returned\n"); + p_info("no instructions returned"); close(fd); return 0; } @@ -545,7 +544,7 @@ static int do_dump(int argc, char **argv) buf = malloc(buf_size); if (!buf) { - err("mem alloc failed\n"); + p_err("mem alloc failed"); close(fd); return -1; } @@ -558,28 +557,28 @@ static int do_dump(int argc, char **argv) err = bpf_obj_get_info_by_fd(fd, &info, &len); close(fd); if (err) { - err("can't get prog info: %s\n", strerror(errno)); + p_err("can't get prog info: %s", strerror(errno)); goto err_free; } if (*member_len > buf_size) { - err("too many instructions returned\n"); + p_err("too many instructions returned"); goto err_free; } if (filepath) { fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { - err("can't open file %s: %s\n", filepath, - strerror(errno)); + p_err("can't open file %s: %s", filepath, + strerror(errno)); goto err_free; } n = write(fd, buf, *member_len); close(fd); if (n != *member_len) { - err("error writing output file: %s\n", - n < 0 ? strerror(errno) : "short write"); + p_err("error writing output file: %s", + n < 0 ? strerror(errno) : "short write"); goto err_free; } } else { -- cgit v1.2.3 From 3fc27b71b894f5e2ad611297d5817b6092c96626 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Tue, 24 Oct 2017 20:11:28 -0700 Subject: tools: bpftool: try to mount bpffs if required for pinning objects One possible cause of failure for `bpftool {prog|map} pin * file FILE` is the FILE not being in an eBPF virtual file system (bpffs). In this case, make bpftool attempt to mount bpffs on the parent directory of the FILE. Then, if this operation is successful, try again to pin the object. The code for mnt_bpffs() is a copy of function bpf_mnt_fs() from iproute2 package (under lib/bpf.c, taken at commit 4b73d52f8a81), with modifications regarding handling of error messages. Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 71 ++++++++++++++++++++++++++++++++++++++++------ tools/bpf/bpftool/main.h | 2 ++ 2 files changed, 65 insertions(+), 8 deletions(-) (limited to 'tools/bpf/bpftool/common.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index b2533f1cae3e..f0288269dae8 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,37 @@ static bool is_bpffs(char *path) return (unsigned long)st_fs.f_type == BPF_FS_MAGIC; } +static int mnt_bpffs(const char *target, char *buff, size_t bufflen) +{ + bool bind_done = false; + + while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) { + if (errno != EINVAL || bind_done) { + snprintf(buff, bufflen, + "mount --make-private %s failed: %s", + target, strerror(errno)); + return -1; + } + + if (mount(target, target, "none", MS_BIND, NULL)) { + snprintf(buff, bufflen, + "mount --bind %s %s failed: %s", + target, target, strerror(errno)); + return -1; + } + + bind_done = true; + } + + if (mount("bpf", target, "bpf", 0, "mode=0700")) { + snprintf(buff, bufflen, "mount -t bpf bpf %s failed: %s", + target, strerror(errno)); + return -1; + } + + return 0; +} + int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) { enum bpf_obj_type type; @@ -89,8 +121,11 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) { + char err_str[ERR_MAX_LEN]; unsigned int id; char *endptr; + char *file; + char *dir; int err; int fd; @@ -117,16 +152,36 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) } err = bpf_obj_pin(fd, *argv); - close(fd); - if (err) { - p_err("can't pin the object (%s): %s", *argv, - errno == EACCES && !is_bpffs(dirname(*argv)) ? - "directory not in bpf file system (bpffs)" : - strerror(errno)); - return -1; + if (!err) + goto out_close; + + file = malloc(strlen(*argv) + 1); + strcpy(file, *argv); + dir = dirname(file); + + if (errno != EPERM || is_bpffs(dir)) { + p_err("can't pin the object (%s): %s", *argv, strerror(errno)); + goto out_free; } - return 0; + /* Attempt to mount bpffs, then retry pinning. */ + err = mnt_bpffs(dir, err_str, ERR_MAX_LEN); + if (!err) { + err = bpf_obj_pin(fd, *argv); + if (err) + p_err("can't pin the object (%s): %s", *argv, + strerror(errno)); + } else { + err_str[ERR_MAX_LEN - 1] = '\0'; + p_err("can't mount BPF file system to pin the object (%s): %s", + *argv, err_str); + } + +out_free: + free(file); +out_close: + close(fd); + return err; } const char *get_fd_type_name(enum bpf_obj_type type) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 2f94bed03a8d..d315d01be645 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -51,6 +51,8 @@ #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) #define BAD_ARG() ({ p_err("what is '%s'?\n", *argv); -1; }) +#define ERR_MAX_LEN 1024 + #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define HELP_SPEC_PROGRAM \ -- cgit v1.2.3 From 0b1c27db12fd338ed912fec18f5cc02d7bd4e54e Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 3 Nov 2017 13:59:07 -0700 Subject: tools: bpftool: move p_err() and p_info() from main.h to common.c The two functions were declared as static inline in a header file. There is no particular reason why they should be inlined, they just happened to remain in the same header file when they were turned from macros to functions in a precious commit. Make them non-inlined functions and move them to common.c file instead. Suggested-by: Joe Perches Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 31 +++++++++++++++++++++++++++++++ tools/bpf/bpftool/main.h | 34 +++------------------------------- 2 files changed, 34 insertions(+), 31 deletions(-) (limited to 'tools/bpf/bpftool/common.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index f0288269dae8..aa7017098b2a 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -50,6 +50,37 @@ #include "main.h" +void p_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (json_output) { + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "error"); + jsonw_vprintf_enquote(json_wtr, fmt, ap); + jsonw_end_object(json_wtr); + } else { + fprintf(stderr, "Error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } + va_end(ap); +} + +void p_info(const char *fmt, ...) +{ + va_list ap; + + if (json_output) + return; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + static bool is_bpffs(char *path) { struct statfs st_fs; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index d315d01be645..ff5ad05b137b 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -71,6 +71,9 @@ extern const char *bin_name; extern json_writer_t *json_wtr; extern bool json_output; +void p_err(const char *fmt, ...); +void p_info(const char *fmt, ...); + bool is_prefix(const char *pfx, const char *str); void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void usage(void) __attribute__((noreturn)); @@ -97,35 +100,4 @@ int prog_parse_fd(int *argc, char ***argv); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); void print_hex_data_json(uint8_t *data, size_t len); -static inline void p_err(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (json_output) { - jsonw_start_object(json_wtr); - jsonw_name(json_wtr, "error"); - jsonw_vprintf_enquote(json_wtr, fmt, ap); - jsonw_end_object(json_wtr); - } else { - fprintf(stderr, "Error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } - va_end(ap); -} - -static inline void p_info(const char *fmt, ...) -{ - va_list ap; - - if (json_output) - return; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); -} - #endif -- cgit v1.2.3 From 1852719658c0f853b5481c9eaed862f1a9355edc Mon Sep 17 00:00:00 2001 From: Prashant Bhole Date: Wed, 8 Nov 2017 13:55:47 +0900 Subject: tools: bpftool: open pinned object without type check This was needed for opening any file in bpf-fs without knowing its object type Signed-off-by: Prashant Bhole Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 15 +++++++++++++-- tools/bpf/bpftool/main.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'tools/bpf/bpftool/common.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index aa7017098b2a..6b3d25d6a782 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -122,9 +122,8 @@ static int mnt_bpffs(const char *target, char *buff, size_t bufflen) return 0; } -int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) +int open_obj_pinned(char *path) { - enum bpf_obj_type type; int fd; fd = bpf_obj_get(path); @@ -136,6 +135,18 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) return -1; } + return fd; +} + +int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) +{ + enum bpf_obj_type type; + int fd; + + fd = open_obj_pinned(path); + if (fd < 0) + return -1; + type = get_fd_type(fd); if (type < 0) { close(fd); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index ff5ad05b137b..2ff2a361af0d 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -89,6 +89,7 @@ int cmd_select(const struct cmd *cmds, int argc, char **argv, int get_fd_type(int fd); const char *get_fd_type_name(enum bpf_obj_type type); char *get_fdinfo(int fd, const char *key); +int open_obj_pinned(char *path); int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type); int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)); -- cgit v1.2.3 From 4990f1f4610b483a60397ed2768d268df228a551 Mon Sep 17 00:00:00 2001 From: Prashant Bhole Date: Wed, 8 Nov 2017 13:55:48 +0900 Subject: tools: bpftool: show filenames of pinned objects Added support to show filenames of pinned objects. For example: root@test# ./bpftool prog 3: tracepoint name tracepoint__irq tag f677a7dd722299a3 loaded_at Oct 26/11:39 uid 0 xlated 160B not jited memlock 4096B map_ids 4 pinned /sys/fs/bpf/softirq_prog 4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6 loaded_at Oct 26/11:39 uid 0 xlated 392B not jited memlock 4096B map_ids 4,6 root@test# ./bpftool --json --pretty prog [{ "id": 3, "type": "tracepoint", "name": "tracepoint__irq", "tag": "f677a7dd722299a3", "loaded_at": "Oct 26/11:39", "uid": 0, "bytes_xlated": 160, "jited": false, "bytes_memlock": 4096, "map_ids": [4 ], "pinned": ["/sys/fs/bpf/softirq_prog" ] },{ "id": 4, "type": "tracepoint", "name": "tracepoint__irq", "tag": "ea5dc530d00b92b6", "loaded_at": "Oct 26/11:39", "uid": 0, "bytes_xlated": 392, "jited": false, "bytes_memlock": 4096, "map_ids": [4,6 ], "pinned": [] } ] root@test# ./bpftool map 4: hash name start flags 0x0 key 4B value 16B max_entries 10240 memlock 1003520B pinned /sys/fs/bpf/softirq_map1 5: hash name iptr flags 0x0 key 4B value 8B max_entries 10240 memlock 921600B root@test# ./bpftool --json --pretty map [{ "id": 4, "type": "hash", "name": "start", "flags": 0, "bytes_key": 4, "bytes_value": 16, "max_entries": 10240, "bytes_memlock": 1003520, "pinned": ["/sys/fs/bpf/softirq_map1" ] },{ "id": 5, "type": "hash", "name": "iptr", "flags": 0, "bytes_key": 4, "bytes_value": 8, "max_entries": 10240, "bytes_memlock": 921600, "pinned": [] } ] Signed-off-by: Prashant Bhole Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ tools/bpf/bpftool/main.c | 8 +++++ tools/bpf/bpftool/main.h | 17 ++++++++++ tools/bpf/bpftool/map.c | 21 ++++++++++++ tools/bpf/bpftool/prog.c | 24 ++++++++++++++ 5 files changed, 152 insertions(+) (limited to 'tools/bpf/bpftool/common.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 6b3d25d6a782..2bd3b280e6dd 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -34,7 +34,9 @@ /* Author: Jakub Kicinski */ #include +#include #include +#include #include #include #include @@ -321,3 +323,83 @@ void print_hex_data_json(uint8_t *data, size_t len) jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]); jsonw_end_array(json_wtr); } + +int build_pinned_obj_table(struct pinned_obj_table *tab, + enum bpf_obj_type type) +{ + struct bpf_prog_info pinned_info = {}; + struct pinned_obj *obj_node = NULL; + __u32 len = sizeof(pinned_info); + struct mntent *mntent = NULL; + enum bpf_obj_type objtype; + FILE *mntfile = NULL; + FTSENT *ftse = NULL; + FTS *fts = NULL; + int fd, err; + + mntfile = setmntent("/proc/mounts", "r"); + if (!mntfile) + return -1; + + while ((mntent = getmntent(mntfile))) { + char *path[] = { mntent->mnt_dir, NULL }; + + if (strncmp(mntent->mnt_type, "bpf", 3) != 0) + continue; + + fts = fts_open(path, 0, NULL); + if (!fts) + continue; + + while ((ftse = fts_read(fts))) { + if (!(ftse->fts_info & FTS_F)) + continue; + fd = open_obj_pinned(ftse->fts_path); + if (fd < 0) + continue; + + objtype = get_fd_type(fd); + if (objtype != type) { + close(fd); + continue; + } + memset(&pinned_info, 0, sizeof(pinned_info)); + err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len); + if (err) { + close(fd); + continue; + } + + obj_node = malloc(sizeof(*obj_node)); + if (!obj_node) { + close(fd); + fts_close(fts); + fclose(mntfile); + return -1; + } + + memset(obj_node, 0, sizeof(*obj_node)); + obj_node->id = pinned_info.id; + obj_node->path = strdup(ftse->fts_path); + hash_add(tab->table, &obj_node->hash, obj_node->id); + + close(fd); + } + fts_close(fts); + } + fclose(mntfile); + return 0; +} + +void delete_pinned_obj_table(struct pinned_obj_table *tab) +{ + struct pinned_obj *obj; + struct hlist_node *tmp; + unsigned int bkt; + + hash_for_each_safe(tab->table, bkt, tmp, obj, hash) { + hash_del(&obj->hash); + free(obj->path); + free(obj); + } +} diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 78d9afb74ef4..6ad53f1797fa 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -54,6 +54,8 @@ static int (*last_do_help)(int argc, char **argv); json_writer_t *json_wtr; bool pretty_output; bool json_output; +struct pinned_obj_table prog_table; +struct pinned_obj_table map_table; void usage(void) { @@ -272,6 +274,9 @@ int main(int argc, char **argv) json_output = false; bin_name = argv[0]; + hash_init(prog_table.table); + hash_init(map_table.table); + while ((opt = getopt_long(argc, argv, "Vhpj", options, NULL)) >= 0) { switch (opt) { @@ -311,5 +316,8 @@ int main(int argc, char **argv) if (json_output) jsonw_destroy(&json_wtr); + delete_pinned_obj_table(&prog_table); + delete_pinned_obj_table(&map_table); + return ret; } diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 2ff2a361af0d..13453de2e570 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "json_writer.h" @@ -70,6 +71,8 @@ extern const char *bin_name; extern json_writer_t *json_wtr; extern bool json_output; +extern struct pinned_obj_table prog_table; +extern struct pinned_obj_table map_table; void p_err(const char *fmt, ...); void p_info(const char *fmt, ...); @@ -78,6 +81,20 @@ bool is_prefix(const char *pfx, const char *str); void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void usage(void) __attribute__((noreturn)); +struct pinned_obj_table { + DECLARE_HASHTABLE(table, 16); +}; + +struct pinned_obj { + __u32 id; + char *path; + struct hlist_node hash; +}; + +int build_pinned_obj_table(struct pinned_obj_table *table, + enum bpf_obj_type type); +void delete_pinned_obj_table(struct pinned_obj_table *tab); + struct cmd { const char *cmd; int (*func)(int argc, char **argv); diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index e978ab23a77f..de0980657cef 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -436,6 +436,18 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); free(memlock); + if (!hash_empty(map_table.table)) { + struct pinned_obj *obj; + + jsonw_name(json_wtr, "pinned"); + jsonw_start_array(json_wtr); + hash_for_each_possible(map_table.table, obj, hash, info->id) { + if (obj->id == info->id) + jsonw_string(json_wtr, obj->path); + } + jsonw_end_array(json_wtr); + } + jsonw_end_object(json_wtr); return 0; @@ -466,7 +478,14 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) free(memlock); printf("\n"); + if (!hash_empty(map_table.table)) { + struct pinned_obj *obj; + hash_for_each_possible(map_table.table, obj, hash, info->id) { + if (obj->id == info->id) + printf("\tpinned %s\n", obj->path); + } + } return 0; } @@ -478,6 +497,8 @@ static int do_show(int argc, char **argv) int err; int fd; + build_pinned_obj_table(&map_table, BPF_OBJ_MAP); + if (argc == 2) { fd = map_parse_fd_and_info(&argc, &argv, &info, &len); if (fd < 0) diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index d3ab808dc882..8f94b8ac2e63 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -272,6 +272,18 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) if (info->nr_map_ids) show_prog_maps(fd, info->nr_map_ids); + if (!hash_empty(prog_table.table)) { + struct pinned_obj *obj; + + jsonw_name(json_wtr, "pinned"); + jsonw_start_array(json_wtr); + hash_for_each_possible(prog_table.table, obj, hash, info->id) { + if (obj->id == info->id) + jsonw_string(json_wtr, obj->path); + } + jsonw_end_array(json_wtr); + } + jsonw_end_object(json_wtr); } @@ -331,6 +343,16 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) if (info->nr_map_ids) show_prog_maps(fd, info->nr_map_ids); + if (!hash_empty(prog_table.table)) { + struct pinned_obj *obj; + + printf("\n"); + hash_for_each_possible(prog_table.table, obj, hash, info->id) { + if (obj->id == info->id) + printf("\tpinned %s\n", obj->path); + } + } + printf("\n"); } @@ -360,6 +382,8 @@ static int do_show(int argc, char **argv) int err; int fd; + build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); + if (argc == 2) { fd = prog_parse_fd(&argc, &argv); if (fd < 0) -- cgit v1.2.3