On 12/13, Quentin Monnet wrote: > 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). > Additionally, a specific keyword can be used to provide a third possible > output so that the parameters are dumped as #define-d macros, ready to > be saved to a header file and included in an eBPF-based project. > > The current patch introduces probing of two simple parameters: > availability of the bpf() system call, and kernel version. Later commits > will add other probes. > > Sample output: > > # bpftool feature probe kernel > Scanning system call and kernel version... > Kernel release is 4.19.0 > bpf() syscall is available > > # bpftool --json --pretty feature probe kernel > { > "syscall_config": { > "kernel_version_code": 267008, > "have_bpf_syscall": true > } > } > > # bpftool feature probe kernel macros prefix BPFTOOL_ > /*** System call and kernel version ***/ > #define BPFTOOL_LINUX_VERSION_CODE 267008 > #define BPFTOOL_BPF_SYSCALL > > 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. > > Signed-off-by: Quentin Monnet <quentin.mon...@netronome.com> > Reviewed-by: Jakub Kicinski <jakub.kicin...@netronome.com> > --- > .../bpftool/Documentation/bpftool-cgroup.rst | 1 + > .../bpftool/Documentation/bpftool-feature.rst | 69 +++++++ > .../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 | 184 ++++++++++++++++++ > tools/bpf/bpftool/main.c | 3 +- > tools/bpf/bpftool/main.h | 1 + > 10 files changed, 262 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..23920a7490e9 > --- /dev/null > +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst > @@ -0,0 +1,69 @@ > +=============== > +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**] [**macros** [**prefix** > *PREFIX*]] > +| **bpftool** **feature help** > + > +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. > + > + Keyword **kernel** can be omitted. > + > + If the **macros** keyword (but not the **-j** option) is > + passed, 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. > + > + **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 5318dcb2085e..b432c5cc20c8 100644 > --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst > +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst > @@ -177,5 +177,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 bb1aeb98b6da..595b4538c0d7 100644 > --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst > +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst > @@ -243,5 +243,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 129b7a9c0f9b..f6526b657677 100644 > --- a/tools/bpf/bpftool/Documentation/bpftool.rst > +++ b/tools/bpf/bpftool/Documentation/bpftool.rst > @@ -68,5 +68,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..e1784611575d > --- /dev/null > +++ b/tools/bpf/bpftool/feature.c > @@ -0,0 +1,184 @@ > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +/* Copyright (c) 2018 Netronome Systems, Inc. */ > + > +#include <errno.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/utsname.h> > + > +#include <linux/filter.h> > +#include <linux/limits.h> > + > +#include <bpf.h> > + > +#include "main.h" > + > +enum probe_component { > + COMPONENT_UNSPEC, > + COMPONENT_KERNEL, > +}; > + > +/* Printing utility functions */ > + > +static void > +print_bool_feature(const char *feat_name, const char *define_name, > + const char *plain_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%s%s\n", define_prefix, > + res ? "" : "NO_", define_name); Should we keep it autoconf style and do: #define XYZ 1 - in case of supported feature /* #undef XYZ */ - in case of unsupported feature ? > + else > + printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); Why not do printf("%s %s\n", feat_name, res ? "yes" : "no") instead? And not complicate (drop) the output with human readability. One possible (dis)advantage - scripts can use this. > +} > + > +static void > +print_start_section(const char *json_title, const char *define_comment, > + const char *plain_title, 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); > + } > +} > + > +/* Probing functions */ > + > +static int probe_kernel_version(const char *define_prefix) > +{ > + int version, subversion, patchlevel, code = 0; > + struct utsname utsn; > + > + if (!uname(&utsn)) > + if (sscanf(utsn.release, "%d.%d.%d", > + &version, &subversion, &patchlevel) == 3) > + code = (version << 16) + (subversion << 8) + patchlevel; > + > + if (json_output) > + jsonw_uint_field(json_wtr, "kernel_version_code", code); > + else if (define_prefix) > + printf("#define %sLINUX_VERSION_CODE %d\n", > + define_prefix, code); > + else if (code) > + printf("Kernel release is %d.%d.%d\n", > + version, subversion, patchlevel); > + else > + printf("Unable to parse kernel release number\n"); > + > + return code; > +} > + > +static bool probe_bpf_syscall(const char *define_prefix) > +{ > + 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", > + "bpf() syscall", > + res, define_prefix); > + > + return res; > +} > + > +static int do_probe(int argc, char **argv) > +{ > + enum probe_component target = COMPONENT_UNSPEC; > + const char *define_prefix = NULL; > + > + /* 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 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', 'macros' > or 'prefix', got: '%s'?", > + *argv); > + return -1; > + } > + } > + > + if (json_output) > + jsonw_start_object(json_wtr); > + > + print_start_section("syscall_config", > + "/*** System call and kernel version ***/", > + "Scanning system call and kernel version...", > + define_prefix); > + > + probe_kernel_version(define_prefix); > + probe_bpf_syscall(define_prefix); > + > + 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] [macros [prefix PREFIX]]\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 9e657e7d5172..baf07f8be737 100644 > --- a/tools/bpf/bpftool/main.c > +++ b/tools/bpf/bpftool/main.c > @@ -55,7 +55,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); > @@ -186,6 +186,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 d2beb88f0e2e..2acd8c08e8b6 100644 > --- a/tools/bpf/bpftool/main.h > +++ b/tools/bpf/bpftool/main.h > @@ -141,6 +141,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); > -- > 2.17.1 >