From 49eb7ab3b2b5a106307494d2a46248ecb1530578 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:50 +0000 Subject: [PATCH 1/9] tools: bpftool: add basic probe capability, probe syscall availability Add a new component and command for bpftool, in order to probe the system to dump a set of eBPF-related parameters so that users can know what features are available on the system. Parameters are dumped in plain or JSON output (with -j/-p options). The current patch introduces probing of one simple parameter: availability of the bpf() system call. Later commits will add other probes. Sample output: # bpftool feature probe kernel Scanning system call availability... bpf() syscall is available # bpftool --json --pretty feature probe kernel { "syscall_config": { "have_bpf_syscall": true } } The optional "kernel" keyword enforces probing of the current system, which is the only possible behaviour at this stage. It can be safely omitted. The feature comes with the relevant man page, but bash completion will come in a dedicated commit. v3: - Do not probe kernel version. Contrarily to what is written below for v2, we can have the kernel version retrieved in libbpf instead of bpftool (in the patch adding probing for program types). v2: - Remove C-style macros output from this patch. - Even though kernel version is no longer needed for testing kprobes availability, note that we still collect it in this patch so that bpftool gets able to probe (in next patches) older kernels as well. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- .../bpftool/Documentation/bpftool-cgroup.rst | 1 + .../bpftool/Documentation/bpftool-feature.rst | 60 ++++++++ .../bpf/bpftool/Documentation/bpftool-map.rst | 1 + .../bpf/bpftool/Documentation/bpftool-net.rst | 1 + .../bpftool/Documentation/bpftool-perf.rst | 1 + .../bpftool/Documentation/bpftool-prog.rst | 1 + tools/bpf/bpftool/Documentation/bpftool.rst | 1 + tools/bpf/bpftool/feature.c | 131 ++++++++++++++++++ tools/bpf/bpftool/main.c | 3 +- tools/bpf/bpftool/main.h | 1 + 10 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 tools/bpf/bpftool/Documentation/bpftool-feature.rst create mode 100644 tools/bpf/bpftool/feature.c diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst index d07ccf8a23f7..d43fce568ef7 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst @@ -142,5 +142,6 @@ SEE ALSO **bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-map**\ (8), + **bpftool-feature**\ (8), **bpftool-net**\ (8), **bpftool-perf**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst new file mode 100644 index 000000000000..40ac13c0b782 --- /dev/null +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -0,0 +1,60 @@ +=============== +bpftool-feature +=============== +------------------------------------------------------------------------------- +tool for inspection of eBPF-related parameters for Linux kernel or net device +------------------------------------------------------------------------------- + +:Manual section: 8 + +SYNOPSIS +======== + + **bpftool** [*OPTIONS*] **feature** *COMMAND* + + *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] } + + *COMMANDS* := { **probe** | **help** } + +MAP COMMANDS +============= + +| **bpftool** **feature probe** [**kernel**] +| **bpftool** **feature help** + +DESCRIPTION +=========== + **bpftool feature probe** [**kernel**] + Probe the running kernel and dump a number of eBPF-related + parameters, such as availability of the **bpf()** system call. + + Keyword **kernel** can be omitted. + + **bpftool feature help** + Print short help message. + +OPTIONS +======= + -h, --help + Print short generic help message (similar to **bpftool help**). + + -v, --version + Print version number (similar to **bpftool version**). + + -j, --json + Generate JSON output. For commands that cannot produce JSON, this + option has no effect. + + -p, --pretty + Generate human-readable JSON output. Implies **-j**. + +SEE ALSO +======== + **bpf**\ (2), + **bpf-helpers**\ (7), + **bpftool**\ (8), + **bpftool-prog**\ (8), + **bpftool-map**\ (8), + **bpftool-cgroup**\ (8), + **bpftool-net**\ (8), + **bpftool-perf**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 0584c31d3fe2..5c984ffc9f01 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -256,5 +256,6 @@ SEE ALSO **bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8), + **bpftool-feature**\ (8), **bpftool-net**\ (8), **bpftool-perf**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-net.rst b/tools/bpf/bpftool/Documentation/bpftool-net.rst index ed87c9b619ad..779dab3650ee 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-net.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-net.rst @@ -142,4 +142,5 @@ SEE ALSO **bpftool-prog**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8), + **bpftool-feature**\ (8), **bpftool-perf**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-perf.rst b/tools/bpf/bpftool/Documentation/bpftool-perf.rst index f4c5e5538bb8..bca5590a80d0 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-perf.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-perf.rst @@ -84,4 +84,5 @@ SEE ALSO **bpftool-prog**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8), + **bpftool-feature**\ (8), **bpftool-net**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 58c8369b77dd..13b56102f528 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -258,5 +258,6 @@ SEE ALSO **bpftool**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8), + **bpftool-feature**\ (8), **bpftool-net**\ (8), **bpftool-perf**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst index e1677e81ed59..27153bb816ac 100644 --- a/tools/bpf/bpftool/Documentation/bpftool.rst +++ b/tools/bpf/bpftool/Documentation/bpftool.rst @@ -72,5 +72,6 @@ SEE ALSO **bpftool-prog**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8), + **bpftool-feature**\ (8), **bpftool-net**\ (8), **bpftool-perf**\ (8) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c new file mode 100644 index 000000000000..954fb12a5228 --- /dev/null +++ b/tools/bpf/bpftool/feature.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (c) 2019 Netronome Systems, Inc. */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "main.h" + +enum probe_component { + COMPONENT_UNSPEC, + COMPONENT_KERNEL, +}; + +/* Printing utility functions */ + +static void +print_bool_feature(const char *feat_name, const char *plain_name, bool res) +{ + if (json_output) + jsonw_bool_field(json_wtr, feat_name, res); + else + printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); +} + +static void +print_start_section(const char *json_title, const char *plain_title) +{ + if (json_output) { + jsonw_name(json_wtr, json_title); + jsonw_start_object(json_wtr); + } else { + printf("%s\n", plain_title); + } +} + +/* Probing functions */ + +static bool probe_bpf_syscall(void) +{ + bool res; + + bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0); + res = (errno != ENOSYS); + + print_bool_feature("have_bpf_syscall", + "bpf() syscall", + res); + + return res; +} + +static int do_probe(int argc, char **argv) +{ + enum probe_component target = COMPONENT_UNSPEC; + + /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). + * Let's approximate, and restrict usage to root user only. + */ + if (geteuid()) { + p_err("please run this command as root user"); + return -1; + } + + set_max_rlimit(); + + while (argc) { + if (is_prefix(*argv, "kernel")) { + if (target != COMPONENT_UNSPEC) { + p_err("component to probe already specified"); + return -1; + } + target = COMPONENT_KERNEL; + NEXT_ARG(); + } else { + p_err("expected no more arguments, 'kernel', got: '%s'?", + *argv); + return -1; + } + } + + if (json_output) + jsonw_start_object(json_wtr); + + print_start_section("syscall_config", + "Scanning system call availability..."); + + probe_bpf_syscall(); + + if (json_output) { + /* End current "section" of probes */ + jsonw_end_object(json_wtr); + /* End root object */ + jsonw_end_object(json_wtr); + } + + return 0; +} + +static int do_help(int argc, char **argv) +{ + if (json_output) { + jsonw_null(json_wtr); + return 0; + } + + fprintf(stderr, + "Usage: %s %s probe [kernel]\n" + " %s %s help\n" + "", + bin_name, argv[-2], bin_name, argv[-2]); + + return 0; +} + +static const struct cmd cmds[] = { + { "help", do_help }, + { "probe", do_probe }, + { 0 } +}; + +int do_feature(int argc, char **argv) +{ + return cmd_select(cmds, argc, argv, do_help); +} diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index f44a1c2c4ea0..a9d5e9e6a732 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -56,7 +56,7 @@ static int do_help(int argc, char **argv) " %s batch file FILE\n" " %s version\n" "\n" - " OBJECT := { prog | map | cgroup | perf | net }\n" + " OBJECT := { prog | map | cgroup | perf | net | feature }\n" " " HELP_SPEC_OPTIONS "\n" "", bin_name, bin_name, bin_name); @@ -187,6 +187,7 @@ static const struct cmd cmds[] = { { "cgroup", do_cgroup }, { "perf", do_perf }, { "net", do_net }, + { "feature", do_feature }, { "version", do_version }, { 0 } }; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 052c91d4dc55..5cfc6601de9b 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -145,6 +145,7 @@ int do_cgroup(int argc, char **arg); int do_perf(int argc, char **arg); int do_net(int argc, char **arg); int do_tracelog(int argc, char **arg); +int do_feature(int argc, char **argv); int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what); int prog_parse_fd(int *argc, char ***argv); From 7a4522bbef0c4c63a87b5363840460996aff97c3 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:51 +0000 Subject: [PATCH 2/9] tools: bpftool: add probes for /proc/ eBPF parameters Add a set of probes to dump the eBPF-related parameters available from /proc/: availability of bpf() syscall for unprivileged users, JIT compiler status and hardening status, kallsyms exports status. Sample output: # bpftool feature probe kernel Scanning system configuration... bpf() syscall for unprivileged users is enabled JIT compiler is disabled JIT compiler hardening is disabled JIT compiler kallsyms exports are disabled Global memory limit for JIT compiler for unprivileged users \ is 264241152 bytes ... # bpftool --json --pretty feature probe kernel { "system_config": { "unprivileged_bpf_disabled": 0, "bpf_jit_enable": 0, "bpf_jit_harden": 0, "bpf_jit_kallsyms": 0, "bpf_jit_limit": 264241152 }, ... } These probes are skipped if procfs is not mounted. v4: - Add bpf_jit_limit parameter. v2: - Remove C-style macros output from this patch. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/feature.c | 187 ++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index 954fb12a5228..cedc44a61c6e 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,11 +14,29 @@ #include "main.h" +#ifndef PROC_SUPER_MAGIC +# define PROC_SUPER_MAGIC 0x9fa0 +#endif + enum probe_component { COMPONENT_UNSPEC, COMPONENT_KERNEL, }; +/* Miscellaneous utility functions */ + +static bool check_procfs(void) +{ + struct statfs st_fs; + + if (statfs("/proc", &st_fs) < 0) + return false; + if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC) + return false; + + return true; +} + /* Printing utility functions */ static void @@ -42,6 +61,153 @@ print_start_section(const char *json_title, const char *plain_title) /* Probing functions */ +static int read_procfs(const char *path) +{ + char *endptr, *line = NULL; + size_t len = 0; + FILE *fd; + int res; + + fd = fopen(path, "r"); + if (!fd) + return -1; + + res = getline(&line, &len, fd); + fclose(fd); + if (res < 0) + return -1; + + errno = 0; + res = strtol(line, &endptr, 10); + if (errno || *line == '\0' || *endptr != '\n') + res = -1; + free(line); + + return res; +} + +static void probe_unprivileged_disabled(void) +{ + int res; + + res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled"); + if (json_output) { + jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res); + } else { + switch (res) { + case 0: + printf("bpf() syscall for unprivileged users is enabled\n"); + break; + case 1: + printf("bpf() syscall restricted to privileged users\n"); + break; + case -1: + printf("Unable to retrieve required privileges for bpf() syscall\n"); + break; + default: + printf("bpf() syscall restriction has unknown value %d\n", res); + } + } +} + +static void probe_jit_enable(void) +{ + int res; + + res = read_procfs("/proc/sys/net/core/bpf_jit_enable"); + if (json_output) { + jsonw_int_field(json_wtr, "bpf_jit_enable", res); + } else { + switch (res) { + case 0: + printf("JIT compiler is disabled\n"); + break; + case 1: + printf("JIT compiler is enabled\n"); + break; + case 2: + printf("JIT compiler is enabled with debugging traces in kernel logs\n"); + break; + case -1: + printf("Unable to retrieve JIT-compiler status\n"); + break; + default: + printf("JIT-compiler status has unknown value %d\n", + res); + } + } +} + +static void probe_jit_harden(void) +{ + int res; + + res = read_procfs("/proc/sys/net/core/bpf_jit_harden"); + if (json_output) { + jsonw_int_field(json_wtr, "bpf_jit_harden", res); + } else { + switch (res) { + case 0: + printf("JIT compiler hardening is disabled\n"); + break; + case 1: + printf("JIT compiler hardening is enabled for unprivileged users\n"); + break; + case 2: + printf("JIT compiler hardening is enabled for all users\n"); + break; + case -1: + printf("Unable to retrieve JIT hardening status\n"); + break; + default: + printf("JIT hardening status has unknown value %d\n", + res); + } + } +} + +static void probe_jit_kallsyms(void) +{ + int res; + + res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms"); + if (json_output) { + jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res); + } else { + switch (res) { + case 0: + printf("JIT compiler kallsyms exports are disabled\n"); + break; + case 1: + printf("JIT compiler kallsyms exports are enabled for root\n"); + break; + case -1: + printf("Unable to retrieve JIT kallsyms export status\n"); + break; + default: + printf("JIT kallsyms exports status has unknown value %d\n", res); + } + } +} + +static void probe_jit_limit(void) +{ + int res; + + res = read_procfs("/proc/sys/net/core/bpf_jit_limit"); + if (json_output) { + jsonw_int_field(json_wtr, "bpf_jit_limit", res); + } else { + switch (res) { + case -1: + printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n"); + break; + default: + printf("Global memory limit for JIT compiler for unprivileged users is %d bytes\n", res); + } + } +} + static bool probe_bpf_syscall(void) { bool res; @@ -88,6 +254,27 @@ static int do_probe(int argc, char **argv) if (json_output) jsonw_start_object(json_wtr); + switch (target) { + case COMPONENT_KERNEL: + case COMPONENT_UNSPEC: + print_start_section("system_config", + "Scanning system configuration..."); + if (check_procfs()) { + probe_unprivileged_disabled(); + probe_jit_enable(); + probe_jit_harden(); + probe_jit_kallsyms(); + probe_jit_limit(); + } else { + p_info("/* procfs not mounted, skipping related probes */"); + } + if (json_output) + jsonw_end_object(json_wtr); + else + printf("\n"); + break; + } + print_start_section("syscall_config", "Scanning system call availability..."); From 4567b983f78c8c496d09868a3b108fb496c8d2c9 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:52 +0000 Subject: [PATCH 3/9] tools: bpftool: add probes for kernel configuration options Add probes to dump a number of options set (or not set) for compiling the kernel image. These parameters provide information about what BPF components should be available on the system. A number of them are not directly related to eBPF, but are in fact used in the kernel as conditions on which to compile, or not to compile, some of the eBPF helper functions. Sample output: # bpftool feature probe kernel Scanning system configuration... ... CONFIG_BPF is set to y CONFIG_BPF_SYSCALL is set to y CONFIG_HAVE_EBPF_JIT is set to y ... # bpftool --pretty --json feature probe kernel { "system_config": { ... "CONFIG_BPF": "y", "CONFIG_BPF_SYSCALL": "y", "CONFIG_HAVE_EBPF_JIT": "y", ... } } v5: - Declare options[] array in probe_kernel_image_config() as static. v4: - Add some options to the list: - CONFIG_TRACING - CONFIG_KPROBE_EVENTS - CONFIG_UPROBE_EVENTS - CONFIG_FTRACE_SYSCALLS - Add comments about those options in the source code. v3: - Add a comment about /proc/config.gz not being supported as a path for the config file at this time. - Use p_info() instead of p_err() on failure to get options from config file, as bpftool keeps probing other parameters and that would possibly create duplicate "error" entries for JSON. v2: - Remove C-style macros output from this patch. - NOT addressed: grouping of those config options into subsections (I don't see an easy way of grouping them at the moment, please see also the discussion on v1 thread). Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/feature.c | 182 ++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index cedc44a61c6e..4a2867439ab7 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res) printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); } +static void print_kernel_option(const char *name, const char *value) +{ + char *endptr; + int res; + + if (json_output) { + if (!value) { + jsonw_null_field(json_wtr, name); + return; + } + errno = 0; + res = strtol(value, &endptr, 0); + if (!errno && *endptr == '\n') + jsonw_int_field(json_wtr, name, res); + else + jsonw_string_field(json_wtr, name, value); + } else { + if (value) + printf("%s is set to %s\n", name, value); + else + printf("%s is not set\n", name); + } +} + static void print_start_section(const char *json_title, const char *plain_title) { @@ -208,6 +232,163 @@ static void probe_jit_limit(void) } } +static char *get_kernel_config_option(FILE *fd, const char *option) +{ + size_t line_n = 0, optlen = strlen(option); + char *res, *strval, *line = NULL; + ssize_t n; + + rewind(fd); + while ((n = getline(&line, &line_n, fd)) > 0) { + if (strncmp(line, option, optlen)) + continue; + /* Check we have at least '=', value, and '\n' */ + if (strlen(line) < optlen + 3) + continue; + if (*(line + optlen) != '=') + continue; + + /* Trim ending '\n' */ + line[strlen(line) - 1] = '\0'; + + /* Copy and return config option value */ + strval = line + optlen + 1; + res = strdup(strval); + free(line); + return res; + } + free(line); + + return NULL; +} + +static void probe_kernel_image_config(void) +{ + static const char * const options[] = { + /* Enable BPF */ + "CONFIG_BPF", + /* Enable bpf() syscall */ + "CONFIG_BPF_SYSCALL", + /* Does selected architecture support eBPF JIT compiler */ + "CONFIG_HAVE_EBPF_JIT", + /* Compile eBPF JIT compiler */ + "CONFIG_BPF_JIT", + /* Avoid compiling eBPF interpreter (use JIT only) */ + "CONFIG_BPF_JIT_ALWAYS_ON", + + /* cgroups */ + "CONFIG_CGROUPS", + /* BPF programs attached to cgroups */ + "CONFIG_CGROUP_BPF", + /* bpf_get_cgroup_classid() helper */ + "CONFIG_CGROUP_NET_CLASSID", + /* bpf_skb_{,ancestor_}cgroup_id() helpers */ + "CONFIG_SOCK_CGROUP_DATA", + + /* Tracing: attach BPF to kprobes, tracepoints, etc. */ + "CONFIG_BPF_EVENTS", + /* Kprobes */ + "CONFIG_KPROBE_EVENTS", + /* Uprobes */ + "CONFIG_UPROBE_EVENTS", + /* Tracepoints */ + "CONFIG_TRACING", + /* Syscall tracepoints */ + "CONFIG_FTRACE_SYSCALLS", + /* bpf_override_return() helper support for selected arch */ + "CONFIG_FUNCTION_ERROR_INJECTION", + /* bpf_override_return() helper */ + "CONFIG_BPF_KPROBE_OVERRIDE", + + /* Network */ + "CONFIG_NET", + /* AF_XDP sockets */ + "CONFIG_XDP_SOCKETS", + /* BPF_PROG_TYPE_LWT_* and related helpers */ + "CONFIG_LWTUNNEL_BPF", + /* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */ + "CONFIG_NET_ACT_BPF", + /* BPF_PROG_TYPE_SCHED_CLS, TC filters */ + "CONFIG_NET_CLS_BPF", + /* TC clsact qdisc */ + "CONFIG_NET_CLS_ACT", + /* Ingress filtering with TC */ + "CONFIG_NET_SCH_INGRESS", + /* bpf_skb_get_xfrm_state() helper */ + "CONFIG_XFRM", + /* bpf_get_route_realm() helper */ + "CONFIG_IP_ROUTE_CLASSID", + /* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */ + "CONFIG_IPV6_SEG6_BPF", + /* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */ + "CONFIG_BPF_LIRC_MODE2", + /* BPF stream parser and BPF socket maps */ + "CONFIG_BPF_STREAM_PARSER", + /* xt_bpf module for passing BPF programs to netfilter */ + "CONFIG_NETFILTER_XT_MATCH_BPF", + /* bpfilter back-end for iptables */ + "CONFIG_BPFILTER", + /* bpftilter module with "user mode helper" */ + "CONFIG_BPFILTER_UMH", + + /* test_bpf module for BPF tests */ + "CONFIG_TEST_BPF", + }; + char *value, *buf = NULL; + struct utsname utsn; + char path[PATH_MAX]; + size_t i, n; + ssize_t ret; + FILE *fd; + + if (uname(&utsn)) + goto no_config; + + snprintf(path, sizeof(path), "/boot/config-%s", utsn.release); + + fd = fopen(path, "r"); + if (!fd && errno == ENOENT) { + /* Some distributions put the config file at /proc/config, give + * it a try. + * Sometimes it is also at /proc/config.gz but we do not try + * this one for now, it would require linking against libz. + */ + fd = fopen("/proc/config", "r"); + } + if (!fd) { + p_info("skipping kernel config, can't open file: %s", + strerror(errno)); + goto no_config; + } + /* Sanity checks */ + ret = getline(&buf, &n, fd); + ret = getline(&buf, &n, fd); + if (!buf || !ret) { + p_info("skipping kernel config, can't read from file: %s", + strerror(errno)); + free(buf); + goto no_config; + } + if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) { + p_info("skipping kernel config, can't find correct file"); + free(buf); + goto no_config; + } + free(buf); + + for (i = 0; i < ARRAY_SIZE(options); i++) { + value = get_kernel_config_option(fd, options[i]); + print_kernel_option(options[i], value); + free(value); + } + fclose(fd); + return; + +no_config: + for (i = 0; i < ARRAY_SIZE(options); i++) + print_kernel_option(options[i], NULL); +} + static bool probe_bpf_syscall(void) { bool res; @@ -268,6 +449,7 @@ static int do_probe(int argc, char **argv) } else { p_info("/* procfs not mounted, skipping related probes */"); } + probe_kernel_image_config(); if (json_output) jsonw_end_object(json_wtr); else From 1bf4b05810fe38c5f09973295e8d4234a4fd5d87 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:53 +0000 Subject: [PATCH 4/9] tools: bpftool: add probes for eBPF program types Introduce probes for supported BPF program types in libbpf, and call it from bpftool to test what types are available on the system. The probe simply consists in loading a very basic program of that type and see if the verifier complains or not. Sample output: # bpftool feature probe kernel ... Scanning eBPF program types... eBPF program_type socket_filter is available eBPF program_type kprobe is available eBPF program_type sched_cls is available ... # bpftool --json --pretty feature probe kernel { ... "program_types": { "have_socket_filter_prog_type": true, "have_kprobe_prog_type": true, "have_sched_cls_prog_type": true, ... } } v5: - In libbpf.map, move global symbol to a new LIBBPF_0.0.2 section. - Rename (non-API function) prog_load() as probe_load(). v3: - Get kernel version for checking kprobes availability from libbpf instead of from bpftool. Do not pass kernel_version as an argument when calling libbpf probes. - Use a switch with all enum values for setting specific program parameters just before probing, so that gcc complains at compile time (-Wswitch-enum) if new prog types were added to the kernel but libbpf was not updated. - Add a comment in libbpf.h about setrlimit() usage to allow many consecutive probe attempts. v2: - Move probes from bpftool to libbpf. - Remove C-style macros output from this patch. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/feature.c | 48 +++++++++++++++++- tools/lib/bpf/Build | 2 +- tools/lib/bpf/libbpf.h | 11 ++++ tools/lib/bpf/libbpf.map | 5 ++ tools/lib/bpf/libbpf_probes.c | 95 +++++++++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 tools/lib/bpf/libbpf_probes.c diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index 4a2867439ab7..d6508dde4808 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (c) 2019 Netronome Systems, Inc. */ +#include #include #include #include @@ -11,6 +12,7 @@ #include #include +#include #include "main.h" @@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title) } } +static void +print_end_then_start_section(const char *json_title, const char *plain_title) +{ + if (json_output) + jsonw_end_object(json_wtr); + else + printf("\n"); + + print_start_section(json_title, plain_title); +} + /* Probing functions */ static int read_procfs(const char *path) @@ -403,9 +416,33 @@ static bool probe_bpf_syscall(void) return res; } +static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types) +{ + const char *plain_comment = "eBPF program_type "; + char feat_name[128], plain_desc[128]; + size_t maxlen; + bool res; + + res = bpf_probe_prog_type(prog_type, 0); + + supported_types[prog_type] |= res; + + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; + if (strlen(prog_type_name[prog_type]) > maxlen) { + p_info("program type name too long"); + return; + } + + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]); + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]); + print_bool_feature(feat_name, plain_desc, res); +} + static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; + bool supported_types[128] = {}; + unsigned int i; /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). * Let's approximate, and restrict usage to root user only. @@ -460,8 +497,17 @@ static int do_probe(int argc, char **argv) print_start_section("syscall_config", "Scanning system call availability..."); - probe_bpf_syscall(); + if (!probe_bpf_syscall()) + /* bpf() syscall unavailable, don't probe other BPF features */ + goto exit_close_json; + print_end_then_start_section("program_types", + "Scanning eBPF program types..."); + + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) + probe_prog_type(i, supported_types); + +exit_close_json: if (json_output) { /* End current "section" of probes */ jsonw_end_object(json_wtr); diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build index 197b40f5b5c6..bfd9bfc82c3b 100644 --- a/tools/lib/bpf/Build +++ b/tools/lib/bpf/Build @@ -1 +1 @@ -libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 5f68d7b75215..8e63821109ab 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -355,6 +355,17 @@ LIBBPF_API const struct bpf_line_info * bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo, __u32 insn_off, __u32 nr_skip); +/* + * Probe for supported system features + * + * Note that running many of these probes in a short amount of time can cause + * the kernel to reach the maximal size of lockable memory allowed for the + * user, causing subsequent probes to fail. In this case, the caller may want + * to adjust that limit with setrlimit(). + */ +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type, + __u32 ifindex); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index cd02cd4e2cc3..c7ec3ffa24e9 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -124,3 +124,8 @@ LIBBPF_0.0.1 { local: *; }; + +LIBBPF_0.0.2 { + global: + bpf_probe_prog_type; +} LIBBPF_0.0.1; diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c new file mode 100644 index 000000000000..056c0c186f2a --- /dev/null +++ b/tools/lib/bpf/libbpf_probes.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2019 Netronome Systems, Inc. */ + +#include +#include +#include + +#include +#include + +#include "bpf.h" +#include "libbpf.h" + +static int get_kernel_version(void) +{ + int version, subversion, patchlevel; + struct utsname utsn; + + /* Return 0 on failure, and attempt to probe with empty kversion */ + if (uname(&utsn)) + return 0; + + if (sscanf(utsn.release, "%d.%d.%d", + &version, &subversion, &patchlevel) != 3) + return 0; + + return (version << 16) + (subversion << 8) + patchlevel; +} + +static void +probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, + size_t insns_cnt, char *buf, size_t buf_len, __u32 ifindex) +{ + struct bpf_load_program_attr xattr = {}; + int fd; + + switch (prog_type) { + case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: + xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT; + break; + case BPF_PROG_TYPE_KPROBE: + xattr.kern_version = get_kernel_version(); + break; + case BPF_PROG_TYPE_UNSPEC: + case BPF_PROG_TYPE_SOCKET_FILTER: + case BPF_PROG_TYPE_SCHED_CLS: + case BPF_PROG_TYPE_SCHED_ACT: + case BPF_PROG_TYPE_TRACEPOINT: + case BPF_PROG_TYPE_XDP: + case BPF_PROG_TYPE_PERF_EVENT: + case BPF_PROG_TYPE_CGROUP_SKB: + case BPF_PROG_TYPE_CGROUP_SOCK: + case BPF_PROG_TYPE_LWT_IN: + case BPF_PROG_TYPE_LWT_OUT: + case BPF_PROG_TYPE_LWT_XMIT: + case BPF_PROG_TYPE_SOCK_OPS: + case BPF_PROG_TYPE_SK_SKB: + case BPF_PROG_TYPE_CGROUP_DEVICE: + case BPF_PROG_TYPE_SK_MSG: + case BPF_PROG_TYPE_RAW_TRACEPOINT: + case BPF_PROG_TYPE_LWT_SEG6LOCAL: + case BPF_PROG_TYPE_LIRC_MODE2: + case BPF_PROG_TYPE_SK_REUSEPORT: + case BPF_PROG_TYPE_FLOW_DISSECTOR: + default: + break; + } + + xattr.prog_type = prog_type; + xattr.insns = insns; + xattr.insns_cnt = insns_cnt; + xattr.license = "GPL"; + xattr.prog_ifindex = ifindex; + + fd = bpf_load_program_xattr(&xattr, buf, buf_len); + if (fd >= 0) + close(fd); +} + +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex) +{ + struct bpf_insn insns[2] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN() + }; + + if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS) + /* nfp returns -EINVAL on exit(0) with TC offload */ + insns[0].imm = 2; + + errno = 0; + probe_load(prog_type, insns, ARRAY_SIZE(insns), NULL, 0, ifindex); + + return errno != EINVAL && errno != EOPNOTSUPP; +} From f99e166397f0298fe78bce24c55c6d074f9bf196 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:54 +0000 Subject: [PATCH 5/9] tools: bpftool: add probes for eBPF map types Add new probes for eBPF map types, to detect what are the ones available on the system. Try creating one map of each type, and see if the kernel complains. Sample output: # bpftool feature probe kernel ... Scanning eBPF map types... eBPF map_type hash is available eBPF map_type array is available eBPF map_type prog_array is available ... # bpftool --json --pretty feature probe kernel { ... "map_types": { "have_hash_map_type": true, "have_array_map_type": true, "have_prog_array_map_type": true, ... } } v5: - In libbpf.map, move global symbol to the new LIBBPF_0.0.2 section. v3: - Use a switch with all enum values for setting specific map parameters, so that gcc complains at compile time (-Wswitch-enum) if new map types were added to the kernel but libbpf was not updated. v2: - Move probes from bpftool to libbpf. - Remove C-style macros output from this patch. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/feature.c | 26 +++++++++++ tools/bpf/bpftool/main.h | 3 ++ tools/bpf/bpftool/map.c | 4 +- tools/lib/bpf/libbpf.h | 1 + tools/lib/bpf/libbpf.map | 1 + tools/lib/bpf/libbpf_probes.c | 84 +++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index d6508dde4808..cc731475c74b 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -438,6 +438,26 @@ static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types) print_bool_feature(feat_name, plain_desc, res); } +static void probe_map_type(enum bpf_map_type map_type) +{ + const char *plain_comment = "eBPF map_type "; + char feat_name[128], plain_desc[128]; + size_t maxlen; + bool res; + + res = bpf_probe_map_type(map_type, 0); + + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; + if (strlen(map_type_name[map_type]) > maxlen) { + p_info("map type name too long"); + return; + } + + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]); + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]); + print_bool_feature(feat_name, plain_desc, res); +} + static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; @@ -507,6 +527,12 @@ static int do_probe(int argc, char **argv) for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) probe_prog_type(i, supported_types); + print_end_then_start_section("map_types", + "Scanning eBPF map types..."); + + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++) + probe_map_type(i); + exit_close_json: if (json_output) { /* End current "section" of probes */ diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 5cfc6601de9b..d7dd84d3c660 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = { [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", }; +extern const char * const map_type_name[]; +extern const size_t map_type_name_size; + enum bpf_obj_type { BPF_OBJ_UNKNOWN, BPF_OBJ_PROG, diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 850c99ac980f..f15c520195b7 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -21,7 +21,7 @@ #include "json_writer.h" #include "main.h" -static const char * const map_type_name[] = { +const char * const map_type_name[] = { [BPF_MAP_TYPE_UNSPEC] = "unspec", [BPF_MAP_TYPE_HASH] = "hash", [BPF_MAP_TYPE_ARRAY] = "array", @@ -48,6 +48,8 @@ static const char * const map_type_name[] = { [BPF_MAP_TYPE_STACK] = "stack", }; +const size_t map_type_name_size = ARRAY_SIZE(map_type_name); + static bool map_is_per_cpu(__u32 type) { return type == BPF_MAP_TYPE_PERCPU_HASH || diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 8e63821109ab..72385f6f9415 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -365,6 +365,7 @@ bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo, */ LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex); +LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex); #ifdef __cplusplus } /* extern "C" */ diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index c7ec3ffa24e9..bb2dfc3b2d7b 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -127,5 +127,6 @@ LIBBPF_0.0.1 { LIBBPF_0.0.2 { global: + bpf_probe_map_type; bpf_probe_prog_type; } LIBBPF_0.0.1; diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c index 056c0c186f2a..f511bd317b87 100644 --- a/tools/lib/bpf/libbpf_probes.c +++ b/tools/lib/bpf/libbpf_probes.c @@ -93,3 +93,87 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex) return errno != EINVAL && errno != EOPNOTSUPP; } + +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex) +{ + int key_size, value_size, max_entries, map_flags; + struct bpf_create_map_attr attr = {}; + int fd = -1, fd_inner; + + key_size = sizeof(__u32); + value_size = sizeof(__u32); + max_entries = 1; + map_flags = 0; + + switch (map_type) { + case BPF_MAP_TYPE_STACK_TRACE: + value_size = sizeof(__u64); + break; + case BPF_MAP_TYPE_LPM_TRIE: + key_size = sizeof(__u64); + value_size = sizeof(__u64); + map_flags = BPF_F_NO_PREALLOC; + break; + case BPF_MAP_TYPE_CGROUP_STORAGE: + case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: + key_size = sizeof(struct bpf_cgroup_storage_key); + value_size = sizeof(__u64); + max_entries = 0; + break; + case BPF_MAP_TYPE_QUEUE: + case BPF_MAP_TYPE_STACK: + key_size = 0; + break; + case BPF_MAP_TYPE_UNSPEC: + case BPF_MAP_TYPE_HASH: + case BPF_MAP_TYPE_ARRAY: + case BPF_MAP_TYPE_PROG_ARRAY: + case BPF_MAP_TYPE_PERF_EVENT_ARRAY: + case BPF_MAP_TYPE_PERCPU_HASH: + case BPF_MAP_TYPE_PERCPU_ARRAY: + case BPF_MAP_TYPE_CGROUP_ARRAY: + case BPF_MAP_TYPE_LRU_HASH: + case BPF_MAP_TYPE_LRU_PERCPU_HASH: + case BPF_MAP_TYPE_ARRAY_OF_MAPS: + case BPF_MAP_TYPE_HASH_OF_MAPS: + case BPF_MAP_TYPE_DEVMAP: + case BPF_MAP_TYPE_SOCKMAP: + case BPF_MAP_TYPE_CPUMAP: + case BPF_MAP_TYPE_XSKMAP: + case BPF_MAP_TYPE_SOCKHASH: + case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: + default: + break; + } + + if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS || + map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { + /* TODO: probe for device, once libbpf has a function to create + * map-in-map for offload + */ + if (ifindex) + return false; + + fd_inner = bpf_create_map(BPF_MAP_TYPE_HASH, + sizeof(__u32), sizeof(__u32), 1, 0); + if (fd_inner < 0) + return false; + fd = bpf_create_map_in_map(map_type, NULL, sizeof(__u32), + fd_inner, 1, 0); + close(fd_inner); + } else { + /* Note: No other restriction on map type probes for offload */ + attr.map_type = map_type; + attr.key_size = key_size; + attr.value_size = value_size; + attr.max_entries = max_entries; + attr.map_flags = map_flags; + attr.map_ifindex = ifindex; + + fd = bpf_create_map_xattr(&attr); + } + if (fd >= 0) + close(fd); + + return fd >= 0; +} From 2d3ea5e85dd867712ba8747cb01c2d88376ead5c Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:55 +0000 Subject: [PATCH 6/9] tools: bpftool: add probes for eBPF helper functions Similarly to what was done for program types and map types, add a set of probes to test the availability of the different eBPF helper functions on the current system. For each known program type, all known helpers are tested, in order to establish a compatibility matrix. Output is provided as a set of lists of available helpers, one per program type. Sample output: # bpftool feature probe kernel ... Scanning eBPF helper functions... eBPF helpers supported for program type socket_filter: - bpf_map_lookup_elem - bpf_map_update_elem - bpf_map_delete_elem ... eBPF helpers supported for program type kprobe: - bpf_map_lookup_elem - bpf_map_update_elem - bpf_map_delete_elem ... # bpftool --json --pretty feature probe kernel { ... "helpers": { "socket_filter_available_helpers": ["bpf_map_lookup_elem", \ "bpf_map_update_elem","bpf_map_delete_elem", ... ], "kprobe_available_helpers": ["bpf_map_lookup_elem", \ "bpf_map_update_elem","bpf_map_delete_elem", ... ], ... } } v5: - In libbpf.map, move global symbol to the new LIBBPF_0.0.2 section. v4: - Use "enum bpf_func_id" instead of "__u32" in bpf_probe_helper() declaration for the type of the argument used to pass the id of the helper to probe. - Undef BPF_HELPER_MAKE_ENTRY after using it. v3: - Do not pass kernel version from bpftool to libbpf probes (kernel version for testing program with kprobes is retrieved directly from libbpf). - Dump one list of available helpers per program type (instead of one list of compatible program types per helper). v2: - Move probes from bpftool to libbpf. - Test all program types for each helper, print a list of working prog types for each helper. - Fall back on include/uapi/linux/bpf.h for names and ids of helpers. - Remove C-style macros output from this patch. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- .../bpftool/Documentation/bpftool-feature.rst | 4 ++ tools/bpf/bpftool/feature.c | 51 +++++++++++++++ tools/lib/bpf/libbpf.h | 2 + tools/lib/bpf/libbpf.map | 1 + tools/lib/bpf/libbpf_probes.c | 63 +++++++++++++++++++ 5 files changed, 121 insertions(+) diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 40ac13c0b782..255e3b3629a0 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -30,6 +30,10 @@ DESCRIPTION Keyword **kernel** can be omitted. + Note that when probed, some eBPF helpers (e.g. + **bpf_trace_printk**\ () or **bpf_probe_write_user**\ ()) may + print warnings to kernel logs. + **bpftool feature help** Print short help message. diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index cc731475c74b..55c8d215ca44 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -25,6 +25,13 @@ enum probe_component { COMPONENT_KERNEL, }; +#define BPF_HELPER_MAKE_ENTRY(name) [BPF_FUNC_ ## name] = "bpf_" # name +static const char * const helper_name[] = { + __BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY) +}; + +#undef BPF_HELPER_MAKE_ENTRY + /* Miscellaneous utility functions */ static bool check_procfs(void) @@ -458,6 +465,44 @@ static void probe_map_type(enum bpf_map_type map_type) print_bool_feature(feat_name, plain_desc, res); } +static void +probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type) +{ + const char *ptype_name = prog_type_name[prog_type]; + char feat_name[128]; + unsigned int id; + bool res; + + if (json_output) { + sprintf(feat_name, "%s_available_helpers", ptype_name); + jsonw_name(json_wtr, feat_name); + jsonw_start_array(json_wtr); + } else { + printf("eBPF helpers supported for program type %s:", + ptype_name); + } + + for (id = 1; id < ARRAY_SIZE(helper_name); id++) { + if (!supported_type) + res = false; + else + res = bpf_probe_helper(id, prog_type, 0); + + if (json_output) { + if (res) + jsonw_string(json_wtr, helper_name[id]); + } else { + if (res) + printf("\n\t- %s", helper_name[id]); + } + } + + if (json_output) + jsonw_end_array(json_wtr); + else + printf("\n"); +} + static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; @@ -533,6 +578,12 @@ static int do_probe(int argc, char **argv) for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++) probe_map_type(i); + print_end_then_start_section("helpers", + "Scanning eBPF helper functions..."); + + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) + probe_helpers_for_progtype(i, supported_types[i]); + exit_close_json: if (json_output) { /* End current "section" of probes */ diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 72385f6f9415..62ae6cb93da1 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -366,6 +366,8 @@ bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo, LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex); LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex); +LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id, + enum bpf_prog_type prog_type, __u32 ifindex); #ifdef __cplusplus } /* extern "C" */ diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index bb2dfc3b2d7b..266bc95d0142 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -127,6 +127,7 @@ LIBBPF_0.0.1 { LIBBPF_0.0.2 { global: + bpf_probe_helper; bpf_probe_map_type; bpf_probe_prog_type; } LIBBPF_0.0.1; diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c index f511bd317b87..8c3a1c04dcb2 100644 --- a/tools/lib/bpf/libbpf_probes.c +++ b/tools/lib/bpf/libbpf_probes.c @@ -2,7 +2,11 @@ /* Copyright (c) 2019 Netronome Systems, Inc. */ #include +#include +#include +#include #include +#include #include #include @@ -11,6 +15,37 @@ #include "bpf.h" #include "libbpf.h" +static bool grep(const char *buffer, const char *pattern) +{ + return !!strstr(buffer, pattern); +} + +static int get_vendor_id(int ifindex) +{ + char ifname[IF_NAMESIZE], path[64], buf[8]; + ssize_t len; + int fd; + + if (!if_indextoname(ifindex, ifname)) + return -1; + + snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname); + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len < 0) + return -1; + if (len >= (ssize_t)sizeof(buf)) + return -1; + buf[len] = '\0'; + + return strtol(buf, NULL, 0); +} + static int get_kernel_version(void) { int version, subversion, patchlevel; @@ -177,3 +212,31 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex) return fd >= 0; } + +bool bpf_probe_helper(enum bpf_func_id id, enum bpf_prog_type prog_type, + __u32 ifindex) +{ + struct bpf_insn insns[2] = { + BPF_EMIT_CALL(id), + BPF_EXIT_INSN() + }; + char buf[4096] = {}; + bool res; + + probe_load(prog_type, insns, ARRAY_SIZE(insns), buf, sizeof(buf), + ifindex); + res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); + + if (ifindex) { + switch (get_vendor_id(ifindex)) { + case 0x19ee: /* Netronome specific */ + res = res && !grep(buf, "not supported by FW") && + !grep(buf, "unsupported function id"); + break; + default: + break; + } + } + + return res; +} From d267cff46753b0e8b2f169ff4a3f1bb40c2387a8 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:56 +0000 Subject: [PATCH 7/9] tools: bpftool: add C-style "#define" output for probes Make bpftool able to dump a subset of the parameters collected by probing the system as a listing of C-style #define macros, so that external projects can reuse the result of this probing and build BPF-based project in accordance with the features available on the system. The new "macros" keyword is used to select this output. An additional "prefix" keyword is added so that users can select a custom prefix for macro names, in order to avoid any namespace conflict. Sample output: # bpftool feature probe kernel macros prefix FOO_ /*** System call availability ***/ #define FOO_HAVE_BPF_SYSCALL /*** eBPF program types ***/ #define FOO_HAVE_SOCKET_FILTER_PROG_TYPE #define FOO_HAVE_KPROBE_PROG_TYPE #define FOO_HAVE_SCHED_CLS_PROG_TYPE ... /*** eBPF map types ***/ #define FOO_HAVE_HASH_MAP_TYPE #define FOO_HAVE_ARRAY_MAP_TYPE #define FOO_HAVE_PROG_ARRAY_MAP_TYPE ... /*** eBPF helper functions ***/ /* * Use FOO_HAVE_PROG_TYPE_HELPER(prog_type_name, helper_name) * to determine if is available for , * e.g. * #if FOO_HAVE_PROG_TYPE_HELPER(xdp, bpf_redirect) * // do stuff with this helper * #elif * // use a workaround * #endif */ #define FOO_HAVE_PROG_TYPE_HELPER(prog_type, helper) \ FOO_BPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper ... #define FOO_BPF__PROG_TYPE_socket_filter__HELPER_bpf_probe_read 0 #define FOO_BPF__PROG_TYPE_socket_filter__HELPER_bpf_ktime_get_ns 1 #define FOO_BPF__PROG_TYPE_socket_filter__HELPER_bpf_trace_printk 1 ... v3: - Change output for helpers again: add a HAVE_PROG_TYPE_HELPER(type, helper) macro that can be used to tell if is available for program . v2: - #define-based output added as a distinct patch. - "HAVE_" prefix appended to macro names. - Output limited to bpf() syscall availability, BPF prog and map types, helper functions. In this version kernel config options, procfs parameter or kernel version are intentionally left aside. - Following the change on helper probes, format for helper probes in this output style has changed (now a list of compatible program types). Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- .../bpftool/Documentation/bpftool-feature.rst | 13 +- tools/bpf/bpftool/feature.c | 148 ++++++++++++++---- 2 files changed, 132 insertions(+), 29 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 255e3b3629a0..53092995f46b 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -19,15 +19,24 @@ SYNOPSIS MAP COMMANDS ============= -| **bpftool** **feature probe** [**kernel**] +| **bpftool** **feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]] | **bpftool** **feature help** DESCRIPTION =========== - **bpftool feature probe** [**kernel**] + **bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]] Probe the running kernel and dump a number of eBPF-related parameters, such as availability of the **bpf()** system call. + If the **macros** keyword (but not the **-j** option) is + passed, a subset of the output is dumped as a list of + **#define** macros that are ready to be included in a C + header file, for example. If, additionally, **prefix** is + used to define a *PREFIX*, the provided string will be used + as a prefix to the names of the macros: this can be used to + avoid conflicts on macro names when including the output of + this command as a header file. + Keyword **kernel** can be omitted. Note that when probed, some eBPF helpers (e.g. diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index 55c8d215ca44..a62e637953b7 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -46,13 +46,25 @@ static bool check_procfs(void) return true; } +static void uppercase(char *str, size_t len) +{ + size_t i; + + for (i = 0; i < len && str[i] != '\0'; i++) + str[i] = toupper(str[i]); +} + /* Printing utility functions */ static void -print_bool_feature(const char *feat_name, const char *plain_name, bool res) +print_bool_feature(const char *feat_name, const char *plain_name, + const char *define_name, bool res, const char *define_prefix) { if (json_output) jsonw_bool_field(json_wtr, feat_name, res); + else if (define_prefix) + printf("#define %s%sHAVE_%s\n", define_prefix, + res ? "" : "NO_", define_name); else printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); } @@ -62,6 +74,8 @@ static void print_kernel_option(const char *name, const char *value) char *endptr; int res; + /* No support for C-style ouptut */ + if (json_output) { if (!value) { jsonw_null_field(json_wtr, name); @@ -82,25 +96,31 @@ static void print_kernel_option(const char *name, const char *value) } static void -print_start_section(const char *json_title, const char *plain_title) +print_start_section(const char *json_title, const char *plain_title, + const char *define_comment, const char *define_prefix) { if (json_output) { jsonw_name(json_wtr, json_title); jsonw_start_object(json_wtr); + } else if (define_prefix) { + printf("%s\n", define_comment); } else { printf("%s\n", plain_title); } } static void -print_end_then_start_section(const char *json_title, const char *plain_title) +print_end_then_start_section(const char *json_title, const char *plain_title, + const char *define_comment, + const char *define_prefix) { if (json_output) jsonw_end_object(json_wtr); else printf("\n"); - print_start_section(json_title, plain_title); + print_start_section(json_title, plain_title, define_comment, + define_prefix); } /* Probing functions */ @@ -134,6 +154,8 @@ static void probe_unprivileged_disabled(void) { int res; + /* No support for C-style ouptut */ + res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled"); if (json_output) { jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res); @@ -158,6 +180,8 @@ static void probe_jit_enable(void) { int res; + /* No support for C-style ouptut */ + res = read_procfs("/proc/sys/net/core/bpf_jit_enable"); if (json_output) { jsonw_int_field(json_wtr, "bpf_jit_enable", res); @@ -186,6 +210,8 @@ static void probe_jit_harden(void) { int res; + /* No support for C-style ouptut */ + res = read_procfs("/proc/sys/net/core/bpf_jit_harden"); if (json_output) { jsonw_int_field(json_wtr, "bpf_jit_harden", res); @@ -214,6 +240,8 @@ static void probe_jit_kallsyms(void) { int res; + /* No support for C-style ouptut */ + res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms"); if (json_output) { jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res); @@ -238,6 +266,8 @@ static void probe_jit_limit(void) { int res; + /* No support for C-style ouptut */ + res = read_procfs("/proc/sys/net/core/bpf_jit_limit"); if (json_output) { jsonw_int_field(json_wtr, "bpf_jit_limit", res); @@ -409,7 +439,7 @@ no_config: print_kernel_option(options[i], NULL); } -static bool probe_bpf_syscall(void) +static bool probe_bpf_syscall(const char *define_prefix) { bool res; @@ -418,15 +448,18 @@ static bool probe_bpf_syscall(void) print_bool_feature("have_bpf_syscall", "bpf() syscall", - res); + "BPF_SYSCALL", + res, define_prefix); return res; } -static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types) +static void +probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, + const char *define_prefix) { + char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF program_type "; - char feat_name[128], plain_desc[128]; size_t maxlen; bool res; @@ -441,14 +474,18 @@ static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types) } sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]); + sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]); + uppercase(define_name, sizeof(define_name)); sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]); - print_bool_feature(feat_name, plain_desc, res); + print_bool_feature(feat_name, plain_desc, define_name, res, + define_prefix); } -static void probe_map_type(enum bpf_map_type map_type) +static void +probe_map_type(enum bpf_map_type map_type, const char *define_prefix) { + char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF map_type "; - char feat_name[128], plain_desc[128]; size_t maxlen; bool res; @@ -461,12 +498,16 @@ static void probe_map_type(enum bpf_map_type map_type) } sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]); + sprintf(define_name, "%s_map_type", map_type_name[map_type]); + uppercase(define_name, sizeof(define_name)); sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]); - print_bool_feature(feat_name, plain_desc, res); + print_bool_feature(feat_name, plain_desc, define_name, res, + define_prefix); } static void -probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type) +probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type, + const char *define_prefix) { const char *ptype_name = prog_type_name[prog_type]; char feat_name[128]; @@ -477,7 +518,7 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type) sprintf(feat_name, "%s_available_helpers", ptype_name); jsonw_name(json_wtr, feat_name); jsonw_start_array(json_wtr); - } else { + } else if (!define_prefix) { printf("eBPF helpers supported for program type %s:", ptype_name); } @@ -491,6 +532,10 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type) if (json_output) { if (res) jsonw_string(json_wtr, helper_name[id]); + } else if (define_prefix) { + printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n", + define_prefix, ptype_name, helper_name[id], + res ? "1" : "0"); } else { if (res) printf("\n\t- %s", helper_name[id]); @@ -499,13 +544,14 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type) if (json_output) jsonw_end_array(json_wtr); - else + else if (!define_prefix) printf("\n"); } static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; + const char *define_prefix = NULL; bool supported_types[128] = {}; unsigned int i; @@ -527,21 +573,45 @@ static int do_probe(int argc, char **argv) } target = COMPONENT_KERNEL; NEXT_ARG(); + } else if (is_prefix(*argv, "macros") && !define_prefix) { + define_prefix = ""; + NEXT_ARG(); + } else if (is_prefix(*argv, "prefix")) { + if (!define_prefix) { + p_err("'prefix' argument can only be use after 'macros'"); + return -1; + } + if (strcmp(define_prefix, "")) { + p_err("'prefix' already defined"); + return -1; + } + NEXT_ARG(); + + if (!REQ_ARGS(1)) + return -1; + define_prefix = GET_ARG(); } else { - p_err("expected no more arguments, 'kernel', got: '%s'?", + p_err("expected no more arguments, 'kernel', 'macros' or 'prefix', got: '%s'?", *argv); return -1; } } - if (json_output) + if (json_output) { + define_prefix = NULL; jsonw_start_object(json_wtr); + } switch (target) { case COMPONENT_KERNEL: case COMPONENT_UNSPEC: + if (define_prefix) + break; + print_start_section("system_config", - "Scanning system configuration..."); + "Scanning system configuration...", + NULL, /* define_comment never used here */ + NULL); /* define_prefix always NULL here */ if (check_procfs()) { probe_unprivileged_disabled(); probe_jit_enable(); @@ -560,29 +630,53 @@ static int do_probe(int argc, char **argv) } print_start_section("syscall_config", - "Scanning system call availability..."); + "Scanning system call availability...", + "/*** System call availability ***/", + define_prefix); - if (!probe_bpf_syscall()) + if (!probe_bpf_syscall(define_prefix)) /* bpf() syscall unavailable, don't probe other BPF features */ goto exit_close_json; print_end_then_start_section("program_types", - "Scanning eBPF program types..."); + "Scanning eBPF program types...", + "/*** eBPF program types ***/", + define_prefix); for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) - probe_prog_type(i, supported_types); + probe_prog_type(i, supported_types, define_prefix); print_end_then_start_section("map_types", - "Scanning eBPF map types..."); + "Scanning eBPF map types...", + "/*** eBPF map types ***/", + define_prefix); for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++) - probe_map_type(i); + probe_map_type(i, define_prefix); print_end_then_start_section("helpers", - "Scanning eBPF helper functions..."); + "Scanning eBPF helper functions...", + "/*** eBPF helper functions ***/", + define_prefix); + if (define_prefix) + printf("/*\n" + " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n" + " * to determine if is available for ,\n" + " * e.g.\n" + " * #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n" + " * // do stuff with this helper\n" + " * #elif\n" + " * // use a workaround\n" + " * #endif\n" + " */\n" + "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper) \\\n" + " %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n", + define_prefix, define_prefix, define_prefix, + define_prefix); for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) - probe_helpers_for_progtype(i, supported_types[i]); + probe_helpers_for_progtype(i, supported_types[i], + define_prefix); exit_close_json: if (json_output) { @@ -603,7 +697,7 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s %s probe [kernel]\n" + "Usage: %s %s probe [kernel] [macros [prefix PREFIX]]\n" " %s %s help\n" "", bin_name, argv[-2], bin_name, argv[-2]); From f9499fedf28547b496066b65de9180eaf3a7dc50 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:57 +0000 Subject: [PATCH 8/9] tools: bpftool: add probes for a network device bpftool gained support for probing the current system in order to see what program and map types, and what helpers are available on that system. This patch adds the possibility to pass an interface index to libbpf (and hence to the kernel) when trying to load the programs or to create the maps, in order to see what items a given network device can support. A new keyword "dev " can be used as an alternative to "kernel" to indicate that the given device should be tested. If no target ("dev" or "kernel") is specified bpftool defaults to probing the kernel. Sample output: # bpftool -p feature probe dev lo { "syscall_config": { "have_bpf_syscall": true }, "program_types": { "have_sched_cls_prog_type": false, "have_xdp_prog_type": false }, ... } As the target is a network device, /proc/ parameters and kernel configuration are NOT dumped. Availability of the bpf() syscall is still probed, so we can return early if that syscall is not usable (since there is no point in attempting the remaining probes in this case). Among the program types, only the ones that can be offloaded are probed. All map types are probed, as there is no specific rule telling which one could or could not be supported by a device in the future. All helpers are probed (but only for offload-able program types). Caveat: as bpftool does not attempt to attach programs to the device at the moment, probes do not entirely reflect what the device accepts: typically, for Netronome's nfp, results will announce that TC cls offload is available even if support has been deactivated (with e.g. ethtool -K eth1 hw-tc-offload off). v2: - All helpers are probed, whereas previous version would only probe the ones compatible with an offload-able program type. This is because we do not keep a default compatible program type for each helper anymore. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- .../bpftool/Documentation/bpftool-feature.rst | 18 ++++- tools/bpf/bpftool/feature.c | 69 ++++++++++++++++--- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 53092995f46b..8d489a26e3c9 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -19,14 +19,18 @@ SYNOPSIS MAP COMMANDS ============= -| **bpftool** **feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]] +| **bpftool** **feature probe** [*COMPONENT*] [**macros** [**prefix** *PREFIX*]] | **bpftool** **feature help** +| +| *COMPONENT* := { **kernel** | **dev** *NAME* } DESCRIPTION =========== **bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]] Probe the running kernel and dump a number of eBPF-related - parameters, such as availability of the **bpf()** system call. + parameters, such as availability of the **bpf()** system call, + JIT status, eBPF program types availability, eBPF helper + functions availability, and more. If the **macros** keyword (but not the **-j** option) is passed, a subset of the output is dumped as a list of @@ -37,12 +41,20 @@ DESCRIPTION avoid conflicts on macro names when including the output of this command as a header file. - Keyword **kernel** can be omitted. + Keyword **kernel** can be omitted. If no probe target is + specified, probing the kernel is the default behaviour. Note that when probed, some eBPF helpers (e.g. **bpf_trace_printk**\ () or **bpf_probe_write_user**\ ()) may print warnings to kernel logs. + **bpftool feature probe dev** *NAME* [**macros** [**prefix** *PREFIX*]] + Probe network device for supported eBPF features and dump + results to the console. + + The two keywords **macros** and **prefix** have the same + role as when probing the kernel. + **bpftool feature help** Print short help message. diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index a62e637953b7..993c6f1e5473 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ enum probe_component { COMPONENT_UNSPEC, COMPONENT_KERNEL, + COMPONENT_DEVICE, }; #define BPF_HELPER_MAKE_ENTRY(name) [BPF_FUNC_ ## name] = "bpf_" # name @@ -456,14 +458,24 @@ static bool probe_bpf_syscall(const char *define_prefix) static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, - const char *define_prefix) + const char *define_prefix, __u32 ifindex) { char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF program_type "; size_t maxlen; bool res; - res = bpf_probe_prog_type(prog_type, 0); + if (ifindex) + /* Only test offload-able program types */ + switch (prog_type) { + case BPF_PROG_TYPE_SCHED_CLS: + case BPF_PROG_TYPE_XDP: + break; + default: + return; + } + + res = bpf_probe_prog_type(prog_type, ifindex); supported_types[prog_type] |= res; @@ -482,14 +494,15 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, } static void -probe_map_type(enum bpf_map_type map_type, const char *define_prefix) +probe_map_type(enum bpf_map_type map_type, const char *define_prefix, + __u32 ifindex) { char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF map_type "; size_t maxlen; bool res; - res = bpf_probe_map_type(map_type, 0); + res = bpf_probe_map_type(map_type, ifindex); maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; if (strlen(map_type_name[map_type]) > maxlen) { @@ -507,13 +520,23 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix) static void probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type, - const char *define_prefix) + const char *define_prefix, __u32 ifindex) { const char *ptype_name = prog_type_name[prog_type]; char feat_name[128]; unsigned int id; bool res; + if (ifindex) + /* Only test helpers for offload-able program types */ + switch (prog_type) { + case BPF_PROG_TYPE_SCHED_CLS: + case BPF_PROG_TYPE_XDP: + break; + default: + return; + } + if (json_output) { sprintf(feat_name, "%s_available_helpers", ptype_name); jsonw_name(json_wtr, feat_name); @@ -527,7 +550,7 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type, if (!supported_type) res = false; else - res = bpf_probe_helper(id, prog_type, 0); + res = bpf_probe_helper(id, prog_type, ifindex); if (json_output) { if (res) @@ -553,7 +576,9 @@ static int do_probe(int argc, char **argv) enum probe_component target = COMPONENT_UNSPEC; const char *define_prefix = NULL; bool supported_types[128] = {}; + __u32 ifindex = 0; unsigned int i; + char *ifname; /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). * Let's approximate, and restrict usage to root user only. @@ -573,6 +598,24 @@ static int do_probe(int argc, char **argv) } target = COMPONENT_KERNEL; NEXT_ARG(); + } else if (is_prefix(*argv, "dev")) { + NEXT_ARG(); + + if (target != COMPONENT_UNSPEC || ifindex) { + p_err("component to probe already specified"); + return -1; + } + if (!REQ_ARGS(1)) + return -1; + + target = COMPONENT_DEVICE; + ifname = GET_ARG(); + ifindex = if_nametoindex(ifname); + if (!ifindex) { + p_err("unrecognized netdevice '%s': %s", ifname, + strerror(errno)); + return -1; + } } else if (is_prefix(*argv, "macros") && !define_prefix) { define_prefix = ""; NEXT_ARG(); @@ -591,7 +634,7 @@ static int do_probe(int argc, char **argv) return -1; define_prefix = GET_ARG(); } else { - p_err("expected no more arguments, 'kernel', 'macros' or 'prefix', got: '%s'?", + p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?", *argv); return -1; } @@ -627,6 +670,8 @@ static int do_probe(int argc, char **argv) else printf("\n"); break; + default: + break; } print_start_section("syscall_config", @@ -644,7 +689,7 @@ static int do_probe(int argc, char **argv) define_prefix); for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) - probe_prog_type(i, supported_types, define_prefix); + probe_prog_type(i, supported_types, define_prefix, ifindex); print_end_then_start_section("map_types", "Scanning eBPF map types...", @@ -652,7 +697,7 @@ static int do_probe(int argc, char **argv) define_prefix); for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++) - probe_map_type(i, define_prefix); + probe_map_type(i, define_prefix, ifindex); print_end_then_start_section("helpers", "Scanning eBPF helper functions...", @@ -676,7 +721,7 @@ static int do_probe(int argc, char **argv) define_prefix); for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) probe_helpers_for_progtype(i, supported_types[i], - define_prefix); + define_prefix, ifindex); exit_close_json: if (json_output) { @@ -697,8 +742,10 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s %s probe [kernel] [macros [prefix PREFIX]]\n" + "Usage: %s %s probe [COMPONENT] [macros [prefix PREFIX]]\n" " %s %s help\n" + "\n" + " COMPONENT := { kernel | dev NAME }\n" "", bin_name, argv[-2], bin_name, argv[-2]); From 948703e80868a7ee00a9ca16c0e37ffe6933f9bb Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 17 Jan 2019 15:27:58 +0000 Subject: [PATCH 9/9] tools: bpftool: add bash completion for bpftool probes Add the bash completion related to the newly introduced "bpftool feature probe" command. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Reviewed-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/bash-completion/bpftool | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 47143d793b33..763dd12482aa 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -679,6 +679,25 @@ _bpftool() ;; esac ;; + feature) + case $command in + probe) + [[ $prev == "dev" ]] && _sysfs_get_netdevs && return 0 + [[ $prev == "prefix" ]] && return 0 + if _bpftool_search_list 'macros'; then + COMPREPLY+=( $( compgen -W 'prefix' -- "$cur" ) ) + else + COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) ) + fi + _bpftool_one_of_list 'kernel dev' + return 0 + ;; + *) + [[ $prev == $object ]] && \ + COMPREPLY=( $( compgen -W 'help probe' -- "$cur" ) ) + ;; + esac + ;; esac } && complete -F _bpftool bpftool