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