Hi,
This set adds a new command to bpftool in order to dump a list of
eBPF-related parameters for the system (or for a specific network
device) to the console. Once again, this is based on a suggestion from
Daniel.
At this time, output includes:
- Availability of bpf() system call
- Availability of bpf() system call for unprivileged users
- JIT status (enabled or not, with or without debugging traces)
- JIT hardening status
- JIT kallsyms exports status
- Status of kernel compilation options related to BPF features
- Release number of the running kernel
- Availability of known eBPF program types
- Availability of known eBPF map types
- Availability of known eBPF helper functions
There are three different ways to dump this information at this time:
- Plain output dumps probe results in plain text. It is the most
flexible options for providing descriptive output to the user, but
should not be relied upon for parsing the output.
- JSON output is supported.
- A third mode, available through the "macros" keyword appended to the
command line, dumps some of those parameters (not all) as a series of
"#define" directives, that can be included into a C header file for
example.
Probes for supported program and map types, and supported helpers, are
directly added to libbpf, so that other applications (or selftests) can
reuse them as necessary.
If the user does not have root privileges (or more precisely, the
CAP_SYS_ADMIN capability) detection will be erroneous for most
parameters. Therefore, forbid non-root users to run the command.
v2 (please also refer to individual patches' history):
- Move probes for prog/map types, helpers, from bpftool to libbpf.
- Move C-style output as a separate patch, and restrict it to a subset of
collected information (bpf() availability, prog/map types, helpers).
- Now probe helpers with all supported program types, and display a list of
compatible program types (as supported on the system) for each helper.
- NOT addressed: grouping compilation options for kernel into subsections
(patch 3) (I don't see an easy way of grouping them at the moment, please
see also the discussion on v1 thread).
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Jesper Dangaard Brouer <redacted>
Cc: Stanislav Fomichev <redacted>
---
I'm sending this v2 as a RFC for feedback, mostly about the relocation of
the probes into libbpf and for the changes in helper probing. As mentioned
in v2 history, the grouping of kernel config options (suggested by Daniel)
is not addressed in this series.
I still expect some discussions on this set, so I do not mind at all being
delayed after the merge window.
Quentin Monnet (9):
tools: bpftool: add basic probe capability, probe syscall and kversion
tools: bpftool: add probes for /proc/ eBPF parameters
tools: bpftool: add probes for kernel configuration options
tools: bpftool: add probes for eBPF program types
tools: bpftool: add probes for eBPF map types
tools: bpftool: add probes for eBPF helper functions
tools: bpftool: add C-style "#define" output for probes
tools: bpftool: add probes for a network device
tools: bpftool: add bash completion for bpftool probes
.../bpftool/Documentation/bpftool-cgroup.rst | 1 +
.../bpftool/Documentation/bpftool-feature.rst | 85 +++
.../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/bash-completion/bpftool | 19 +
tools/bpf/bpftool/common.c | 2 +-
tools/bpf/bpftool/feature.c | 702 ++++++++++++++++++
tools/bpf/bpftool/main.c | 3 +-
tools/bpf/bpftool/main.h | 5 +
tools/bpf/bpftool/map.c | 4 +-
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 9 +
tools/lib/bpf/libbpf.map | 3 +
tools/lib/bpf/libbpf_probes.c | 178 +++++
17 files changed, 1014 insertions(+), 4 deletions(-)
create mode 100644 tools/bpf/bpftool/Documentation/bpftool-feature.rst
create mode 100644 tools/bpf/bpftool/feature.c
create mode 100644 tools/lib/bpf/libbpf_probes.c
--
2.17.1
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 two simple parameters:
availability of the bpf() system call, and kernel version. Later commits
will add other probes. Kernel version will be used in some of those
later commits to check e.g. kprobes availability.
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
}
}
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.
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 <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
.../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 | 153 ++++++++++++++++++
tools/bpf/bpftool/main.c | 3 +-
tools/bpf/bpftool/main.h | 1 +
10 files changed, 222 insertions(+), 1 deletion(-)
create mode 100644 tools/bpf/bpftool/Documentation/bpftool-feature.rst
create mode 100644 tools/bpf/bpftool/feature.c
@@ -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)
@@ -0,0 +1,153 @@+// 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"++enumprobe_component{+COMPONENT_UNSPEC,+COMPONENT_KERNEL,+};++/* Printing utility functions */++staticvoid+print_bool_feature(constchar*feat_name,constchar*plain_name,boolres)+{+if(json_output)+jsonw_bool_field(json_wtr,feat_name,res);+else+printf("%s is %savailable\n",plain_name,res?"":"NOT ");+}++staticvoid+print_start_section(constchar*json_title,constchar*plain_title)+{+if(json_output){+jsonw_name(json_wtr,json_title);+jsonw_start_object(json_wtr);+}else{+printf("%s\n",plain_title);+}+}++/* Probing functions */++staticintprobe_kernel_version(void)+{+intversion,subversion,patchlevel,code=0;+structutsnameutsn;++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);+elseif(code)+printf("Kernel release is %d.%d.%d\n",+version,subversion,patchlevel);+else+printf("Unable to parse kernel release number\n");++returncode;+}++staticboolprobe_bpf_syscall(void)+{+boolres;++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);++returnres;+}++staticintdo_probe(intargc,char**argv)+{+enumprobe_componenttarget=COMPONENT_UNSPEC;++/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).+*Let'sapproximate,andrestrictusagetorootuseronly.+*/+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 and kernel version...");++probe_kernel_version();+probe_bpf_syscall();++if(json_output){+/* End current "section" of probes */+jsonw_end_object(json_wtr);+/* End root object */+jsonw_end_object(json_wtr);+}++return0;+}++staticintdo_help(intargc,char**argv)+{+if(json_output){+jsonw_null(json_wtr);+return0;+}++fprintf(stderr,+"Usage: %s %s probe [kernel]\n"+" %s %s help\n"+"",+bin_name,argv[-2],bin_name,argv[-2]);++return0;+}++staticconststructcmdcmds[]={+{"help",do_help},+{"probe",do_probe},+{0}+};++intdo_feature(intargc,char**argv)+{+returncmd_select(cmds,argc,argv,do_help);+}
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
...
# bpftool --json --pretty feature probe kernel
{
"system_config": {
"unprivileged_bpf_disabled": 0,
"bpf_jit_enable": 0,
"bpf_jit_harden": 0,
"bpf_jit_kallsyms": 0
},
...
}
These probes are skipped if procfs is not mounted.
v2:
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
tools/bpf/bpftool/feature.c | 168 ++++++++++++++++++++++++++++++++++++
1 file changed, 168 insertions(+)
@@ -42,6 +61,135 @@ print_start_section(const char *json_title, const char *plain_title)/* Probing functions */+staticintread_procfs(constchar*path)+{+char*endptr,*line=NULL;+size_tlen=0;+FILE*fd;+intres;++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);++returnres;+}++staticvoidprobe_unprivileged_disabled(void)+{+intres;++res=read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");+if(json_output){+jsonw_int_field(json_wtr,"unprivileged_bpf_disabled",res);+}else{+switch(res){+case0:+printf("bpf() syscall for unprivileged users is enabled\n");+break;+case1:+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);+}+}+}++staticvoidprobe_jit_enable(void)+{+intres;++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){+case0:+printf("JIT compiler is disabled\n");+break;+case1:+printf("JIT compiler is enabled\n");+break;+case2:+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);+}+}+}++staticvoidprobe_jit_harden(void)+{+intres;++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){+case0:+printf("JIT compiler hardening is disabled\n");+break;+case1:+printf("JIT compiler hardening is enabled for unprivileged users\n");+break;+case2:+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);+}+}+}++staticvoidprobe_jit_kallsyms(void)+{+intres;++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){+case0:+printf("JIT compiler kallsyms exports are disabled\n");+break;+case1:+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);+}+}+}+staticintprobe_kernel_version(void){intversion,subversion,patchlevel,code=0;
@@ -109,6 +257,26 @@ static int do_probe(int argc, char **argv)if(json_output)jsonw_start_object(json_wtr);+switch(target){+caseCOMPONENT_KERNEL:+caseCOMPONENT_UNSPEC:+print_start_section("system_config",+"Scanning system configuration...");+if(check_procfs()){+probe_unprivileged_disabled();+probe_jit_enable();+probe_jit_harden();+probe_jit_kallsyms();+}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 and kernel version...");
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",
...
}
}
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 <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
@@ -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 ");}+staticvoidprint_kernel_option(constchar*name,constchar*value)+{+char*endptr;+intres;++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);+}+}+staticvoidprint_start_section(constchar*json_title,constchar*plain_title){
@@ -190,6 +214,118 @@ static void probe_jit_kallsyms(void)}}+staticchar*get_kernel_config_option(FILE*fd,constchar*option)+{+size_tline_n=0,optlen=strlen(option);+char*res,*strval,*line=NULL;+ssize_tn;++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);+returnres;+}+free(line);++returnNULL;+}++staticvoidprobe_kernel_image_config(void)+{+constchar*constoptions[]={+"CONFIG_BPF",+"CONFIG_BPF_SYSCALL",+"CONFIG_HAVE_EBPF_JIT",+"CONFIG_BPF_JIT",+"CONFIG_BPF_JIT_ALWAYS_ON",+"CONFIG_NET",+"CONFIG_XDP_SOCKETS",+"CONFIG_CGROUPS",+"CONFIG_CGROUP_BPF",+"CONFIG_CGROUP_NET_CLASSID",+"CONFIG_BPF_EVENTS",+"CONFIG_LWTUNNEL_BPF",+"CONFIG_NET_ACT_BPF",+"CONFIG_NET_CLS_ACT",+"CONFIG_NET_CLS_BPF",+"CONFIG_NET_SCH_INGRESS",+"CONFIG_XFRM",+"CONFIG_SOCK_CGROUP_DATA",+"CONFIG_IP_ROUTE_CLASSID",+"CONFIG_IPV6_SEG6_BPF",+"CONFIG_FUNCTION_ERROR_INJECTION",+"CONFIG_BPF_KPROBE_OVERRIDE",+"CONFIG_BPF_LIRC_MODE2",+"CONFIG_NETFILTER_XT_MATCH_BPF",+"CONFIG_TEST_BPF",+"CONFIG_BPFILTER",+"CONFIG_BPFILTER_UMH",+"CONFIG_BPF_STREAM_PARSER",+};+char*value,*buf=NULL;+structutsnameutsn;+charpath[PATH_MAX];+size_ti,n;+ssize_tret;+FILE*fd;++if(uname(&utsn))+gotono_config;++snprintf(path,sizeof(path),"/boot/config-%s",utsn.release);++fd=fopen(path,"r");+if(!fd&&errno==ENOENT){+/* Sometimes config is at /proc/config */+fd=fopen("/proc/config","r");+}+if(!fd){+p_err("can't open kernel config file: %s",strerror(errno));+gotono_config;+}+/* Sanity checks */+ret=getline(&buf,&n,fd);+ret=getline(&buf,&n,fd);+if(!buf||!ret){+p_err("can't read from kernel config file: %s",+strerror(errno));+free(buf);+gotono_config;+}+if(strcmp(buf,"# Automatically generated file; DO NOT EDIT.\n")){+p_err("can't find correct kernel config file");+free(buf);+gotono_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);+}+staticintprobe_kernel_version(void){intversion,subversion,patchlevel,code=0;
@@ -270,6 +406,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
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 simple 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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)returnres;}+staticvoid+probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+bool*supported_types)+{+constchar*plain_comment="eBPF program_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++res=bpf_probe_prog_type(prog_type,kernel_version,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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+boolsupported_types[128]={};+intkernel_version;+unsignedinti;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)print_start_section("syscall_config","Scanning system call and kernel version...");-probe_kernel_version();-probe_bpf_syscall();+kernel_version=probe_kernel_version();+if(!probe_bpf_syscall())+/* bpf() syscall unavailable, don't probe other BPF features */+gotoexit_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,kernel_version,supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */jsonw_end_object(json_wtr);
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)++/* Copyright (c) 2018 Netronome Systems, Inc. */++#include<errno.h>+#include<unistd.h>++#include<linux/filter.h>+#include<linux/kernel.h>++#include"bpf.h"+#include"libbpf.h"++staticvoid+prog_load(enumbpf_prog_typeprog_type,conststructbpf_insn*insns,+size_tinsns_cnt,intkernel_version,char*buf,size_tbuf_len,+__u32ifindex)+{+structbpf_load_program_attrxattr={};+intfd;++/* Some prog type require an expected_attach_type */+if(prog_type==BPF_PROG_TYPE_CGROUP_SOCK_ADDR)+xattr.expected_attach_type=BPF_CGROUP_INET4_CONNECT;++xattr.prog_type=prog_type;+xattr.insns=insns;+xattr.insns_cnt=insns_cnt;+xattr.license="GPL";+xattr.kern_version=kernel_version;+xattr.prog_ifindex=ifindex;++fd=bpf_load_program_xattr(&xattr,buf,buf_len);+if(fd>=0)+close(fd);+}++boolbpf_probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+__u32ifindex)+{+structbpf_insninsns[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;+prog_load(prog_type,insns,ARRAY_SIZE(insns),kernel_version,+NULL,0,ifindex);++returnerrno!=EINVAL&&errno!=EOPNOTSUPP;+}
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.
Each known helper is tested with all program types supported by the
system, in order to establish a compatibility matrix. Output is provided
as a list of compatible program types, for each helper.
Sample output:
# bpftool feature probe kernel
...
Scanning eBPF helper functions...
...
eBPF helper bpf_skb_change_head supported for program types: \
lwt_xmit sk_skb
eBPF helper bpf_xdp_adjust_head supported for program types: xdp
eBPF helper bpf_probe_read_str supported for program types: \
kprobe tracepoint perf_event raw_tracepoint
...
# bpftool --json --pretty feature probe kernel
{
...
"helpers": {
...
"bpf_skb_change_head_compat_list": ["lwt_xmit","sk_skb"
],
"bpf_xdp_adjust_head_compat_list": ["xdp"
],
"bpf_probe_read_str_compat_list": ["kprobe","tracepoint", \
"perf_event","raw_tracepoint"
],
...
}
}
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 <redacted>
---
.../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(+)
@@ -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.
@@ -418,6 +423,45 @@ static void probe_map_type(enum bpf_map_type map_type)print_bool_feature(feat_name,plain_desc,res);}+staticvoid+probe_helper(__u32id,constchar*name,intkernel_version,+bool*supported_types)+{+charfeat_name[128],plain_desc[128];+unsignedinti;++sprintf(feat_name,"%s_compat_list",name);+sprintf(plain_desc,+"eBPF helper %s supported for program types:",+name);++if(json_output){+jsonw_name(json_wtr,feat_name);+jsonw_start_array(json_wtr);+}else{+printf("%s",plain_desc);+}++for(i=BPF_PROG_TYPE_UNSPEC+1;+i<ARRAY_SIZE(prog_type_name);i++){+if(!supported_types[i])+continue;++if(!bpf_probe_helper(id,i,kernel_version,0))+continue;++if(json_output)+jsonw_string(json_wtr,prog_type_name[i]);+else+printf(" %s",prog_type_name[i]);+}++if(json_output)+jsonw_end_array(json_wtr);+else+printf("\n");+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -494,6 +538,13 @@ 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=1;i<ARRAY_SIZE(helper_name);i++)+probe_helper(i,helper_name[i],kernel_version,+supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
@@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,returnerrno!=EINVAL&&errno!=EOPNOTSUPP;}++boolbpf_probe_map_type(enumbpf_map_typemap_type,__u32ifindex)+{+intkey_size,value_size,max_entries,map_flags;+structbpf_create_map_attrattr={};+intfd=-1,fd_inner;++key_size=sizeof(__u32);+value_size=sizeof(__u32);+max_entries=1;+map_flags=0;++if(map_type==BPF_MAP_TYPE_LPM_TRIE){+key_size=sizeof(__u64);+value_size=sizeof(__u64);+map_flags=BPF_F_NO_PREALLOC;+}elseif(map_type==BPF_MAP_TYPE_STACK_TRACE){+value_size=sizeof(__u64);+}elseif(map_type==BPF_MAP_TYPE_CGROUP_STORAGE||+map_type==BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE){+key_size=sizeof(structbpf_cgroup_storage_key);+value_size=sizeof(__u64);+max_entries=0;+}elseif(map_type==BPF_MAP_TYPE_QUEUE||+map_type==BPF_MAP_TYPE_STACK){+key_size=0;+}++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-mapforoffload+*/+if(ifindex)+returnfalse;++fd_inner=bpf_create_map(BPF_MAP_TYPE_HASH,+sizeof(__u32),sizeof(__u32),1,0);+if(fd_inner<0)+returnfalse;+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);++returnfd>=0;+}
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 <ifname>" 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": {
"kernel_version_code": 267008,
"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 and
kernel version are still probed, as they are necessary for the remaining
probes (although kernel version is no longer needed on the latest
kernels, it is required for probing availability of kprobes on older
kernels).
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 as well, for all supported 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 <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
.../bpftool/Documentation/bpftool-feature.rst | 18 +++++-
tools/bpf/bpftool/common.c | 2 +-
tools/bpf/bpftool/feature.c | 58 +++++++++++++++----
tools/bpf/bpftool/main.h | 1 +
4 files changed, 65 insertions(+), 14 deletions(-)
@@ -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.
@@ -514,7 +527,9 @@ static int do_probe(int argc, char **argv)constchar*define_prefix=NULL;boolsupported_types[128]={};intkernel_version;+__u32ifindex=0;unsignedinti;+char*ifname;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -534,6 +549,24 @@ static int do_probe(int argc, char **argv)}target=COMPONENT_KERNEL;NEXT_ARG();+}elseif(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;+}}elseif(is_prefix(*argv,"macros")&&!define_prefix){define_prefix="";NEXT_ARG();
@@ -552,7 +585,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;}
@@ -587,6 +620,8 @@ static int do_probe(int argc, char **argv)elseprintf("\n");break;+default:+break;}print_start_section("syscall_config",
@@ -594,6 +629,7 @@ static int do_probe(int argc, char **argv)"/*** System call availability ***/",define_prefix);+/* Get kernel version in all cases, we need it for kprobe programs */kernel_version=probe_kernel_version(define_prefix);if(!probe_bpf_syscall(define_prefix))/* bpf() syscall unavailable, don't probe other BPF features */
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 ***/
...
#define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
"lwt_xmit " \
"sk_skb "
#define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
"xdp "
#define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
"kprobe " \
"tracepoint " \
"perf_event " \
"raw_tracepoint "
...
v2:
- #define-based output added as a distinct patch.
- "HAVE_" prefix appended to all 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 <redacted>
---
.../bpftool/Documentation/bpftool-feature.rst | 13 +-
tools/bpf/bpftool/feature.c | 138 ++++++++++++++----
2 files changed, 120 insertions(+), 31 deletions(-)
@@ -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.
@@ -132,6 +152,8 @@ static void probe_unprivileged_disabled(void){intres;+/* 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);
@@ -156,6 +178,8 @@ static void probe_jit_enable(void){intres;+/* 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);
@@ -184,6 +208,8 @@ static void probe_jit_harden(void){intres;+/* 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);
@@ -212,6 +238,8 @@ static void probe_jit_kallsyms(void){intres;+/* 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);
@@ -354,6 +382,10 @@ static int probe_kernel_version(void)&version,&subversion,&patchlevel)==3)code=(version<<16)+(subversion<<8)+patchlevel;+if(define_prefix)+/* Nothing currently displayed for this ouptut */+returncode;+if(json_output)jsonw_uint_field(json_wtr,"kernel_version_code",code);elseif(code)
@@ -365,7 +397,7 @@ static int probe_kernel_version(void)returncode;}-staticboolprobe_bpf_syscall(void)+staticboolprobe_bpf_syscall(constchar*define_prefix){boolres;
@@ -452,19 +496,22 @@ probe_helper(__u32 id, const char *name, int kernel_version,if(json_output)jsonw_string(json_wtr,prog_type_name[i]);+elseif(define_prefix)+printf("\t\\\n\t\"%s \"",prog_type_name[i]);elseprintf(" %s",prog_type_name[i]);}if(json_output)jsonw_end_array(json_wtr);-else+else/* For both C-style and plain output */printf("\n");}staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+constchar*define_prefix=NULL;boolsupported_types[128]={};intkernel_version;unsignedinti;
@@ -487,21 +534,45 @@ static int do_probe(int argc, char **argv)}target=COMPONENT_KERNEL;NEXT_ARG();+}elseif(is_prefix(*argv,"macros")&&!define_prefix){+define_prefix="";+NEXT_ARG();+}elseif(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){caseCOMPONENT_KERNEL:caseCOMPONENT_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();
@@ -519,31 +590,40 @@ static int do_probe(int argc, char **argv)}print_start_section("syscall_config",-"Scanning system call and kernel version...");+"Scanning system call and kernel version...",+"/*** System call availability ***/",+define_prefix);-kernel_version=probe_kernel_version();-if(!probe_bpf_syscall())+kernel_version=probe_kernel_version(define_prefix);+if(!probe_bpf_syscall(define_prefix))/* bpf() syscall unavailable, don't probe other BPF features */gotoexit_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,kernel_version,supported_types);+probe_prog_type(i,kernel_version,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);for(i=1;i<ARRAY_SIZE(helper_name);i++)probe_helper(i,helper_name[i],kernel_version,-supported_types);+supported_types,define_prefix);exit_close_json:if(json_output){
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-12-20 17:25:27
On 12/20/2018 01:24 PM, Quentin Monnet wrote:
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 ***/
...
#define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
"lwt_xmit " \
"sk_skb "
#define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
"xdp "
#define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
"kprobe " \
"tracepoint " \
"perf_event " \
"raw_tracepoint "
...
Rest looks good to me, just a comment here. How would programs use the
compat list? Wouldn't it be nicer to provide a PROG(HELPER) availability
query along the lines of ...
#if FOO_HAVE_SOCKET_FILTER(skb_load_bytes) == 1
// ...
#endif
... where the helper provides these macro functions and results in the
header file? So they can be used easily in the code from BPF prog or
normal C applications?
v2:
- #define-based output added as a distinct patch.
- "HAVE_" prefix appended to all 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 <redacted>
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 17:40:29
On 12/20, Quentin Monnet wrote:
quoted hunk
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",
...
}
}
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 <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
@@ -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 ");}+staticvoidprint_kernel_option(constchar*name,constchar*value)+{+char*endptr;+intres;++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);+}+}+staticvoidprint_start_section(constchar*json_title,constchar*plain_title){
@@ -190,6 +214,118 @@ static void probe_jit_kallsyms(void)}}+staticchar*get_kernel_config_option(FILE*fd,constchar*option)+{+size_tline_n=0,optlen=strlen(option);+char*res,*strval,*line=NULL;+ssize_tn;++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);+returnres;+}+free(line);++returnNULL;+}++staticvoidprobe_kernel_image_config(void)+{+constchar*constoptions[]={+"CONFIG_BPF",+"CONFIG_BPF_SYSCALL",+"CONFIG_HAVE_EBPF_JIT",+"CONFIG_BPF_JIT",+"CONFIG_BPF_JIT_ALWAYS_ON",+"CONFIG_NET",+"CONFIG_XDP_SOCKETS",+"CONFIG_CGROUPS",+"CONFIG_CGROUP_BPF",+"CONFIG_CGROUP_NET_CLASSID",+"CONFIG_BPF_EVENTS",+"CONFIG_LWTUNNEL_BPF",+"CONFIG_NET_ACT_BPF",+"CONFIG_NET_CLS_ACT",+"CONFIG_NET_CLS_BPF",+"CONFIG_NET_SCH_INGRESS",+"CONFIG_XFRM",+"CONFIG_SOCK_CGROUP_DATA",+"CONFIG_IP_ROUTE_CLASSID",+"CONFIG_IPV6_SEG6_BPF",+"CONFIG_FUNCTION_ERROR_INJECTION",+"CONFIG_BPF_KPROBE_OVERRIDE",+"CONFIG_BPF_LIRC_MODE2",+"CONFIG_NETFILTER_XT_MATCH_BPF",+"CONFIG_TEST_BPF",+"CONFIG_BPFILTER",+"CONFIG_BPFILTER_UMH",+"CONFIG_BPF_STREAM_PARSER",+};+char*value,*buf=NULL;+structutsnameutsn;+charpath[PATH_MAX];+size_ti,n;+ssize_tret;+FILE*fd;++if(uname(&utsn))+gotono_config;+
[..]
+ snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
+
+ fd = fopen(path, "r");
+ if (!fd && errno == ENOENT) {
+ /* Sometimes config is at /proc/config */
+ fd = fopen("/proc/config", "r");
I wonder whether the order here should be reversed?
1. try /proc/config.gz (CONFIG_IKCONFIG_PROC)
2. if not avail, try /boot/config-$(uname -r)
Because, at the end, /proc/config.gz is the real source of truth (if
available).
What is /proc/config btw? I can see only /proc/config.gz being exported.
quoted hunk
+ }
+ if (!fd) {
+ p_err("can't open kernel config file: %s", strerror(errno));
+ goto no_config;
+ }
+ /* Sanity checks */
+ ret = getline(&buf, &n, fd);
+ ret = getline(&buf, &n, fd);
+ if (!buf || !ret) {
+ p_err("can't read from kernel config file: %s",
+ strerror(errno));
+ free(buf);
+ goto no_config;
+ }
+ if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
+ p_err("can't find correct kernel config 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 int probe_kernel_version(void)
{
int version, subversion, patchlevel, code = 0;
@@ -270,6 +406,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: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 17:45:39
On 12/20, Quentin Monnet wrote:
quoted hunk
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 simple 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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)returnres;}+staticvoid+probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+bool*supported_types)+{+constchar*plain_comment="eBPF program_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++res=bpf_probe_prog_type(prog_type,kernel_version,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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+boolsupported_types[128]={};+intkernel_version;+unsignedinti;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)print_start_section("syscall_config","Scanning system call and kernel version...");-probe_kernel_version();-probe_bpf_syscall();+kernel_version=probe_kernel_version();+if(!probe_bpf_syscall())+/* bpf() syscall unavailable, don't probe other BPF features */+gotoexit_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,kernel_version,supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */jsonw_end_object(json_wtr);
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)++/* Copyright (c) 2018 Netronome Systems, Inc. */++#include<errno.h>+#include<unistd.h>++#include<linux/filter.h>+#include<linux/kernel.h>++#include"bpf.h"+#include"libbpf.h"++staticvoid+prog_load(enumbpf_prog_typeprog_type,conststructbpf_insn*insns,+size_tinsns_cnt,intkernel_version,char*buf,size_tbuf_len,+__u32ifindex)+{+structbpf_load_program_attrxattr={};+intfd;++/* Some prog type require an expected_attach_type */+if(prog_type==BPF_PROG_TYPE_CGROUP_SOCK_ADDR)+xattr.expected_attach_type=BPF_CGROUP_INET4_CONNECT;++xattr.prog_type=prog_type;+xattr.insns=insns;+xattr.insns_cnt=insns_cnt;+xattr.license="GPL";+xattr.kern_version=kernel_version;+xattr.prog_ifindex=ifindex;++fd=bpf_load_program_xattr(&xattr,buf,buf_len);+if(fd>=0)+close(fd);+}++boolbpf_probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,
Do we really need this kernel_version argument? Isn't it going away in
the future (I saw a patch from Daniel that drops kernel check under
sys_bpf). Going forward, does it make sense to have it at the API
level?
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 17:47:26
On 12/20, Quentin Monnet wrote:
quoted hunk
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 17:53:04
On 12/20, Quentin Monnet wrote:
quoted hunk
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.
Each known helper is tested with all program types supported by the
system, in order to establish a compatibility matrix. Output is provided
as a list of compatible program types, for each helper.
Sample output:
# bpftool feature probe kernel
...
Scanning eBPF helper functions...
...
eBPF helper bpf_skb_change_head supported for program types: \
lwt_xmit sk_skb
eBPF helper bpf_xdp_adjust_head supported for program types: xdp
eBPF helper bpf_probe_read_str supported for program types: \
kprobe tracepoint perf_event raw_tracepoint
...
# bpftool --json --pretty feature probe kernel
{
...
"helpers": {
...
"bpf_skb_change_head_compat_list": ["lwt_xmit","sk_skb"
],
"bpf_xdp_adjust_head_compat_list": ["xdp"
],
"bpf_probe_read_str_compat_list": ["kprobe","tracepoint", \
"perf_event","raw_tracepoint"
],
...
}
}
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 <redacted>
---
.../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(+)
@@ -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.
@@ -418,6 +423,45 @@ static void probe_map_type(enum bpf_map_type map_type)print_bool_feature(feat_name,plain_desc,res);}+staticvoid+probe_helper(__u32id,constchar*name,intkernel_version,+bool*supported_types)+{+charfeat_name[128],plain_desc[128];+unsignedinti;++sprintf(feat_name,"%s_compat_list",name);+sprintf(plain_desc,+"eBPF helper %s supported for program types:",+name);++if(json_output){+jsonw_name(json_wtr,feat_name);+jsonw_start_array(json_wtr);+}else{+printf("%s",plain_desc);+}++for(i=BPF_PROG_TYPE_UNSPEC+1;+i<ARRAY_SIZE(prog_type_name);i++){+if(!supported_types[i])+continue;++if(!bpf_probe_helper(id,i,kernel_version,0))+continue;++if(json_output)+jsonw_string(json_wtr,prog_type_name[i]);+else+printf(" %s",prog_type_name[i]);+}++if(json_output)+jsonw_end_array(json_wtr);+else+printf("\n");+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -494,6 +538,13 @@ 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=1;i<ARRAY_SIZE(helper_name);i++)+probe_helper(i,helper_name[i],kernel_version,+supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */
2018-12-20 09:40 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
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",
...
}
}
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 <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
+ snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
+
+ fd = fopen(path, "r");
+ if (!fd && errno == ENOENT) {
+ /* Sometimes config is at /proc/config */
+ fd = fopen("/proc/config", "r");
I wonder whether the order here should be reversed?
1. try /proc/config.gz (CONFIG_IKCONFIG_PROC)
2. if not avail, try /boot/config-$(uname -r)
Because, at the end, /proc/config.gz is the real source of truth (if
available).
What is /proc/config btw? I can see only /proc/config.gz being exported.
Hi Stanislav,
Cilium checks for /proc/config (apparently this is where CoreOS puts its
config file), /proc/config.gz and /boot/config-$(uname -r) [0]. I took
inspiration on that but didn't implement the /proc/config.gz file,
because it would require linking with the libz and I'm not sure this is
worth it. Could be added as a follow-up if necessary.
[0] https://github.com/cilium/cilium/blob/master/bpf/run_probes.sh#L42
2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
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 simple 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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)returnres;}+staticvoid+probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+bool*supported_types)+{+constchar*plain_comment="eBPF program_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++res=bpf_probe_prog_type(prog_type,kernel_version,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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+boolsupported_types[128]={};+intkernel_version;+unsignedinti;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)print_start_section("syscall_config","Scanning system call and kernel version...");-probe_kernel_version();-probe_bpf_syscall();+kernel_version=probe_kernel_version();+if(!probe_bpf_syscall())+/* bpf() syscall unavailable, don't probe other BPF features */+gotoexit_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,kernel_version,supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */jsonw_end_object(json_wtr);
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)++/* Copyright (c) 2018 Netronome Systems, Inc. */++#include<errno.h>+#include<unistd.h>++#include<linux/filter.h>+#include<linux/kernel.h>++#include"bpf.h"+#include"libbpf.h"++staticvoid+prog_load(enumbpf_prog_typeprog_type,conststructbpf_insn*insns,+size_tinsns_cnt,intkernel_version,char*buf,size_tbuf_len,+__u32ifindex)+{+structbpf_load_program_attrxattr={};+intfd;++/* Some prog type require an expected_attach_type */+if(prog_type==BPF_PROG_TYPE_CGROUP_SOCK_ADDR)+xattr.expected_attach_type=BPF_CGROUP_INET4_CONNECT;++xattr.prog_type=prog_type;+xattr.insns=insns;+xattr.insns_cnt=insns_cnt;+xattr.license="GPL";+xattr.kern_version=kernel_version;+xattr.prog_ifindex=ifindex;++fd=bpf_load_program_xattr(&xattr,buf,buf_len);+if(fd>=0)+close(fd);+}++boolbpf_probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,
Do we really need this kernel_version argument? Isn't it going away in
the future (I saw a patch from Daniel that drops kernel check under
sys_bpf). Going forward, does it make sense to have it at the API
level?
It does go away and should be left aside for new applications. The
objective with bpftool is to be able to probe systems possibly running
older kernels though, and the only way to probe for kprobes support on
anything older than a 4.21 is to keep the version number.
2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
Hmm so I had a switch in the first place and it complained even with a
"default" label because -Wno-switch-enum is used for compiling. So you
would have me use a switch with all existing map types as explicit
labels, even if we do not use them? To limit the risk of forgetting to
update for new map types that would need specific parameters for the
probe, is that correct?
2018-12-20 09:53 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
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.
Each known helper is tested with all program types supported by the
system, in order to establish a compatibility matrix. Output is provided
as a list of compatible program types, for each helper.
Sample output:
# bpftool feature probe kernel
...
Scanning eBPF helper functions...
...
eBPF helper bpf_skb_change_head supported for program types: \
lwt_xmit sk_skb
eBPF helper bpf_xdp_adjust_head supported for program types: xdp
eBPF helper bpf_probe_read_str supported for program types: \
kprobe tracepoint perf_event raw_tracepoint
...
# bpftool --json --pretty feature probe kernel
{
...
"helpers": {
...
"bpf_skb_change_head_compat_list": ["lwt_xmit","sk_skb"
],
"bpf_xdp_adjust_head_compat_list": ["xdp"
],
"bpf_probe_read_str_compat_list": ["kprobe","tracepoint", \
"perf_event","raw_tracepoint"
],
...
}
}
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 <redacted>
---
.../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(+)
@@ -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.
@@ -418,6 +423,45 @@ static void probe_map_type(enum bpf_map_type map_type)print_bool_feature(feat_name,plain_desc,res);}+staticvoid+probe_helper(__u32id,constchar*name,intkernel_version,+bool*supported_types)+{+charfeat_name[128],plain_desc[128];+unsignedinti;++sprintf(feat_name,"%s_compat_list",name);+sprintf(plain_desc,+"eBPF helper %s supported for program types:",+name);++if(json_output){+jsonw_name(json_wtr,feat_name);+jsonw_start_array(json_wtr);+}else{+printf("%s",plain_desc);+}++for(i=BPF_PROG_TYPE_UNSPEC+1;+i<ARRAY_SIZE(prog_type_name);i++){+if(!supported_types[i])+continue;++if(!bpf_probe_helper(id,i,kernel_version,0))+continue;++if(json_output)+jsonw_string(json_wtr,prog_type_name[i]);+else+printf(" %s",prog_type_name[i]);+}++if(json_output)+jsonw_end_array(json_wtr);+else+printf("\n");+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -494,6 +538,13 @@ 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=1;i<ARRAY_SIZE(helper_name);i++)+probe_helper(i,helper_name[i],kernel_version,+supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */
Because your program can be rejected for a whole lot of reasons here,
since we do not make any effort on ensuring the call to the helper would
be valid. So if I remember correctly, -EINVAL would be returned if the
helper is not supported, or if the number of arguments is incorrect, or
if the arguments are not of the correct type, or...
So instead I'm looking for those error messages in the log buffer, which
seems much more reliable in this case.
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 18:14:08
On 12/20, Quentin Monnet wrote:
2018-12-20 09:40 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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",
...
}
}
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 <redacted>
Reviewed-by: Jakub Kicinski <redacted>
---
tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
+ snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
+
+ fd = fopen(path, "r");
+ if (!fd && errno == ENOENT) {
+ /* Sometimes config is at /proc/config */
+ fd = fopen("/proc/config", "r");
I wonder whether the order here should be reversed?
1. try /proc/config.gz (CONFIG_IKCONFIG_PROC)
2. if not avail, try /boot/config-$(uname -r)
Because, at the end, /proc/config.gz is the real source of truth (if
available).
What is /proc/config btw? I can see only /proc/config.gz being exported.
Hi Stanislav,
Cilium checks for /proc/config (apparently this is where CoreOS puts its
config file), /proc/config.gz and /boot/config-$(uname -r) [0]. I took
inspiration on that but didn't implement the /proc/config.gz file, because
it would require linking with the libz and I'm not sure this is worth it.
Could be added as a follow-up if necessary.
Makes sense, then maybe add a comment? So future readers know
that /proc/config.gz is explicitly missing, /proc/config comes from coreos,
and on the other distros we expect /boot/config-$(uname -r).
I'm mainly wondering because my arch box doesn't have
/boot/config-$(uname -r) and has only /proc/config.gz :-) Sigh..
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 18:17:11
On 12/20, Quentin Monnet wrote:
2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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 simple 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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)returnres;}+staticvoid+probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+bool*supported_types)+{+constchar*plain_comment="eBPF program_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++res=bpf_probe_prog_type(prog_type,kernel_version,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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+boolsupported_types[128]={};+intkernel_version;+unsignedinti;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)print_start_section("syscall_config","Scanning system call and kernel version...");-probe_kernel_version();-probe_bpf_syscall();+kernel_version=probe_kernel_version();+if(!probe_bpf_syscall())+/* bpf() syscall unavailable, don't probe other BPF features */+gotoexit_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,kernel_version,supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */jsonw_end_object(json_wtr);
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)++/* Copyright (c) 2018 Netronome Systems, Inc. */++#include<errno.h>+#include<unistd.h>++#include<linux/filter.h>+#include<linux/kernel.h>++#include"bpf.h"+#include"libbpf.h"++staticvoid+prog_load(enumbpf_prog_typeprog_type,conststructbpf_insn*insns,+size_tinsns_cnt,intkernel_version,char*buf,size_tbuf_len,+__u32ifindex)+{+structbpf_load_program_attrxattr={};+intfd;++/* Some prog type require an expected_attach_type */+if(prog_type==BPF_PROG_TYPE_CGROUP_SOCK_ADDR)+xattr.expected_attach_type=BPF_CGROUP_INET4_CONNECT;++xattr.prog_type=prog_type;+xattr.insns=insns;+xattr.insns_cnt=insns_cnt;+xattr.license="GPL";+xattr.kern_version=kernel_version;+xattr.prog_ifindex=ifindex;++fd=bpf_load_program_xattr(&xattr,buf,buf_len);+if(fd>=0)+close(fd);+}++boolbpf_probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,
Do we really need this kernel_version argument? Isn't it going away in
the future (I saw a patch from Daniel that drops kernel check under
sys_bpf). Going forward, does it make sense to have it at the API
level?
It does go away and should be left aside for new applications. The objective
with bpftool is to be able to probe systems possibly running older kernels
though, and the only way to probe for kprobes support on anything older than
a 4.21 is to keep the version number.
But isn't kernel_version irrelevant for probing? For probing, we always want
to have kernel_version == <running kernel version>. So I don't
understand why libbpf user should care about the version.
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 18:18:47
On 12/20, Quentin Monnet wrote:
2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
Hmm so I had a switch in the first place and it complained even with a
"default" label because -Wno-switch-enum is used for compiling. So you would
have me use a switch with all existing map types as explicit labels, even if
we do not use them? To limit the risk of forgetting to update for new map
types that would need specific parameters for the probe, is that correct?
Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
looks more rigid, it makes sure we never forget to update the probes
when adding new map types.
2018-12-20 10:17 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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 simple 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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)returnres;}+staticvoid+probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+bool*supported_types)+{+constchar*plain_comment="eBPF program_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++res=bpf_probe_prog_type(prog_type,kernel_version,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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+boolsupported_types[128]={};+intkernel_version;+unsignedinti;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)print_start_section("syscall_config","Scanning system call and kernel version...");-probe_kernel_version();-probe_bpf_syscall();+kernel_version=probe_kernel_version();+if(!probe_bpf_syscall())+/* bpf() syscall unavailable, don't probe other BPF features */+gotoexit_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,kernel_version,supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */jsonw_end_object(json_wtr);
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)++/* Copyright (c) 2018 Netronome Systems, Inc. */++#include<errno.h>+#include<unistd.h>++#include<linux/filter.h>+#include<linux/kernel.h>++#include"bpf.h"+#include"libbpf.h"++staticvoid+prog_load(enumbpf_prog_typeprog_type,conststructbpf_insn*insns,+size_tinsns_cnt,intkernel_version,char*buf,size_tbuf_len,+__u32ifindex)+{+structbpf_load_program_attrxattr={};+intfd;++/* Some prog type require an expected_attach_type */+if(prog_type==BPF_PROG_TYPE_CGROUP_SOCK_ADDR)+xattr.expected_attach_type=BPF_CGROUP_INET4_CONNECT;++xattr.prog_type=prog_type;+xattr.insns=insns;+xattr.insns_cnt=insns_cnt;+xattr.license="GPL";+xattr.kern_version=kernel_version;+xattr.prog_ifindex=ifindex;++fd=bpf_load_program_xattr(&xattr,buf,buf_len);+if(fd>=0)+close(fd);+}++boolbpf_probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,
Do we really need this kernel_version argument? Isn't it going away in
the future (I saw a patch from Daniel that drops kernel check under
sys_bpf). Going forward, does it make sense to have it at the API
level?
It does go away and should be left aside for new applications. The objective
with bpftool is to be able to probe systems possibly running older kernels
though, and the only way to probe for kprobes support on anything older than
a 4.21 is to keep the version number.
But isn't kernel_version irrelevant for probing? For probing, we always want
to have kernel_version == <running kernel version>. So I don't
understand why libbpf user should care about the version.
Oh, do you mean it should be collected in libbpf instead of bpftool and
not passed by the caller? Sorry I didn't understand in the first place.
If so, you're absolutely right, I should fix that, thanks!
2018-12-20 10:18 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
Hmm so I had a switch in the first place and it complained even with a
"default" label because -Wno-switch-enum is used for compiling. So you would
have me use a switch with all existing map types as explicit labels, even if
we do not use them? To limit the risk of forgetting to update for new map
types that would need specific parameters for the probe, is that correct?
Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
looks more rigid, it makes sure we never forget to update the probes
when adding new map types.
True... If it is acceptable to take the risk of breaking compilation for
libbpf if we forget to update the list when new map types are added (I
mean, this is the desired effect for keeping the list of parameters
up-to-date, but people e.g. just trying to compile bpftool might have
issues in such a case), I can change my code to use a switch.
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 18:29:55
On 12/20, Quentin Monnet wrote:
2018-12-20 10:17 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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 simple 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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)returnres;}+staticvoid+probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,+bool*supported_types)+{+constchar*plain_comment="eBPF program_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++res=bpf_probe_prog_type(prog_type,kernel_version,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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;+boolsupported_types[128]={};+intkernel_version;+unsignedinti;/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).*Let'sapproximate,andrestrictusagetorootuseronly.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)print_start_section("syscall_config","Scanning system call and kernel version...");-probe_kernel_version();-probe_bpf_syscall();+kernel_version=probe_kernel_version();+if(!probe_bpf_syscall())+/* bpf() syscall unavailable, don't probe other BPF features */+gotoexit_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,kernel_version,supported_types);+exit_close_json:if(json_output){/* End current "section" of probes */jsonw_end_object(json_wtr);
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)++/* Copyright (c) 2018 Netronome Systems, Inc. */++#include<errno.h>+#include<unistd.h>++#include<linux/filter.h>+#include<linux/kernel.h>++#include"bpf.h"+#include"libbpf.h"++staticvoid+prog_load(enumbpf_prog_typeprog_type,conststructbpf_insn*insns,+size_tinsns_cnt,intkernel_version,char*buf,size_tbuf_len,+__u32ifindex)+{+structbpf_load_program_attrxattr={};+intfd;++/* Some prog type require an expected_attach_type */+if(prog_type==BPF_PROG_TYPE_CGROUP_SOCK_ADDR)+xattr.expected_attach_type=BPF_CGROUP_INET4_CONNECT;++xattr.prog_type=prog_type;+xattr.insns=insns;+xattr.insns_cnt=insns_cnt;+xattr.license="GPL";+xattr.kern_version=kernel_version;+xattr.prog_ifindex=ifindex;++fd=bpf_load_program_xattr(&xattr,buf,buf_len);+if(fd>=0)+close(fd);+}++boolbpf_probe_prog_type(enumbpf_prog_typeprog_type,intkernel_version,
Do we really need this kernel_version argument? Isn't it going away in
the future (I saw a patch from Daniel that drops kernel check under
sys_bpf). Going forward, does it make sense to have it at the API
level?
It does go away and should be left aside for new applications. The objective
with bpftool is to be able to probe systems possibly running older kernels
though, and the only way to probe for kprobes support on anything older than
a 4.21 is to keep the version number.
But isn't kernel_version irrelevant for probing? For probing, we always want
to have kernel_version == <running kernel version>. So I don't
understand why libbpf user should care about the version.
Oh, do you mean it should be collected in libbpf instead of bpftool and not
passed by the caller? Sorry I didn't understand in the first place. If so,
you're absolutely right, I should fix that, thanks!
Yes, I was basically trying to understand why the user needs to pass it.
Querying/setting it for type==trace_point in prog_load seems like a way to go.
From: Stanislav Fomichev <sdf@fomichev.me> Date: 2018-12-20 18:42:08
On 12/20, Quentin Monnet wrote:
2018-12-20 10:18 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
Hmm so I had a switch in the first place and it complained even with a
"default" label because -Wno-switch-enum is used for compiling. So you would
have me use a switch with all existing map types as explicit labels, even if
we do not use them? To limit the risk of forgetting to update for new map
types that would need specific parameters for the probe, is that correct?
Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
looks more rigid, it makes sure we never forget to update the probes
when adding new map types.
True... If it is acceptable to take the risk of breaking compilation for
libbpf if we forget to update the list when new map types are added (I mean,
this is the desired effect for keeping the list of parameters up-to-date,
but people e.g. just trying to compile bpftool might have issues in such a
case), I can change my code to use a switch.
I think we already have a precedent, see bpf_prog_type__needs_kver in
libbpf ;-)
2018-12-20 10:42 UTC-0800 ~ Stanislav Fomichev [off-list ref]
On 12/20, Quentin Monnet wrote:
quoted
2018-12-20 10:18 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev [off-list ref]
quoted
On 12/20, Quentin Monnet wrote:
quoted
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,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <redacted>
---
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 | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,print_bool_feature(feat_name,plain_desc,res);}+staticvoidprobe_map_type(enumbpf_map_typemap_type)+{+constchar*plain_comment="eBPF map_type ";+charfeat_name[128],plain_desc[128];+size_tmaxlen;+boolres;++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);+}+staticintdo_probe(intargc,char**argv){enumprobe_componenttarget=COMPONENT_UNSPEC;
@@ -468,6 +488,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,kernel_version,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 */
Hmm so I had a switch in the first place and it complained even with a
"default" label because -Wno-switch-enum is used for compiling. So you would
have me use a switch with all existing map types as explicit labels, even if
we do not use them? To limit the risk of forgetting to update for new map
types that would need specific parameters for the probe, is that correct?
Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
looks more rigid, it makes sure we never forget to update the probes
when adding new map types.
True... If it is acceptable to take the risk of breaking compilation for
libbpf if we forget to update the list when new map types are added (I mean,
this is the desired effect for keeping the list of parameters up-to-date,
but people e.g. just trying to compile bpftool might have issues in such a
case), I can change my code to use a switch.
I think we already have a precedent, see bpf_prog_type__needs_kver in
libbpf ;-)
True, thanks! Although that function might well disappear in the future.
2018-12-20 18:25 UTC+0100 ~ Daniel Borkmann [off-list ref]
On 12/20/2018 01:24 PM, Quentin Monnet wrote:
quoted
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 ***/
...
#define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
"lwt_xmit " \
"sk_skb "
#define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
"xdp "
#define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
"kprobe " \
"tracepoint " \
"perf_event " \
"raw_tracepoint "
...
Rest looks good to me, just a comment here. How would programs use the
compat list? Wouldn't it be nicer to provide a PROG(HELPER) availability
query along the lines of ...
#if FOO_HAVE_SOCKET_FILTER(skb_load_bytes) == 1
// ...
#endif
... where the helper provides these macro functions and results in the
header file? So they can be used easily in the code from BPF prog or
normal C applications?
I was thinking about strstr() here, but it is true it is limited to
runtime and will not work with the preprocessor :s.
How would you dump parameters to make your PROG(HELPER) work? Something
along the following?
#define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_skb_load_bytes 1
#define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_bind 0
...
#define HAVE_SOCKET_FILTER_HELPER(name) \
BPF__PROG_TYPE_SOCKET_FILTER__HELPER_ ## name
or even
#define HAVE_PROG_TYPE_HELPER(type, helper) \
BPF__PROG_TYPE_ ## type ## __HELPER_ ## helper
Does that correspond to what you have in mind?
Thanks,
Quentin
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-12-20 22:29:59
On 12/20/2018 09:05 PM, Quentin Monnet wrote:
2018-12-20 18:25 UTC+0100 ~ Daniel Borkmann [off-list ref]
quoted
On 12/20/2018 01:24 PM, Quentin Monnet wrote:
quoted
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 ***/
...
#define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
"lwt_xmit " \
"sk_skb "
#define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
"xdp "
#define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
"kprobe " \
"tracepoint " \
"perf_event " \
"raw_tracepoint "
...
Rest looks good to me, just a comment here. How would programs use the
compat list? Wouldn't it be nicer to provide a PROG(HELPER) availability
query along the lines of ...
#if FOO_HAVE_SOCKET_FILTER(skb_load_bytes) == 1
// ...
#endif
... where the helper provides these macro functions and results in the
header file? So they can be used easily in the code from BPF prog or
normal C applications?
I was thinking about strstr() here, but it is true it is limited to runtime and will not work with the preprocessor :s.
How would you dump parameters to make your PROG(HELPER) work? Something along the following?
#define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_skb_load_bytes 1
#define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_bind 0
...
#define HAVE_SOCKET_FILTER_HELPER(name) \
BPF__PROG_TYPE_SOCKET_FILTER__HELPER_ ## name
or even
#define HAVE_PROG_TYPE_HELPER(type, helper) \
BPF__PROG_TYPE_ ## type ## __HELPER_ ## helper
Does that correspond to what you have in mind?
Yeah, something like this; slight preference to the latter.
Thanks,
Daniel