162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (c) 2019 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <ctype.h> 562306a36Sopenharmony_ci#include <errno.h> 662306a36Sopenharmony_ci#include <fcntl.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci#include <unistd.h> 962306a36Sopenharmony_ci#include <net/if.h> 1062306a36Sopenharmony_ci#ifdef USE_LIBCAP 1162306a36Sopenharmony_ci#include <sys/capability.h> 1262306a36Sopenharmony_ci#endif 1362306a36Sopenharmony_ci#include <sys/utsname.h> 1462306a36Sopenharmony_ci#include <sys/vfs.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/filter.h> 1762306a36Sopenharmony_ci#include <linux/limits.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <bpf/bpf.h> 2062306a36Sopenharmony_ci#include <bpf/libbpf.h> 2162306a36Sopenharmony_ci#include <zlib.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "main.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#ifndef PROC_SUPER_MAGIC 2662306a36Sopenharmony_ci# define PROC_SUPER_MAGIC 0x9fa0 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cienum probe_component { 3062306a36Sopenharmony_ci COMPONENT_UNSPEC, 3162306a36Sopenharmony_ci COMPONENT_KERNEL, 3262306a36Sopenharmony_ci COMPONENT_DEVICE, 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define BPF_HELPER_MAKE_ENTRY(name) [BPF_FUNC_ ## name] = "bpf_" # name 3662306a36Sopenharmony_cistatic const char * const helper_name[] = { 3762306a36Sopenharmony_ci __BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY) 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#undef BPF_HELPER_MAKE_ENTRY 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool full_mode; 4362306a36Sopenharmony_ci#ifdef USE_LIBCAP 4462306a36Sopenharmony_cistatic bool run_as_unprivileged; 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Miscellaneous utility functions */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic bool grep(const char *buffer, const char *pattern) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return !!strstr(buffer, pattern); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic bool check_procfs(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct statfs st_fs; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (statfs("/proc", &st_fs) < 0) 5962306a36Sopenharmony_ci return false; 6062306a36Sopenharmony_ci if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC) 6162306a36Sopenharmony_ci return false; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return true; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void uppercase(char *str, size_t len) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci size_t i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = 0; i < len && str[i] != '\0'; i++) 7162306a36Sopenharmony_ci str[i] = toupper(str[i]); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Printing utility functions */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void 7762306a36Sopenharmony_ciprint_bool_feature(const char *feat_name, const char *plain_name, 7862306a36Sopenharmony_ci const char *define_name, bool res, const char *define_prefix) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (json_output) 8162306a36Sopenharmony_ci jsonw_bool_field(json_wtr, feat_name, res); 8262306a36Sopenharmony_ci else if (define_prefix) 8362306a36Sopenharmony_ci printf("#define %s%sHAVE_%s\n", define_prefix, 8462306a36Sopenharmony_ci res ? "" : "NO_", define_name); 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void print_kernel_option(const char *name, const char *value, 9062306a36Sopenharmony_ci const char *define_prefix) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci char *endptr; 9362306a36Sopenharmony_ci int res; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (json_output) { 9662306a36Sopenharmony_ci if (!value) { 9762306a36Sopenharmony_ci jsonw_null_field(json_wtr, name); 9862306a36Sopenharmony_ci return; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci errno = 0; 10162306a36Sopenharmony_ci res = strtol(value, &endptr, 0); 10262306a36Sopenharmony_ci if (!errno && *endptr == '\n') 10362306a36Sopenharmony_ci jsonw_int_field(json_wtr, name, res); 10462306a36Sopenharmony_ci else 10562306a36Sopenharmony_ci jsonw_string_field(json_wtr, name, value); 10662306a36Sopenharmony_ci } else if (define_prefix) { 10762306a36Sopenharmony_ci if (value) 10862306a36Sopenharmony_ci printf("#define %s%s %s\n", define_prefix, 10962306a36Sopenharmony_ci name, value); 11062306a36Sopenharmony_ci else 11162306a36Sopenharmony_ci printf("/* %s%s is not set */\n", define_prefix, name); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci if (value) 11462306a36Sopenharmony_ci printf("%s is set to %s\n", name, value); 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci printf("%s is not set\n", name); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void 12162306a36Sopenharmony_ciprint_start_section(const char *json_title, const char *plain_title, 12262306a36Sopenharmony_ci const char *define_comment, const char *define_prefix) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci if (json_output) { 12562306a36Sopenharmony_ci jsonw_name(json_wtr, json_title); 12662306a36Sopenharmony_ci jsonw_start_object(json_wtr); 12762306a36Sopenharmony_ci } else if (define_prefix) { 12862306a36Sopenharmony_ci printf("%s\n", define_comment); 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci printf("%s\n", plain_title); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void print_end_section(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci if (json_output) 13762306a36Sopenharmony_ci jsonw_end_object(json_wtr); 13862306a36Sopenharmony_ci else 13962306a36Sopenharmony_ci printf("\n"); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* Probing functions */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int get_vendor_id(int ifindex) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci char ifname[IF_NAMESIZE], path[64], buf[8]; 14762306a36Sopenharmony_ci ssize_t len; 14862306a36Sopenharmony_ci int fd; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!if_indextoname(ifindex, ifname)) 15162306a36Sopenharmony_ci return -1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci fd = open(path, O_RDONLY | O_CLOEXEC); 15662306a36Sopenharmony_ci if (fd < 0) 15762306a36Sopenharmony_ci return -1; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci len = read(fd, buf, sizeof(buf)); 16062306a36Sopenharmony_ci close(fd); 16162306a36Sopenharmony_ci if (len < 0) 16262306a36Sopenharmony_ci return -1; 16362306a36Sopenharmony_ci if (len >= (ssize_t)sizeof(buf)) 16462306a36Sopenharmony_ci return -1; 16562306a36Sopenharmony_ci buf[len] = '\0'; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return strtol(buf, NULL, 0); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic long read_procfs(const char *path) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci char *endptr, *line = NULL; 17362306a36Sopenharmony_ci size_t len = 0; 17462306a36Sopenharmony_ci FILE *fd; 17562306a36Sopenharmony_ci long res; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci fd = fopen(path, "r"); 17862306a36Sopenharmony_ci if (!fd) 17962306a36Sopenharmony_ci return -1; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci res = getline(&line, &len, fd); 18262306a36Sopenharmony_ci fclose(fd); 18362306a36Sopenharmony_ci if (res < 0) 18462306a36Sopenharmony_ci return -1; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci errno = 0; 18762306a36Sopenharmony_ci res = strtol(line, &endptr, 10); 18862306a36Sopenharmony_ci if (errno || *line == '\0' || *endptr != '\n') 18962306a36Sopenharmony_ci res = -1; 19062306a36Sopenharmony_ci free(line); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return res; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void probe_unprivileged_disabled(void) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci long res; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* No support for C-style ouptut */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled"); 20262306a36Sopenharmony_ci if (json_output) { 20362306a36Sopenharmony_ci jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res); 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci switch (res) { 20662306a36Sopenharmony_ci case 0: 20762306a36Sopenharmony_ci printf("bpf() syscall for unprivileged users is enabled\n"); 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci case 1: 21062306a36Sopenharmony_ci printf("bpf() syscall restricted to privileged users (without recovery)\n"); 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci case 2: 21362306a36Sopenharmony_ci printf("bpf() syscall restricted to privileged users (admin can change)\n"); 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci case -1: 21662306a36Sopenharmony_ci printf("Unable to retrieve required privileges for bpf() syscall\n"); 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci default: 21962306a36Sopenharmony_ci printf("bpf() syscall restriction has unknown value %ld\n", res); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void probe_jit_enable(void) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci long res; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* No support for C-style ouptut */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci res = read_procfs("/proc/sys/net/core/bpf_jit_enable"); 23162306a36Sopenharmony_ci if (json_output) { 23262306a36Sopenharmony_ci jsonw_int_field(json_wtr, "bpf_jit_enable", res); 23362306a36Sopenharmony_ci } else { 23462306a36Sopenharmony_ci switch (res) { 23562306a36Sopenharmony_ci case 0: 23662306a36Sopenharmony_ci printf("JIT compiler is disabled\n"); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case 1: 23962306a36Sopenharmony_ci printf("JIT compiler is enabled\n"); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case 2: 24262306a36Sopenharmony_ci printf("JIT compiler is enabled with debugging traces in kernel logs\n"); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case -1: 24562306a36Sopenharmony_ci printf("Unable to retrieve JIT-compiler status\n"); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci default: 24862306a36Sopenharmony_ci printf("JIT-compiler status has unknown value %ld\n", 24962306a36Sopenharmony_ci res); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void probe_jit_harden(void) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci long res; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* No support for C-style ouptut */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci res = read_procfs("/proc/sys/net/core/bpf_jit_harden"); 26162306a36Sopenharmony_ci if (json_output) { 26262306a36Sopenharmony_ci jsonw_int_field(json_wtr, "bpf_jit_harden", res); 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci switch (res) { 26562306a36Sopenharmony_ci case 0: 26662306a36Sopenharmony_ci printf("JIT compiler hardening is disabled\n"); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case 1: 26962306a36Sopenharmony_ci printf("JIT compiler hardening is enabled for unprivileged users\n"); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case 2: 27262306a36Sopenharmony_ci printf("JIT compiler hardening is enabled for all users\n"); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case -1: 27562306a36Sopenharmony_ci printf("Unable to retrieve JIT hardening status\n"); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci printf("JIT hardening status has unknown value %ld\n", 27962306a36Sopenharmony_ci res); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void probe_jit_kallsyms(void) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci long res; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* No support for C-style ouptut */ 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms"); 29162306a36Sopenharmony_ci if (json_output) { 29262306a36Sopenharmony_ci jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res); 29362306a36Sopenharmony_ci } else { 29462306a36Sopenharmony_ci switch (res) { 29562306a36Sopenharmony_ci case 0: 29662306a36Sopenharmony_ci printf("JIT compiler kallsyms exports are disabled\n"); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case 1: 29962306a36Sopenharmony_ci printf("JIT compiler kallsyms exports are enabled for root\n"); 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci case -1: 30262306a36Sopenharmony_ci printf("Unable to retrieve JIT kallsyms export status\n"); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci default: 30562306a36Sopenharmony_ci printf("JIT kallsyms exports status has unknown value %ld\n", res); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void probe_jit_limit(void) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci long res; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* No support for C-style ouptut */ 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci res = read_procfs("/proc/sys/net/core/bpf_jit_limit"); 31762306a36Sopenharmony_ci if (json_output) { 31862306a36Sopenharmony_ci jsonw_int_field(json_wtr, "bpf_jit_limit", res); 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci switch (res) { 32162306a36Sopenharmony_ci case -1: 32262306a36Sopenharmony_ci printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n"); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci default: 32562306a36Sopenharmony_ci printf("Global memory limit for JIT compiler for unprivileged users is %ld bytes\n", res); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic bool read_next_kernel_config_option(gzFile file, char *buf, size_t n, 33162306a36Sopenharmony_ci char **value) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci char *sep; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci while (gzgets(file, buf, n)) { 33662306a36Sopenharmony_ci if (strncmp(buf, "CONFIG_", 7)) 33762306a36Sopenharmony_ci continue; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci sep = strchr(buf, '='); 34062306a36Sopenharmony_ci if (!sep) 34162306a36Sopenharmony_ci continue; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Trim ending '\n' */ 34462306a36Sopenharmony_ci buf[strlen(buf) - 1] = '\0'; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Split on '=' and ensure that a value is present. */ 34762306a36Sopenharmony_ci *sep = '\0'; 34862306a36Sopenharmony_ci if (!sep[1]) 34962306a36Sopenharmony_ci continue; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci *value = sep + 1; 35262306a36Sopenharmony_ci return true; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return false; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void probe_kernel_image_config(const char *define_prefix) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci static const struct { 36162306a36Sopenharmony_ci const char * const name; 36262306a36Sopenharmony_ci bool macro_dump; 36362306a36Sopenharmony_ci } options[] = { 36462306a36Sopenharmony_ci /* Enable BPF */ 36562306a36Sopenharmony_ci { "CONFIG_BPF", }, 36662306a36Sopenharmony_ci /* Enable bpf() syscall */ 36762306a36Sopenharmony_ci { "CONFIG_BPF_SYSCALL", }, 36862306a36Sopenharmony_ci /* Does selected architecture support eBPF JIT compiler */ 36962306a36Sopenharmony_ci { "CONFIG_HAVE_EBPF_JIT", }, 37062306a36Sopenharmony_ci /* Compile eBPF JIT compiler */ 37162306a36Sopenharmony_ci { "CONFIG_BPF_JIT", }, 37262306a36Sopenharmony_ci /* Avoid compiling eBPF interpreter (use JIT only) */ 37362306a36Sopenharmony_ci { "CONFIG_BPF_JIT_ALWAYS_ON", }, 37462306a36Sopenharmony_ci /* Kernel BTF debug information available */ 37562306a36Sopenharmony_ci { "CONFIG_DEBUG_INFO_BTF", }, 37662306a36Sopenharmony_ci /* Kernel module BTF debug information available */ 37762306a36Sopenharmony_ci { "CONFIG_DEBUG_INFO_BTF_MODULES", }, 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* cgroups */ 38062306a36Sopenharmony_ci { "CONFIG_CGROUPS", }, 38162306a36Sopenharmony_ci /* BPF programs attached to cgroups */ 38262306a36Sopenharmony_ci { "CONFIG_CGROUP_BPF", }, 38362306a36Sopenharmony_ci /* bpf_get_cgroup_classid() helper */ 38462306a36Sopenharmony_ci { "CONFIG_CGROUP_NET_CLASSID", }, 38562306a36Sopenharmony_ci /* bpf_skb_{,ancestor_}cgroup_id() helpers */ 38662306a36Sopenharmony_ci { "CONFIG_SOCK_CGROUP_DATA", }, 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Tracing: attach BPF to kprobes, tracepoints, etc. */ 38962306a36Sopenharmony_ci { "CONFIG_BPF_EVENTS", }, 39062306a36Sopenharmony_ci /* Kprobes */ 39162306a36Sopenharmony_ci { "CONFIG_KPROBE_EVENTS", }, 39262306a36Sopenharmony_ci /* Uprobes */ 39362306a36Sopenharmony_ci { "CONFIG_UPROBE_EVENTS", }, 39462306a36Sopenharmony_ci /* Tracepoints */ 39562306a36Sopenharmony_ci { "CONFIG_TRACING", }, 39662306a36Sopenharmony_ci /* Syscall tracepoints */ 39762306a36Sopenharmony_ci { "CONFIG_FTRACE_SYSCALLS", }, 39862306a36Sopenharmony_ci /* bpf_override_return() helper support for selected arch */ 39962306a36Sopenharmony_ci { "CONFIG_FUNCTION_ERROR_INJECTION", }, 40062306a36Sopenharmony_ci /* bpf_override_return() helper */ 40162306a36Sopenharmony_ci { "CONFIG_BPF_KPROBE_OVERRIDE", }, 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Network */ 40462306a36Sopenharmony_ci { "CONFIG_NET", }, 40562306a36Sopenharmony_ci /* AF_XDP sockets */ 40662306a36Sopenharmony_ci { "CONFIG_XDP_SOCKETS", }, 40762306a36Sopenharmony_ci /* BPF_PROG_TYPE_LWT_* and related helpers */ 40862306a36Sopenharmony_ci { "CONFIG_LWTUNNEL_BPF", }, 40962306a36Sopenharmony_ci /* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */ 41062306a36Sopenharmony_ci { "CONFIG_NET_ACT_BPF", }, 41162306a36Sopenharmony_ci /* BPF_PROG_TYPE_SCHED_CLS, TC filters */ 41262306a36Sopenharmony_ci { "CONFIG_NET_CLS_BPF", }, 41362306a36Sopenharmony_ci /* TC clsact qdisc */ 41462306a36Sopenharmony_ci { "CONFIG_NET_CLS_ACT", }, 41562306a36Sopenharmony_ci /* Ingress filtering with TC */ 41662306a36Sopenharmony_ci { "CONFIG_NET_SCH_INGRESS", }, 41762306a36Sopenharmony_ci /* bpf_skb_get_xfrm_state() helper */ 41862306a36Sopenharmony_ci { "CONFIG_XFRM", }, 41962306a36Sopenharmony_ci /* bpf_get_route_realm() helper */ 42062306a36Sopenharmony_ci { "CONFIG_IP_ROUTE_CLASSID", }, 42162306a36Sopenharmony_ci /* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */ 42262306a36Sopenharmony_ci { "CONFIG_IPV6_SEG6_BPF", }, 42362306a36Sopenharmony_ci /* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */ 42462306a36Sopenharmony_ci { "CONFIG_BPF_LIRC_MODE2", }, 42562306a36Sopenharmony_ci /* BPF stream parser and BPF socket maps */ 42662306a36Sopenharmony_ci { "CONFIG_BPF_STREAM_PARSER", }, 42762306a36Sopenharmony_ci /* xt_bpf module for passing BPF programs to netfilter */ 42862306a36Sopenharmony_ci { "CONFIG_NETFILTER_XT_MATCH_BPF", }, 42962306a36Sopenharmony_ci /* bpfilter back-end for iptables */ 43062306a36Sopenharmony_ci { "CONFIG_BPFILTER", }, 43162306a36Sopenharmony_ci /* bpftilter module with "user mode helper" */ 43262306a36Sopenharmony_ci { "CONFIG_BPFILTER_UMH", }, 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* test_bpf module for BPF tests */ 43562306a36Sopenharmony_ci { "CONFIG_TEST_BPF", }, 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Misc configs useful in BPF C programs */ 43862306a36Sopenharmony_ci /* jiffies <-> sec conversion for bpf_jiffies64() helper */ 43962306a36Sopenharmony_ci { "CONFIG_HZ", true, } 44062306a36Sopenharmony_ci }; 44162306a36Sopenharmony_ci char *values[ARRAY_SIZE(options)] = { }; 44262306a36Sopenharmony_ci struct utsname utsn; 44362306a36Sopenharmony_ci char path[PATH_MAX]; 44462306a36Sopenharmony_ci gzFile file = NULL; 44562306a36Sopenharmony_ci char buf[4096]; 44662306a36Sopenharmony_ci char *value; 44762306a36Sopenharmony_ci size_t i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!uname(&utsn)) { 45062306a36Sopenharmony_ci snprintf(path, sizeof(path), "/boot/config-%s", utsn.release); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* gzopen also accepts uncompressed files. */ 45362306a36Sopenharmony_ci file = gzopen(path, "r"); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!file) { 45762306a36Sopenharmony_ci /* Some distributions build with CONFIG_IKCONFIG=y and put the 45862306a36Sopenharmony_ci * config file at /proc/config.gz. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci file = gzopen("/proc/config.gz", "r"); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci if (!file) { 46362306a36Sopenharmony_ci p_info("skipping kernel config, can't open file: %s", 46462306a36Sopenharmony_ci strerror(errno)); 46562306a36Sopenharmony_ci goto end_parse; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci /* Sanity checks */ 46862306a36Sopenharmony_ci if (!gzgets(file, buf, sizeof(buf)) || 46962306a36Sopenharmony_ci !gzgets(file, buf, sizeof(buf))) { 47062306a36Sopenharmony_ci p_info("skipping kernel config, can't read from file: %s", 47162306a36Sopenharmony_ci strerror(errno)); 47262306a36Sopenharmony_ci goto end_parse; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) { 47562306a36Sopenharmony_ci p_info("skipping kernel config, can't find correct file"); 47662306a36Sopenharmony_ci goto end_parse; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci while (read_next_kernel_config_option(file, buf, sizeof(buf), &value)) { 48062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(options); i++) { 48162306a36Sopenharmony_ci if ((define_prefix && !options[i].macro_dump) || 48262306a36Sopenharmony_ci values[i] || strcmp(buf, options[i].name)) 48362306a36Sopenharmony_ci continue; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci values[i] = strdup(value); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(options); i++) { 49062306a36Sopenharmony_ci if (define_prefix && !options[i].macro_dump) 49162306a36Sopenharmony_ci continue; 49262306a36Sopenharmony_ci print_kernel_option(options[i].name, values[i], define_prefix); 49362306a36Sopenharmony_ci free(values[i]); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ciend_parse: 49762306a36Sopenharmony_ci if (file) 49862306a36Sopenharmony_ci gzclose(file); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic bool probe_bpf_syscall(const char *define_prefix) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci bool res; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci bpf_prog_load(BPF_PROG_TYPE_UNSPEC, NULL, NULL, NULL, 0, NULL); 50662306a36Sopenharmony_ci res = (errno != ENOSYS); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci print_bool_feature("have_bpf_syscall", 50962306a36Sopenharmony_ci "bpf() syscall", 51062306a36Sopenharmony_ci "BPF_SYSCALL", 51162306a36Sopenharmony_ci res, define_prefix); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return res; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic bool 51762306a36Sopenharmony_ciprobe_prog_load_ifindex(enum bpf_prog_type prog_type, 51862306a36Sopenharmony_ci const struct bpf_insn *insns, size_t insns_cnt, 51962306a36Sopenharmony_ci char *log_buf, size_t log_buf_sz, 52062306a36Sopenharmony_ci __u32 ifindex) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci LIBBPF_OPTS(bpf_prog_load_opts, opts, 52362306a36Sopenharmony_ci .log_buf = log_buf, 52462306a36Sopenharmony_ci .log_size = log_buf_sz, 52562306a36Sopenharmony_ci .log_level = log_buf ? 1 : 0, 52662306a36Sopenharmony_ci .prog_ifindex = ifindex, 52762306a36Sopenharmony_ci ); 52862306a36Sopenharmony_ci int fd; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci errno = 0; 53162306a36Sopenharmony_ci fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts); 53262306a36Sopenharmony_ci if (fd >= 0) 53362306a36Sopenharmony_ci close(fd); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return fd >= 0 && errno != EINVAL && errno != EOPNOTSUPP; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci /* nfp returns -EINVAL on exit(0) with TC offload */ 54162306a36Sopenharmony_ci struct bpf_insn insns[2] = { 54262306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 2), 54362306a36Sopenharmony_ci BPF_EXIT_INSN() 54462306a36Sopenharmony_ci }; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), 54762306a36Sopenharmony_ci NULL, 0, ifindex); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void 55162306a36Sopenharmony_ciprobe_prog_type(enum bpf_prog_type prog_type, const char *prog_type_str, 55262306a36Sopenharmony_ci bool *supported_types, const char *define_prefix, __u32 ifindex) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci char feat_name[128], plain_desc[128], define_name[128]; 55562306a36Sopenharmony_ci const char *plain_comment = "eBPF program_type "; 55662306a36Sopenharmony_ci size_t maxlen; 55762306a36Sopenharmony_ci bool res; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (ifindex) { 56062306a36Sopenharmony_ci switch (prog_type) { 56162306a36Sopenharmony_ci case BPF_PROG_TYPE_SCHED_CLS: 56262306a36Sopenharmony_ci case BPF_PROG_TYPE_XDP: 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci default: 56562306a36Sopenharmony_ci return; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci res = probe_prog_type_ifindex(prog_type, ifindex); 56962306a36Sopenharmony_ci } else { 57062306a36Sopenharmony_ci res = libbpf_probe_bpf_prog_type(prog_type, NULL) > 0; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci#ifdef USE_LIBCAP 57462306a36Sopenharmony_ci /* Probe may succeed even if program load fails, for unprivileged users 57562306a36Sopenharmony_ci * check that we did not fail because of insufficient permissions 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci if (run_as_unprivileged && errno == EPERM) 57862306a36Sopenharmony_ci res = false; 57962306a36Sopenharmony_ci#endif 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci supported_types[prog_type] |= res; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; 58462306a36Sopenharmony_ci if (strlen(prog_type_str) > maxlen) { 58562306a36Sopenharmony_ci p_info("program type name too long"); 58662306a36Sopenharmony_ci return; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci sprintf(feat_name, "have_%s_prog_type", prog_type_str); 59062306a36Sopenharmony_ci sprintf(define_name, "%s_prog_type", prog_type_str); 59162306a36Sopenharmony_ci uppercase(define_name, sizeof(define_name)); 59262306a36Sopenharmony_ci sprintf(plain_desc, "%s%s", plain_comment, prog_type_str); 59362306a36Sopenharmony_ci print_bool_feature(feat_name, plain_desc, define_name, res, 59462306a36Sopenharmony_ci define_prefix); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci LIBBPF_OPTS(bpf_map_create_opts, opts); 60062306a36Sopenharmony_ci int key_size, value_size, max_entries; 60162306a36Sopenharmony_ci int fd; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci opts.map_ifindex = ifindex; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci key_size = sizeof(__u32); 60662306a36Sopenharmony_ci value_size = sizeof(__u32); 60762306a36Sopenharmony_ci max_entries = 1; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries, 61062306a36Sopenharmony_ci &opts); 61162306a36Sopenharmony_ci if (fd >= 0) 61262306a36Sopenharmony_ci close(fd); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return fd >= 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void 61862306a36Sopenharmony_ciprobe_map_type(enum bpf_map_type map_type, char const *map_type_str, 61962306a36Sopenharmony_ci const char *define_prefix, __u32 ifindex) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci char feat_name[128], plain_desc[128], define_name[128]; 62262306a36Sopenharmony_ci const char *plain_comment = "eBPF map_type "; 62362306a36Sopenharmony_ci size_t maxlen; 62462306a36Sopenharmony_ci bool res; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (ifindex) { 62762306a36Sopenharmony_ci switch (map_type) { 62862306a36Sopenharmony_ci case BPF_MAP_TYPE_HASH: 62962306a36Sopenharmony_ci case BPF_MAP_TYPE_ARRAY: 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci default: 63262306a36Sopenharmony_ci return; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci res = probe_map_type_ifindex(map_type, ifindex); 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci res = libbpf_probe_bpf_map_type(map_type, NULL) > 0; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Probe result depends on the success of map creation, no additional 64162306a36Sopenharmony_ci * check required for unprivileged users 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; 64562306a36Sopenharmony_ci if (strlen(map_type_str) > maxlen) { 64662306a36Sopenharmony_ci p_info("map type name too long"); 64762306a36Sopenharmony_ci return; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci sprintf(feat_name, "have_%s_map_type", map_type_str); 65162306a36Sopenharmony_ci sprintf(define_name, "%s_map_type", map_type_str); 65262306a36Sopenharmony_ci uppercase(define_name, sizeof(define_name)); 65362306a36Sopenharmony_ci sprintf(plain_desc, "%s%s", plain_comment, map_type_str); 65462306a36Sopenharmony_ci print_bool_feature(feat_name, plain_desc, define_name, res, 65562306a36Sopenharmony_ci define_prefix); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic bool 65962306a36Sopenharmony_ciprobe_helper_ifindex(enum bpf_func_id id, enum bpf_prog_type prog_type, 66062306a36Sopenharmony_ci __u32 ifindex) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct bpf_insn insns[2] = { 66362306a36Sopenharmony_ci BPF_EMIT_CALL(id), 66462306a36Sopenharmony_ci BPF_EXIT_INSN() 66562306a36Sopenharmony_ci }; 66662306a36Sopenharmony_ci char buf[4096] = {}; 66762306a36Sopenharmony_ci bool res; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), buf, 67062306a36Sopenharmony_ci sizeof(buf), ifindex); 67162306a36Sopenharmony_ci res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci switch (get_vendor_id(ifindex)) { 67462306a36Sopenharmony_ci case 0x19ee: /* Netronome specific */ 67562306a36Sopenharmony_ci res = res && !grep(buf, "not supported by FW") && 67662306a36Sopenharmony_ci !grep(buf, "unsupported function id"); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci default: 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return res; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic bool 68662306a36Sopenharmony_ciprobe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type, 68762306a36Sopenharmony_ci const char *define_prefix, unsigned int id, 68862306a36Sopenharmony_ci const char *ptype_name, __u32 ifindex) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci bool res = false; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (supported_type) { 69362306a36Sopenharmony_ci if (ifindex) 69462306a36Sopenharmony_ci res = probe_helper_ifindex(id, prog_type, ifindex); 69562306a36Sopenharmony_ci else 69662306a36Sopenharmony_ci res = libbpf_probe_bpf_helper(prog_type, id, NULL) > 0; 69762306a36Sopenharmony_ci#ifdef USE_LIBCAP 69862306a36Sopenharmony_ci /* Probe may succeed even if program load fails, for 69962306a36Sopenharmony_ci * unprivileged users check that we did not fail because of 70062306a36Sopenharmony_ci * insufficient permissions 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci if (run_as_unprivileged && errno == EPERM) 70362306a36Sopenharmony_ci res = false; 70462306a36Sopenharmony_ci#endif 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (json_output) { 70862306a36Sopenharmony_ci if (res) 70962306a36Sopenharmony_ci jsonw_string(json_wtr, helper_name[id]); 71062306a36Sopenharmony_ci } else if (define_prefix) { 71162306a36Sopenharmony_ci printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n", 71262306a36Sopenharmony_ci define_prefix, ptype_name, helper_name[id], 71362306a36Sopenharmony_ci res ? "1" : "0"); 71462306a36Sopenharmony_ci } else { 71562306a36Sopenharmony_ci if (res) 71662306a36Sopenharmony_ci printf("\n\t- %s", helper_name[id]); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return res; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic void 72362306a36Sopenharmony_ciprobe_helpers_for_progtype(enum bpf_prog_type prog_type, 72462306a36Sopenharmony_ci const char *prog_type_str, bool supported_type, 72562306a36Sopenharmony_ci const char *define_prefix, __u32 ifindex) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci char feat_name[128]; 72862306a36Sopenharmony_ci unsigned int id; 72962306a36Sopenharmony_ci bool probe_res = false; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (ifindex) 73262306a36Sopenharmony_ci /* Only test helpers for offload-able program types */ 73362306a36Sopenharmony_ci switch (prog_type) { 73462306a36Sopenharmony_ci case BPF_PROG_TYPE_SCHED_CLS: 73562306a36Sopenharmony_ci case BPF_PROG_TYPE_XDP: 73662306a36Sopenharmony_ci break; 73762306a36Sopenharmony_ci default: 73862306a36Sopenharmony_ci return; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (json_output) { 74262306a36Sopenharmony_ci sprintf(feat_name, "%s_available_helpers", prog_type_str); 74362306a36Sopenharmony_ci jsonw_name(json_wtr, feat_name); 74462306a36Sopenharmony_ci jsonw_start_array(json_wtr); 74562306a36Sopenharmony_ci } else if (!define_prefix) { 74662306a36Sopenharmony_ci printf("eBPF helpers supported for program type %s:", 74762306a36Sopenharmony_ci prog_type_str); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci for (id = 1; id < ARRAY_SIZE(helper_name); id++) { 75162306a36Sopenharmony_ci /* Skip helper functions which emit dmesg messages when not in 75262306a36Sopenharmony_ci * the full mode. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci switch (id) { 75562306a36Sopenharmony_ci case BPF_FUNC_trace_printk: 75662306a36Sopenharmony_ci case BPF_FUNC_trace_vprintk: 75762306a36Sopenharmony_ci case BPF_FUNC_probe_write_user: 75862306a36Sopenharmony_ci if (!full_mode) 75962306a36Sopenharmony_ci continue; 76062306a36Sopenharmony_ci fallthrough; 76162306a36Sopenharmony_ci default: 76262306a36Sopenharmony_ci probe_res |= probe_helper_for_progtype(prog_type, supported_type, 76362306a36Sopenharmony_ci define_prefix, id, prog_type_str, 76462306a36Sopenharmony_ci ifindex); 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (json_output) 76962306a36Sopenharmony_ci jsonw_end_array(json_wtr); 77062306a36Sopenharmony_ci else if (!define_prefix) { 77162306a36Sopenharmony_ci printf("\n"); 77262306a36Sopenharmony_ci if (!probe_res) { 77362306a36Sopenharmony_ci if (!supported_type) 77462306a36Sopenharmony_ci printf("\tProgram type not supported\n"); 77562306a36Sopenharmony_ci else 77662306a36Sopenharmony_ci printf("\tCould not determine which helpers are available\n"); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic void 78462306a36Sopenharmony_ciprobe_misc_feature(struct bpf_insn *insns, size_t len, 78562306a36Sopenharmony_ci const char *define_prefix, __u32 ifindex, 78662306a36Sopenharmony_ci const char *feat_name, const char *plain_name, 78762306a36Sopenharmony_ci const char *define_name) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci LIBBPF_OPTS(bpf_prog_load_opts, opts, 79062306a36Sopenharmony_ci .prog_ifindex = ifindex, 79162306a36Sopenharmony_ci ); 79262306a36Sopenharmony_ci bool res; 79362306a36Sopenharmony_ci int fd; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci errno = 0; 79662306a36Sopenharmony_ci fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", 79762306a36Sopenharmony_ci insns, len, &opts); 79862306a36Sopenharmony_ci res = fd >= 0 || !errno; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (fd >= 0) 80162306a36Sopenharmony_ci close(fd); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci print_bool_feature(feat_name, plain_name, define_name, res, 80462306a36Sopenharmony_ci define_prefix); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/* 80862306a36Sopenharmony_ci * Probe for availability of kernel commit (5.3): 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * c04c0d2b968a ("bpf: increase complexity limit and maximum program size") 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_cistatic void probe_large_insn_limit(const char *define_prefix, __u32 ifindex) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct bpf_insn insns[BPF_MAXINSNS + 1]; 81562306a36Sopenharmony_ci int i; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci for (i = 0; i < BPF_MAXINSNS; i++) 81862306a36Sopenharmony_ci insns[i] = BPF_MOV64_IMM(BPF_REG_0, 1); 81962306a36Sopenharmony_ci insns[BPF_MAXINSNS] = BPF_EXIT_INSN(); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci probe_misc_feature(insns, ARRAY_SIZE(insns), 82262306a36Sopenharmony_ci define_prefix, ifindex, 82362306a36Sopenharmony_ci "have_large_insn_limit", 82462306a36Sopenharmony_ci "Large program size limit", 82562306a36Sopenharmony_ci "LARGE_INSN_LIMIT"); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci/* 82962306a36Sopenharmony_ci * Probe for bounded loop support introduced in commit 2589726d12a1 83062306a36Sopenharmony_ci * ("bpf: introduce bounded loops"). 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_cistatic void 83362306a36Sopenharmony_ciprobe_bounded_loops(const char *define_prefix, __u32 ifindex) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct bpf_insn insns[4] = { 83662306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 10), 83762306a36Sopenharmony_ci BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, 1), 83862306a36Sopenharmony_ci BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, -2), 83962306a36Sopenharmony_ci BPF_EXIT_INSN() 84062306a36Sopenharmony_ci }; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci probe_misc_feature(insns, ARRAY_SIZE(insns), 84362306a36Sopenharmony_ci define_prefix, ifindex, 84462306a36Sopenharmony_ci "have_bounded_loops", 84562306a36Sopenharmony_ci "Bounded loop support", 84662306a36Sopenharmony_ci "BOUNDED_LOOPS"); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/* 85062306a36Sopenharmony_ci * Probe for the v2 instruction set extension introduced in commit 92b31a9af73b 85162306a36Sopenharmony_ci * ("bpf: add BPF_J{LT,LE,SLT,SLE} instructions"). 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_cistatic void 85462306a36Sopenharmony_ciprobe_v2_isa_extension(const char *define_prefix, __u32 ifindex) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct bpf_insn insns[4] = { 85762306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 0), 85862306a36Sopenharmony_ci BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 0, 1), 85962306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 1), 86062306a36Sopenharmony_ci BPF_EXIT_INSN() 86162306a36Sopenharmony_ci }; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci probe_misc_feature(insns, ARRAY_SIZE(insns), 86462306a36Sopenharmony_ci define_prefix, ifindex, 86562306a36Sopenharmony_ci "have_v2_isa_extension", 86662306a36Sopenharmony_ci "ISA extension v2", 86762306a36Sopenharmony_ci "V2_ISA_EXTENSION"); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/* 87162306a36Sopenharmony_ci * Probe for the v3 instruction set extension introduced in commit 092ed0968bb6 87262306a36Sopenharmony_ci * ("bpf: verifier support JMP32"). 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_cistatic void 87562306a36Sopenharmony_ciprobe_v3_isa_extension(const char *define_prefix, __u32 ifindex) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct bpf_insn insns[4] = { 87862306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 0), 87962306a36Sopenharmony_ci BPF_JMP32_IMM(BPF_JLT, BPF_REG_0, 0, 1), 88062306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 1), 88162306a36Sopenharmony_ci BPF_EXIT_INSN() 88262306a36Sopenharmony_ci }; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci probe_misc_feature(insns, ARRAY_SIZE(insns), 88562306a36Sopenharmony_ci define_prefix, ifindex, 88662306a36Sopenharmony_ci "have_v3_isa_extension", 88762306a36Sopenharmony_ci "ISA extension v3", 88862306a36Sopenharmony_ci "V3_ISA_EXTENSION"); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic void 89262306a36Sopenharmony_cisection_system_config(enum probe_component target, const char *define_prefix) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci switch (target) { 89562306a36Sopenharmony_ci case COMPONENT_KERNEL: 89662306a36Sopenharmony_ci case COMPONENT_UNSPEC: 89762306a36Sopenharmony_ci print_start_section("system_config", 89862306a36Sopenharmony_ci "Scanning system configuration...", 89962306a36Sopenharmony_ci "/*** Misc kernel config items ***/", 90062306a36Sopenharmony_ci define_prefix); 90162306a36Sopenharmony_ci if (!define_prefix) { 90262306a36Sopenharmony_ci if (check_procfs()) { 90362306a36Sopenharmony_ci probe_unprivileged_disabled(); 90462306a36Sopenharmony_ci probe_jit_enable(); 90562306a36Sopenharmony_ci probe_jit_harden(); 90662306a36Sopenharmony_ci probe_jit_kallsyms(); 90762306a36Sopenharmony_ci probe_jit_limit(); 90862306a36Sopenharmony_ci } else { 90962306a36Sopenharmony_ci p_info("/* procfs not mounted, skipping related probes */"); 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci probe_kernel_image_config(define_prefix); 91362306a36Sopenharmony_ci print_end_section(); 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci default: 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic bool section_syscall_config(const char *define_prefix) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci bool res; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci print_start_section("syscall_config", 92562306a36Sopenharmony_ci "Scanning system call availability...", 92662306a36Sopenharmony_ci "/*** System call availability ***/", 92762306a36Sopenharmony_ci define_prefix); 92862306a36Sopenharmony_ci res = probe_bpf_syscall(define_prefix); 92962306a36Sopenharmony_ci print_end_section(); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return res; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic void 93562306a36Sopenharmony_cisection_program_types(bool *supported_types, const char *define_prefix, 93662306a36Sopenharmony_ci __u32 ifindex) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci unsigned int prog_type = BPF_PROG_TYPE_UNSPEC; 93962306a36Sopenharmony_ci const char *prog_type_str; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci print_start_section("program_types", 94262306a36Sopenharmony_ci "Scanning eBPF program types...", 94362306a36Sopenharmony_ci "/*** eBPF program types ***/", 94462306a36Sopenharmony_ci define_prefix); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci while (true) { 94762306a36Sopenharmony_ci prog_type++; 94862306a36Sopenharmony_ci prog_type_str = libbpf_bpf_prog_type_str(prog_type); 94962306a36Sopenharmony_ci /* libbpf will return NULL for variants unknown to it. */ 95062306a36Sopenharmony_ci if (!prog_type_str) 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci probe_prog_type(prog_type, prog_type_str, supported_types, define_prefix, 95462306a36Sopenharmony_ci ifindex); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci print_end_section(); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic void section_map_types(const char *define_prefix, __u32 ifindex) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci unsigned int map_type = BPF_MAP_TYPE_UNSPEC; 96362306a36Sopenharmony_ci const char *map_type_str; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci print_start_section("map_types", 96662306a36Sopenharmony_ci "Scanning eBPF map types...", 96762306a36Sopenharmony_ci "/*** eBPF map types ***/", 96862306a36Sopenharmony_ci define_prefix); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci while (true) { 97162306a36Sopenharmony_ci map_type++; 97262306a36Sopenharmony_ci map_type_str = libbpf_bpf_map_type_str(map_type); 97362306a36Sopenharmony_ci /* libbpf will return NULL for variants unknown to it. */ 97462306a36Sopenharmony_ci if (!map_type_str) 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci probe_map_type(map_type, map_type_str, define_prefix, ifindex); 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci print_end_section(); 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic void 98462306a36Sopenharmony_cisection_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci unsigned int prog_type = BPF_PROG_TYPE_UNSPEC; 98762306a36Sopenharmony_ci const char *prog_type_str; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci print_start_section("helpers", 99062306a36Sopenharmony_ci "Scanning eBPF helper functions...", 99162306a36Sopenharmony_ci "/*** eBPF helper functions ***/", 99262306a36Sopenharmony_ci define_prefix); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (define_prefix) 99562306a36Sopenharmony_ci printf("/*\n" 99662306a36Sopenharmony_ci " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n" 99762306a36Sopenharmony_ci " * to determine if <helper_name> is available for <prog_type_name>,\n" 99862306a36Sopenharmony_ci " * e.g.\n" 99962306a36Sopenharmony_ci " * #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n" 100062306a36Sopenharmony_ci " * // do stuff with this helper\n" 100162306a36Sopenharmony_ci " * #elif\n" 100262306a36Sopenharmony_ci " * // use a workaround\n" 100362306a36Sopenharmony_ci " * #endif\n" 100462306a36Sopenharmony_ci " */\n" 100562306a36Sopenharmony_ci "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper) \\\n" 100662306a36Sopenharmony_ci " %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n", 100762306a36Sopenharmony_ci define_prefix, define_prefix, define_prefix, 100862306a36Sopenharmony_ci define_prefix); 100962306a36Sopenharmony_ci while (true) { 101062306a36Sopenharmony_ci prog_type++; 101162306a36Sopenharmony_ci prog_type_str = libbpf_bpf_prog_type_str(prog_type); 101262306a36Sopenharmony_ci /* libbpf will return NULL for variants unknown to it. */ 101362306a36Sopenharmony_ci if (!prog_type_str) 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci probe_helpers_for_progtype(prog_type, prog_type_str, 101762306a36Sopenharmony_ci supported_types[prog_type], 101862306a36Sopenharmony_ci define_prefix, 101962306a36Sopenharmony_ci ifindex); 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci print_end_section(); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic void section_misc(const char *define_prefix, __u32 ifindex) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci print_start_section("misc", 102862306a36Sopenharmony_ci "Scanning miscellaneous eBPF features...", 102962306a36Sopenharmony_ci "/*** eBPF misc features ***/", 103062306a36Sopenharmony_ci define_prefix); 103162306a36Sopenharmony_ci probe_large_insn_limit(define_prefix, ifindex); 103262306a36Sopenharmony_ci probe_bounded_loops(define_prefix, ifindex); 103362306a36Sopenharmony_ci probe_v2_isa_extension(define_prefix, ifindex); 103462306a36Sopenharmony_ci probe_v3_isa_extension(define_prefix, ifindex); 103562306a36Sopenharmony_ci print_end_section(); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci#ifdef USE_LIBCAP 103962306a36Sopenharmony_ci#define capability(c) { c, false, #c } 104062306a36Sopenharmony_ci#define capability_msg(a, i) a[i].set ? "" : a[i].name, a[i].set ? "" : ", " 104162306a36Sopenharmony_ci#endif 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int handle_perms(void) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci#ifdef USE_LIBCAP 104662306a36Sopenharmony_ci struct { 104762306a36Sopenharmony_ci cap_value_t cap; 104862306a36Sopenharmony_ci bool set; 104962306a36Sopenharmony_ci char name[14]; /* strlen("CAP_SYS_ADMIN") */ 105062306a36Sopenharmony_ci } bpf_caps[] = { 105162306a36Sopenharmony_ci capability(CAP_SYS_ADMIN), 105262306a36Sopenharmony_ci#ifdef CAP_BPF 105362306a36Sopenharmony_ci capability(CAP_BPF), 105462306a36Sopenharmony_ci capability(CAP_NET_ADMIN), 105562306a36Sopenharmony_ci capability(CAP_PERFMON), 105662306a36Sopenharmony_ci#endif 105762306a36Sopenharmony_ci }; 105862306a36Sopenharmony_ci cap_value_t cap_list[ARRAY_SIZE(bpf_caps)]; 105962306a36Sopenharmony_ci unsigned int i, nb_bpf_caps = 0; 106062306a36Sopenharmony_ci bool cap_sys_admin_only = true; 106162306a36Sopenharmony_ci cap_flag_value_t val; 106262306a36Sopenharmony_ci int res = -1; 106362306a36Sopenharmony_ci cap_t caps; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci caps = cap_get_proc(); 106662306a36Sopenharmony_ci if (!caps) { 106762306a36Sopenharmony_ci p_err("failed to get capabilities for process: %s", 106862306a36Sopenharmony_ci strerror(errno)); 106962306a36Sopenharmony_ci return -1; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci#ifdef CAP_BPF 107362306a36Sopenharmony_ci if (CAP_IS_SUPPORTED(CAP_BPF)) 107462306a36Sopenharmony_ci cap_sys_admin_only = false; 107562306a36Sopenharmony_ci#endif 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bpf_caps); i++) { 107862306a36Sopenharmony_ci const char *cap_name = bpf_caps[i].name; 107962306a36Sopenharmony_ci cap_value_t cap = bpf_caps[i].cap; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val)) { 108262306a36Sopenharmony_ci p_err("bug: failed to retrieve %s status: %s", cap_name, 108362306a36Sopenharmony_ci strerror(errno)); 108462306a36Sopenharmony_ci goto exit_free; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (val == CAP_SET) { 108862306a36Sopenharmony_ci bpf_caps[i].set = true; 108962306a36Sopenharmony_ci cap_list[nb_bpf_caps++] = cap; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (cap_sys_admin_only) 109362306a36Sopenharmony_ci /* System does not know about CAP_BPF, meaning that 109462306a36Sopenharmony_ci * CAP_SYS_ADMIN is the only capability required. We 109562306a36Sopenharmony_ci * just checked it, break. 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ci break; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if ((run_as_unprivileged && !nb_bpf_caps) || 110162306a36Sopenharmony_ci (!run_as_unprivileged && nb_bpf_caps == ARRAY_SIZE(bpf_caps)) || 110262306a36Sopenharmony_ci (!run_as_unprivileged && cap_sys_admin_only && nb_bpf_caps)) { 110362306a36Sopenharmony_ci /* We are all good, exit now */ 110462306a36Sopenharmony_ci res = 0; 110562306a36Sopenharmony_ci goto exit_free; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (!run_as_unprivileged) { 110962306a36Sopenharmony_ci if (cap_sys_admin_only) 111062306a36Sopenharmony_ci p_err("missing %s, required for full feature probing; run as root or use 'unprivileged'", 111162306a36Sopenharmony_ci bpf_caps[0].name); 111262306a36Sopenharmony_ci else 111362306a36Sopenharmony_ci p_err("missing %s%s%s%s%s%s%s%srequired for full feature probing; run as root or use 'unprivileged'", 111462306a36Sopenharmony_ci capability_msg(bpf_caps, 0), 111562306a36Sopenharmony_ci#ifdef CAP_BPF 111662306a36Sopenharmony_ci capability_msg(bpf_caps, 1), 111762306a36Sopenharmony_ci capability_msg(bpf_caps, 2), 111862306a36Sopenharmony_ci capability_msg(bpf_caps, 3) 111962306a36Sopenharmony_ci#else 112062306a36Sopenharmony_ci "", "", "", "", "", "" 112162306a36Sopenharmony_ci#endif /* CAP_BPF */ 112262306a36Sopenharmony_ci ); 112362306a36Sopenharmony_ci goto exit_free; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* if (run_as_unprivileged && nb_bpf_caps > 0), drop capabilities. */ 112762306a36Sopenharmony_ci if (cap_set_flag(caps, CAP_EFFECTIVE, nb_bpf_caps, cap_list, 112862306a36Sopenharmony_ci CAP_CLEAR)) { 112962306a36Sopenharmony_ci p_err("bug: failed to clear capabilities: %s", strerror(errno)); 113062306a36Sopenharmony_ci goto exit_free; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (cap_set_proc(caps)) { 113462306a36Sopenharmony_ci p_err("failed to drop capabilities: %s", strerror(errno)); 113562306a36Sopenharmony_ci goto exit_free; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci res = 0; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ciexit_free: 114162306a36Sopenharmony_ci if (cap_free(caps) && !res) { 114262306a36Sopenharmony_ci p_err("failed to clear storage object for capabilities: %s", 114362306a36Sopenharmony_ci strerror(errno)); 114462306a36Sopenharmony_ci res = -1; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return res; 114862306a36Sopenharmony_ci#else 114962306a36Sopenharmony_ci /* Detection assumes user has specific privileges. 115062306a36Sopenharmony_ci * We do not use libcap so let's approximate, and restrict usage to 115162306a36Sopenharmony_ci * root user only. 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci if (geteuid()) { 115462306a36Sopenharmony_ci p_err("full feature probing requires root privileges"); 115562306a36Sopenharmony_ci return -1; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci#endif /* USE_LIBCAP */ 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic int do_probe(int argc, char **argv) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci enum probe_component target = COMPONENT_UNSPEC; 116562306a36Sopenharmony_ci const char *define_prefix = NULL; 116662306a36Sopenharmony_ci bool supported_types[128] = {}; 116762306a36Sopenharmony_ci __u32 ifindex = 0; 116862306a36Sopenharmony_ci char *ifname; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci set_max_rlimit(); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci while (argc) { 117362306a36Sopenharmony_ci if (is_prefix(*argv, "kernel")) { 117462306a36Sopenharmony_ci if (target != COMPONENT_UNSPEC) { 117562306a36Sopenharmony_ci p_err("component to probe already specified"); 117662306a36Sopenharmony_ci return -1; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci target = COMPONENT_KERNEL; 117962306a36Sopenharmony_ci NEXT_ARG(); 118062306a36Sopenharmony_ci } else if (is_prefix(*argv, "dev")) { 118162306a36Sopenharmony_ci NEXT_ARG(); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (target != COMPONENT_UNSPEC || ifindex) { 118462306a36Sopenharmony_ci p_err("component to probe already specified"); 118562306a36Sopenharmony_ci return -1; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci if (!REQ_ARGS(1)) 118862306a36Sopenharmony_ci return -1; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci target = COMPONENT_DEVICE; 119162306a36Sopenharmony_ci ifname = GET_ARG(); 119262306a36Sopenharmony_ci ifindex = if_nametoindex(ifname); 119362306a36Sopenharmony_ci if (!ifindex) { 119462306a36Sopenharmony_ci p_err("unrecognized netdevice '%s': %s", ifname, 119562306a36Sopenharmony_ci strerror(errno)); 119662306a36Sopenharmony_ci return -1; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci } else if (is_prefix(*argv, "full")) { 119962306a36Sopenharmony_ci full_mode = true; 120062306a36Sopenharmony_ci NEXT_ARG(); 120162306a36Sopenharmony_ci } else if (is_prefix(*argv, "macros") && !define_prefix) { 120262306a36Sopenharmony_ci define_prefix = ""; 120362306a36Sopenharmony_ci NEXT_ARG(); 120462306a36Sopenharmony_ci } else if (is_prefix(*argv, "prefix")) { 120562306a36Sopenharmony_ci if (!define_prefix) { 120662306a36Sopenharmony_ci p_err("'prefix' argument can only be use after 'macros'"); 120762306a36Sopenharmony_ci return -1; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci if (strcmp(define_prefix, "")) { 121062306a36Sopenharmony_ci p_err("'prefix' already defined"); 121162306a36Sopenharmony_ci return -1; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci NEXT_ARG(); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (!REQ_ARGS(1)) 121662306a36Sopenharmony_ci return -1; 121762306a36Sopenharmony_ci define_prefix = GET_ARG(); 121862306a36Sopenharmony_ci } else if (is_prefix(*argv, "unprivileged")) { 121962306a36Sopenharmony_ci#ifdef USE_LIBCAP 122062306a36Sopenharmony_ci run_as_unprivileged = true; 122162306a36Sopenharmony_ci NEXT_ARG(); 122262306a36Sopenharmony_ci#else 122362306a36Sopenharmony_ci p_err("unprivileged run not supported, recompile bpftool with libcap"); 122462306a36Sopenharmony_ci return -1; 122562306a36Sopenharmony_ci#endif 122662306a36Sopenharmony_ci } else { 122762306a36Sopenharmony_ci p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?", 122862306a36Sopenharmony_ci *argv); 122962306a36Sopenharmony_ci return -1; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Full feature detection requires specific privileges. 123462306a36Sopenharmony_ci * Let's approximate, and warn if user is not root. 123562306a36Sopenharmony_ci */ 123662306a36Sopenharmony_ci if (handle_perms()) 123762306a36Sopenharmony_ci return -1; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (json_output) { 124062306a36Sopenharmony_ci define_prefix = NULL; 124162306a36Sopenharmony_ci jsonw_start_object(json_wtr); 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci section_system_config(target, define_prefix); 124562306a36Sopenharmony_ci if (!section_syscall_config(define_prefix)) 124662306a36Sopenharmony_ci /* bpf() syscall unavailable, don't probe other BPF features */ 124762306a36Sopenharmony_ci goto exit_close_json; 124862306a36Sopenharmony_ci section_program_types(supported_types, define_prefix, ifindex); 124962306a36Sopenharmony_ci section_map_types(define_prefix, ifindex); 125062306a36Sopenharmony_ci section_helpers(supported_types, define_prefix, ifindex); 125162306a36Sopenharmony_ci section_misc(define_prefix, ifindex); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ciexit_close_json: 125462306a36Sopenharmony_ci if (json_output) 125562306a36Sopenharmony_ci /* End root object */ 125662306a36Sopenharmony_ci jsonw_end_object(json_wtr); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci return 0; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic const char *get_helper_name(unsigned int id) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci if (id >= ARRAY_SIZE(helper_name)) 126462306a36Sopenharmony_ci return NULL; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return helper_name[id]; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int do_list_builtins(int argc, char **argv) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci const char *(*get_name)(unsigned int id); 127262306a36Sopenharmony_ci unsigned int id = 0; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (argc < 1) 127562306a36Sopenharmony_ci usage(); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (is_prefix(*argv, "prog_types")) { 127862306a36Sopenharmony_ci get_name = (const char *(*)(unsigned int))libbpf_bpf_prog_type_str; 127962306a36Sopenharmony_ci } else if (is_prefix(*argv, "map_types")) { 128062306a36Sopenharmony_ci get_name = (const char *(*)(unsigned int))libbpf_bpf_map_type_str; 128162306a36Sopenharmony_ci } else if (is_prefix(*argv, "attach_types")) { 128262306a36Sopenharmony_ci get_name = (const char *(*)(unsigned int))libbpf_bpf_attach_type_str; 128362306a36Sopenharmony_ci } else if (is_prefix(*argv, "link_types")) { 128462306a36Sopenharmony_ci get_name = (const char *(*)(unsigned int))libbpf_bpf_link_type_str; 128562306a36Sopenharmony_ci } else if (is_prefix(*argv, "helpers")) { 128662306a36Sopenharmony_ci get_name = get_helper_name; 128762306a36Sopenharmony_ci } else { 128862306a36Sopenharmony_ci p_err("expected 'prog_types', 'map_types', 'attach_types', 'link_types' or 'helpers', got: %s", *argv); 128962306a36Sopenharmony_ci return -1; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (json_output) 129362306a36Sopenharmony_ci jsonw_start_array(json_wtr); /* root array */ 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci while (true) { 129662306a36Sopenharmony_ci const char *name; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci name = get_name(id++); 129962306a36Sopenharmony_ci if (!name) 130062306a36Sopenharmony_ci break; 130162306a36Sopenharmony_ci if (json_output) 130262306a36Sopenharmony_ci jsonw_string(json_wtr, name); 130362306a36Sopenharmony_ci else 130462306a36Sopenharmony_ci printf("%s\n", name); 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (json_output) 130862306a36Sopenharmony_ci jsonw_end_array(json_wtr); /* root array */ 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci return 0; 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic int do_help(int argc, char **argv) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci if (json_output) { 131662306a36Sopenharmony_ci jsonw_null(json_wtr); 131762306a36Sopenharmony_ci return 0; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci fprintf(stderr, 132162306a36Sopenharmony_ci "Usage: %1$s %2$s probe [COMPONENT] [full] [unprivileged] [macros [prefix PREFIX]]\n" 132262306a36Sopenharmony_ci " %1$s %2$s list_builtins GROUP\n" 132362306a36Sopenharmony_ci " %1$s %2$s help\n" 132462306a36Sopenharmony_ci "\n" 132562306a36Sopenharmony_ci " COMPONENT := { kernel | dev NAME }\n" 132662306a36Sopenharmony_ci " GROUP := { prog_types | map_types | attach_types | link_types | helpers }\n" 132762306a36Sopenharmony_ci " " HELP_SPEC_OPTIONS " }\n" 132862306a36Sopenharmony_ci "", 132962306a36Sopenharmony_ci bin_name, argv[-2]); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci return 0; 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic const struct cmd cmds[] = { 133562306a36Sopenharmony_ci { "probe", do_probe }, 133662306a36Sopenharmony_ci { "list_builtins", do_list_builtins }, 133762306a36Sopenharmony_ci { "help", do_help }, 133862306a36Sopenharmony_ci { 0 } 133962306a36Sopenharmony_ci}; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ciint do_feature(int argc, char **argv) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci return cmd_select(cmds, argc, argv, do_help); 134462306a36Sopenharmony_ci} 1345