bpftool: Add bpf_link show and pin support
authorAndrii Nakryiko <andriin@fb.com>
Wed, 29 Apr 2020 00:16:12 +0000 (17:16 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 29 Apr 2020 00:27:08 +0000 (17:27 -0700)
Add `bpftool link show` and `bpftool link pin` commands.

Example plain output for `link show` (with showing pinned paths):

[vmuser@archvm bpf]$ sudo ~/local/linux/tools/bpf/bpftool/bpftool -f link
1: tracing  prog 12
        prog_type tracing  attach_type fentry
        pinned /sys/fs/bpf/my_test_link
        pinned /sys/fs/bpf/my_test_link2
2: tracing  prog 13
        prog_type tracing  attach_type fentry
3: tracing  prog 14
        prog_type tracing  attach_type fentry
4: tracing  prog 15
        prog_type tracing  attach_type fentry
5: tracing  prog 16
        prog_type tracing  attach_type fentry
6: tracing  prog 17
        prog_type tracing  attach_type fentry
7: raw_tracepoint  prog 21
        tp 'sys_enter'
8: cgroup  prog 25
        cgroup_id 584  attach_type egress
9: cgroup  prog 25
        cgroup_id 599  attach_type egress
10: cgroup  prog 25
        cgroup_id 614  attach_type egress
11: cgroup  prog 25
        cgroup_id 629  attach_type egress

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Reviewed-by: Quentin Monnet <quentin@isovalent.com>
Link: https://lore.kernel.org/bpf/20200429001614.1544-9-andriin@fb.com
tools/bpf/bpftool/common.c
tools/bpf/bpftool/link.c [new file with mode: 0644]
tools/bpf/bpftool/main.c
tools/bpf/bpftool/main.h

index f2223db..c47bdc6 100644 (file)
@@ -262,6 +262,8 @@ int get_fd_type(int fd)
                return BPF_OBJ_MAP;
        else if (strstr(buf, "bpf-prog"))
                return BPF_OBJ_PROG;
+       else if (strstr(buf, "bpf-link"))
+               return BPF_OBJ_LINK;
 
        return BPF_OBJ_UNKNOWN;
 }
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
new file mode 100644 (file)
index 0000000..adc7dc4
--- /dev/null
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2020 Facebook */
+
+#include <errno.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <bpf/bpf.h>
+
+#include "json_writer.h"
+#include "main.h"
+
+static const char * const link_type_name[] = {
+       [BPF_LINK_TYPE_UNSPEC]                  = "unspec",
+       [BPF_LINK_TYPE_RAW_TRACEPOINT]          = "raw_tracepoint",
+       [BPF_LINK_TYPE_TRACING]                 = "tracing",
+       [BPF_LINK_TYPE_CGROUP]                  = "cgroup",
+};
+
+static int link_parse_fd(int *argc, char ***argv)
+{
+       if (is_prefix(**argv, "id")) {
+               unsigned int id;
+               char *endptr;
+
+               NEXT_ARGP();
+
+               id = strtoul(**argv, &endptr, 0);
+               if (*endptr) {
+                       p_err("can't parse %s as ID", **argv);
+                       return -1;
+               }
+               NEXT_ARGP();
+
+               return bpf_link_get_fd_by_id(id);
+       } else if (is_prefix(**argv, "pinned")) {
+               char *path;
+
+               NEXT_ARGP();
+
+               path = **argv;
+               NEXT_ARGP();
+
+               return open_obj_pinned_any(path, BPF_OBJ_LINK);
+       }
+
+       p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
+       return -1;
+}
+
+static void
+show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
+{
+       jsonw_uint_field(wtr, "id", info->id);
+       if (info->type < ARRAY_SIZE(link_type_name))
+               jsonw_string_field(wtr, "type", link_type_name[info->type]);
+       else
+               jsonw_uint_field(wtr, "type", info->type);
+
+       jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
+}
+
+static int get_prog_info(int prog_id, struct bpf_prog_info *info)
+{
+       __u32 len = sizeof(*info);
+       int err, prog_fd;
+
+       prog_fd = bpf_prog_get_fd_by_id(prog_id);
+       if (prog_fd < 0)
+               return prog_fd;
+
+       memset(info, 0, sizeof(*info));
+       err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
+       if (err)
+               p_err("can't get prog info: %s", strerror(errno));
+       close(prog_fd);
+       return err;
+}
+
+static int show_link_close_json(int fd, struct bpf_link_info *info)
+{
+       struct bpf_prog_info prog_info;
+       int err;
+
+       jsonw_start_object(json_wtr);
+
+       show_link_header_json(info, json_wtr);
+
+       switch (info->type) {
+       case BPF_LINK_TYPE_RAW_TRACEPOINT:
+               jsonw_string_field(json_wtr, "tp_name",
+                                  (const char *)info->raw_tracepoint.tp_name);
+               break;
+       case BPF_LINK_TYPE_TRACING:
+               err = get_prog_info(info->prog_id, &prog_info);
+               if (err)
+                       return err;
+
+               if (prog_info.type < ARRAY_SIZE(prog_type_name))
+                       jsonw_string_field(json_wtr, "prog_type",
+                                          prog_type_name[prog_info.type]);
+               else
+                       jsonw_uint_field(json_wtr, "prog_type",
+                                        prog_info.type);
+
+               if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name))
+                       jsonw_string_field(json_wtr, "attach_type",
+                              attach_type_name[info->tracing.attach_type]);
+               else
+                       jsonw_uint_field(json_wtr, "attach_type",
+                                        info->tracing.attach_type);
+               break;
+       case BPF_LINK_TYPE_CGROUP:
+               jsonw_lluint_field(json_wtr, "cgroup_id",
+                                  info->cgroup.cgroup_id);
+               if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name))
+                       jsonw_string_field(json_wtr, "attach_type",
+                              attach_type_name[info->cgroup.attach_type]);
+               else
+                       jsonw_uint_field(json_wtr, "attach_type",
+                                        info->cgroup.attach_type);
+               break;
+       default:
+               break;
+       }
+
+       if (!hash_empty(link_table.table)) {
+               struct pinned_obj *obj;
+
+               jsonw_name(json_wtr, "pinned");
+               jsonw_start_array(json_wtr);
+               hash_for_each_possible(link_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;
+}
+
+static void show_link_header_plain(struct bpf_link_info *info)
+{
+       printf("%u: ", info->id);
+       if (info->type < ARRAY_SIZE(link_type_name))
+               printf("%s  ", link_type_name[info->type]);
+       else
+               printf("type %u  ", info->type);
+
+       printf("prog %u  ", info->prog_id);
+}
+
+static int show_link_close_plain(int fd, struct bpf_link_info *info)
+{
+       struct bpf_prog_info prog_info;
+       int err;
+
+       show_link_header_plain(info);
+
+       switch (info->type) {
+       case BPF_LINK_TYPE_RAW_TRACEPOINT:
+               printf("\n\ttp '%s'  ",
+                      (const char *)info->raw_tracepoint.tp_name);
+               break;
+       case BPF_LINK_TYPE_TRACING:
+               err = get_prog_info(info->prog_id, &prog_info);
+               if (err)
+                       return err;
+
+               if (prog_info.type < ARRAY_SIZE(prog_type_name))
+                       printf("\n\tprog_type %s  ",
+                              prog_type_name[prog_info.type]);
+               else
+                       printf("\n\tprog_type %u  ", prog_info.type);
+
+               if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name))
+                       printf("attach_type %s  ",
+                              attach_type_name[info->tracing.attach_type]);
+               else
+                       printf("attach_type %u  ", info->tracing.attach_type);
+               break;
+       case BPF_LINK_TYPE_CGROUP:
+               printf("\n\tcgroup_id %zu  ", (size_t)info->cgroup.cgroup_id);
+               if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name))
+                       printf("attach_type %s  ",
+                              attach_type_name[info->cgroup.attach_type]);
+               else
+                       printf("attach_type %u  ", info->cgroup.attach_type);
+               break;
+       default:
+               break;
+       }
+
+       if (!hash_empty(link_table.table)) {
+               struct pinned_obj *obj;
+
+               hash_for_each_possible(link_table.table, obj, hash, info->id) {
+                       if (obj->id == info->id)
+                               printf("\n\tpinned %s", obj->path);
+               }
+       }
+
+       printf("\n");
+
+       return 0;
+}
+
+static int do_show_link(int fd)
+{
+       struct bpf_link_info info;
+       __u32 len = sizeof(info);
+       char raw_tp_name[256];
+       int err;
+
+       memset(&info, 0, sizeof(info));
+again:
+       err = bpf_obj_get_info_by_fd(fd, &info, &len);
+       if (err) {
+               p_err("can't get link info: %s",
+                     strerror(errno));
+               close(fd);
+               return err;
+       }
+       if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
+           !info.raw_tracepoint.tp_name) {
+               info.raw_tracepoint.tp_name = (unsigned long)&raw_tp_name;
+               info.raw_tracepoint.tp_name_len = sizeof(raw_tp_name);
+               goto again;
+       }
+
+       if (json_output)
+               show_link_close_json(fd, &info);
+       else
+               show_link_close_plain(fd, &info);
+
+       close(fd);
+       return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+       __u32 id = 0;
+       int err, fd;
+
+       if (show_pinned)
+               build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
+
+       if (argc == 2) {
+               fd = link_parse_fd(&argc, &argv);
+               if (fd < 0)
+                       return fd;
+               return do_show_link(fd);
+       }
+
+       if (argc)
+               return BAD_ARG();
+
+       if (json_output)
+               jsonw_start_array(json_wtr);
+       while (true) {
+               err = bpf_link_get_next_id(id, &id);
+               if (err) {
+                       if (errno == ENOENT)
+                               break;
+                       p_err("can't get next link: %s%s", strerror(errno),
+                             errno == EINVAL ? " -- kernel too old?" : "");
+                       break;
+               }
+
+               fd = bpf_link_get_fd_by_id(id);
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               continue;
+                       p_err("can't get link by id (%u): %s",
+                             id, strerror(errno));
+                       break;
+               }
+
+               err = do_show_link(fd);
+               if (err)
+                       break;
+       }
+       if (json_output)
+               jsonw_end_array(json_wtr);
+
+       return errno == ENOENT ? 0 : -1;
+}
+
+static int do_pin(int argc, char **argv)
+{
+       int err;
+
+       err = do_pin_any(argc, argv, link_parse_fd);
+       if (!err && json_output)
+               jsonw_null(json_wtr);
+       return err;
+}
+
+static int do_help(int argc, char **argv)
+{
+       if (json_output) {
+               jsonw_null(json_wtr);
+               return 0;
+       }
+
+       fprintf(stderr,
+               "Usage: %1$s %2$s { show | list }   [LINK]\n"
+               "       %1$s %2$s pin        LINK  FILE\n"
+               "       %1$s %2$s help\n"
+               "\n"
+               "       " HELP_SPEC_LINK "\n"
+               "       " HELP_SPEC_PROGRAM "\n"
+               "       " HELP_SPEC_OPTIONS "\n"
+               "",
+               bin_name, argv[-2]);
+
+       return 0;
+}
+
+static const struct cmd cmds[] = {
+       { "show",       do_show },
+       { "list",       do_show },
+       { "help",       do_help },
+       { "pin",        do_pin },
+       { 0 }
+};
+
+int do_link(int argc, char **argv)
+{
+       return cmd_select(cmds, argc, argv, do_help);
+}
index 466c269..1413a15 100644 (file)
@@ -30,6 +30,7 @@ bool verifier_logs;
 bool relaxed_maps;
 struct pinned_obj_table prog_table;
 struct pinned_obj_table map_table;
