bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes
@ 2020-02-18 19:02 Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 1/6] bpftool: Move out sections to separate functions Michal Rostecki
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, linux-kselftest

This patch series extend the "bpftool feature" subcommand with the
new positional arguments:

- "section", which allows to select a specific section of probes (i.e.
  "system_config", "program_types", "map_types");
- "filter_in", which allows to select only probes which matches the
  given regex pattern;
- "filter_out", which allows to filter out probes which do not match the
  given regex pattern.

The main motivation behind those changes is ability the fact that some
probes (for example those related to "trace" or "write_user" helpers)
emit dmesg messages which might be confusing for people who are running
on production environments. For details see the Cilium issue[0].

[0] https://github.com/cilium/cilium/issues/10048

Michal Rostecki (6):
  bpftool: Move out sections to separate functions
  bpftool: Allow to select a specific section to probe
  bpftool: Add arguments for filtering in and filtering out probes
  bpftool: Update documentation of "bpftool feature" command
  bpftool: Update bash completion for "bpftool feature" command
  selftests/bpf: Add test for "bpftool feature" command

 .../bpftool/Documentation/bpftool-feature.rst |  37 +-
 tools/bpf/bpftool/bash-completion/bpftool     |  32 +-
 tools/bpf/bpftool/feature.c                   | 592 +++++++++++++-----
 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 +
 7 files changed, 811 insertions(+), 157 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/test_bpftool.py
 create mode 100755 tools/testing/selftests/bpf/test_bpftool.sh

-- 
2.25.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 1/6] bpftool: Move out sections to separate functions
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
@ 2020-02-18 19:02 ` Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 2/6] bpftool: Allow to select a specific section to probe Michal Rostecki
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, David S. Miller,
	Jesper Dangaard Brouer, John Fastabend, linux-kselftest

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 <mrostecki@opensuse.org>
---
 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 <helper_name> is available for <prog_type_name>,\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 <helper_name> is available for <prog_type_name>,\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;
 }
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 2/6] bpftool: Allow to select a specific section to probe
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 1/6] bpftool: Move out sections to separate functions Michal Rostecki
@ 2020-02-18 19:02 ` Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 3/6] bpftool: Add arguments for filtering in and filtering out probes Michal Rostecki
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, linux-kselftest

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 <mrostecki@opensuse.org>
---
 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;
 }
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 3/6] bpftool: Add arguments for filtering in and filtering out probes
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 1/6] bpftool: Move out sections to separate functions Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 2/6] bpftool: Allow to select a specific section to probe Michal Rostecki
@ 2020-02-18 19:02 ` Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 4/6] bpftool: Update documentation of "bpftool feature" command Michal Rostecki
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, linux-kselftest

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 <mrostecki@opensuse.org>
---
 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 <ctype.h>
 #include <errno.h>
+#include <regex.h>
 #include <string.h>
 #include <unistd.h>
 #include <net/if.h>
@@ -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"
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 4/6] bpftool: Update documentation of "bpftool feature" command
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
                   ` (2 preceding siblings ...)
  2020-02-18 19:02 ` [PATCH bpf-next 3/6] bpftool: Add arguments for filtering in and filtering out probes Michal Rostecki
@ 2020-02-18 19:02 ` Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 5/6] bpftool: Update bash completion for " Michal Rostecki
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, linux-kselftest

Update documentation of "bpftool feature" command with information about
new arguments: "section", "filter_in" and "filter_out".

Signed-off-by: Michal Rostecki <mrostecki@opensuse.org>
---
 .../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.
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 5/6] bpftool: Update bash completion for "bpftool feature" command
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
                   ` (3 preceding siblings ...)
  2020-02-18 19:02 ` [PATCH bpf-next 4/6] bpftool: Update documentation of "bpftool feature" command Michal Rostecki
