From patchwork Tue Feb 18 19:02:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Rostecki X-Patchwork-Id: 11389303 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2CFF313A4 for ; Tue, 18 Feb 2020 19:03:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0F7CC24672 for ; Tue, 18 Feb 2020 19:03:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726496AbgBRTCg (ORCPT ); Tue, 18 Feb 2020 14:02:36 -0500 Received: from mx2.suse.de ([195.135.220.15]:35592 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726464AbgBRTCg (ORCPT ); Tue, 18 Feb 2020 14:02:36 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 3EA46ABEA; Tue, 18 Feb 2020 19:02:33 +0000 (UTC) From: Michal Rostecki To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , Quentin Monnet , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Shuah Khan , "David S. Miller" , Jesper Dangaard Brouer , John Fastabend , linux-kselftest@vger.kernel.org Subject: [PATCH bpf-next 1/6] bpftool: Move out sections to separate functions Date: Tue, 18 Feb 2020 20:02:18 +0100 Message-Id: <20200218190224.22508-2-mrostecki@opensuse.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218190224.22508-1-mrostecki@opensuse.org> References: <20200218190224.22508-1-mrostecki@opensuse.org> MIME-Version: 1.0 Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Remove all calls of print_end_then_start_section function and for loops out from the do_probe function. Instead, provide separate functions for each section (like i.e. section_helpers) which are called in do_probe. Next changes are going to allow probing chosen subsets of features. This change will make them more readable. Signed-off-by: Michal Rostecki --- tools/bpf/bpftool/feature.c | 219 +++++++++++++++++++++--------------- 1 file changed, 126 insertions(+), 93 deletions(-) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index 941873d778d8..345e4a2b4f53 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -112,18 +112,12 @@ print_start_section(const char *json_title, const char *plain_title, } } -static void -print_end_then_start_section(const char *json_title, const char *plain_title, - const char *define_comment, - const char *define_prefix) +static void print_end_section(void) { if (json_output) jsonw_end_object(json_wtr); else printf("\n"); - - print_start_section(json_title, plain_title, define_comment, - define_prefix); } /* Probing functions */ @@ -584,13 +578,130 @@ probe_large_insn_limit(const char *define_prefix, __u32 ifindex) res, define_prefix); } +static void +section_system_config(enum probe_component target, const char *define_prefix) +{ + switch (target) { + case COMPONENT_KERNEL: + case COMPONENT_UNSPEC: + if (define_prefix) + break; + + print_start_section("system_config", + "Scanning system configuration...", + NULL, /* define_comment never used here */ + NULL); /* define_prefix always NULL here */ + if (check_procfs()) { + probe_unprivileged_disabled(); + probe_jit_enable(); + probe_jit_harden(); + probe_jit_kallsyms(); + probe_jit_limit(); + } else { + p_info("/* procfs not mounted, skipping related probes */"); + } + probe_kernel_image_config(); + print_end_section(); + break; + default: + break; + } +} + +static bool section_syscall_config(const char *define_prefix) +{ + bool res; + + print_start_section("syscall_config", + "Scanning system call availability...", + "/*** System call availability ***/", + define_prefix); + res = probe_bpf_syscall(define_prefix); + print_end_section(); + + return res; +} + +static void +section_program_types(bool *supported_types, const char *define_prefix, + __u32 ifindex) +{ + unsigned int i; + + print_start_section("program_types", + "Scanning eBPF program types...", + "/*** eBPF program types ***/", + define_prefix); + + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) + probe_prog_type(i, supported_types, define_prefix, ifindex); + + print_end_section(); +} + +static void section_map_types(const char *define_prefix, __u32 ifindex) +{ + unsigned int i; + + print_start_section("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, define_prefix, ifindex); + + print_end_section(); +} + +static void +section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex) +{ + unsigned int i; + + print_start_section("helpers", + "Scanning eBPF helper functions...", + "/*** eBPF helper functions ***/", + define_prefix); + + if (define_prefix) + printf("/*\n" + " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n" + " * to determine if is available for ,\n" + " * e.g.\n" + " * #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n" + " * // do stuff with this helper\n" + " * #elif\n" + " * // use a workaround\n" + " * #endif\n" + " */\n" + "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper) \\\n" + " %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n", + define_prefix, define_prefix, define_prefix, + define_prefix); + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) + probe_helpers_for_progtype(i, supported_types[i], + define_prefix, ifindex); + + print_end_section(); +} + +static void section_misc(const char *define_prefix, __u32 ifindex) +{ + print_start_section("misc", + "Scanning miscellaneous eBPF features...", + "/*** eBPF misc features ***/", + define_prefix); + probe_large_insn_limit(define_prefix, ifindex); + print_end_section(); +} + static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; const char *define_prefix = NULL; bool supported_types[128] = {}; __u32 ifindex = 0; - unsigned int i; char *ifname; /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). @@ -658,97 +769,19 @@ static int do_probe(int argc, char **argv) jsonw_start_object(json_wtr); } - switch (target) { - case COMPONENT_KERNEL: - case COMPONENT_UNSPEC: - if (define_prefix) - break; - - print_start_section("system_config", - "Scanning system configuration...", - NULL, /* define_comment never used here */ - NULL); /* define_prefix always NULL here */ - if (check_procfs()) { - probe_unprivileged_disabled(); - probe_jit_enable(); - probe_jit_harden(); - probe_jit_kallsyms(); - probe_jit_limit(); - } else { - p_info("/* procfs not mounted, skipping related probes */"); - } - probe_kernel_image_config(); - if (json_output) - jsonw_end_object(json_wtr); - else - printf("\n"); - break; - default: - break; - } - - print_start_section("syscall_config", - "Scanning system call availability...", - "/*** System call availability ***/", - define_prefix); - - if (!probe_bpf_syscall(define_prefix)) + section_system_config(target, define_prefix); + if (!section_syscall_config(define_prefix)) /* bpf() syscall unavailable, don't probe other BPF features */ goto exit_close_json; - - print_end_then_start_section("program_types", - "Scanning eBPF program types...", - "/*** eBPF program types ***/", - define_prefix); - - for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) - probe_prog_type(i, supported_types, define_prefix, ifindex); - - print_end_then_start_section("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, define_prefix, ifindex); - - print_end_then_start_section("helpers", - "Scanning eBPF helper functions...", - "/*** eBPF helper functions ***/", - define_prefix); - - if (define_prefix) - printf("/*\n" - " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n" - " * to determine if is available for ,\n" - " * e.g.\n" - " * #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n" - " * // do stuff with this helper\n" - " * #elif\n" - " * // use a workaround\n" - " * #endif\n" - " */\n" - "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper) \\\n" - " %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n", - define_prefix, define_prefix, define_prefix, - define_prefix); - for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) - probe_helpers_for_progtype(i, supported_types[i], - define_prefix, ifindex); - - print_end_then_start_section("misc", - "Scanning miscellaneous eBPF features...", - "/*** eBPF misc features ***/", - define_prefix); - probe_large_insn_limit(define_prefix, ifindex); + section_program_types(supported_types, define_prefix, ifindex); + section_map_types(define_prefix, ifindex); + section_helpers(supported_types, define_prefix, ifindex); + section_misc(define_prefix, ifindex); exit_close_json: - if (json_output) { - /* End current "section" of probes */ - jsonw_end_object(json_wtr); + if (json_output) /* End root object */ jsonw_end_object(json_wtr); - } return 0; } From patchwork Tue Feb 18 19:02:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Rostecki X-Patchwork-Id: 11389301 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7117213A4 for ; Tue, 18 Feb 2020 19:03:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 499C924672 for ; Tue, 18 Feb 2020 19:03:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726582AbgBRTCj (ORCPT ); Tue, 18 Feb 2020 14:02:39 -0500 Received: from mx2.suse.de ([195.135.220.15]:35632 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726283AbgBRTCh (ORCPT ); Tue, 18 Feb 2020 14:02:37 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id C8EDDADBB; Tue, 18 Feb 2020 19:02:34 +0000 (UTC) From: Michal Rostecki To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , Quentin Monnet , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Shuah Khan , linux-kselftest@vger.kernel.org Subject: [PATCH bpf-next 2/6] bpftool: Allow to select a specific section to probe Date: Tue, 18 Feb 2020 20:02:19 +0100 Message-Id: <20200218190224.22508-3-mrostecki@opensuse.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218190224.22508-1-mrostecki@opensuse.org> References: <20200218190224.22508-1-mrostecki@opensuse.org> MIME-Version: 1.0 Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org This change introduces a new positional argument "section" which takes the following arguments: - system_config - syscall_config - program_types - map_types - helpers - misc If "section" argument is defined, only that particular section is going to be probed and printed. The only section which is always going to be probed is "syscall_config", but if the other section was provided as an argument, "syscall_config" check will perform silently without printing and exit bpftool if the bpf() syscall is not available (because in that case running any probe has no sense). Signed-off-by: Michal Rostecki --- tools/bpf/bpftool/feature.c | 159 ++++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 36 deletions(-) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index 345e4a2b4f53..cfba0faf148f 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -22,6 +22,13 @@ # define PROC_SUPER_MAGIC 0x9fa0 #endif +#define SECTION_SYSCALL_CONFIG "syscall_config" +#define SECTION_SYSTEM_CONFIG "system_config" +#define SECTION_PROGRAM_TYPES "program_types" +#define SECTION_MAP_TYPES "map_types" +#define SECTION_HELPERS "helpers" +#define SECTION_MISC "misc" + enum probe_component { COMPONENT_UNSPEC, COMPONENT_KERNEL, @@ -436,24 +443,26 @@ static void probe_kernel_image_config(void) } } -static bool probe_bpf_syscall(const char *define_prefix) +static bool +probe_bpf_syscall(bool print_syscall_config, const char *define_prefix) { bool res; bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0); res = (errno != ENOSYS); - print_bool_feature("have_bpf_syscall", - "bpf() syscall", - "BPF_SYSCALL", - res, define_prefix); + if (print_syscall_config) + print_bool_feature("have_bpf_syscall", + "bpf() syscall", + "BPF_SYSCALL", + res, define_prefix); return res; } static void -probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, - const char *define_prefix, __u32 ifindex) +probe_prog_type(bool print_program_types, enum bpf_prog_type prog_type, + bool *supported_types, const char *define_prefix, __u32 ifindex) { char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF program_type "; @@ -484,8 +493,10 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types, sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]); uppercase(define_name, sizeof(define_name)); sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]); - print_bool_feature(feat_name, plain_desc, define_name, res, - define_prefix); + + if (print_program_types) + print_bool_feature(feat_name, plain_desc, define_name, res, + define_prefix); } static void @@ -587,7 +598,7 @@ section_system_config(enum probe_component target, const char *define_prefix) if (define_prefix) break; - print_start_section("system_config", + print_start_section(SECTION_SYSTEM_CONFIG, "Scanning system configuration...", NULL, /* define_comment never used here */ NULL); /* define_prefix always NULL here */ @@ -608,42 +619,48 @@ section_system_config(enum probe_component target, const char *define_prefix) } } -static bool section_syscall_config(const char *define_prefix) +static bool +section_syscall_config(bool print_syscall_config, const char *define_prefix) { bool res; - print_start_section("syscall_config", - "Scanning system call availability...", - "/*** System call availability ***/", - define_prefix); - res = probe_bpf_syscall(define_prefix); - print_end_section(); + if (print_syscall_config) + print_start_section(SECTION_SYSCALL_CONFIG, + "Scanning system call availability...", + "/*** System call availability ***/", + define_prefix); + res = probe_bpf_syscall(print_syscall_config, define_prefix); + if (print_syscall_config) + print_end_section(); return res; } static void -section_program_types(bool *supported_types, const char *define_prefix, - __u32 ifindex) +section_program_types(bool print_program_types, bool *supported_types, + const char *define_prefix, __u32 ifindex) { unsigned int i; - print_start_section("program_types", - "Scanning eBPF program types...", - "/*** eBPF program types ***/", - define_prefix); + if (print_program_types) + print_start_section(SECTION_PROGRAM_TYPES, + "Scanning eBPF program types...", + "/*** eBPF program types ***/", + define_prefix); for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) - probe_prog_type(i, supported_types, define_prefix, ifindex); + probe_prog_type(print_program_types, i, supported_types, + define_prefix, ifindex); - print_end_section(); + if (print_program_types) + print_end_section(); } static void section_map_types(const char *define_prefix, __u32 ifindex) { unsigned int i; - print_start_section("map_types", + print_start_section(SECTION_MAP_TYPES, "Scanning eBPF map types...", "/*** eBPF map types ***/", define_prefix); @@ -659,7 +676,7 @@ section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex) { unsigned int i; - print_start_section("helpers", + print_start_section(SECTION_HELPERS, "Scanning eBPF helper functions...", "/*** eBPF helper functions ***/", define_prefix); @@ -688,7 +705,7 @@ section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex) static void section_misc(const char *define_prefix, __u32 ifindex) { - print_start_section("misc", + print_start_section(SECTION_MISC, "Scanning miscellaneous eBPF features...", "/*** eBPF misc features ***/", define_prefix); @@ -699,8 +716,25 @@ static void section_misc(const char *define_prefix, __u32 ifindex) static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; + /* Syscall probe is always performed, because performing any other + * checks without bpf() syscall does not make sense and the program + * should exit. + */ + bool print_syscall_config = false; const char *define_prefix = NULL; + bool check_system_config = false; + /* Program types probes are needed if helper probes are going to be + * performed. Therefore we should differentiate between checking and + * printing supported program types. If only helper checks were + * requested, program types probes will be performed, but not printed. + */ + bool check_program_types = false; + bool print_program_types = false; bool supported_types[128] = {}; + bool check_map_types = false; + bool check_helpers = false; + bool check_section = false; + bool check_misc = false; __u32 ifindex = 0; char *ifname; @@ -740,6 +774,39 @@ static int do_probe(int argc, char **argv) strerror(errno)); return -1; } + } else if (is_prefix(*argv, "section")) { + check_section = true; + NEXT_ARG(); + if (is_prefix(*argv, SECTION_SYSTEM_CONFIG)) { + check_system_config = true; + } else if (is_prefix(*argv, SECTION_SYSCALL_CONFIG)) { + print_syscall_config = true; + } else if (is_prefix(*argv, SECTION_PROGRAM_TYPES)) { + check_program_types = true; + print_program_types = true; + } else if (is_prefix(*argv, SECTION_MAP_TYPES)) { + check_map_types = true; + } else if (is_prefix(*argv, SECTION_HELPERS)) { + /* When helpers probes are requested, program + * types probes have to be performed, but they + * may not be printed. + */ + check_program_types = true; + check_helpers = true; + } else if (is_prefix(*argv, SECTION_MISC)) { + check_misc = true; + } else { + p_err("unrecognized section '%s', available sections: %s, %s, %s, %s, %s, %s", + *argv, + SECTION_SYSTEM_CONFIG, + SECTION_SYSCALL_CONFIG, + SECTION_PROGRAM_TYPES, + SECTION_MAP_TYPES, + SECTION_HELPERS, + SECTION_MISC); + return -1; + } + NEXT_ARG(); } else if (is_prefix(*argv, "macros") && !define_prefix) { define_prefix = ""; NEXT_ARG(); @@ -764,19 +831,36 @@ static int do_probe(int argc, char **argv) } } + /* Perform all checks if specific section check was not requested. */ + if (!check_section) { + print_syscall_config = true; + check_system_config = true; + check_program_types = true; + print_program_types = true; + check_map_types = true; + check_helpers = true; + check_misc = true; + } + if (json_output) { define_prefix = NULL; jsonw_start_object(json_wtr); } - section_system_config(target, define_prefix); - if (!section_syscall_config(define_prefix)) + if (check_system_config) + section_system_config(target, define_prefix); + if (!section_syscall_config(print_syscall_config, define_prefix)) /* bpf() syscall unavailable, don't probe other BPF features */ goto exit_close_json; - section_program_types(supported_types, define_prefix, ifindex); - section_map_types(define_prefix, ifindex); - section_helpers(supported_types, define_prefix, ifindex); - section_misc(define_prefix, ifindex); + if (check_program_types) + section_program_types(print_program_types, supported_types, + define_prefix, ifindex); + if (check_map_types) + section_map_types(define_prefix, ifindex); + if (check_helpers) + section_helpers(supported_types, define_prefix, ifindex); + if (check_misc) + section_misc(define_prefix, ifindex); exit_close_json: if (json_output) @@ -794,12 +878,15 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s %s probe [COMPONENT] [macros [prefix PREFIX]]\n" + "Usage: %s %s probe [COMPONENT] [section SECTION] [macros [prefix PREFIX]]\n" " %s %s help\n" "\n" " COMPONENT := { kernel | dev NAME }\n" + " SECTION := { %s | %s | %s | %s | %s | %s }\n" "", - bin_name, argv[-2], bin_name, argv[-2]); + bin_name, argv[-2], bin_name, argv[-2], SECTION_SYSTEM_CONFIG, + SECTION_SYSCALL_CONFIG, SECTION_PROGRAM_TYPES, + SECTION_MAP_TYPES, SECTION_HELPERS, SECTION_MISC); return 0; } From patchwork Tue Feb 18 19:02:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Rostecki X-Patchwork-Id: 11389299 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B42951580 for ; Tue, 18 Feb 2020 19:03:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8D10E24655 for ; Tue, 18 Feb 2020 19:03:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726650AbgBRTCl (ORCPT ); Tue, 18 Feb 2020 14:02:41 -0500 Received: from mx2.suse.de ([195.135.220.15]:35646 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726464AbgBRTCl (ORCPT ); Tue, 18 Feb 2020 14:02:41 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id AD6E7ADBE; Tue, 18 Feb 2020 19:02:36 +0000 (UTC) From: Michal Rostecki To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , Quentin Monnet , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Shuah Khan , linux-kselftest@vger.kernel.org Subject: [PATCH bpf-next 3/6] bpftool: Add arguments for filtering in and filtering out probes Date: Tue, 18 Feb 2020 20:02:20 +0100 Message-Id: <20200218190224.22508-4-mrostecki@opensuse.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218190224.22508-1-mrostecki@opensuse.org> References: <20200218190224.22508-1-mrostecki@opensuse.org> MIME-Version: 1.0 Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add positional arguments "filter_in" and "filter_out" to the feature command of bpftool. If "filter_in" is defined, bpftool is going to perform and print only checks which match the given pattern. If "filter_out" is defined, bpftool will not perform and print checks which match the given pattern. Signed-off-by: Michal Rostecki --- tools/bpf/bpftool/feature.c | 324 ++++++++++++++++++++++++++++-------- 1 file changed, 256 insertions(+), 68 deletions(-) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index cfba0faf148f..f10e928d18c8 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,55 @@ static void uppercase(char *str, size_t len) str[i] = toupper(str[i]); } +/* Filtering utility functions */ + +static bool +check_filters(const char *name, regex_t *filter_in, regex_t *filter_out) +{ + char err_buf[100]; + int ret; + + /* Do not probe if filter_in was defined and string does not match + * against the pattern. + */ + if (filter_in) { + ret = regexec(filter_in, name, 0, NULL, 0); + switch (ret) { + case 0: + break; + case REG_NOMATCH: + return false; + default: + regerror(ret, filter_in, err_buf, ARRAY_SIZE(err_buf)); + p_err("could not match regex: %s", err_buf); + free(filter_in); + free(filter_out); + exit(1); + } + } + + /* Do not probe if filter_out was defined and string matches against the + * pattern. + */ + if (filter_out) { + ret = regexec(filter_out, name, 0, NULL, 0); + switch (ret) { + case 0: + return false; + case REG_NOMATCH: + break; + default: + regerror(ret, filter_out, err_buf, ARRAY_SIZE(err_buf)); + p_err("could not match regex: %s", err_buf); + free(filter_in); + free(filter_out); + exit(1); + } + } + + return true; +} + /* Printing utility functions */ static void @@ -79,11 +129,16 @@ print_bool_feature(const char *feat_name, const char *plain_name, printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); } -static void print_kernel_option(const char *name, const char *value) +static void +print_kernel_option(const char *name, const char *value, regex_t *filter_in, + regex_t *filter_out) { char *endptr; int res; + if (!check_filters(name, filter_in, filter_out)) + return; + /* No support for C-style ouptut */ if (json_output) { @@ -154,15 +209,19 @@ static int read_procfs(const char *path) return res; } -static void probe_unprivileged_disabled(void) +static void probe_unprivileged_disabled(regex_t *filter_in, regex_t *filter_out) { + const char *feat_name = "unprivileged_bpf_disabled"; int res; /* No support for C-style ouptut */ + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled"); if (json_output) { - jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res); + jsonw_int_field(json_wtr, feat_name, res); } else { switch (res) { case 0: @@ -180,15 +239,19 @@ static void probe_unprivileged_disabled(void) } } -static void probe_jit_enable(void) +static void probe_jit_enable(regex_t *filter_in, regex_t *filter_out) { + const char *feat_name = "bpf_jit_enable"; int res; /* No support for C-style ouptut */ + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = read_procfs("/proc/sys/net/core/bpf_jit_enable"); if (json_output) { - jsonw_int_field(json_wtr, "bpf_jit_enable", res); + jsonw_int_field(json_wtr, feat_name, res); } else { switch (res) { case 0: @@ -210,15 +273,19 @@ static void probe_jit_enable(void) } } -static void probe_jit_harden(void) +static void probe_jit_harden(regex_t *filter_in, regex_t *filter_out) { + const char *feat_name = "bpf_jit_harden"; int res; /* No support for C-style ouptut */ + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = read_procfs("/proc/sys/net/core/bpf_jit_harden"); if (json_output) { - jsonw_int_field(json_wtr, "bpf_jit_harden", res); + jsonw_int_field(json_wtr, feat_name, res); } else { switch (res) { case 0: @@ -240,15 +307,19 @@ static void probe_jit_harden(void) } } -static void probe_jit_kallsyms(void) +static void probe_jit_kallsyms(regex_t *filter_in, regex_t *filter_out) { + const char *feat_name = "bpf_jit_kallsyms"; int res; /* No support for C-style ouptut */ + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms"); if (json_output) { - jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res); + jsonw_int_field(json_wtr, feat_name, res); } else { switch (res) { case 0: @@ -266,15 +337,19 @@ static void probe_jit_kallsyms(void) } } -static void probe_jit_limit(void) +static void probe_jit_limit(regex_t *filter_in, regex_t *filter_out) { + const char *feat_name = "bpf_jit_limit"; int res; /* No support for C-style ouptut */ + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = read_procfs("/proc/sys/net/core/bpf_jit_limit"); if (json_output) { - jsonw_int_field(json_wtr, "bpf_jit_limit", res); + jsonw_int_field(json_wtr, feat_name, res); } else { switch (res) { case -1: @@ -314,7 +389,8 @@ static bool read_next_kernel_config_option(gzFile file, char *buf, size_t n, return false; } -static void probe_kernel_image_config(void) +static void +probe_kernel_image_config(regex_t *filter_in, regex_t *filter_out) { static const char * const options[] = { /* Enable BPF */ @@ -438,23 +514,31 @@ static void probe_kernel_image_config(void) gzclose(file); for (i = 0; i < ARRAY_SIZE(options); i++) { - print_kernel_option(options[i], values[i]); + print_kernel_option(options[i], values[i], filter_in, + filter_out); free(values[i]); } } static bool -probe_bpf_syscall(bool print_syscall_config, const char *define_prefix) +probe_bpf_syscall(bool print_syscall_config, const char *define_prefix, + regex_t *filter_in, regex_t *filter_out) { + const char *feat_name = "have_bpf_syscall"; + const char *plain_desc = "bpf() syscall"; + const char *define_name = "BPF_SYSCALL"; bool res; bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0); res = (errno != ENOSYS); + if (!check_filters(feat_name, filter_in, filter_out)) + print_syscall_config = false; + if (print_syscall_config) - print_bool_feature("have_bpf_syscall", - "bpf() syscall", - "BPF_SYSCALL", + print_bool_feature(feat_name, + plain_desc, + define_name, res, define_prefix); return res; @@ -462,13 +546,19 @@ probe_bpf_syscall(bool print_syscall_config, const char *define_prefix) static void probe_prog_type(bool print_program_types, enum bpf_prog_type prog_type, - bool *supported_types, const char *define_prefix, __u32 ifindex) + bool *supported_types, const char *define_prefix, + regex_t *filter_in, regex_t *filter_out, __u32 ifindex) { char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF program_type "; size_t maxlen; bool res; + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]); + sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]); + uppercase(define_name, sizeof(define_name)); + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]); + if (ifindex) /* Only test offload-able program types */ switch (prog_type) { @@ -489,10 +579,8 @@ probe_prog_type(bool print_program_types, enum bpf_prog_type prog_type, return; } - sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]); - sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]); - uppercase(define_name, sizeof(define_name)); - sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]); + if (!check_filters(feat_name, filter_in, filter_out)) + return; if (print_program_types) print_bool_feature(feat_name, plain_desc, define_name, res, @@ -501,13 +589,21 @@ probe_prog_type(bool print_program_types, enum bpf_prog_type prog_type, static void probe_map_type(enum bpf_map_type map_type, const char *define_prefix, - __u32 ifindex) + regex_t *filter_in, regex_t *filter_out, __u32 ifindex) { char feat_name[128], plain_desc[128], define_name[128]; const char *plain_comment = "eBPF map_type "; size_t maxlen; bool res; + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]); + sprintf(define_name, "%s_map_type", map_type_name[map_type]); + uppercase(define_name, sizeof(define_name)); + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]); + + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = bpf_probe_map_type(map_type, ifindex); maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; @@ -516,23 +612,23 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix, return; } - sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]); - sprintf(define_name, "%s_map_type", map_type_name[map_type]); - uppercase(define_name, sizeof(define_name)); - sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]); print_bool_feature(feat_name, plain_desc, define_name, res, define_prefix); } static void probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type, - const char *define_prefix, __u32 ifindex) + const char *define_prefix, regex_t *filter_in, + regex_t *filter_out, __u32 ifindex) { const char *ptype_name = prog_type_name[prog_type]; char feat_name[128]; unsigned int id; bool res; + if (!check_filters(ptype_name, filter_in, filter_out)) + return; + if (ifindex) /* Only test helpers for offload-able program types */ switch (prog_type) { @@ -553,6 +649,9 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type, } for (id = 1; id < ARRAY_SIZE(helper_name); id++) { + if (!check_filters(helper_name[id], filter_in, filter_out)) + continue; + if (!supported_type) res = false; else @@ -578,19 +677,27 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type, } static void -probe_large_insn_limit(const char *define_prefix, __u32 ifindex) +probe_large_insn_limit(const char *define_prefix, regex_t *filter_in, + regex_t *filter_out, __u32 ifindex) { + const char *plain_desc = "Large program size limit"; + const char *feat_name = "have_large_insn_limit"; + const char *define_name = "LARGE_INSN_LIMIT"; bool res; + if (!check_filters(feat_name, filter_in, filter_out)) + return; + res = bpf_probe_large_insn_limit(ifindex); - print_bool_feature("have_large_insn_limit", - "Large program size limit", - "LARGE_INSN_LIMIT", + print_bool_feature(feat_name, + plain_desc, + define_name, res, define_prefix); } static void -section_system_config(enum probe_component target, const char *define_prefix) +section_system_config(enum probe_component target, const char *define_prefix, + regex_t *filter_in, regex_t *filter_out) { switch (target) { case COMPONENT_KERNEL: @@ -603,15 +710,15 @@ section_system_config(enum probe_component target, const char *define_prefix) NULL, /* define_comment never used here */ NULL); /* define_prefix always NULL here */ if (check_procfs()) { - probe_unprivileged_disabled(); - probe_jit_enable(); - probe_jit_harden(); - probe_jit_kallsyms(); - probe_jit_limit(); + probe_unprivileged_disabled(filter_in, filter_out); + probe_jit_enable(filter_in, filter_out); + probe_jit_harden(filter_in, filter_out); + probe_jit_kallsyms(filter_in, filter_out); + probe_jit_limit(filter_in, filter_out); } else { p_info("/* procfs not mounted, skipping related probes */"); } - probe_kernel_image_config(); + probe_kernel_image_config(filter_in, filter_out); print_end_section(); break; default: @@ -620,7 +727,8 @@ section_system_config(enum probe_component target, const char *define_prefix) } static bool -section_syscall_config(bool print_syscall_config, const char *define_prefix) +section_syscall_config(bool print_syscall_config, const char *define_prefix, + regex_t *filter_in, regex_t *filter_out) { bool res; @@ -629,7 +737,8 @@ section_syscall_config(bool print_syscall_config, const char *define_prefix) "Scanning system call availability...", "/*** System call availability ***/", define_prefix); - res = probe_bpf_syscall(print_syscall_config, define_prefix); + res = probe_bpf_syscall(print_syscall_config, define_prefix, + filter_in, filter_out); if (print_syscall_config) print_end_section(); @@ -638,7 +747,8 @@ section_syscall_config(bool print_syscall_config, const char *define_prefix) static void section_program_types(bool print_program_types, bool *supported_types, - const char *define_prefix, __u32 ifindex) + const char *define_prefix, regex_t *filter_in, + regex_t *filter_out, __u32 ifindex) { unsigned int i; @@ -650,13 +760,14 @@ section_program_types(bool print_program_types, bool *supported_types, for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) probe_prog_type(print_program_types, i, supported_types, - define_prefix, ifindex); + define_prefix, filter_in, filter_out, ifindex); if (print_program_types) print_end_section(); } -static void section_map_types(const char *define_prefix, __u32 ifindex) +static void section_map_types(const char *define_prefix, regex_t *filter_in, + regex_t *filter_out, __u32 ifindex) { unsigned int i; @@ -666,13 +777,15 @@ static void section_map_types(const char *define_prefix, __u32 ifindex) define_prefix); for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++) - probe_map_type(i, define_prefix, ifindex); + probe_map_type(i, define_prefix, filter_in, filter_out, + ifindex); print_end_section(); } static void -section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex) +section_helpers(bool *supported_types, const char *define_prefix, + regex_t *filter_in, regex_t *filter_out, __u32 ifindex) { unsigned int i; @@ -698,18 +811,20 @@ section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex) define_prefix); for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) probe_helpers_for_progtype(i, supported_types[i], - define_prefix, ifindex); + define_prefix, filter_in, filter_out, + ifindex); print_end_section(); } -static void section_misc(const char *define_prefix, __u32 ifindex) +static void section_misc(const char *define_prefix, regex_t *filter_in, + regex_t *filter_out, __u32 ifindex) { print_start_section(SECTION_MISC, "Scanning miscellaneous eBPF features...", "/*** eBPF misc features ***/", define_prefix); - probe_large_insn_limit(define_prefix, ifindex); + probe_large_insn_limit(define_prefix, filter_in, filter_out, ifindex); print_end_section(); } @@ -721,6 +836,8 @@ static int do_probe(int argc, char **argv) * should exit. */ bool print_syscall_config = false; + const char *filter_out_raw = NULL; + const char *filter_in_raw = NULL; const char *define_prefix = NULL; bool check_system_config = false; /* Program types probes are needed if helper probes are going to be @@ -734,9 +851,14 @@ static int do_probe(int argc, char **argv) bool check_map_types = false; bool check_helpers = false; bool check_section = false; + regex_t *filter_out = NULL; + regex_t *filter_in = NULL; bool check_misc = false; + char regerror_buf[100]; __u32 ifindex = 0; char *ifname; + int reg_ret; + int ret = 0; /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). * Let's approximate, and restrict usage to root user only. @@ -752,7 +874,8 @@ static int do_probe(int argc, char **argv) if (is_prefix(*argv, "kernel")) { if (target != COMPONENT_UNSPEC) { p_err("component to probe already specified"); - return -1; + ret = -1; + goto cleanup; } target = COMPONENT_KERNEL; NEXT_ARG(); @@ -761,10 +884,13 @@ static int do_probe(int argc, char **argv) if (target != COMPONENT_UNSPEC || ifindex) { p_err("component to probe already specified"); - return -1; + ret = -1; + goto cleanup; + } + if (!REQ_ARGS(1)) { + ret = -1; + goto cleanup; } - if (!REQ_ARGS(1)) - return -1; target = COMPONENT_DEVICE; ifname = GET_ARG(); @@ -772,7 +898,8 @@ static int do_probe(int argc, char **argv) if (!ifindex) { p_err("unrecognized netdevice '%s': %s", ifname, strerror(errno)); - return -1; + ret = -1; + goto cleanup; } } else if (is_prefix(*argv, "section")) { check_section = true; @@ -804,30 +931,82 @@ static int do_probe(int argc, char **argv) SECTION_MAP_TYPES, SECTION_HELPERS, SECTION_MISC); - return -1; + ret = -1; + goto cleanup; + } + NEXT_ARG(); + } else if (is_prefix(*argv, "filter_in")) { + if (filter_in_raw) { + p_err("filter_in can be used only once"); + ret = -1; + goto cleanup; } NEXT_ARG(); + if (!REQ_ARGS(1)) { + ret = -1; + goto cleanup; + } + filter_in_raw = GET_ARG(); + + filter_in = malloc(sizeof(regex_t)); + reg_ret = regcomp(filter_in, filter_in_raw, 0); + if (reg_ret) { + regerror(reg_ret, filter_in, regerror_buf, + ARRAY_SIZE(regerror_buf)); + p_err("could not compile regex: %s", + regerror_buf); + ret = -1; + goto cleanup; + } + } else if (is_prefix(*argv, "filter_out")) { + if (filter_out_raw) { + p_err("filter_out can be used only once"); + ret = -1; + goto cleanup; + } + NEXT_ARG(); + if (!REQ_ARGS(1)) { + ret = -1; + goto cleanup; + } + filter_out_raw = GET_ARG(); + + filter_out = malloc(sizeof(regex_t)); + reg_ret = regcomp(filter_out, filter_out_raw, 0); + if (reg_ret) { + regerror(reg_ret, filter_out, regerror_buf, + ARRAY_SIZE(regerror_buf)); + p_err("could not compile regex: %s", + regerror_buf); + ret = -1; + goto cleanup; + } } else if (is_prefix(*argv, "macros") && !define_prefix) { define_prefix = ""; NEXT_ARG(); } else if (is_prefix(*argv, "prefix")) { if (!define_prefix) { p_err("'prefix' argument can only be use after 'macros'"); - return -1; + ret = -1; + goto cleanup; } if (strcmp(define_prefix, "")) { p_err("'prefix' already defined"); - return -1; + ret = -1; + goto cleanup; } NEXT_ARG(); - if (!REQ_ARGS(1)) - return -1; + if (!REQ_ARGS(1)) { + ret = -1; + goto cleanup; + } define_prefix = GET_ARG(); } else { p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?", *argv); - return -1; + ret = -1; + goto cleanup; } } @@ -848,26 +1027,35 @@ static int do_probe(int argc, char **argv) } if (check_system_config) - section_system_config(target, define_prefix); - if (!section_syscall_config(print_syscall_config, define_prefix)) + section_system_config(target, define_prefix, filter_in, + filter_out); + if (!section_syscall_config(print_syscall_config, define_prefix, + filter_in, filter_out)) /* bpf() syscall unavailable, don't probe other BPF features */ goto exit_close_json; if (check_program_types) section_program_types(print_program_types, supported_types, - define_prefix, ifindex); + define_prefix, filter_in, filter_out, + ifindex); if (check_map_types) - section_map_types(define_prefix, ifindex); + section_map_types(define_prefix, filter_in, filter_out, + ifindex); if (check_helpers) - section_helpers(supported_types, define_prefix, ifindex); + section_helpers(supported_types, define_prefix, filter_in, + filter_out, ifindex); if (check_misc) - section_misc(define_prefix, ifindex); + section_misc(define_prefix, filter_in, filter_out, ifindex); exit_close_json: if (json_output) /* End root object */ jsonw_end_object(json_wtr); - return 0; +cleanup: + free(filter_in); + free(filter_out); + + return ret; } static int do_help(int argc, char **argv) @@ -878,7 +1066,7 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s %s probe [COMPONENT] [section SECTION] [macros [prefix PREFIX]]\n" + "Usage: %s %s probe [COMPONENT] [section SECTION] [filter_in PATTERN] [filter_out PATTERN] [macros [prefix PREFIX]]\n" " %s %s help\n" "\n" " COMPONENT := { kernel | dev NAME }\n" From patchwork Tue Feb 18 19:02:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Rostecki X-Patchwork-Id: 11389297 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 83C9C13A4 for ; Tue, 18 Feb 2020 19:03:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6E48924655 for ; Tue, 18 Feb 2020 19:03:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726656AbgBRTCl (ORCPT ); Tue, 18 Feb 2020 14:02:41 -0500 Received: from mx2.suse.de ([195.135.220.15]:35632 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726613AbgBRTCl (ORCPT ); Tue, 18 Feb 2020 14:02:41 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 7DD7BAD4F; Tue, 18 Feb 2020 19:02:38 +0000 (UTC) From: Michal Rostecki To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , Quentin Monnet , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Shuah Khan , linux-kselftest@vger.kernel.org Subject: [PATCH bpf-next 4/6] bpftool: Update documentation of "bpftool feature" command Date: Tue, 18 Feb 2020 20:02:21 +0100 Message-Id: <20200218190224.22508-5-mrostecki@opensuse.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218190224.22508-1-mrostecki@opensuse.org> References: <20200218190224.22508-1-mrostecki@opensuse.org> MIME-Version: 1.0 Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Update documentation of "bpftool feature" command with information about new arguments: "section", "filter_in" and "filter_out". Signed-off-by: Michal Rostecki --- .../bpftool/Documentation/bpftool-feature.rst | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 4d08f35034a2..39b4c47e3c75 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -19,19 +19,45 @@ SYNOPSIS FEATURE COMMANDS ================ -| **bpftool** **feature probe** [*COMPONENT*] [**macros** [**prefix** *PREFIX*]] +| **bpftool** **feature probe** [*COMPONENT*] [**section** *SECTION*] [**filter_in** *PATTERN*] [**filter_out** *PATTERN*] [**macros** [**prefix** *PREFIX*]] | **bpftool** **feature help** | | *COMPONENT* := { **kernel** | **dev** *NAME* } +| *SECTION* := { **system_config** | **syscall_config** | **program_types** | **map_types** | **helpers** | **misc** } DESCRIPTION =========== - **bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]] + **bpftool feature probe** [**kernel**] [**section** *SECTION*] [**filter_in** *PATTERN*] [**filter_out** *PATTERN*] [**macros** [**prefix** *PREFIX*]] Probe the running kernel and dump a number of eBPF-related parameters, such as availability of the **bpf()** system call, JIT status, eBPF program types availability, eBPF helper functions availability, and more. + If the **section** keyword is passed, only the specified + probes section will be checked and printed. The only probe + which is always going to be performed is **syscall_config**, + but if the other section was provided as an argument, + **syscall_config** check will perform silently without + printing the result and bpftool will exit if the **bpf()** + syscall is not available (because in that case performing + other checks relying on the **bpf()** system call does not + make sense). + + If the **filter_in** keyword is passed, only checks with + names matching the given *PATTERN* are going the be printed + and performed. + + If the **filter_out** keyword is passed, checks with names + matching the given *PATTERN* are not going to be printed and + performed. + + Please refer to the **regex**\ (7) man page for details on + the syntax for *PATTERN*. + + **filter_in** is executed before **filter_out** which means + that **filter_out** is always applied only on probes + selected by **filter_in** if both arguments are used together. + 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 @@ -48,12 +74,13 @@ DESCRIPTION **bpf_trace_printk**\ () or **bpf_probe_write_user**\ ()) may print warnings to kernel logs. - **bpftool feature probe dev** *NAME* [**macros** [**prefix** *PREFIX*]] + **bpftool feature probe dev** *NAME* [**section** *SECTION*] [**filter_in** *PATTERN*] [**filter_out** *PATTERN*] [**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. + The keywords **section**, **filter_in**, **filter_out**, + **macros** and **prefix** have the same role as when probing + the kernel. **bpftool feature help** Print short help message. From patchwork Tue Feb 18 19:02:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Rostecki X-Patchwork-Id: 11389295 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A9FC813A4 for ; Tue, 18 Feb 2020 19:02:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 94E4924689 for ; Tue, 18 Feb 2020 19:02:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726736AbgBRTCo (ORCPT ); Tue, 18 Feb 2020 14:02:44 -0500 Received: from mx2.suse.de ([195.135.220.15]:35758 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726691AbgBRTCo (ORCPT ); Tue, 18 Feb 2020 14:02:44 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 43D69ADE4; Tue, 18 Feb 2020 19:02:40 +0000 (UTC) From: Michal Rostecki To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , Quentin Monnet , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Shuah Khan , linux-kselftest@vger.kernel.org Subject: [PATCH bpf-next 5/6] bpftool: Update bash completion for "bpftool feature" command Date: Tue, 18 Feb 2020 20:02:22 +0100 Message-Id: <20200218190224.22508-6-mrostecki@opensuse.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218190224.22508-1-mrostecki@opensuse.org> References: <20200218190224.22508-1-mrostecki@opensuse.org> MIME-Version: 1.0 Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Update bash completion for "bpftool feature" command with information about new arguments: "section", "filter_id" and "filter_out". Signed-off-by: Michal Rostecki --- tools/bpf/bpftool/bash-completion/bpftool | 32 +++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 754d8395e451..ff8ac9bebdda 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -981,14 +981,30 @@ _bpftool() feature) case $command in probe) - [[ $prev == "prefix" ]] && return 0 - if _bpftool_search_list 'macros'; then - COMPREPLY+=( $( compgen -W 'prefix' -- "$cur" ) ) - else - COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) ) - fi - _bpftool_one_of_list 'kernel dev' - return 0 + case $prev in + $command) + COMPREPLY+=( $( compgen -W 'kernel dev section filter_in filter_out macros' -- \ + "$cur" ) ) + return 0 + ;; + section) + COMPREPLY+=( $( compgen -W 'system_config syscall_config program_types map_types helpers misc' -- \ + "$cur" ) ) + return 0 + ;; + filter_in|filter_out|prefix) + return 0 + ;; + macros) + COMPREPLY+=( $( compgen -W 'prefix' -- "$cur" ) ) + return 0 + ;; + *) + _bpftool_one_of_list 'kernel dev' + _bpftool_once_attr 'section filter_in filter_out macros' + return 0 + ;; + esac ;; *) [[ $prev == $object ]] && \ From patchwork Tue Feb 18 19:02:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Rostecki X-Patchwork-Id: 11389293 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9AFF313A4 for ; Tue, 18 Feb 2020 19:02:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 736BD24672 for ; Tue, 18 Feb 2020 19:02:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726438AbgBRTCr (ORCPT ); Tue, 18 Feb 2020 14:02:47 -0500 Received: from mx2.suse.de ([195.135.220.15]:35786 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726700AbgBRTCp (ORCPT ); Tue, 18 Feb 2020 14:02:45 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 59328ADAB; Tue, 18 Feb 2020 19:02:42 +0000 (UTC) From: Michal Rostecki To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , Quentin Monnet , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Shuah Khan , "David S. Miller" , Jesper Dangaard Brouer , John Fastabend , linux-kselftest@vger.kernel.org Subject: [PATCH bpf-next 6/6] selftests/bpf: Add test for "bpftool feature" command Date: Tue, 18 Feb 2020 20:02:23 +0100 Message-Id: <20200218190224.22508-7-mrostecki@opensuse.org> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218190224.22508-1-mrostecki@opensuse.org> References: <20200218190224.22508-1-mrostecki@opensuse.org> MIME-Version: 1.0 Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add Python module with tests for "bpftool feature" command which check whether: - probing kernel and network devices works - "section" option selects sections properly - "filter_in" and "filter_out" options filter results properly - "macro" option generates C macros properly Signed-off-by: Michal Rostecki --- tools/testing/selftests/.gitignore | 5 +- tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/test_bpftool.py | 294 ++++++++++++++++++++ tools/testing/selftests/bpf/test_bpftool.sh | 5 + 4 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/bpf/test_bpftool.py create mode 100755 tools/testing/selftests/bpf/test_bpftool.sh diff --git a/tools/testing/selftests/.gitignore b/tools/testing/selftests/.gitignore index 61df01cdf0b2..304fdf1a21dc 100644 --- a/tools/testing/selftests/.gitignore +++ b/tools/testing/selftests/.gitignore @@ -3,4 +3,7 @@ gpiogpio-hammer gpioinclude/ gpiolsgpio tpm2/SpaceTest.log -tpm2/*.pyc + +# Python bytecode and cache +__pycache__/ +*.py[cod] diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 257a1aaaa37d..e7d822259c50 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -62,7 +62,8 @@ TEST_PROGS := test_kmod.sh \ test_tc_tunnel.sh \ test_tc_edt.sh \ test_xdping.sh \ - test_bpftool_build.sh + test_bpftool_build.sh \ + test_bpftool.sh TEST_PROGS_EXTENDED := with_addr.sh \ with_tunnels.sh \ diff --git a/tools/testing/selftests/bpf/test_bpftool.py b/tools/testing/selftests/bpf/test_bpftool.py new file mode 100644 index 000000000000..e298dca5fdcf --- /dev/null +++ b/tools/testing/selftests/bpf/test_bpftool.py @@ -0,0 +1,294 @@ +# Copyright (c) 2020 SUSE LLC. +# +# This software is licensed under the GNU General License Version 2, +# June 1991 as shown in the file COPYING in the top-level directory of this +# source tree. +# +# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" +# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +import collections +import functools +import json +import os +import socket +import subprocess +import unittest + + +# Add the source tree of bpftool and /usr/local/sbin to PATH +cur_dir = os.path.dirname(os.path.realpath(__file__)) +bpftool_dir = os.path.abspath(os.path.join(cur_dir, "..", "..", "..", "..", + "tools", "bpf", "bpftool")) +os.environ["PATH"] = bpftool_dir + ":/usr/local/sbin:" + os.environ["PATH"] + +# Probe sections +SECTION_SYSTEM_CONFIG_NAME = "system_config" +SECTION_SYSCALL_CONFIG_NAME = "syscall_config" +SECTION_PROGRAM_TYPES_NAME = "program_types" +SECTION_MAP_TYPES_NAME = "map_types" +SECTION_HELPERS_NAME = "helpers" +SECTION_MISC_NAME = "misc" +SECTION_SYSTEM_CONFIG_PATTERN = b"Scanning system configuration..." +SECTION_SYSCALL_CONFIG_PATTERN = b"Scanning system call availability..." +SECTION_PROGRAM_TYPES_PATTERN = b"Scanning eBPF program types..." +SECTION_MAP_TYPES_PATTERN = b"Scanning eBPF map types..." +SECTION_HELPERS_PATTERN = b"Scanning eBPF helper functions..." +SECTION_MISC_PATTERN = b"Scanning miscellaneous eBPF features..." + + +class IfaceNotFoundError(Exception): + pass + + +class UnprivilegedUserError(Exception): + pass + + +def _bpftool(args, json=True): + _args = ["bpftool"] + if json: + _args.append("-j") + _args.extend(args) + + res = subprocess.run(_args, capture_output=True) + return res.stdout + + +def bpftool(args): + return _bpftool(args, json=False) + + +def bpftool_json(args): + res = _bpftool(args) + return json.loads(res) + + +def get_default_iface(): + for iface in socket.if_nameindex(): + if iface[1] != "lo": + return iface[1] + raise IfaceNotFoundError("Could not find any network interface to probe") + + +def default_iface(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + iface = get_default_iface() + return f(*args, iface, **kwargs) + return wrapper + + +class TestBpftool(unittest.TestCase): + @classmethod + def setUpClass(cls): + if os.getuid() != 0: + raise UnprivilegedUserError("This test suite eeeds root privileges") + + @default_iface + def test_feature_dev(self, iface): + expected_lines = [ + SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_HELPERS_PATTERN, + SECTION_MISC_PATTERN, + ] + + res = bpftool(["feature", "probe", "dev", iface]) + for expected_line in expected_lines: + self.assertIn(expected_line, res) + + @default_iface + def test_feature_dev_json(self, iface): + expected_keys = [ + "syscall_config", + "program_types", + "map_types", + "helpers", + "misc", + ] + + res = bpftool_json(["feature", "probe", "dev", iface]) + self.assertCountEqual(res.keys(), expected_keys) + + def test_feature_kernel(self): + expected_lines = [ + SECTION_SYSTEM_CONFIG_PATTERN, + SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_HELPERS_PATTERN, + SECTION_MISC_PATTERN, + ] + + res_default1 = bpftool(["feature"]) + res_default2 = bpftool(["feature", "probe"]) + res = bpftool(["feature", "probe", "kernel"]) + + for expected_line in expected_lines: + self.assertIn(expected_line, res_default1) + self.assertIn(expected_line, res_default2) + self.assertIn(expected_line, res) + + def test_feature_kernel_json(self): + expected_keys = [ + "system_config", + "syscall_config", + "program_types", + "map_types", + "helpers", + "misc", + ] + + res_default1 = bpftool_json(["feature"]) + self.assertCountEqual(res_default1.keys(), expected_keys) + + res_default2 = bpftool_json(["feature", "probe"]) + self.assertCountEqual(res_default2.keys(), expected_keys) + + res = bpftool_json(["feature", "probe", "kernel"]) + self.assertCountEqual(res.keys(), expected_keys) + + def test_feature_section(self): + SectionTestCase = collections.namedtuple( + "SectionTestCase", + ["section_name", "expected_pattern", "unexpected_patterns"]) + test_cases = [ + SectionTestCase( + section_name=SECTION_SYSTEM_CONFIG_NAME, + expected_pattern=SECTION_SYSTEM_CONFIG_PATTERN, + unexpected_patterns=[SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_HELPERS_PATTERN, + SECTION_MISC_PATTERN]), + SectionTestCase( + section_name=SECTION_SYSCALL_CONFIG_NAME, + expected_pattern=SECTION_SYSCALL_CONFIG_PATTERN, + unexpected_patterns=[SECTION_SYSTEM_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_HELPERS_PATTERN, + SECTION_MISC_PATTERN]), + SectionTestCase( + section_name=SECTION_PROGRAM_TYPES_NAME, + expected_pattern=SECTION_PROGRAM_TYPES_PATTERN, + unexpected_patterns=[SECTION_SYSTEM_CONFIG_PATTERN, + SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_HELPERS_PATTERN, + SECTION_MISC_PATTERN]), + SectionTestCase( + section_name=SECTION_MAP_TYPES_NAME, + expected_pattern=SECTION_MAP_TYPES_PATTERN, + unexpected_patterns=[SECTION_SYSTEM_CONFIG_PATTERN, + SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_HELPERS_PATTERN, + SECTION_MISC_PATTERN]), + SectionTestCase( + section_name=SECTION_HELPERS_NAME, + expected_pattern=SECTION_HELPERS_PATTERN, + unexpected_patterns=[SECTION_SYSTEM_CONFIG_PATTERN, + SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_MISC_PATTERN]), + SectionTestCase( + section_name=SECTION_MISC_NAME, + expected_pattern=SECTION_MISC_PATTERN, + unexpected_patterns=[SECTION_SYSTEM_CONFIG_PATTERN, + SECTION_SYSCALL_CONFIG_PATTERN, + SECTION_PROGRAM_TYPES_PATTERN, + SECTION_MAP_TYPES_PATTERN, + SECTION_HELPERS_PATTERN]), + ] + + for tc in test_cases: + res = bpftool(["feature", "probe", "kernel", + "section", tc.section_name]) + self.assertIn(tc.expected_pattern, res) + for pattern in tc.unexpected_patterns: + self.assertNotIn(pattern, res) + + def test_feature_section_json(self): + res_syscall_config = bpftool_json(["feature", "probe", "kernel", + "section", "syscall_config"]) + self.assertCountEqual(res_syscall_config.keys(), ["syscall_config"]) + + res_system_config = bpftool_json(["feature", "probe", "kernel", + "section", "system_config"]) + self.assertCountEqual(res_system_config.keys(), ["system_config"]) + + res_program_types = bpftool_json(["feature", "probe", "kernel", + "section", "program_types"]) + self.assertCountEqual(res_program_types.keys(), ["program_types"]) + + res_map_types = bpftool_json(["feature", "probe", "kernel", + "section", "map_types"]) + self.assertCountEqual(res_map_types.keys(), ["map_types"]) + + res_helpers = bpftool_json(["feature", "probe", "kernel", + "section", "helpers"]) + self.assertCountEqual(res_helpers.keys(), ["helpers"]) + + res_misc = bpftool_json(["feature", "probe", "kernel", + "section", "misc"]) + self.assertCountEqual(res_misc.keys(), ["misc"]) + + def _assert_pattern_in_dict(self, dct, pattern, check_keys=False): + """Check if all string values inside dictionary contain the given + pattern. + """ + for key, value in dct.items(): + if check_keys: + self.assertIn(pattern, key) + if isinstance(value, dict): + self._assert_pattern_in_dict(value, pattern, check_keys=True) + elif isinstance(value, str): + self.assertIn(pattern, value) + + def _assert_pattern_not_in_dict(self, dct, pattern, check_keys=False): + """Check if all string values inside dictionary do not containe the + given pattern. + """ + for key, value in dct.items(): + if check_keys: + self.assertNotIn(pattern, key) + if isinstance(value, dict): + self._assert_pattern_not_in_dict(value, pattern, + check_keys=True) + elif isinstance(value, str): + self.assertNotIn(pattern, value) + + def test_feature_filter_in_json(self): + res = bpftool_json(["feature", "probe", "kernel", + "filter_in", "trace"]) + self._assert_pattern_in_dict(res, "trace") + + def test_feature_filter_out_json(self): + res = bpftool_json(["feature", "probe", "kernel", + "filter_out", "trace"]) + self._assert_pattern_not_in_dict(res, "trace") + + def test_feature_macros(self): + expected_patterns = [ + b"/\*\*\* System call availability \*\*\*/", + b"#define HAVE_BPF_SYSCALL", + b"/\*\*\* eBPF program types \*\*\*/", + b"#define HAVE.*PROG_TYPE", + b"/\*\*\* eBPF map types \*\*\*/", + b"#define HAVE.*MAP_TYPE", + b"/\*\*\* eBPF helper functions \*\*\*/", + b"#define HAVE.*HELPER", + b"/\*\*\* eBPF misc features \*\*\*/", + ] + + res = bpftool(["feature", "probe", "macros"]) + for pattern in expected_patterns: + self.assertRegex(res, pattern) diff --git a/tools/testing/selftests/bpf/test_bpftool.sh b/tools/testing/selftests/bpf/test_bpftool.sh new file mode 100755 index 000000000000..66690778e36d --- /dev/null +++ b/tools/testing/selftests/bpf/test_bpftool.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2020 SUSE LLC. + +python3 -m unittest -v test_bpftool.TestBpftool