+struct pinned_obj_table link_table;
 
 static void __noreturn clean_and_exit(int i)
 {
@@ -58,7 +59,7 @@ static int do_help(int argc, char **argv)
                "       %s batch file FILE\n"
                "       %s version\n"
                "\n"
-               "       OBJECT := { prog | map | cgroup | perf | net | feature | btf | gen | struct_ops }\n"
+               "       OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops }\n"
                "       " HELP_SPEC_OPTIONS "\n"
                "",
                bin_name, bin_name, bin_name);
@@ -215,6 +216,7 @@ static const struct cmd cmds[] = {
        { "batch",      do_batch },
        { "prog",       do_prog },
        { "map",        do_map },
+       { "link",       do_link },
        { "cgroup",     do_cgroup },
        { "perf",       do_perf },
        { "net",        do_net },
@@ -364,6 +366,7 @@ int main(int argc, char **argv)
 
        hash_init(prog_table.table);
        hash_init(map_table.table);
+       hash_init(link_table.table);
 
        opterr = 0;
        while ((opt = getopt_long(argc, argv, "Vhpjfmnd",
@@ -422,6 +425,7 @@ int main(int argc, char **argv)
        if (show_pinned) {
                delete_pinned_obj_table(&prog_table);
                delete_pinned_obj_table(&map_table);
+               delete_pinned_obj_table(&link_table);
        }
 
        return ret;
index 99d84bd..9b1fb81 100644 (file)
@@ -50,6 +50,8 @@
        "\t            {-m|--mapcompat} | {-n|--nomount} }"
 #define HELP_SPEC_MAP                                                  \
        "MAP := { id MAP_ID | pinned FILE | name MAP_NAME }"
+#define HELP_SPEC_LINK                                                 \
+       "LINK := { id LINK_ID | pinned FILE }"
 
 static const char * const prog_type_name[] = {
        [BPF_PROG_TYPE_UNSPEC]                  = "unspec",
@@ -122,6 +124,7 @@ enum bpf_obj_type {
        BPF_OBJ_UNKNOWN,
        BPF_OBJ_PROG,
        BPF_OBJ_MAP,
+       BPF_OBJ_LINK,
 };
 
 extern const char *bin_name;
@@ -134,6 +137,7 @@ extern bool verifier_logs;
 extern bool relaxed_maps;
 extern struct pinned_obj_table prog_table;
 extern struct pinned_obj_table map_table;
+extern struct pinned_obj_table link_table;
 
 void __printf(1, 2) p_err(const char *fmt, ...);
 void __printf(1, 2) p_info(const char *fmt, ...);
@@ -185,6 +189,7 @@ int do_pin_fd(int fd, const char *name);
 
 int do_prog(int argc, char **arg);
 int do_map(int argc, char **arg);
+int do_link(int argc, char **arg);
 int do_event_pipe(int argc, char **argv);
 int do_cgroup(int argc, char **arg);
 int do_perf(int argc, char **arg);