@ 2020-02-18 19:02 ` Michal Rostecki
  2020-02-18 19:02 ` [PATCH bpf-next 6/6] selftests/bpf: Add test " Michal Rostecki
  2020-02-19  3:02 ` [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Alexei Starovoitov
  6 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, linux-kselftest

Update bash completion for "bpftool feature" command with information
about new arguments: "section", "filter_id" and "filter_out".

Signed-off-by: Michal Rostecki <mrostecki@opensuse.org>
---
 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 ]] && \
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 6/6] selftests/bpf: Add test for "bpftool feature" command
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
                   ` (4 preceding siblings ...)
  2020-02-18 19:02 ` [PATCH bpf-next 5/6] bpftool: Update bash completion for " Michal Rostecki
@ 2020-02-18 19:02 ` Michal Rostecki
  2020-02-19  3:02 ` [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Alexei Starovoitov
  6 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-18 19:02 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Quentin Monnet, Jakub Kicinski,
	netdev, linux-kernel, Shuah Khan, David S. Miller,
	Jesper Dangaard Brouer, John Fastabend, linux-kselftest

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 <mrostecki@opensuse.org>
---
 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
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes
  2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
                   ` (5 preceding siblings ...)
  2020-02-18 19:02 ` [PATCH bpf-next 6/6] selftests/bpf: Add test " Michal Rostecki
@ 2020-02-19  3:02 ` Alexei Starovoitov
  2020-02-19 12:33   ` Michal Rostecki
  6 siblings, 1 reply; 12+ messages in thread
From: Alexei Starovoitov @ 2020-02-19  3:02 UTC (permalink / raw)
  To: Michal Rostecki
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau,
	Song Liu, Yonghong Song, Andrii Nakryiko, Quentin Monnet,
	Jakub Kicinski, Network Development, LKML, Shuah Khan,
	open list:KERNEL SELFTEST FRAMEWORK

On Tue, Feb 18, 2020 at 11:02 AM Michal Rostecki <mrostecki@opensuse.org> wrote:
>
> This patch series extend the "bpftool feature" subcommand with the
> new positional arguments:
>
> - "section", which allows to select a specific section of probes (i.e.
>   "system_config", "program_types", "map_types");
> - "filter_in", which allows to select only probes which matches the
>   given regex pattern;
> - "filter_out", which allows to filter out probes which do not match the
>   given regex pattern.
>
> The main motivation behind those changes is ability the fact that some
> probes (for example those related to "trace" or "write_user" helpers)
> emit dmesg messages which might be confusing for people who are running
> on production environments. For details see the Cilium issue[0].
>
> [0] https://github.com/cilium/cilium/issues/10048

The motivation is clear, but I think the users shouldn't be made
aware of such implementation details. I think instead of filter_in/out
it's better to do 'full or safe' mode of probing.
By default it can do all the probing that doesn't cause
extra dmesgs and in 'full' mode it can probe everything.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes
  2020-02-19  3:02 ` [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Alexei Starovoitov
@ 2020-02-19 12:33   ` Michal Rostecki
  2020-02-19 16:37     ` Alexei Starovoitov
  0 siblings, 1 reply; 12+ messages in thread
From: Michal Rostecki @ 2020-02-19 12:33 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau,
	Song Liu, Yonghong Song, Andrii Nakryiko, Quentin Monnet,
	Jakub Kicinski, Network Development, LKML, Shuah Khan,
	open list:KERNEL SELFTEST FRAMEWORK

On 2/19/20 4:02 AM, Alexei Starovoitov wrote:
> The motivation is clear, but I think the users shouldn't be made
> aware of such implementation details. I think instead of filter_in/out
> it's better to do 'full or safe' mode of probing.
> By default it can do all the probing that doesn't cause
> extra dmesgs and in 'full' mode it can probe everything.

Alright, then I will send later v2 where the "internal" implementation
(filtering out based on regex) stays similar (filter_out will stay in
the code without being exposed to users, filter_in will be removed). And
the exposed option of "safe" probing will just apply the
"(trace|write_user)" filter_out pattern. Does it sound good?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes
  2020-02-19 12:33   ` Michal Rostecki
@ 2020-02-19 16:37     ` Alexei Starovoitov
  2020-02-19 16:52       ` Daniel Borkmann
  0 siblings, 1 reply; 12+ messages in thread
From: Alexei Starovoitov @ 2020-02-19 16:37 UTC (permalink / raw)
  To: Michal Rostecki
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau,
	Song Liu, Yonghong Song, Andrii Nakryiko, Jakub Kicinski,
	Network Development, LKML, Shuah Khan,
	open list:KERNEL SELFTEST FRAMEWORK

On Wed, Feb 19, 2020 at 4:33 AM Michal Rostecki <mrostecki@opensuse.org> wrote:
>
> On 2/19/20 4:02 AM, Alexei Starovoitov wrote:
> > The motivation is clear, but I think the users shouldn't be made
> > aware of such implementation details. I think instead of filter_in/out
> > it's better to do 'full or safe' mode of probing.
> > By default it can do all the probing that doesn't cause
> > extra dmesgs and in 'full' mode it can probe everything.
>
> Alright, then I will send later v2 where the "internal" implementation
> (filtering out based on regex) stays similar (filter_out will stay in
> the code without being exposed to users, filter_in will be removed). And
> the exposed option of "safe" probing will just apply the
> "(trace|write_user)" filter_out pattern. Does it sound good?

yes. If implementation is doing filter_in and applying 'trace_printk|write_user'
strings hidden within bpftool than I think it should be good.
What do you think the default should be?
It feels to me that the default should not be causing dmesg prints.
So only addition flag for bpftool command line will be 'bpftool
feature probe full'

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes
  2020-02-19 16:37     ` Alexei Starovoitov
@ 2020-02-19 16:52       ` Daniel Borkmann
  2020-02-19 20:57         ` Michal Rostecki
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Borkmann @ 2020-02-19 16:52 UTC (permalink / raw)
  To: Alexei Starovoitov, Michal Rostecki
  Cc: bpf, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Jakub Kicinski,
	Network Development, LKML, Shuah Khan,
	open list:KERNEL SELFTEST FRAMEWORK

On 2/19/20 5:37 PM, Alexei Starovoitov wrote:
> On Wed, Feb 19, 2020 at 4:33 AM Michal Rostecki <mrostecki@opensuse.org> wrote:
>>
>> On 2/19/20 4:02 AM, Alexei Starovoitov wrote:
>>> The motivation is clear, but I think the users shouldn't be made
>>> aware of such implementation details. I think instead of filter_in/out
>>> it's better to do 'full or safe' mode of probing.
>>> By default it can do all the probing that doesn't cause
>>> extra dmesgs and in 'full' mode it can probe everything.
>>
>> Alright, then I will send later v2 where the "internal" implementation
>> (filtering out based on regex) stays similar (filter_out will stay in
>> the code without being exposed to users, filter_in will be removed). And
>> the exposed option of "safe" probing will just apply the
>> "(trace|write_user)" filter_out pattern. Does it sound good?
> 
> yes. If implementation is doing filter_in and applying 'trace_printk|write_user'
> strings hidden within bpftool than I think it should be good.
> What do you think the default should be?
> It feels to me that the default should not be causing dmesg prints.
> So only addition flag for bpftool command line will be 'bpftool
> feature probe full'

Agree, that makes sense to me.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes
  2020-02-19 16:52       ` Daniel Borkmann
@ 2020-02-19 20:57         ` Michal Rostecki
  0 siblings, 0 replies; 12+ messages in thread
From: Michal Rostecki @ 2020-02-19 20:57 UTC (permalink / raw)
  To: Daniel Borkmann, Alexei Starovoitov
  Cc: bpf, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Andrii Nakryiko, Jakub Kicinski,
	Network Development, LKML, Shuah Khan,
	open list:KERNEL SELFTEST FRAMEWORK

On 2/19/20 5:52 PM, Daniel Borkmann wrote:
> On 2/19/20 5:37 PM, Alexei Starovoitov wrote:
>> On Wed, Feb 19, 2020 at 4:33 AM Michal Rostecki
>> <mrostecki@opensuse.org> wrote:
>>>
>>> On 2/19/20 4:02 AM, Alexei Starovoitov wrote:
>>>> The motivation is clear, but I think the users shouldn't be made
>>>> aware of such implementation details. I think instead of filter_in/out
>>>> it's better to do 'full or safe' mode of probing.
>>>> By default it can do all the probing that doesn't cause
>>>> extra dmesgs and in 'full' mode it can probe everything.
>>>
>>> Alright, then I will send later v2 where the "internal" implementation
>>> (filtering out based on regex) stays similar (filter_out will stay in
>>> the code without being exposed to users, filter_in will be removed). And
>>> the exposed option of "safe" probing will just apply the
>>> "(trace|write_user)" filter_out pattern. Does it sound good?
>>
>> yes. If implementation is doing filter_in and applying
>> 'trace_printk|write_user'
>> strings hidden within bpftool than I think it should be good.
>> What do you think the default should be?
>> It feels to me that the default should not be causing dmesg prints.
>> So only addition flag for bpftool command line will be 'bpftool
>> feature probe full'
> 
> Agree, that makes sense to me.

Makes sense to me as well. I will do that in v2.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-02-19 20:57 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-18 19:02 [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Michal Rostecki
2020-02-18 19:02 ` [PATCH bpf-next 1/6] bpftool: Move out sections to separate functions Michal Rostecki
2020-02-18 19:02 ` [PATCH bpf-next 2/6] bpftool: Allow to select a specific section to probe Michal Rostecki
2020-02-18 19:02 ` [PATCH bpf-next 3/6] bpftool: Add arguments for filtering in and filtering out probes Michal Rostecki
2020-02-18 19:02 ` [PATCH bpf-next 4/6] bpftool: Update documentation of "bpftool feature" command Michal Rostecki
2020-02-18 19:02 ` [PATCH bpf-next 5/6] bpftool: Update bash completion for " Michal Rostecki
2020-02-18 19:02 ` [PATCH bpf-next 6/6] selftests/bpf: Add test " Michal Rostecki
2020-02-19  3:02 ` [PATCH bpf-next 0/6] bpftool: Allow to select sections and filter probes Alexei Starovoitov
2020-02-19 12:33   ` Michal Rostecki
2020-02-19 16:37     ` Alexei Starovoitov
2020-02-19 16:52       ` Daniel Borkmann
2020-02-19 20:57         ` Michal Rostecki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).