xref: /openbmc/linux/tools/bpf/bpftool/feature.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
149eb7ab3SQuentin Monnet // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
249eb7ab3SQuentin Monnet /* Copyright (c) 2019 Netronome Systems, Inc. */
349eb7ab3SQuentin Monnet 
41bf4b058SQuentin Monnet #include <ctype.h>
549eb7ab3SQuentin Monnet #include <errno.h>
6f655c088SNiklas Söderlund #include <fcntl.h>
749eb7ab3SQuentin Monnet #include <string.h>
849eb7ab3SQuentin Monnet #include <unistd.h>
9f9499fedSQuentin Monnet #include <net/if.h>
100b3b9ca3SQuentin Monnet #ifdef USE_LIBCAP
11cf9bf714SQuentin Monnet #include <sys/capability.h>
120b3b9ca3SQuentin Monnet #endif
1349eb7ab3SQuentin Monnet #include <sys/utsname.h>
147a4522bbSQuentin Monnet #include <sys/vfs.h>
1549eb7ab3SQuentin Monnet 
1649eb7ab3SQuentin Monnet #include <linux/filter.h>
1749eb7ab3SQuentin Monnet #include <linux/limits.h>
1849eb7ab3SQuentin Monnet 
19229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
20229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h>
21a664a834SPeter Wu #include <zlib.h>
2249eb7ab3SQuentin Monnet 
2349eb7ab3SQuentin Monnet #include "main.h"
2449eb7ab3SQuentin Monnet 
257a4522bbSQuentin Monnet #ifndef PROC_SUPER_MAGIC
267a4522bbSQuentin Monnet # define PROC_SUPER_MAGIC	0x9fa0
277a4522bbSQuentin Monnet #endif
287a4522bbSQuentin Monnet 
2949eb7ab3SQuentin Monnet enum probe_component {
3049eb7ab3SQuentin Monnet 	COMPONENT_UNSPEC,
3149eb7ab3SQuentin Monnet 	COMPONENT_KERNEL,
32f9499fedSQuentin Monnet 	COMPONENT_DEVICE,
3349eb7ab3SQuentin Monnet };
3449eb7ab3SQuentin Monnet 
352d3ea5e8SQuentin Monnet #define BPF_HELPER_MAKE_ENTRY(name)	[BPF_FUNC_ ## name] = "bpf_" # name
362d3ea5e8SQuentin Monnet static const char * const helper_name[] = {
372d3ea5e8SQuentin Monnet 	__BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY)
382d3ea5e8SQuentin Monnet };
392d3ea5e8SQuentin Monnet 
402d3ea5e8SQuentin Monnet #undef BPF_HELPER_MAKE_ENTRY
412d3ea5e8SQuentin Monnet 
42e3450b79SQuentin Monnet static bool full_mode;
430b3b9ca3SQuentin Monnet #ifdef USE_LIBCAP
44cf9bf714SQuentin Monnet static bool run_as_unprivileged;
450b3b9ca3SQuentin Monnet #endif
46e3450b79SQuentin Monnet 
477a4522bbSQuentin Monnet /* Miscellaneous utility functions */
487a4522bbSQuentin Monnet 
grep(const char * buffer,const char * pattern)49f655c088SNiklas Söderlund static bool grep(const char *buffer, const char *pattern)
50f655c088SNiklas Söderlund {
51f655c088SNiklas Söderlund 	return !!strstr(buffer, pattern);
52f655c088SNiklas Söderlund }
53f655c088SNiklas Söderlund 
check_procfs(void)547a4522bbSQuentin Monnet static bool check_procfs(void)
557a4522bbSQuentin Monnet {
567a4522bbSQuentin Monnet 	struct statfs st_fs;
577a4522bbSQuentin Monnet 
587a4522bbSQuentin Monnet 	if (statfs("/proc", &st_fs) < 0)
597a4522bbSQuentin Monnet 		return false;
607a4522bbSQuentin Monnet 	if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC)
617a4522bbSQuentin Monnet 		return false;
627a4522bbSQuentin Monnet 
637a4522bbSQuentin Monnet 	return true;
647a4522bbSQuentin Monnet }
657a4522bbSQuentin Monnet 
uppercase(char * str,size_t len)66d267cff4SQuentin Monnet static void uppercase(char *str, size_t len)
67d267cff4SQuentin Monnet {
68d267cff4SQuentin Monnet 	size_t i;
69d267cff4SQuentin Monnet 
70d267cff4SQuentin Monnet 	for (i = 0; i < len && str[i] != '\0'; i++)
71d267cff4SQuentin Monnet 		str[i] = toupper(str[i]);
72d267cff4SQuentin Monnet }
73d267cff4SQuentin Monnet 
7449eb7ab3SQuentin Monnet /* Printing utility functions */
7549eb7ab3SQuentin Monnet 
7649eb7ab3SQuentin Monnet static void
print_bool_feature(const char * feat_name,const char * plain_name,const char * define_name,bool res,const char * define_prefix)77d267cff4SQuentin Monnet print_bool_feature(const char *feat_name, const char *plain_name,
78d267cff4SQuentin Monnet 		   const char *define_name, bool res, const char *define_prefix)
7949eb7ab3SQuentin Monnet {
8049eb7ab3SQuentin Monnet 	if (json_output)
8149eb7ab3SQuentin Monnet 		jsonw_bool_field(json_wtr, feat_name, res);
82d267cff4SQuentin Monnet 	else if (define_prefix)
83d267cff4SQuentin Monnet 		printf("#define %s%sHAVE_%s\n", define_prefix,
84d267cff4SQuentin Monnet 		       res ? "" : "NO_", define_name);
8549eb7ab3SQuentin Monnet 	else
8649eb7ab3SQuentin Monnet 		printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
8749eb7ab3SQuentin Monnet }
8849eb7ab3SQuentin Monnet 
print_kernel_option(const char * name,const char * value,const char * define_prefix)890ee52c0fSDaniel Borkmann static void print_kernel_option(const char *name, const char *value,
900ee52c0fSDaniel Borkmann 				const char *define_prefix)
914567b983SQuentin Monnet {
924567b983SQuentin Monnet 	char *endptr;
934567b983SQuentin Monnet 	int res;
944567b983SQuentin Monnet 
954567b983SQuentin Monnet 	if (json_output) {
964567b983SQuentin Monnet 		if (!value) {
974567b983SQuentin Monnet 			jsonw_null_field(json_wtr, name);
984567b983SQuentin Monnet 			return;
994567b983SQuentin Monnet 		}
1004567b983SQuentin Monnet 		errno = 0;
1014567b983SQuentin Monnet 		res = strtol(value, &endptr, 0);
1024567b983SQuentin Monnet 		if (!errno && *endptr == '\n')
1034567b983SQuentin Monnet 			jsonw_int_field(json_wtr, name, res);
1044567b983SQuentin Monnet 		else
1054567b983SQuentin Monnet 			jsonw_string_field(json_wtr, name, value);
1060ee52c0fSDaniel Borkmann 	} else if (define_prefix) {
1070ee52c0fSDaniel Borkmann 		if (value)
1080ee52c0fSDaniel Borkmann 			printf("#define %s%s %s\n", define_prefix,
1090ee52c0fSDaniel Borkmann 			       name, value);
1100ee52c0fSDaniel Borkmann 		else
1110ee52c0fSDaniel Borkmann 			printf("/* %s%s is not set */\n", define_prefix, name);
1124567b983SQuentin Monnet 	} else {
1134567b983SQuentin Monnet 		if (value)
1144567b983SQuentin Monnet 			printf("%s is set to %s\n", name, value);
1154567b983SQuentin Monnet 		else
1164567b983SQuentin Monnet 			printf("%s is not set\n", name);
1174567b983SQuentin Monnet 	}
1184567b983SQuentin Monnet }
1194567b983SQuentin Monnet 
12049eb7ab3SQuentin Monnet static void
print_start_section(const char * json_title,const char * plain_title,const char * define_comment,const char * define_prefix)121d267cff4SQuentin Monnet print_start_section(const char *json_title, const char *plain_title,
122d267cff4SQuentin Monnet 		    const char *define_comment, const char *define_prefix)
12349eb7ab3SQuentin Monnet {
12449eb7ab3SQuentin Monnet 	if (json_output) {
12549eb7ab3SQuentin Monnet 		jsonw_name(json_wtr, json_title);
12649eb7ab3SQuentin Monnet 		jsonw_start_object(json_wtr);
127d267cff4SQuentin Monnet 	} else if (define_prefix) {
128d267cff4SQuentin Monnet 		printf("%s\n", define_comment);
12949eb7ab3SQuentin Monnet 	} else {
13049eb7ab3SQuentin Monnet 		printf("%s\n", plain_title);
13149eb7ab3SQuentin Monnet 	}
13249eb7ab3SQuentin Monnet }
13349eb7ab3SQuentin Monnet 
print_end_section(void)1346b52ca44SMichal Rostecki static void print_end_section(void)
1351bf4b058SQuentin Monnet {
1361bf4b058SQuentin Monnet 	if (json_output)
1371bf4b058SQuentin Monnet 		jsonw_end_object(json_wtr);
1381bf4b058SQuentin Monnet 	else
1391bf4b058SQuentin Monnet 		printf("\n");
1401bf4b058SQuentin Monnet }
1411bf4b058SQuentin Monnet 
14249eb7ab3SQuentin Monnet /* Probing functions */
14349eb7ab3SQuentin Monnet 
get_vendor_id(int ifindex)144f655c088SNiklas Söderlund static int get_vendor_id(int ifindex)
145f655c088SNiklas Söderlund {
146f655c088SNiklas Söderlund 	char ifname[IF_NAMESIZE], path[64], buf[8];
147f655c088SNiklas Söderlund 	ssize_t len;
148f655c088SNiklas Söderlund 	int fd;
149f655c088SNiklas Söderlund 
150f655c088SNiklas Söderlund 	if (!if_indextoname(ifindex, ifname))
151f655c088SNiklas Söderlund 		return -1;
152f655c088SNiklas Söderlund 
153f655c088SNiklas Söderlund 	snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname);
154f655c088SNiklas Söderlund 
155f655c088SNiklas Söderlund 	fd = open(path, O_RDONLY | O_CLOEXEC);
156f655c088SNiklas Söderlund 	if (fd < 0)
157f655c088SNiklas Söderlund 		return -1;
158f655c088SNiklas Söderlund 
159f655c088SNiklas Söderlund 	len = read(fd, buf, sizeof(buf));
160f655c088SNiklas Söderlund 	close(fd);
161f655c088SNiklas Söderlund 	if (len < 0)
162f655c088SNiklas Söderlund 		return -1;
163f655c088SNiklas Söderlund 	if (len >= (ssize_t)sizeof(buf))
164f655c088SNiklas Söderlund 		return -1;
165f655c088SNiklas Söderlund 	buf[len] = '\0';
166f655c088SNiklas Söderlund 
167f655c088SNiklas Söderlund 	return strtol(buf, NULL, 0);
168f655c088SNiklas Söderlund }
169f655c088SNiklas Söderlund 
read_procfs(const char * path)17004cb8453SAlan Maguire static long read_procfs(const char *path)
1717a4522bbSQuentin Monnet {
1727a4522bbSQuentin Monnet 	char *endptr, *line = NULL;
1737a4522bbSQuentin Monnet 	size_t len = 0;
1747a4522bbSQuentin Monnet 	FILE *fd;
17504cb8453SAlan Maguire 	long res;
1767a4522bbSQuentin Monnet 
1777a4522bbSQuentin Monnet 	fd = fopen(path, "r");
1787a4522bbSQuentin Monnet 	if (!fd)
1797a4522bbSQuentin Monnet 		return -1;
1807a4522bbSQuentin Monnet 
1817a4522bbSQuentin Monnet 	res = getline(&line, &len, fd);
1827a4522bbSQuentin Monnet 	fclose(fd);
1837a4522bbSQuentin Monnet 	if (res < 0)
1847a4522bbSQuentin Monnet 		return -1;
1857a4522bbSQuentin Monnet 
1867a4522bbSQuentin Monnet 	errno = 0;
1877a4522bbSQuentin Monnet 	res = strtol(line, &endptr, 10);
1887a4522bbSQuentin Monnet 	if (errno || *line == '\0' || *endptr != '\n')
1897a4522bbSQuentin Monnet 		res = -1;
1907a4522bbSQuentin Monnet 	free(line);
1917a4522bbSQuentin Monnet 
1927a4522bbSQuentin Monnet 	return res;
1937a4522bbSQuentin Monnet }
1947a4522bbSQuentin Monnet 
probe_unprivileged_disabled(void)1957a4522bbSQuentin Monnet static void probe_unprivileged_disabled(void)
1967a4522bbSQuentin Monnet {
19704cb8453SAlan Maguire 	long res;
1987a4522bbSQuentin Monnet 
199d267cff4SQuentin Monnet 	/* No support for C-style ouptut */
200d267cff4SQuentin Monnet 
2017a4522bbSQuentin Monnet 	res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
2027a4522bbSQuentin Monnet 	if (json_output) {
2037a4522bbSQuentin Monnet 		jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
2047a4522bbSQuentin Monnet 	} else {
2057a4522bbSQuentin Monnet 		switch (res) {
2067a4522bbSQuentin Monnet 		case 0:
2077a4522bbSQuentin Monnet 			printf("bpf() syscall for unprivileged users is enabled\n");
2087a4522bbSQuentin Monnet 			break;
2097a4522bbSQuentin Monnet 		case 1:
2108c1b2119SMilan Landaverde 			printf("bpf() syscall restricted to privileged users (without recovery)\n");
2118c1b2119SMilan Landaverde 			break;
2128c1b2119SMilan Landaverde 		case 2:
2138c1b2119SMilan Landaverde 			printf("bpf() syscall restricted to privileged users (admin can change)\n");
2147a4522bbSQuentin Monnet 			break;
2157a4522bbSQuentin Monnet 		case -1:
2167a4522bbSQuentin Monnet 			printf("Unable to retrieve required privileges for bpf() syscall\n");
2177a4522bbSQuentin Monnet 			break;
2187a4522bbSQuentin Monnet 		default:
21904cb8453SAlan Maguire 			printf("bpf() syscall restriction has unknown value %ld\n", res);
2207a4522bbSQuentin Monnet 		}
2217a4522bbSQuentin Monnet 	}
2227a4522bbSQuentin Monnet }
2237a4522bbSQuentin Monnet 
probe_jit_enable(void)2247a4522bbSQuentin Monnet static void probe_jit_enable(void)
2257a4522bbSQuentin Monnet {
22604cb8453SAlan Maguire 	long res;
2277a4522bbSQuentin Monnet 
228d267cff4SQuentin Monnet 	/* No support for C-style ouptut */
229d267cff4SQuentin Monnet 
2307a4522bbSQuentin Monnet 	res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
2317a4522bbSQuentin Monnet 	if (json_output) {
2327a4522bbSQuentin Monnet 		jsonw_int_field(json_wtr, "bpf_jit_enable", res);
2337a4522bbSQuentin Monnet 	} else {
2347a4522bbSQuentin Monnet 		switch (res) {
2357a4522bbSQuentin Monnet 		case 0:
2367a4522bbSQuentin Monnet 			printf("JIT compiler is disabled\n");
2377a4522bbSQuentin Monnet 			break;
2387a4522bbSQuentin Monnet 		case 1:
2397a4522bbSQuentin Monnet 			printf("JIT compiler is enabled\n");
2407a4522bbSQuentin Monnet 			break;
2417a4522bbSQuentin Monnet 		case 2:
2427a4522bbSQuentin Monnet 			printf("JIT compiler is enabled with debugging traces in kernel logs\n");
2437a4522bbSQuentin Monnet 			break;
2447a4522bbSQuentin Monnet 		case -1:
2457a4522bbSQuentin Monnet 			printf("Unable to retrieve JIT-compiler status\n");
2467a4522bbSQuentin Monnet 			break;
2477a4522bbSQuentin Monnet 		default:
24804cb8453SAlan Maguire 			printf("JIT-compiler status has unknown value %ld\n",
2497a4522bbSQuentin Monnet 			       res);
2507a4522bbSQuentin Monnet 		}
2517a4522bbSQuentin Monnet 	}
2527a4522bbSQuentin Monnet }
2537a4522bbSQuentin Monnet 
probe_jit_harden(void)2547a4522bbSQuentin Monnet static void probe_jit_harden(void)
2557a4522bbSQuentin Monnet {
25604cb8453SAlan Maguire 	long res;
2577a4522bbSQuentin Monnet 
258d267cff4SQuentin Monnet 	/* No support for C-style ouptut */
259d267cff4SQuentin Monnet 
2607a4522bbSQuentin Monnet 	res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
2617a4522bbSQuentin Monnet 	if (json_output) {
2627a4522bbSQuentin Monnet 		jsonw_int_field(json_wtr, "bpf_jit_harden", res);
2637a4522bbSQuentin Monnet 	} else {
2647a4522bbSQuentin Monnet 		switch (res) {
2657a4522bbSQuentin Monnet 		case 0:
2667a4522bbSQuentin Monnet 			printf("JIT compiler hardening is disabled\n");
2677a4522bbSQuentin Monnet 			break;
2687a4522bbSQuentin Monnet 		case 1:
2697a4522bbSQuentin Monnet 			printf("JIT compiler hardening is enabled for unprivileged users\n");
2707a4522bbSQuentin Monnet 			break;
2717a4522bbSQuentin Monnet 		case 2:
2727a4522bbSQuentin Monnet 			printf("JIT compiler hardening is enabled for all users\n");
2737a4522bbSQuentin Monnet 			break;
2747a4522bbSQuentin Monnet 		case -1:
2757a4522bbSQuentin Monnet 			printf("Unable to retrieve JIT hardening status\n");
2767a4522bbSQuentin Monnet 			break;
2777a4522bbSQuentin Monnet 		default:
27804cb8453SAlan Maguire 			printf("JIT hardening status has unknown value %ld\n",
2797a4522bbSQuentin Monnet 			       res);
2807a4522bbSQuentin Monnet 		}
2817a4522bbSQuentin Monnet 	}
2827a4522bbSQuentin Monnet }
2837a4522bbSQuentin Monnet 
probe_jit_kallsyms(void)2847a4522bbSQuentin Monnet static void probe_jit_kallsyms(void)
2857a4522bbSQuentin Monnet {
28604cb8453SAlan Maguire 	long res;
2877a4522bbSQuentin Monnet 
288d267cff4SQuentin Monnet 	/* No support for C-style ouptut */
289d267cff4SQuentin Monnet 
2907a4522bbSQuentin Monnet 	res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
2917a4522bbSQuentin Monnet 	if (json_output) {
2927a4522bbSQuentin Monnet 		jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
2937a4522bbSQuentin Monnet 	} else {
2947a4522bbSQuentin Monnet 		switch (res) {
2957a4522bbSQuentin Monnet 		case 0:
2967a4522bbSQuentin Monnet 			printf("JIT compiler kallsyms exports are disabled\n");
2977a4522bbSQuentin Monnet 			break;
2987a4522bbSQuentin Monnet 		case 1:
2997a4522bbSQuentin Monnet 			printf("JIT compiler kallsyms exports are enabled for root\n");
3007a4522bbSQuentin Monnet 			break;
3017a4522bbSQuentin Monnet 		case -1:
3027a4522bbSQuentin Monnet 			printf("Unable to retrieve JIT kallsyms export status\n");
3037a4522bbSQuentin Monnet 			break;
3047a4522bbSQuentin Monnet 		default:
30504cb8453SAlan Maguire 			printf("JIT kallsyms exports status has unknown value %ld\n", res);
3067a4522bbSQuentin Monnet 		}
3077a4522bbSQuentin Monnet 	}
3087a4522bbSQuentin Monnet }
3097a4522bbSQuentin Monnet 
probe_jit_limit(void)3107a4522bbSQuentin Monnet static void probe_jit_limit(void)
3117a4522bbSQuentin Monnet {
31204cb8453SAlan Maguire 	long res;
3137a4522bbSQuentin Monnet 
314d267cff4SQuentin Monnet 	/* No support for C-style ouptut */
315d267cff4SQuentin Monnet 
3167a4522bbSQuentin Monnet 	res = read_procfs("/proc/sys/net/core/bpf_jit_limit");
3177a4522bbSQuentin Monnet 	if (json_output) {
3187a4522bbSQuentin Monnet 		jsonw_int_field(json_wtr, "bpf_jit_limit", res);
3197a4522bbSQuentin Monnet 	} else {
3207a4522bbSQuentin Monnet 		switch (res) {
3217a4522bbSQuentin Monnet 		case -1:
3227a4522bbSQuentin Monnet 			printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n");
3237a4522bbSQuentin Monnet 			break;
3247a4522bbSQuentin Monnet 		default:
32504cb8453SAlan Maguire 			printf("Global memory limit for JIT compiler for unprivileged users is %ld bytes\n", res);
3267a4522bbSQuentin Monnet 		}
3277a4522bbSQuentin Monnet 	}
3287a4522bbSQuentin Monnet }
3297a4522bbSQuentin Monnet 
read_next_kernel_config_option(gzFile file,char * buf,size_t n,char ** value)330a664a834SPeter Wu static bool read_next_kernel_config_option(gzFile file, char *buf, size_t n,
331a664a834SPeter Wu 					   char **value)
3324567b983SQuentin Monnet {
333a664a834SPeter Wu 	char *sep;
3344567b983SQuentin Monnet 
335a664a834SPeter Wu 	while (gzgets(file, buf, n)) {
336a664a834SPeter Wu 		if (strncmp(buf, "CONFIG_", 7))
3374567b983SQuentin Monnet 			continue;
338a664a834SPeter Wu 
339a664a834SPeter Wu 		sep = strchr(buf, '=');
340a664a834SPeter Wu 		if (!sep)
3414567b983SQuentin Monnet 			continue;
3424567b983SQuentin Monnet 
3434567b983SQuentin Monnet 		/* Trim ending '\n' */
344a664a834SPeter Wu 		buf[strlen(buf) - 1] = '\0';
3454567b983SQuentin Monnet 
346a664a834SPeter Wu 		/* Split on '=' and ensure that a value is present. */
347a664a834SPeter Wu 		*sep = '\0';
348a664a834SPeter Wu 		if (!sep[1])
349a664a834SPeter Wu 			continue;
350a664a834SPeter Wu 
351a664a834SPeter Wu 		*value = sep + 1;
352a664a834SPeter Wu 		return true;
3534567b983SQuentin Monnet 	}
3544567b983SQuentin Monnet 
355a664a834SPeter Wu 	return false;
3564567b983SQuentin Monnet }
3574567b983SQuentin Monnet 
probe_kernel_image_config(const char * define_prefix)3580ee52c0fSDaniel Borkmann static void probe_kernel_image_config(const char *define_prefix)
3594567b983SQuentin Monnet {
3600ee52c0fSDaniel Borkmann 	static const struct {
3610ee52c0fSDaniel Borkmann 		const char * const name;
3620ee52c0fSDaniel Borkmann 		bool macro_dump;
3630ee52c0fSDaniel Borkmann 	} options[] = {
3644567b983SQuentin Monnet 		/* Enable BPF */
3650ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF", },
3664567b983SQuentin Monnet 		/* Enable bpf() syscall */
3670ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_SYSCALL", },
3684567b983SQuentin Monnet 		/* Does selected architecture support eBPF JIT compiler */
3690ee52c0fSDaniel Borkmann 		{ "CONFIG_HAVE_EBPF_JIT", },
3704567b983SQuentin Monnet 		/* Compile eBPF JIT compiler */
3710ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_JIT", },
3724567b983SQuentin Monnet 		/* Avoid compiling eBPF interpreter (use JIT only) */
3730ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_JIT_ALWAYS_ON", },
374b9fc8b4aSGrant Seltzer 		/* Kernel BTF debug information available */
375b9fc8b4aSGrant Seltzer 		{ "CONFIG_DEBUG_INFO_BTF", },
376b9fc8b4aSGrant Seltzer 		/* Kernel module BTF debug information available */
377b9fc8b4aSGrant Seltzer 		{ "CONFIG_DEBUG_INFO_BTF_MODULES", },
3784567b983SQuentin Monnet 
3794567b983SQuentin Monnet 		/* cgroups */
3800ee52c0fSDaniel Borkmann 		{ "CONFIG_CGROUPS", },
3814567b983SQuentin Monnet 		/* BPF programs attached to cgroups */
3820ee52c0fSDaniel Borkmann 		{ "CONFIG_CGROUP_BPF", },
3834567b983SQuentin Monnet 		/* bpf_get_cgroup_classid() helper */
3840ee52c0fSDaniel Borkmann 		{ "CONFIG_CGROUP_NET_CLASSID", },
3854567b983SQuentin Monnet 		/* bpf_skb_{,ancestor_}cgroup_id() helpers */
3860ee52c0fSDaniel Borkmann 		{ "CONFIG_SOCK_CGROUP_DATA", },
3874567b983SQuentin Monnet 
3884567b983SQuentin Monnet 		/* Tracing: attach BPF to kprobes, tracepoints, etc. */
3890ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_EVENTS", },
3904567b983SQuentin Monnet 		/* Kprobes */
3910ee52c0fSDaniel Borkmann 		{ "CONFIG_KPROBE_EVENTS", },
3924567b983SQuentin Monnet 		/* Uprobes */
3930ee52c0fSDaniel Borkmann 		{ "CONFIG_UPROBE_EVENTS", },
3944567b983SQuentin Monnet 		/* Tracepoints */
3950ee52c0fSDaniel Borkmann 		{ "CONFIG_TRACING", },
3964567b983SQuentin Monnet 		/* Syscall tracepoints */
3970ee52c0fSDaniel Borkmann 		{ "CONFIG_FTRACE_SYSCALLS", },
3984567b983SQuentin Monnet 		/* bpf_override_return() helper support for selected arch */
3990ee52c0fSDaniel Borkmann 		{ "CONFIG_FUNCTION_ERROR_INJECTION", },
4004567b983SQuentin Monnet 		/* bpf_override_return() helper */
4010ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_KPROBE_OVERRIDE", },
4024567b983SQuentin Monnet 
4034567b983SQuentin Monnet 		/* Network */
4040ee52c0fSDaniel Borkmann 		{ "CONFIG_NET", },
4054567b983SQuentin Monnet 		/* AF_XDP sockets */
4060ee52c0fSDaniel Borkmann 		{ "CONFIG_XDP_SOCKETS", },
4074567b983SQuentin Monnet 		/* BPF_PROG_TYPE_LWT_* and related helpers */
4080ee52c0fSDaniel Borkmann 		{ "CONFIG_LWTUNNEL_BPF", },
4094567b983SQuentin Monnet 		/* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */
4100ee52c0fSDaniel Borkmann 		{ "CONFIG_NET_ACT_BPF", },
4114567b983SQuentin Monnet 		/* BPF_PROG_TYPE_SCHED_CLS, TC filters */
4120ee52c0fSDaniel Borkmann 		{ "CONFIG_NET_CLS_BPF", },
4134567b983SQuentin Monnet 		/* TC clsact qdisc */
4140ee52c0fSDaniel Borkmann 		{ "CONFIG_NET_CLS_ACT", },
4154567b983SQuentin Monnet 		/* Ingress filtering with TC */
4160ee52c0fSDaniel Borkmann 		{ "CONFIG_NET_SCH_INGRESS", },
4174567b983SQuentin Monnet 		/* bpf_skb_get_xfrm_state() helper */
4180ee52c0fSDaniel Borkmann 		{ "CONFIG_XFRM", },
4194567b983SQuentin Monnet 		/* bpf_get_route_realm() helper */
4200ee52c0fSDaniel Borkmann 		{ "CONFIG_IP_ROUTE_CLASSID", },
4214567b983SQuentin Monnet 		/* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */
4220ee52c0fSDaniel Borkmann 		{ "CONFIG_IPV6_SEG6_BPF", },
4234567b983SQuentin Monnet 		/* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */
4240ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_LIRC_MODE2", },
4254567b983SQuentin Monnet 		/* BPF stream parser and BPF socket maps */
4260ee52c0fSDaniel Borkmann 		{ "CONFIG_BPF_STREAM_PARSER", },
4274567b983SQuentin Monnet 		/* xt_bpf module for passing BPF programs to netfilter  */
4280ee52c0fSDaniel Borkmann 		{ "CONFIG_NETFILTER_XT_MATCH_BPF", },
4294567b983SQuentin Monnet 		/* bpfilter back-end for iptables */
4300ee52c0fSDaniel Borkmann 		{ "CONFIG_BPFILTER", },
4314567b983SQuentin Monnet 		/* bpftilter module with "user mode helper" */
4320ee52c0fSDaniel Borkmann 		{ "CONFIG_BPFILTER_UMH", },
4334567b983SQuentin Monnet 
4344567b983SQuentin Monnet 		/* test_bpf module for BPF tests */
4350ee52c0fSDaniel Borkmann 		{ "CONFIG_TEST_BPF", },
4360ee52c0fSDaniel Borkmann 
4370ee52c0fSDaniel Borkmann 		/* Misc configs useful in BPF C programs */
4380ee52c0fSDaniel Borkmann 		/* jiffies <-> sec conversion for bpf_jiffies64() helper */
4390ee52c0fSDaniel Borkmann 		{ "CONFIG_HZ", true, }
4404567b983SQuentin Monnet 	};
441a664a834SPeter Wu 	char *values[ARRAY_SIZE(options)] = { };
4424567b983SQuentin Monnet 	struct utsname utsn;
4434567b983SQuentin Monnet 	char path[PATH_MAX];
444a664a834SPeter Wu 	gzFile file = NULL;
445a664a834SPeter Wu 	char buf[4096];
446a664a834SPeter Wu 	char *value;
447a664a834SPeter Wu 	size_t i;
4484567b983SQuentin Monnet 
449a664a834SPeter Wu 	if (!uname(&utsn)) {
4504567b983SQuentin Monnet 		snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
4514567b983SQuentin Monnet 
452a664a834SPeter Wu 		/* gzopen also accepts uncompressed files. */
453a664a834SPeter Wu 		file = gzopen(path, "r");
4544567b983SQuentin Monnet 	}
455a664a834SPeter Wu 
456a664a834SPeter Wu 	if (!file) {
457a664a834SPeter Wu 		/* Some distributions build with CONFIG_IKCONFIG=y and put the
458a664a834SPeter Wu 		 * config file at /proc/config.gz.
459a664a834SPeter Wu 		 */
460a664a834SPeter Wu 		file = gzopen("/proc/config.gz", "r");
461a664a834SPeter Wu 	}
462a664a834SPeter Wu 	if (!file) {
4634567b983SQuentin Monnet 		p_info("skipping kernel config, can't open file: %s",
4644567b983SQuentin Monnet 		       strerror(errno));
465a664a834SPeter Wu 		goto end_parse;
4664567b983SQuentin Monnet 	}
4674567b983SQuentin Monnet 	/* Sanity checks */
468a664a834SPeter Wu 	if (!gzgets(file, buf, sizeof(buf)) ||
469a664a834SPeter Wu 	    !gzgets(file, buf, sizeof(buf))) {
4704567b983SQuentin Monnet 		p_info("skipping kernel config, can't read from file: %s",
4714567b983SQuentin Monnet 		       strerror(errno));
472a664a834SPeter Wu 		goto end_parse;
4734567b983SQuentin Monnet 	}
4744567b983SQuentin Monnet 	if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
4754567b983SQuentin Monnet 		p_info("skipping kernel config, can't find correct file");
476a664a834SPeter Wu 		goto end_parse;
4774567b983SQuentin Monnet 	}
478a664a834SPeter Wu 
479a664a834SPeter Wu 	while (read_next_kernel_config_option(file, buf, sizeof(buf), &value)) {
480a664a834SPeter Wu 		for (i = 0; i < ARRAY_SIZE(options); i++) {
4810ee52c0fSDaniel Borkmann 			if ((define_prefix && !options[i].macro_dump) ||
4820ee52c0fSDaniel Borkmann 			    values[i] || strcmp(buf, options[i].name))
483a664a834SPeter Wu 				continue;
484a664a834SPeter Wu 
485a664a834SPeter Wu 			values[i] = strdup(value);
486a664a834SPeter Wu 		}
487a664a834SPeter Wu 	}
488a664a834SPeter Wu 
4894567b983SQuentin Monnet 	for (i = 0; i < ARRAY_SIZE(options); i++) {
4900ee52c0fSDaniel Borkmann 		if (define_prefix && !options[i].macro_dump)
4910ee52c0fSDaniel Borkmann 			continue;
4920ee52c0fSDaniel Borkmann 		print_kernel_option(options[i].name, values[i], define_prefix);
493a664a834SPeter Wu 		free(values[i]);
4944567b983SQuentin Monnet 	}
49575514e4cSChethan Suresh 
49675514e4cSChethan Suresh end_parse:
49775514e4cSChethan Suresh 	if (file)
49875514e4cSChethan Suresh 		gzclose(file);
4994567b983SQuentin Monnet }
5004567b983SQuentin Monnet 
probe_bpf_syscall(const char * define_prefix)501d267cff4SQuentin Monnet static bool probe_bpf_syscall(const char *define_prefix)
50249eb7ab3SQuentin Monnet {
50349eb7ab3SQuentin Monnet 	bool res;
50449eb7ab3SQuentin Monnet 
505a3c7c7e8SAndrii Nakryiko 	bpf_prog_load(BPF_PROG_TYPE_UNSPEC, NULL, NULL, NULL, 0, NULL);
50649eb7ab3SQuentin Monnet 	res = (errno != ENOSYS);
50749eb7ab3SQuentin Monnet 
50849eb7ab3SQuentin Monnet 	print_bool_feature("have_bpf_syscall",
50949eb7ab3SQuentin Monnet 			   "bpf() syscall",
510d267cff4SQuentin Monnet 			   "BPF_SYSCALL",
511d267cff4SQuentin Monnet 			   res, define_prefix);
51249eb7ab3SQuentin Monnet 
51349eb7ab3SQuentin Monnet 	return res;
51449eb7ab3SQuentin Monnet }
51549eb7ab3SQuentin Monnet 
516f655c088SNiklas Söderlund static bool
probe_prog_load_ifindex(enum bpf_prog_type prog_type,const struct bpf_insn * insns,size_t insns_cnt,char * log_buf,size_t log_buf_sz,__u32 ifindex)517f655c088SNiklas Söderlund probe_prog_load_ifindex(enum bpf_prog_type prog_type,
518f655c088SNiklas Söderlund 			const struct bpf_insn *insns, size_t insns_cnt,
519f655c088SNiklas Söderlund 			char *log_buf, size_t log_buf_sz,
520f655c088SNiklas Söderlund 			__u32 ifindex)
521f655c088SNiklas Söderlund {
522f655c088SNiklas Söderlund 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
523f655c088SNiklas Söderlund 		    .log_buf = log_buf,
524f655c088SNiklas Söderlund 		    .log_size = log_buf_sz,
525f655c088SNiklas Söderlund 		    .log_level = log_buf ? 1 : 0,
526f655c088SNiklas Söderlund 		    .prog_ifindex = ifindex,
527f655c088SNiklas Söderlund 		   );
528f655c088SNiklas Söderlund 	int fd;
529f655c088SNiklas Söderlund 
530f655c088SNiklas Söderlund 	errno = 0;
531f655c088SNiklas Söderlund 	fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);
532f655c088SNiklas Söderlund 	if (fd >= 0)
533f655c088SNiklas Söderlund 		close(fd);
534f655c088SNiklas Söderlund 
535f655c088SNiklas Söderlund 	return fd >= 0 && errno != EINVAL && errno != EOPNOTSUPP;
536f655c088SNiklas Söderlund }
537f655c088SNiklas Söderlund 
probe_prog_type_ifindex(enum bpf_prog_type prog_type,__u32 ifindex)538f655c088SNiklas Söderlund static bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex)
539f655c088SNiklas Söderlund {
540f655c088SNiklas Söderlund 	/* nfp returns -EINVAL on exit(0) with TC offload */
541f655c088SNiklas Söderlund 	struct bpf_insn insns[2] = {
542f655c088SNiklas Söderlund 		BPF_MOV64_IMM(BPF_REG_0, 2),
543f655c088SNiklas Söderlund 		BPF_EXIT_INSN()
544f655c088SNiklas Söderlund 	};
545f655c088SNiklas Söderlund 
546f655c088SNiklas Söderlund 	return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns),
547f655c088SNiklas Söderlund 				       NULL, 0, ifindex);
548f655c088SNiklas Söderlund }
549f655c088SNiklas Söderlund 
550d267cff4SQuentin Monnet static void
probe_prog_type(enum bpf_prog_type prog_type,const char * prog_type_str,bool * supported_types,const char * define_prefix,__u32 ifindex)551b700eeb4SDaniel Müller probe_prog_type(enum bpf_prog_type prog_type, const char *prog_type_str,
552b700eeb4SDaniel Müller 		bool *supported_types, const char *define_prefix, __u32 ifindex)
5531bf4b058SQuentin Monnet {
554d267cff4SQuentin Monnet 	char feat_name[128], plain_desc[128], define_name[128];
5551bf4b058SQuentin Monnet 	const char *plain_comment = "eBPF program_type ";
5561bf4b058SQuentin Monnet 	size_t maxlen;
5571bf4b058SQuentin Monnet 	bool res;
5581bf4b058SQuentin Monnet 
5591a56c18eSAndrii Nakryiko 	if (ifindex) {
560f655c088SNiklas Söderlund 		switch (prog_type) {
561f655c088SNiklas Söderlund 		case BPF_PROG_TYPE_SCHED_CLS:
562f655c088SNiklas Söderlund 		case BPF_PROG_TYPE_XDP:
563f655c088SNiklas Söderlund 			break;
564f655c088SNiklas Söderlund 		default:
565f9499fedSQuentin Monnet 			return;
566f9499fedSQuentin Monnet 		}
567f9499fedSQuentin Monnet 
568f655c088SNiklas Söderlund 		res = probe_prog_type_ifindex(prog_type, ifindex);
569f655c088SNiklas Söderlund 	} else {
5707b53eaa6SMilan Landaverde 		res = libbpf_probe_bpf_prog_type(prog_type, NULL) > 0;
571f655c088SNiklas Söderlund 	}
572f655c088SNiklas Söderlund 
5730b3b9ca3SQuentin Monnet #ifdef USE_LIBCAP
574cf9bf714SQuentin Monnet 	/* Probe may succeed even if program load fails, for unprivileged users
575cf9bf714SQuentin Monnet 	 * check that we did not fail because of insufficient permissions
576cf9bf714SQuentin Monnet 	 */
577cf9bf714SQuentin Monnet 	if (run_as_unprivileged && errno == EPERM)
578cf9bf714SQuentin Monnet 		res = false;
5790b3b9ca3SQuentin Monnet #endif
5801bf4b058SQuentin Monnet 
5811bf4b058SQuentin Monnet 	supported_types[prog_type] |= res;
5821bf4b058SQuentin Monnet 
5831bf4b058SQuentin Monnet 	maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
584b700eeb4SDaniel Müller 	if (strlen(prog_type_str) > maxlen) {
5851bf4b058SQuentin Monnet 		p_info("program type name too long");
5861bf4b058SQuentin Monnet 		return;
5871bf4b058SQuentin Monnet 	}
5881bf4b058SQuentin Monnet 
589b700eeb4SDaniel Müller 	sprintf(feat_name, "have_%s_prog_type", prog_type_str);
590b700eeb4SDaniel Müller 	sprintf(define_name, "%s_prog_type", prog_type_str);
591d267cff4SQuentin Monnet 	uppercase(define_name, sizeof(define_name));
592b700eeb4SDaniel Müller 	sprintf(plain_desc, "%s%s", plain_comment, prog_type_str);
593d267cff4SQuentin Monnet 	print_bool_feature(feat_name, plain_desc, define_name, res,
594d267cff4SQuentin Monnet 			   define_prefix);
5951bf4b058SQuentin Monnet }
5961bf4b058SQuentin Monnet 
probe_map_type_ifindex(enum bpf_map_type map_type,__u32 ifindex)597f655c088SNiklas Söderlund static bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex)
598f655c088SNiklas Söderlund {
599f655c088SNiklas Söderlund 	LIBBPF_OPTS(bpf_map_create_opts, opts);
600f655c088SNiklas Söderlund 	int key_size, value_size, max_entries;
601f655c088SNiklas Söderlund 	int fd;
602f655c088SNiklas Söderlund 
603f655c088SNiklas Söderlund 	opts.map_ifindex = ifindex;
604f655c088SNiklas Söderlund 
605f655c088SNiklas Söderlund 	key_size = sizeof(__u32);
606f655c088SNiklas Söderlund 	value_size = sizeof(__u32);
607f655c088SNiklas Söderlund 	max_entries = 1;
608f655c088SNiklas Söderlund 
609f655c088SNiklas Söderlund 	fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries,
610f655c088SNiklas Söderlund 			    &opts);
611f655c088SNiklas Söderlund 	if (fd >= 0)
612f655c088SNiklas Söderlund 		close(fd);
613f655c088SNiklas Söderlund 
614f655c088SNiklas Söderlund 	return fd >= 0;
615f655c088SNiklas Söderlund }
616f655c088SNiklas Söderlund 
617d267cff4SQuentin Monnet static void
probe_map_type(enum bpf_map_type map_type,char const * map_type_str,const char * define_prefix,__u32 ifindex)6182e98964bSDaniel Müller probe_map_type(enum bpf_map_type map_type, char const *map_type_str,
6192e98964bSDaniel Müller 	       const char *define_prefix, __u32 ifindex)
620f99e1663SQuentin Monnet {
621d267cff4SQuentin Monnet 	char feat_name[128], plain_desc[128], define_name[128];
622f99e1663SQuentin Monnet 	const char *plain_comment = "eBPF map_type ";
623f99e1663SQuentin Monnet 	size_t maxlen;
624f99e1663SQuentin Monnet 	bool res;
625f99e1663SQuentin Monnet 
6261a56c18eSAndrii Nakryiko 	if (ifindex) {
627f655c088SNiklas Söderlund 		switch (map_type) {
628f655c088SNiklas Söderlund 		case BPF_MAP_TYPE_HASH:
629f655c088SNiklas Söderlund 		case BPF_MAP_TYPE_ARRAY:
630f655c088SNiklas Söderlund 			break;
631f655c088SNiklas Söderlund 		default:
6321a56c18eSAndrii Nakryiko 			return;
6331a56c18eSAndrii Nakryiko 		}
6341a56c18eSAndrii Nakryiko 
635f655c088SNiklas Söderlund 		res = probe_map_type_ifindex(map_type, ifindex);
636f655c088SNiklas Söderlund 	} else {
6376d9f63b9SMilan Landaverde 		res = libbpf_probe_bpf_map_type(map_type, NULL) > 0;
638f655c088SNiklas Söderlund 	}
639f99e1663SQuentin Monnet 
640cf9bf714SQuentin Monnet 	/* Probe result depends on the success of map creation, no additional
641cf9bf714SQuentin Monnet 	 * check required for unprivileged users
642cf9bf714SQuentin Monnet 	 */
643cf9bf714SQuentin Monnet 
644f99e1663SQuentin Monnet 	maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
6452e98964bSDaniel Müller 	if (strlen(map_type_str) > maxlen) {
646f99e1663SQuentin Monnet 		p_info("map type name too long");
647f99e1663SQuentin Monnet 		return;
648f99e1663SQuentin Monnet 	}
649f99e1663SQuentin Monnet 
6502e98964bSDaniel Müller 	sprintf(feat_name, "have_%s_map_type", map_type_str);
6512e98964bSDaniel Müller 	sprintf(define_name, "%s_map_type", map_type_str);
652d267cff4SQuentin Monnet 	uppercase(define_name, sizeof(define_name));
6532e98964bSDaniel Müller 	sprintf(plain_desc, "%s%s", plain_comment, map_type_str);
654d267cff4SQuentin Monnet 	print_bool_feature(feat_name, plain_desc, define_name, res,
655d267cff4SQuentin Monnet 			   define_prefix);
656f99e1663SQuentin Monnet }
657f99e1663SQuentin Monnet 
658f655c088SNiklas Söderlund static bool
probe_helper_ifindex(enum bpf_func_id id,enum bpf_prog_type prog_type,__u32 ifindex)659f655c088SNiklas Söderlund probe_helper_ifindex(enum bpf_func_id id, enum bpf_prog_type prog_type,
660f655c088SNiklas Söderlund 		     __u32 ifindex)
661f655c088SNiklas Söderlund {
662f655c088SNiklas Söderlund 	struct bpf_insn insns[2] = {
663f655c088SNiklas Söderlund 		BPF_EMIT_CALL(id),
664f655c088SNiklas Söderlund 		BPF_EXIT_INSN()
665f655c088SNiklas Söderlund 	};
666f655c088SNiklas Söderlund 	char buf[4096] = {};
667f655c088SNiklas Söderlund 	bool res;
668f655c088SNiklas Söderlund 
669f655c088SNiklas Söderlund 	probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), buf,
670f655c088SNiklas Söderlund 				sizeof(buf), ifindex);
671f655c088SNiklas Söderlund 	res = !grep(buf, "invalid func ") && !grep(buf, "unknown func ");
672f655c088SNiklas Söderlund 
673f655c088SNiklas Söderlund 	switch (get_vendor_id(ifindex)) {
674f655c088SNiklas Söderlund 	case 0x19ee: /* Netronome specific */
675f655c088SNiklas Söderlund 		res = res && !grep(buf, "not supported by FW") &&
676f655c088SNiklas Söderlund 			!grep(buf, "unsupported function id");
677f655c088SNiklas Söderlund 		break;
678f655c088SNiklas Söderlund 	default:
679f655c088SNiklas Söderlund 		break;
680f655c088SNiklas Söderlund 	}
681f655c088SNiklas Söderlund 
682f655c088SNiklas Söderlund 	return res;
683f655c088SNiklas Söderlund }
684f655c088SNiklas Söderlund 
685b06a92a1SMilan Landaverde static bool
probe_helper_for_progtype(enum bpf_prog_type prog_type,bool supported_type,const char * define_prefix,unsigned int id,const char * ptype_name,__u32 ifindex)686368cb0e7SMichal Rostecki probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
687368cb0e7SMichal Rostecki 			  const char *define_prefix, unsigned int id,
688368cb0e7SMichal Rostecki 			  const char *ptype_name, __u32 ifindex)
689368cb0e7SMichal Rostecki {
690cf9bf714SQuentin Monnet 	bool res = false;
691368cb0e7SMichal Rostecki 
692cf9bf714SQuentin Monnet 	if (supported_type) {
693f655c088SNiklas Söderlund 		if (ifindex)
694f655c088SNiklas Söderlund 			res = probe_helper_ifindex(id, prog_type, ifindex);
695f655c088SNiklas Söderlund 		else
6966d9f63b9SMilan Landaverde 			res = libbpf_probe_bpf_helper(prog_type, id, NULL) > 0;
6970b3b9ca3SQuentin Monnet #ifdef USE_LIBCAP
698cf9bf714SQuentin Monnet 		/* Probe may succeed even if program load fails, for
699cf9bf714SQuentin Monnet 		 * unprivileged users check that we did not fail because of
700cf9bf714SQuentin Monnet 		 * insufficient permissions
701cf9bf714SQuentin Monnet 		 */
702cf9bf714SQuentin Monnet 		if (run_as_unprivileged && errno == EPERM)
703cf9bf714SQuentin Monnet 			res = false;
7040b3b9ca3SQuentin Monnet #endif
705cf9bf714SQuentin Monnet 	}
706368cb0e7SMichal Rostecki 
707368cb0e7SMichal Rostecki 	if (json_output) {
708368cb0e7SMichal Rostecki 		if (res)
709368cb0e7SMichal Rostecki 			jsonw_string(json_wtr, helper_name[id]);
710368cb0e7SMichal Rostecki 	} else if (define_prefix) {
711368cb0e7SMichal Rostecki 		printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n",
712368cb0e7SMichal Rostecki 		       define_prefix, ptype_name, helper_name[id],
713368cb0e7SMichal Rostecki 		       res ? "1" : "0");
714368cb0e7SMichal Rostecki 	} else {
715368cb0e7SMichal Rostecki 		if (res)
716368cb0e7SMichal Rostecki 			printf("\n\t- %s", helper_name[id]);
717368cb0e7SMichal Rostecki 	}
718b06a92a1SMilan Landaverde 
719b06a92a1SMilan Landaverde 	return res;
720368cb0e7SMichal Rostecki }
721368cb0e7SMichal Rostecki 
722368cb0e7SMichal Rostecki static void
probe_helpers_for_progtype(enum bpf_prog_type prog_type,const char * prog_type_str,bool supported_type,const char * define_prefix,__u32 ifindex)723b700eeb4SDaniel Müller probe_helpers_for_progtype(enum bpf_prog_type prog_type,
724b700eeb4SDaniel Müller 			   const char *prog_type_str, bool supported_type,
725e3450b79SQuentin Monnet 			   const char *define_prefix, __u32 ifindex)
7262d3ea5e8SQuentin Monnet {
7272d3ea5e8SQuentin Monnet 	char feat_name[128];
7282d3ea5e8SQuentin Monnet 	unsigned int id;
729b06a92a1SMilan Landaverde 	bool probe_res = false;
7302d3ea5e8SQuentin Monnet 
731f9499fedSQuentin Monnet 	if (ifindex)
732f9499fedSQuentin Monnet 		/* Only test helpers for offload-able program types */
733f9499fedSQuentin Monnet 		switch (prog_type) {
734f9499fedSQuentin Monnet 		case BPF_PROG_TYPE_SCHED_CLS:
735f9499fedSQuentin Monnet 		case BPF_PROG_TYPE_XDP:
736f9499fedSQuentin Monnet 			break;
737f9499fedSQuentin Monnet 		default:
738f9499fedSQuentin Monnet 			return;
739f9499fedSQuentin Monnet 		}
740f9499fedSQuentin Monnet 
7412d3ea5e8SQuentin Monnet 	if (json_output) {
742b700eeb4SDaniel Müller 		sprintf(feat_name, "%s_available_helpers", prog_type_str);
7432d3ea5e8SQuentin Monnet 		jsonw_name(json_wtr, feat_name);
7442d3ea5e8SQuentin Monnet 		jsonw_start_array(json_wtr);
745d267cff4SQuentin Monnet 	} else if (!define_prefix) {
7462d3ea5e8SQuentin Monnet 		printf("eBPF helpers supported for program type %s:",
747b700eeb4SDaniel Müller 		       prog_type_str);
7482d3ea5e8SQuentin Monnet 	}
7492d3ea5e8SQuentin Monnet 
7502d3ea5e8SQuentin Monnet 	for (id = 1; id < ARRAY_SIZE(helper_name); id++) {
751368cb0e7SMichal Rostecki 		/* Skip helper functions which emit dmesg messages when not in
752368cb0e7SMichal Rostecki 		 * the full mode.
753368cb0e7SMichal Rostecki 		 */
754368cb0e7SMichal Rostecki 		switch (id) {
755368cb0e7SMichal Rostecki 		case BPF_FUNC_trace_printk:
7564190c299SDave Marchevsky 		case BPF_FUNC_trace_vprintk:
757368cb0e7SMichal Rostecki 		case BPF_FUNC_probe_write_user:
758368cb0e7SMichal Rostecki 			if (!full_mode)
759368cb0e7SMichal Rostecki 				continue;
760*0a5550b1SQuentin Monnet 			fallthrough;
761368cb0e7SMichal Rostecki 		default:
762b06a92a1SMilan Landaverde 			probe_res |= probe_helper_for_progtype(prog_type, supported_type,
763b700eeb4SDaniel Müller 						  define_prefix, id, prog_type_str,
764368cb0e7SMichal Rostecki 						  ifindex);
7652d3ea5e8SQuentin Monnet 		}
7662d3ea5e8SQuentin Monnet 	}
7672d3ea5e8SQuentin Monnet 
7682d3ea5e8SQuentin Monnet 	if (json_output)
7692d3ea5e8SQuentin Monnet 		jsonw_end_array(json_wtr);
770b06a92a1SMilan Landaverde 	else if (!define_prefix) {
7712d3ea5e8SQuentin Monnet 		printf("\n");
772b06a92a1SMilan Landaverde 		if (!probe_res) {
773b06a92a1SMilan Landaverde 			if (!supported_type)
774b06a92a1SMilan Landaverde 				printf("\tProgram type not supported\n");
775b06a92a1SMilan Landaverde 			else
776b06a92a1SMilan Landaverde 				printf("\tCould not determine which helpers are available\n");
777b06a92a1SMilan Landaverde 		}
778b06a92a1SMilan Landaverde 	}
779b06a92a1SMilan Landaverde 
780b06a92a1SMilan Landaverde 
7812d3ea5e8SQuentin Monnet }
7822d3ea5e8SQuentin Monnet 
783b22bf1b9SPaul Chaignon static void
probe_misc_feature(struct bpf_insn * insns,size_t len,const char * define_prefix,__u32 ifindex,const char * feat_name,const char * plain_name,const char * define_name)784b22bf1b9SPaul Chaignon probe_misc_feature(struct bpf_insn *insns, size_t len,
785b22bf1b9SPaul Chaignon 		   const char *define_prefix, __u32 ifindex,
786b22bf1b9SPaul Chaignon 		   const char *feat_name, const char *plain_name,
787b22bf1b9SPaul Chaignon 		   const char *define_name)
788b22bf1b9SPaul Chaignon {
789b22bf1b9SPaul Chaignon 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
790b22bf1b9SPaul Chaignon 		.prog_ifindex = ifindex,
791b22bf1b9SPaul Chaignon 	);
792b22bf1b9SPaul Chaignon 	bool res;
793b22bf1b9SPaul Chaignon 	int fd;
794b22bf1b9SPaul Chaignon 
795b22bf1b9SPaul Chaignon 	errno = 0;
796b22bf1b9SPaul Chaignon 	fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
797b22bf1b9SPaul Chaignon 			   insns, len, &opts);
798b22bf1b9SPaul Chaignon 	res = fd >= 0 || !errno;
799b22bf1b9SPaul Chaignon 
800b22bf1b9SPaul Chaignon 	if (fd >= 0)
801b22bf1b9SPaul Chaignon 		close(fd);
802b22bf1b9SPaul Chaignon 
803b22bf1b9SPaul Chaignon 	print_bool_feature(feat_name, plain_name, define_name, res,
804b22bf1b9SPaul Chaignon 			   define_prefix);
805b22bf1b9SPaul Chaignon }
806b22bf1b9SPaul Chaignon 
807e967a20aSAndrii Nakryiko /*
808e967a20aSAndrii Nakryiko  * Probe for availability of kernel commit (5.3):
809e967a20aSAndrii Nakryiko  *
810e967a20aSAndrii Nakryiko  * c04c0d2b968a ("bpf: increase complexity limit and maximum program size")
811e967a20aSAndrii Nakryiko  */
probe_large_insn_limit(const char * define_prefix,__u32 ifindex)812e967a20aSAndrii Nakryiko static void probe_large_insn_limit(const char *define_prefix, __u32 ifindex)
8132faef64aSMichal Rostecki {
814e967a20aSAndrii Nakryiko 	struct bpf_insn insns[BPF_MAXINSNS + 1];
815b22bf1b9SPaul Chaignon 	int i;
8162faef64aSMichal Rostecki 
817e967a20aSAndrii Nakryiko 	for (i = 0; i < BPF_MAXINSNS; i++)
818e967a20aSAndrii Nakryiko 		insns[i] = BPF_MOV64_IMM(BPF_REG_0, 1);
819e967a20aSAndrii Nakryiko 	insns[BPF_MAXINSNS] = BPF_EXIT_INSN();
820e967a20aSAndrii Nakryiko 
821b22bf1b9SPaul Chaignon 	probe_misc_feature(insns, ARRAY_SIZE(insns),
822b22bf1b9SPaul Chaignon 			   define_prefix, ifindex,
823b22bf1b9SPaul Chaignon 			   "have_large_insn_limit",
8242faef64aSMichal Rostecki 			   "Large program size limit",
825b22bf1b9SPaul Chaignon 			   "LARGE_INSN_LIMIT");
8262faef64aSMichal Rostecki }
8272faef64aSMichal Rostecki 
828c04fb2b0SPaul Chaignon /*
829c04fb2b0SPaul Chaignon  * Probe for bounded loop support introduced in commit 2589726d12a1
830c04fb2b0SPaul Chaignon  * ("bpf: introduce bounded loops").
831c04fb2b0SPaul Chaignon  */
832c04fb2b0SPaul Chaignon static void
probe_bounded_loops(const char * define_prefix,__u32 ifindex)833c04fb2b0SPaul Chaignon probe_bounded_loops(const char *define_prefix, __u32 ifindex)
834c04fb2b0SPaul Chaignon {
835c04fb2b0SPaul Chaignon 	struct bpf_insn insns[4] = {
836c04fb2b0SPaul Chaignon 		BPF_MOV64_IMM(BPF_REG_0, 10),
837c04fb2b0SPaul Chaignon 		BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, 1),
838c04fb2b0SPaul Chaignon 		BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, -2),
839c04fb2b0SPaul Chaignon 		BPF_EXIT_INSN()
840c04fb2b0SPaul Chaignon 	};
841c04fb2b0SPaul Chaignon 
842c04fb2b0SPaul Chaignon 	probe_misc_feature(insns, ARRAY_SIZE(insns),
843c04fb2b0SPaul Chaignon 			   define_prefix, ifindex,
844c04fb2b0SPaul Chaignon 			   "have_bounded_loops",
845c04fb2b0SPaul Chaignon 			   "Bounded loop support",
846c04fb2b0SPaul Chaignon 			   "BOUNDED_LOOPS");
847c04fb2b0SPaul Chaignon }
848c04fb2b0SPaul Chaignon 
8490fd800b2SPaul Chaignon /*
8500fd800b2SPaul Chaignon  * Probe for the v2 instruction set extension introduced in commit 92b31a9af73b
8510fd800b2SPaul Chaignon  * ("bpf: add BPF_J{LT,LE,SLT,SLE} instructions").
8520fd800b2SPaul Chaignon  */
8530fd800b2SPaul Chaignon static void
probe_v2_isa_extension(const char * define_prefix,__u32 ifindex)8540fd800b2SPaul Chaignon probe_v2_isa_extension(const char *define_prefix, __u32 ifindex)
8550fd800b2SPaul Chaignon {
8560fd800b2SPaul Chaignon 	struct bpf_insn insns[4] = {
8570fd800b2SPaul Chaignon 		BPF_MOV64_IMM(BPF_REG_0, 0),
8580fd800b2SPaul Chaignon 		BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 0, 1),
8590fd800b2SPaul Chaignon 		BPF_MOV64_IMM(BPF_REG_0, 1),
8600fd800b2SPaul Chaignon 		BPF_EXIT_INSN()
8610fd800b2SPaul Chaignon 	};
8620fd800b2SPaul Chaignon 
8630fd800b2SPaul Chaignon 	probe_misc_feature(insns, ARRAY_SIZE(insns),
8640fd800b2SPaul Chaignon 			   define_prefix, ifindex,
8650fd800b2SPaul Chaignon 			   "have_v2_isa_extension",
8660fd800b2SPaul Chaignon 			   "ISA extension v2",
8670fd800b2SPaul Chaignon 			   "V2_ISA_EXTENSION");
8680fd800b2SPaul Chaignon }
8690fd800b2SPaul Chaignon 
8700fd800b2SPaul Chaignon /*
8710fd800b2SPaul Chaignon  * Probe for the v3 instruction set extension introduced in commit 092ed0968bb6
8720fd800b2SPaul Chaignon  * ("bpf: verifier support JMP32").
8730fd800b2SPaul Chaignon  */
8740fd800b2SPaul Chaignon static void
probe_v3_isa_extension(const char * define_prefix,__u32 ifindex)8750fd800b2SPaul Chaignon probe_v3_isa_extension(const char *define_prefix, __u32 ifindex)
8760fd800b2SPaul Chaignon {
8770fd800b2SPaul Chaignon 	struct bpf_insn insns[4] = {
8780fd800b2SPaul Chaignon 		BPF_MOV64_IMM(BPF_REG_0, 0),
8790fd800b2SPaul Chaignon 		BPF_JMP32_IMM(BPF_JLT, BPF_REG_0, 0, 1),
8800fd800b2SPaul Chaignon 		BPF_MOV64_IMM(BPF_REG_0, 1),
8810fd800b2SPaul Chaignon 		BPF_EXIT_INSN()
8820fd800b2SPaul Chaignon 	};
8830fd800b2SPaul Chaignon 
8840fd800b2SPaul Chaignon 	probe_misc_feature(insns, ARRAY_SIZE(insns),
8850fd800b2SPaul Chaignon 			   define_prefix, ifindex,
8860fd800b2SPaul Chaignon 			   "have_v3_isa_extension",
8870fd800b2SPaul Chaignon 			   "ISA extension v3",
8880fd800b2SPaul Chaignon 			   "V3_ISA_EXTENSION");
8890fd800b2SPaul Chaignon }
8900fd800b2SPaul Chaignon 
8916b52ca44SMichal Rostecki static void
section_system_config(enum probe_component target,const char * define_prefix)8926b52ca44SMichal Rostecki section_system_config(enum probe_component target, const char *define_prefix)
8936b52ca44SMichal Rostecki {
8946b52ca44SMichal Rostecki 	switch (target) {
8956b52ca44SMichal Rostecki 	case COMPONENT_KERNEL:
8966b52ca44SMichal Rostecki 	case COMPONENT_UNSPEC:
8976b52ca44SMichal Rostecki 		print_start_section("system_config",
8986b52ca44SMichal Rostecki 				    "Scanning system configuration...",
8990ee52c0fSDaniel Borkmann 				    "/*** Misc kernel config items ***/",
9000ee52c0fSDaniel Borkmann 				    define_prefix);
9010ee52c0fSDaniel Borkmann 		if (!define_prefix) {
9026b52ca44SMichal Rostecki 			if (check_procfs()) {
9036b52ca44SMichal Rostecki 				probe_unprivileged_disabled();
9046b52ca44SMichal Rostecki 				probe_jit_enable();
9056b52ca44SMichal Rostecki 				probe_jit_harden();
9066b52ca44SMichal Rostecki 				probe_jit_kallsyms();
9076b52ca44SMichal Rostecki 				probe_jit_limit();
9086b52ca44SMichal Rostecki 			} else {
9096b52ca44SMichal Rostecki 				p_info("/* procfs not mounted, skipping related probes */");
9106b52ca44SMichal Rostecki 			}
9110ee52c0fSDaniel Borkmann 		}
9120ee52c0fSDaniel Borkmann 		probe_kernel_image_config(define_prefix);
9136b52ca44SMichal Rostecki 		print_end_section();
9146b52ca44SMichal Rostecki 		break;
9156b52ca44SMichal Rostecki 	default:
9166b52ca44SMichal Rostecki 		break;
9176b52ca44SMichal Rostecki 	}
9186b52ca44SMichal Rostecki }
9196b52ca44SMichal Rostecki 
section_syscall_config(const char * define_prefix)9206b52ca44SMichal Rostecki static bool section_syscall_config(const char *define_prefix)
9216b52ca44SMichal Rostecki {
9226b52ca44SMichal Rostecki 	bool res;
9236b52ca44SMichal Rostecki 
9246b52ca44SMichal Rostecki 	print_start_section("syscall_config",
9256b52ca44SMichal Rostecki 			    "Scanning system call availability...",
9266b52ca44SMichal Rostecki 			    "/*** System call availability ***/",
9276b52ca44SMichal Rostecki 			    define_prefix);
9286b52ca44SMichal Rostecki 	res = probe_bpf_syscall(define_prefix);
9296b52ca44SMichal Rostecki 	print_end_section();
9306b52ca44SMichal Rostecki 
9316b52ca44SMichal Rostecki 	return res;
9326b52ca44SMichal Rostecki }
9336b52ca44SMichal Rostecki 
9346b52ca44SMichal Rostecki static void
section_program_types(bool * supported_types,const char * define_prefix,__u32 ifindex)9356b52ca44SMichal Rostecki section_program_types(bool *supported_types, const char *define_prefix,
9366b52ca44SMichal Rostecki 		      __u32 ifindex)
9376b52ca44SMichal Rostecki {
938b700eeb4SDaniel Müller 	unsigned int prog_type = BPF_PROG_TYPE_UNSPEC;
939b700eeb4SDaniel Müller 	const char *prog_type_str;
9406b52ca44SMichal Rostecki 
9416b52ca44SMichal Rostecki 	print_start_section("program_types",
9426b52ca44SMichal Rostecki 			    "Scanning eBPF program types...",
9436b52ca44SMichal Rostecki 			    "/*** eBPF program types ***/",
9446b52ca44SMichal Rostecki 			    define_prefix);
9456b52ca44SMichal Rostecki 
946b700eeb4SDaniel Müller 	while (true) {
947b700eeb4SDaniel Müller 		prog_type++;
948b700eeb4SDaniel Müller 		prog_type_str = libbpf_bpf_prog_type_str(prog_type);
949b700eeb4SDaniel Müller 		/* libbpf will return NULL for variants unknown to it. */
950b700eeb4SDaniel Müller 		if (!prog_type_str)
951b700eeb4SDaniel Müller 			break;
952b700eeb4SDaniel Müller 
953b700eeb4SDaniel Müller 		probe_prog_type(prog_type, prog_type_str, supported_types, define_prefix,
954b700eeb4SDaniel Müller 				ifindex);
955b700eeb4SDaniel Müller 	}
9566b52ca44SMichal Rostecki 
9576b52ca44SMichal Rostecki 	print_end_section();
9586b52ca44SMichal Rostecki }
9596b52ca44SMichal Rostecki 
section_map_types(const char * define_prefix,__u32 ifindex)9606b52ca44SMichal Rostecki static void section_map_types(const char *define_prefix, __u32 ifindex)
9616b52ca44SMichal Rostecki {
9622e98964bSDaniel Müller 	unsigned int map_type = BPF_MAP_TYPE_UNSPEC;
9632e98964bSDaniel Müller 	const char *map_type_str;
9646b52ca44SMichal Rostecki 
9656b52ca44SMichal Rostecki 	print_start_section("map_types",
9666b52ca44SMichal Rostecki 			    "Scanning eBPF map types...",
9676b52ca44SMichal Rostecki 			    "/*** eBPF map types ***/",
9686b52ca44SMichal Rostecki 			    define_prefix);
9696b52ca44SMichal Rostecki 
9702e98964bSDaniel Müller 	while (true) {
9712e98964bSDaniel Müller 		map_type++;
9722e98964bSDaniel Müller 		map_type_str = libbpf_bpf_map_type_str(map_type);
9732e98964bSDaniel Müller 		/* libbpf will return NULL for variants unknown to it. */
9742e98964bSDaniel Müller 		if (!map_type_str)
9752e98964bSDaniel Müller 			break;
9762e98964bSDaniel Müller 
9772e98964bSDaniel Müller 		probe_map_type(map_type, map_type_str, define_prefix, ifindex);
9782e98964bSDaniel Müller 	}
9796b52ca44SMichal Rostecki 
9806b52ca44SMichal Rostecki 	print_end_section();
9816b52ca44SMichal Rostecki }
9826b52ca44SMichal Rostecki 
9836b52ca44SMichal Rostecki static void
section_helpers(bool * supported_types,const char * define_prefix,__u32 ifindex)984e3450b79SQuentin Monnet section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex)
9856b52ca44SMichal Rostecki {
986b700eeb4SDaniel Müller 	unsigned int prog_type = BPF_PROG_TYPE_UNSPEC;
987b700eeb4SDaniel Müller 	const char *prog_type_str;
9886b52ca44SMichal Rostecki 
9896b52ca44SMichal Rostecki 	print_start_section("helpers",
9906b52ca44SMichal Rostecki 			    "Scanning eBPF helper functions...",
9916b52ca44SMichal Rostecki 			    "/*** eBPF helper functions ***/",
9926b52ca44SMichal Rostecki 			    define_prefix);
9936b52ca44SMichal Rostecki 
9946b52ca44SMichal Rostecki 	if (define_prefix)
9956b52ca44SMichal Rostecki 		printf("/*\n"
9966b52ca44SMichal Rostecki 		       " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n"
9976b52ca44SMichal Rostecki 		       " * to determine if <helper_name> is available for <prog_type_name>,\n"
9986b52ca44SMichal Rostecki 		       " * e.g.\n"
9996b52ca44SMichal Rostecki 		       " *	#if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n"
10006b52ca44SMichal Rostecki 		       " *		// do stuff with this helper\n"
10016b52ca44SMichal Rostecki 		       " *	#elif\n"
10026b52ca44SMichal Rostecki 		       " *		// use a workaround\n"
10036b52ca44SMichal Rostecki 		       " *	#endif\n"
10046b52ca44SMichal Rostecki 		       " */\n"
10056b52ca44SMichal Rostecki 		       "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper)	\\\n"
10066b52ca44SMichal Rostecki 		       "	%sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n",
10076b52ca44SMichal Rostecki 		       define_prefix, define_prefix, define_prefix,
10086b52ca44SMichal Rostecki 		       define_prefix);
1009b700eeb4SDaniel Müller 	while (true) {
1010b700eeb4SDaniel Müller 		prog_type++;
1011b700eeb4SDaniel Müller 		prog_type_str = libbpf_bpf_prog_type_str(prog_type);
1012b700eeb4SDaniel Müller 		/* libbpf will return NULL for variants unknown to it. */
1013b700eeb4SDaniel Müller 		if (!prog_type_str)
1014b700eeb4SDaniel Müller 			break;
1015b700eeb4SDaniel Müller 
1016b700eeb4SDaniel Müller 		probe_helpers_for_progtype(prog_type, prog_type_str,
1017b700eeb4SDaniel Müller 					   supported_types[prog_type],
1018b700eeb4SDaniel Müller 					   define_prefix,
1019e3450b79SQuentin Monnet 					   ifindex);
1020b700eeb4SDaniel Müller 	}
10216b52ca44SMichal Rostecki 
10226b52ca44SMichal Rostecki 	print_end_section();
10236b52ca44SMichal Rostecki }
10246b52ca44SMichal Rostecki 
section_misc(const char * define_prefix,__u32 ifindex)10256b52ca44SMichal Rostecki static void section_misc(const char *define_prefix, __u32 ifindex)
10266b52ca44SMichal Rostecki {
10276b52ca44SMichal Rostecki 	print_start_section("misc",
10286b52ca44SMichal Rostecki 			    "Scanning miscellaneous eBPF features...",
10296b52ca44SMichal Rostecki 			    "/*** eBPF misc features ***/",
10306b52ca44SMichal Rostecki 			    define_prefix);
10316b52ca44SMichal Rostecki 	probe_large_insn_limit(define_prefix, ifindex);
1032c04fb2b0SPaul Chaignon 	probe_bounded_loops(define_prefix, ifindex);
10330fd800b2SPaul Chaignon 	probe_v2_isa_extension(define_prefix, ifindex);
10340fd800b2SPaul Chaignon 	probe_v3_isa_extension(define_prefix, ifindex);
10356b52ca44SMichal Rostecki 	print_end_section();
10366b52ca44SMichal Rostecki }
10376b52ca44SMichal Rostecki 
103873a4f040SQuentin Monnet #ifdef USE_LIBCAP
103973a4f040SQuentin Monnet #define capability(c) { c, false, #c }
104073a4f040SQuentin Monnet #define capability_msg(a, i) a[i].set ? "" : a[i].name, a[i].set ? "" : ", "
104173a4f040SQuentin Monnet #endif
104273a4f040SQuentin Monnet 
handle_perms(void)1043cf9bf714SQuentin Monnet static int handle_perms(void)
1044cf9bf714SQuentin Monnet {
10450b3b9ca3SQuentin Monnet #ifdef USE_LIBCAP
104673a4f040SQuentin Monnet 	struct {
104773a4f040SQuentin Monnet 		cap_value_t cap;
104873a4f040SQuentin Monnet 		bool set;
104973a4f040SQuentin Monnet 		char name[14];	/* strlen("CAP_SYS_ADMIN") */
105073a4f040SQuentin Monnet 	} bpf_caps[] = {
105173a4f040SQuentin Monnet 		capability(CAP_SYS_ADMIN),
105273a4f040SQuentin Monnet #ifdef CAP_BPF
105373a4f040SQuentin Monnet 		capability(CAP_BPF),
105473a4f040SQuentin Monnet 		capability(CAP_NET_ADMIN),
105573a4f040SQuentin Monnet 		capability(CAP_PERFMON),
105673a4f040SQuentin Monnet #endif
105773a4f040SQuentin Monnet 	};
105873a4f040SQuentin Monnet 	cap_value_t cap_list[ARRAY_SIZE(bpf_caps)];
105973a4f040SQuentin Monnet 	unsigned int i, nb_bpf_caps = 0;
106073a4f040SQuentin Monnet 	bool cap_sys_admin_only = true;
1061cf9bf714SQuentin Monnet 	cap_flag_value_t val;
1062cf9bf714SQuentin Monnet 	int res = -1;
1063cf9bf714SQuentin Monnet 	cap_t caps;
1064cf9bf714SQuentin Monnet 
1065cf9bf714SQuentin Monnet 	caps = cap_get_proc();
1066cf9bf714SQuentin Monnet 	if (!caps) {
1067cf9bf714SQuentin Monnet 		p_err("failed to get capabilities for process: %s",
1068cf9bf714SQuentin Monnet 		      strerror(errno));
1069cf9bf714SQuentin Monnet 		return -1;
1070cf9bf714SQuentin Monnet 	}
1071cf9bf714SQuentin Monnet 
107273a4f040SQuentin Monnet #ifdef CAP_BPF
107373a4f040SQuentin Monnet 	if (CAP_IS_SUPPORTED(CAP_BPF))
107473a4f040SQuentin Monnet 		cap_sys_admin_only = false;
107573a4f040SQuentin Monnet #endif
1076cf9bf714SQuentin Monnet 
107773a4f040SQuentin Monnet 	for (i = 0; i < ARRAY_SIZE(bpf_caps); i++) {
107873a4f040SQuentin Monnet 		const char *cap_name = bpf_caps[i].name;
107973a4f040SQuentin Monnet 		cap_value_t cap = bpf_caps[i].cap;
108073a4f040SQuentin Monnet 
108173a4f040SQuentin Monnet 		if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val)) {
108273a4f040SQuentin Monnet 			p_err("bug: failed to retrieve %s status: %s", cap_name,
108373a4f040SQuentin Monnet 			      strerror(errno));
1084cf9bf714SQuentin Monnet 			goto exit_free;
1085cf9bf714SQuentin Monnet 		}
1086cf9bf714SQuentin Monnet 
108773a4f040SQuentin Monnet 		if (val == CAP_SET) {
108873a4f040SQuentin Monnet 			bpf_caps[i].set = true;
108973a4f040SQuentin Monnet 			cap_list[nb_bpf_caps++] = cap;
109073a4f040SQuentin Monnet 		}
109173a4f040SQuentin Monnet 
109273a4f040SQuentin Monnet 		if (cap_sys_admin_only)
109373a4f040SQuentin Monnet 			/* System does not know about CAP_BPF, meaning that
109473a4f040SQuentin Monnet 			 * CAP_SYS_ADMIN is the only capability required. We
109573a4f040SQuentin Monnet 			 * just checked it, break.
109673a4f040SQuentin Monnet 			 */
109773a4f040SQuentin Monnet 			break;
109873a4f040SQuentin Monnet 	}
109973a4f040SQuentin Monnet 
110073a4f040SQuentin Monnet 	if ((run_as_unprivileged && !nb_bpf_caps) ||
110173a4f040SQuentin Monnet 	    (!run_as_unprivileged && nb_bpf_caps == ARRAY_SIZE(bpf_caps)) ||
110273a4f040SQuentin Monnet 	    (!run_as_unprivileged && cap_sys_admin_only && nb_bpf_caps)) {
1103cf9bf714SQuentin Monnet 		/* We are all good, exit now */
1104cf9bf714SQuentin Monnet 		res = 0;
1105cf9bf714SQuentin Monnet 		goto exit_free;
1106cf9bf714SQuentin Monnet 	}
1107cf9bf714SQuentin Monnet 
110873a4f040SQuentin Monnet 	if (!run_as_unprivileged) {
110973a4f040SQuentin Monnet 		if (cap_sys_admin_only)
111073a4f040SQuentin Monnet 			p_err("missing %s, required for full feature probing; run as root or use 'unprivileged'",
111173a4f040SQuentin Monnet 			      bpf_caps[0].name);
111273a4f040SQuentin Monnet 		else
111373a4f040SQuentin Monnet 			p_err("missing %s%s%s%s%s%s%s%srequired for full feature probing; run as root or use 'unprivileged'",
111473a4f040SQuentin Monnet 			      capability_msg(bpf_caps, 0),
11151e6f5dccSIan Rogers #ifdef CAP_BPF
111673a4f040SQuentin Monnet 			      capability_msg(bpf_caps, 1),
111773a4f040SQuentin Monnet 			      capability_msg(bpf_caps, 2),
11181e6f5dccSIan Rogers 			      capability_msg(bpf_caps, 3)
11191e6f5dccSIan Rogers #else
11201e6f5dccSIan Rogers 				"", "", "", "", "", ""
11211e6f5dccSIan Rogers #endif /* CAP_BPF */
11221e6f5dccSIan Rogers 				);
112373a4f040SQuentin Monnet 		goto exit_free;
112473a4f040SQuentin Monnet 	}
1125cf9bf714SQuentin Monnet 
112673a4f040SQuentin Monnet 	/* if (run_as_unprivileged && nb_bpf_caps > 0), drop capabilities. */
112773a4f040SQuentin Monnet 	if (cap_set_flag(caps, CAP_EFFECTIVE, nb_bpf_caps, cap_list,
1128cf9bf714SQuentin Monnet 			 CAP_CLEAR)) {
112973a4f040SQuentin Monnet 		p_err("bug: failed to clear capabilities: %s", strerror(errno));
1130cf9bf714SQuentin Monnet 		goto exit_free;
1131cf9bf714SQuentin Monnet 	}
1132cf9bf714SQuentin Monnet 
1133cf9bf714SQuentin Monnet 	if (cap_set_proc(caps)) {
113473a4f040SQuentin Monnet 		p_err("failed to drop capabilities: %s", strerror(errno));
1135cf9bf714SQuentin Monnet 		goto exit_free;
1136cf9bf714SQuentin Monnet 	}
1137cf9bf714SQuentin Monnet 
1138cf9bf714SQuentin Monnet 	res = 0;
1139cf9bf714SQuentin Monnet 
1140cf9bf714SQuentin Monnet exit_free:
1141cf9bf714SQuentin Monnet 	if (cap_free(caps) && !res) {
1142cf9bf714SQuentin Monnet 		p_err("failed to clear storage object for capabilities: %s",
1143cf9bf714SQuentin Monnet 		      strerror(errno));
1144cf9bf714SQuentin Monnet 		res = -1;
1145cf9bf714SQuentin Monnet 	}
1146cf9bf714SQuentin Monnet 
1147cf9bf714SQuentin Monnet 	return res;
11480b3b9ca3SQuentin Monnet #else
114973a4f040SQuentin Monnet 	/* Detection assumes user has specific privileges.
115054c93977SQuentin Monnet 	 * We do not use libcap so let's approximate, and restrict usage to
11510b3b9ca3SQuentin Monnet 	 * root user only.
11520b3b9ca3SQuentin Monnet 	 */
11530b3b9ca3SQuentin Monnet 	if (geteuid()) {
11540b3b9ca3SQuentin Monnet 		p_err("full feature probing requires root privileges");
11550b3b9ca3SQuentin Monnet 		return -1;
11560b3b9ca3SQuentin Monnet 	}
11570b3b9ca3SQuentin Monnet 
11580b3b9ca3SQuentin Monnet 	return 0;
11590b3b9ca3SQuentin Monnet #endif /* USE_LIBCAP */
1160cf9bf714SQuentin Monnet }
1161cf9bf714SQuentin Monnet 
do_probe(int argc,char ** argv)116249eb7ab3SQuentin Monnet static int do_probe(int argc, char **argv)
116349eb7ab3SQuentin Monnet {
116449eb7ab3SQuentin Monnet 	enum probe_component target = COMPONENT_UNSPEC;
1165d267cff4SQuentin Monnet 	const char *define_prefix = NULL;
11661bf4b058SQuentin Monnet 	bool supported_types[128] = {};
1167f9499fedSQuentin Monnet 	__u32 ifindex = 0;
1168f9499fedSQuentin Monnet 	char *ifname;
116949eb7ab3SQuentin Monnet 
11706b4384ffSQuentin Monnet 	set_max_rlimit();
11716b4384ffSQuentin Monnet 
117249eb7ab3SQuentin Monnet 	while (argc) {
117349eb7ab3SQuentin Monnet 		if (is_prefix(*argv, "kernel")) {
117449eb7ab3SQuentin Monnet 			if (target != COMPONENT_UNSPEC) {
117549eb7ab3SQuentin Monnet 				p_err("component to probe already specified");
117649eb7ab3SQuentin Monnet 				return -1;
117749eb7ab3SQuentin Monnet 			}
117849eb7ab3SQuentin Monnet 			target = COMPONENT_KERNEL;
117949eb7ab3SQuentin Monnet 			NEXT_ARG();
1180f9499fedSQuentin Monnet 		} else if (is_prefix(*argv, "dev")) {
1181f9499fedSQuentin Monnet 			NEXT_ARG();
1182f9499fedSQuentin Monnet 
1183f9499fedSQuentin Monnet 			if (target != COMPONENT_UNSPEC || ifindex) {
1184f9499fedSQuentin Monnet 				p_err("component to probe already specified");
1185f9499fedSQuentin Monnet 				return -1;
1186f9499fedSQuentin Monnet 			}
1187f9499fedSQuentin Monnet 			if (!REQ_ARGS(1))
1188f9499fedSQuentin Monnet 				return -1;
1189f9499fedSQuentin Monnet 
1190f9499fedSQuentin Monnet 			target = COMPONENT_DEVICE;
1191f9499fedSQuentin Monnet 			ifname = GET_ARG();
1192f9499fedSQuentin Monnet 			ifindex = if_nametoindex(ifname);
1193f9499fedSQuentin Monnet 			if (!ifindex) {
1194f9499fedSQuentin Monnet 				p_err("unrecognized netdevice '%s': %s", ifname,
1195f9499fedSQuentin Monnet 				      strerror(errno));
1196f9499fedSQuentin Monnet 				return -1;
1197f9499fedSQuentin Monnet 			}
1198368cb0e7SMichal Rostecki 		} else if (is_prefix(*argv, "full")) {
1199368cb0e7SMichal Rostecki 			full_mode = true;
1200368cb0e7SMichal Rostecki 			NEXT_ARG();
1201d267cff4SQuentin Monnet 		} else if (is_prefix(*argv, "macros") && !define_prefix) {
1202d267cff4SQuentin Monnet 			define_prefix = "";
1203d267cff4SQuentin Monnet 			NEXT_ARG();
1204d267cff4SQuentin Monnet 		} else if (is_prefix(*argv, "prefix")) {
1205d267cff4SQuentin Monnet 			if (!define_prefix) {
1206d267cff4SQuentin Monnet 				p_err("'prefix' argument can only be use after 'macros'");
1207d267cff4SQuentin Monnet 				return -1;
1208d267cff4SQuentin Monnet 			}
1209d267cff4SQuentin Monnet 			if (strcmp(define_prefix, "")) {
1210d267cff4SQuentin Monnet 				p_err("'prefix' already defined");
1211d267cff4SQuentin Monnet 				return -1;
1212d267cff4SQuentin Monnet 			}
1213d267cff4SQuentin Monnet 			NEXT_ARG();
1214d267cff4SQuentin Monnet 
1215d267cff4SQuentin Monnet 			if (!REQ_ARGS(1))
1216d267cff4SQuentin Monnet 				return -1;
1217d267cff4SQuentin Monnet 			define_prefix = GET_ARG();
1218cf9bf714SQuentin Monnet 		} else if (is_prefix(*argv, "unprivileged")) {
12190b3b9ca3SQuentin Monnet #ifdef USE_LIBCAP
1220cf9bf714SQuentin Monnet 			run_as_unprivileged = true;
1221cf9bf714SQuentin Monnet 			NEXT_ARG();
12220b3b9ca3SQuentin Monnet #else
12230b3b9ca3SQuentin Monnet 			p_err("unprivileged run not supported, recompile bpftool with libcap");
12240b3b9ca3SQuentin Monnet 			return -1;
12250b3b9ca3SQuentin Monnet #endif
122649eb7ab3SQuentin Monnet 		} else {
1227f9499fedSQuentin Monnet 			p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",
122849eb7ab3SQuentin Monnet 			      *argv);
122949eb7ab3SQuentin Monnet 			return -1;
123049eb7ab3SQuentin Monnet 		}
123149eb7ab3SQuentin Monnet 	}
123249eb7ab3SQuentin Monnet 
123373a4f040SQuentin Monnet 	/* Full feature detection requires specific privileges.
1234cf9bf714SQuentin Monnet 	 * Let's approximate, and warn if user is not root.
1235cf9bf714SQuentin Monnet 	 */
1236cf9bf714SQuentin Monnet 	if (handle_perms())
1237cf9bf714SQuentin Monnet 		return -1;
1238cf9bf714SQuentin Monnet 
1239d267cff4SQuentin Monnet 	if (json_output) {
1240d267cff4SQuentin Monnet 		define_prefix = NULL;
124149eb7ab3SQuentin Monnet 		jsonw_start_object(json_wtr);
1242d267cff4SQuentin Monnet 	}
124349eb7ab3SQuentin Monnet 
12446b52ca44SMichal Rostecki 	section_system_config(target, define_prefix);
12456b52ca44SMichal Rostecki 	if (!section_syscall_config(define_prefix))
12461bf4b058SQuentin Monnet 		/* bpf() syscall unavailable, don't probe other BPF features */
12471bf4b058SQuentin Monnet 		goto exit_close_json;
12486b52ca44SMichal Rostecki 	section_program_types(supported_types, define_prefix, ifindex);
12496b52ca44SMichal Rostecki 	section_map_types(define_prefix, ifindex);
1250e3450b79SQuentin Monnet 	section_helpers(supported_types, define_prefix, ifindex);
12516b52ca44SMichal Rostecki 	section_misc(define_prefix, ifindex);
12522faef64aSMichal Rostecki 
12531bf4b058SQuentin Monnet exit_close_json:
12546b52ca44SMichal Rostecki 	if (json_output)
125549eb7ab3SQuentin Monnet 		/* End root object */
125649eb7ab3SQuentin Monnet 		jsonw_end_object(json_wtr);
125749eb7ab3SQuentin Monnet 
125849eb7ab3SQuentin Monnet 	return 0;
125949eb7ab3SQuentin Monnet }
126049eb7ab3SQuentin Monnet 
get_helper_name(unsigned int id)126127b3f705SQuentin Monnet static const char *get_helper_name(unsigned int id)
126227b3f705SQuentin Monnet {
126327b3f705SQuentin Monnet 	if (id >= ARRAY_SIZE(helper_name))
126427b3f705SQuentin Monnet 		return NULL;
126527b3f705SQuentin Monnet 
126627b3f705SQuentin Monnet 	return helper_name[id];
126727b3f705SQuentin Monnet }
126827b3f705SQuentin Monnet 
do_list_builtins(int argc,char ** argv)1269990a6194SQuentin Monnet static int do_list_builtins(int argc, char **argv)
127027b3f705SQuentin Monnet {
127127b3f705SQuentin Monnet 	const char *(*get_name)(unsigned int id);
127227b3f705SQuentin Monnet 	unsigned int id = 0;
127327b3f705SQuentin Monnet 
127427b3f705SQuentin Monnet 	if (argc < 1)
127527b3f705SQuentin Monnet 		usage();
127627b3f705SQuentin Monnet 
127727b3f705SQuentin Monnet 	if (is_prefix(*argv, "prog_types")) {
127827b3f705SQuentin Monnet 		get_name = (const char *(*)(unsigned int))libbpf_bpf_prog_type_str;
127927b3f705SQuentin Monnet 	} else if (is_prefix(*argv, "map_types")) {
128027b3f705SQuentin Monnet 		get_name = (const char *(*)(unsigned int))libbpf_bpf_map_type_str;
128127b3f705SQuentin Monnet 	} else if (is_prefix(*argv, "attach_types")) {
128227b3f705SQuentin Monnet 		get_name = (const char *(*)(unsigned int))libbpf_bpf_attach_type_str;
128327b3f705SQuentin Monnet 	} else if (is_prefix(*argv, "link_types")) {
128427b3f705SQuentin Monnet 		get_name = (const char *(*)(unsigned int))libbpf_bpf_link_type_str;
128527b3f705SQuentin Monnet 	} else if (is_prefix(*argv, "helpers")) {
128627b3f705SQuentin Monnet 		get_name = get_helper_name;
128727b3f705SQuentin Monnet 	} else {
128827b3f705SQuentin Monnet 		p_err("expected 'prog_types', 'map_types', 'attach_types', 'link_types' or 'helpers', got: %s", *argv);
128927b3f705SQuentin Monnet 		return -1;
129027b3f705SQuentin Monnet 	}
129127b3f705SQuentin Monnet 
129227b3f705SQuentin Monnet 	if (json_output)
129327b3f705SQuentin Monnet 		jsonw_start_array(json_wtr);	/* root array */
129427b3f705SQuentin Monnet 
129527b3f705SQuentin Monnet 	while (true) {
129627b3f705SQuentin Monnet 		const char *name;
129727b3f705SQuentin Monnet 
129827b3f705SQuentin Monnet 		name = get_name(id++);
129927b3f705SQuentin Monnet 		if (!name)
130027b3f705SQuentin Monnet 			break;
130127b3f705SQuentin Monnet 		if (json_output)
130227b3f705SQuentin Monnet 			jsonw_string(json_wtr, name);
130327b3f705SQuentin Monnet 		else
130427b3f705SQuentin Monnet 			printf("%s\n", name);
130527b3f705SQuentin Monnet 	}
130627b3f705SQuentin Monnet 
130727b3f705SQuentin Monnet 	if (json_output)
130827b3f705SQuentin Monnet 		jsonw_end_array(json_wtr);	/* root array */
130927b3f705SQuentin Monnet 
131027b3f705SQuentin Monnet 	return 0;
131127b3f705SQuentin Monnet }
131227b3f705SQuentin Monnet 
do_help(int argc,char ** argv)131349eb7ab3SQuentin Monnet static int do_help(int argc, char **argv)
131449eb7ab3SQuentin Monnet {
131549eb7ab3SQuentin Monnet 	if (json_output) {
131649eb7ab3SQuentin Monnet 		jsonw_null(json_wtr);
131749eb7ab3SQuentin Monnet 		return 0;
131849eb7ab3SQuentin Monnet 	}
131949eb7ab3SQuentin Monnet 
132049eb7ab3SQuentin Monnet 	fprintf(stderr,
132190040351SQuentin Monnet 		"Usage: %1$s %2$s probe [COMPONENT] [full] [unprivileged] [macros [prefix PREFIX]]\n"
1322990a6194SQuentin Monnet 		"       %1$s %2$s list_builtins GROUP\n"
132390040351SQuentin Monnet 		"       %1$s %2$s help\n"
1324f9499fedSQuentin Monnet 		"\n"
1325f9499fedSQuentin Monnet 		"       COMPONENT := { kernel | dev NAME }\n"
132627b3f705SQuentin Monnet 		"       GROUP := { prog_types | map_types | attach_types | link_types | helpers }\n"
1327c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " }\n"
132849eb7ab3SQuentin Monnet 		"",
132990040351SQuentin Monnet 		bin_name, argv[-2]);
133049eb7ab3SQuentin Monnet 
133149eb7ab3SQuentin Monnet 	return 0;
133249eb7ab3SQuentin Monnet }
133349eb7ab3SQuentin Monnet 
133449eb7ab3SQuentin Monnet static const struct cmd cmds[] = {
133549eb7ab3SQuentin Monnet 	{ "probe",		do_probe },
1336990a6194SQuentin Monnet 	{ "list_builtins",	do_list_builtins },
1337866e6ac4SPrashant Bhole 	{ "help",		do_help },
133849eb7ab3SQuentin Monnet 	{ 0 }
133949eb7ab3SQuentin Monnet };
134049eb7ab3SQuentin Monnet 
do_feature(int argc,char ** argv)134149eb7ab3SQuentin Monnet int do_feature(int argc, char **argv)
134249eb7ab3SQuentin Monnet {
134349eb7ab3SQuentin Monnet 	return cmd_select(cmds, argc, argv, do_help);
134449eb7ab3SQuentin Monnet }
1345