1342 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/* Copyright (c) 2019 Netronome Systems, Inc. */3 4#include <ctype.h>5#include <errno.h>6#include <fcntl.h>7#include <string.h>8#include <unistd.h>9#include <net/if.h>10#ifdef USE_LIBCAP11#include <sys/capability.h>12#endif13#include <sys/utsname.h>14#include <sys/vfs.h>15 16#include <linux/filter.h>17#include <linux/limits.h>18 19#include <bpf/bpf.h>20#include <bpf/libbpf.h>21#include <zlib.h>22 23#include "main.h"24 25#ifndef PROC_SUPER_MAGIC26# define PROC_SUPER_MAGIC 0x9fa027#endif28 29enum probe_component {30 COMPONENT_UNSPEC,31 COMPONENT_KERNEL,32 COMPONENT_DEVICE,33};34 35#define BPF_HELPER_MAKE_ENTRY(name) [BPF_FUNC_ ## name] = "bpf_" # name36static const char * const helper_name[] = {37 __BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY)38};39 40#undef BPF_HELPER_MAKE_ENTRY41 42static bool full_mode;43#ifdef USE_LIBCAP44static bool run_as_unprivileged;45#endif46 47/* Miscellaneous utility functions */48 49static bool grep(const char *buffer, const char *pattern)50{51 return !!strstr(buffer, pattern);52}53 54static bool check_procfs(void)55{56 struct statfs st_fs;57 58 if (statfs("/proc", &st_fs) < 0)59 return false;60 if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC)61 return false;62 63 return true;64}65 66static void uppercase(char *str, size_t len)67{68 size_t i;69 70 for (i = 0; i < len && str[i] != '\0'; i++)71 str[i] = toupper(str[i]);72}73 74/* Printing utility functions */75 76static void77print_bool_feature(const char *feat_name, const char *plain_name,78 const char *define_name, bool res, const char *define_prefix)79{80 if (json_output)81 jsonw_bool_field(json_wtr, feat_name, res);82 else if (define_prefix)83 printf("#define %s%sHAVE_%s\n", define_prefix,84 res ? "" : "NO_", define_name);85 else86 printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");87}88 89static void print_kernel_option(const char *name, const char *value,90 const char *define_prefix)91{92 char *endptr;93 int res;94 95 if (json_output) {96 if (!value) {97 jsonw_null_field(json_wtr, name);98 return;99 }100 errno = 0;101 res = strtol(value, &endptr, 0);102 if (!errno && *endptr == '\n')103 jsonw_int_field(json_wtr, name, res);104 else105 jsonw_string_field(json_wtr, name, value);106 } else if (define_prefix) {107 if (value)108 printf("#define %s%s %s\n", define_prefix,109 name, value);110 else111 printf("/* %s%s is not set */\n", define_prefix, name);112 } else {113 if (value)114 printf("%s is set to %s\n", name, value);115 else116 printf("%s is not set\n", name);117 }118}119 120static void121print_start_section(const char *json_title, const char *plain_title,122 const char *define_comment, const char *define_prefix)123{124 if (json_output) {125 jsonw_name(json_wtr, json_title);126 jsonw_start_object(json_wtr);127 } else if (define_prefix) {128 printf("%s\n", define_comment);129 } else {130 printf("%s\n", plain_title);131 }132}133 134static void print_end_section(void)135{136 if (json_output)137 jsonw_end_object(json_wtr);138 else139 printf("\n");140}141 142/* Probing functions */143 144static int get_vendor_id(int ifindex)145{146 char ifname[IF_NAMESIZE], path[64], buf[8];147 ssize_t len;148 int fd;149 150 if (!if_indextoname(ifindex, ifname))151 return -1;152 153 snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname);154 155 fd = open(path, O_RDONLY | O_CLOEXEC);156 if (fd < 0)157 return -1;158 159 len = read(fd, buf, sizeof(buf));160 close(fd);161 if (len < 0)162 return -1;163 if (len >= (ssize_t)sizeof(buf))164 return -1;165 buf[len] = '\0';166 167 return strtol(buf, NULL, 0);168}169 170static long read_procfs(const char *path)171{172 char *endptr, *line = NULL;173 size_t len = 0;174 FILE *fd;175 long res;176 177 fd = fopen(path, "r");178 if (!fd)179 return -1;180 181 res = getline(&line, &len, fd);182 fclose(fd);183 if (res < 0)184 return -1;185 186 errno = 0;187 res = strtol(line, &endptr, 10);188 if (errno || *line == '\0' || *endptr != '\n')189 res = -1;190 free(line);191 192 return res;193}194 195static void probe_unprivileged_disabled(void)196{197 long res;198 199 /* No support for C-style output */200 201 res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");202 if (json_output) {203 jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);204 } else {205 switch (res) {206 case 0:207 printf("bpf() syscall for unprivileged users is enabled\n");208 break;209 case 1:210 printf("bpf() syscall restricted to privileged users (without recovery)\n");211 break;212 case 2:213 printf("bpf() syscall restricted to privileged users (admin can change)\n");214 break;215 case -1:216 printf("Unable to retrieve required privileges for bpf() syscall\n");217 break;218 default:219 printf("bpf() syscall restriction has unknown value %ld\n", res);220 }221 }222}223 224static void probe_jit_enable(void)225{226 long res;227 228 /* No support for C-style output */229 230 res = read_procfs("/proc/sys/net/core/bpf_jit_enable");231 if (json_output) {232 jsonw_int_field(json_wtr, "bpf_jit_enable", res);233 } else {234 switch (res) {235 case 0:236 printf("JIT compiler is disabled\n");237 break;238 case 1:239 printf("JIT compiler is enabled\n");240 break;241 case 2:242 printf("JIT compiler is enabled with debugging traces in kernel logs\n");243 break;244 case -1:245 printf("Unable to retrieve JIT-compiler status\n");246 break;247 default:248 printf("JIT-compiler status has unknown value %ld\n",249 res);250 }251 }252}253 254static void probe_jit_harden(void)255{256 long res;257 258 /* No support for C-style output */259 260 res = read_procfs("/proc/sys/net/core/bpf_jit_harden");261 if (json_output) {262 jsonw_int_field(json_wtr, "bpf_jit_harden", res);263 } else {264 switch (res) {265 case 0:266 printf("JIT compiler hardening is disabled\n");267 break;268 case 1:269 printf("JIT compiler hardening is enabled for unprivileged users\n");270 break;271 case 2:272 printf("JIT compiler hardening is enabled for all users\n");273 break;274 case -1:275 printf("Unable to retrieve JIT hardening status\n");276 break;277 default:278 printf("JIT hardening status has unknown value %ld\n",279 res);280 }281 }282}283 284static void probe_jit_kallsyms(void)285{286 long res;287 288 /* No support for C-style output */289 290 res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");291 if (json_output) {292 jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);293 } else {294 switch (res) {295 case 0:296 printf("JIT compiler kallsyms exports are disabled\n");297 break;298 case 1:299 printf("JIT compiler kallsyms exports are enabled for root\n");300 break;301 case -1:302 printf("Unable to retrieve JIT kallsyms export status\n");303 break;304 default:305 printf("JIT kallsyms exports status has unknown value %ld\n", res);306 }307 }308}309 310static void probe_jit_limit(void)311{312 long res;313 314 /* No support for C-style output */315 316 res = read_procfs("/proc/sys/net/core/bpf_jit_limit");317 if (json_output) {318 jsonw_int_field(json_wtr, "bpf_jit_limit", res);319 } else {320 switch (res) {321 case -1:322 printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n");323 break;324 default:325 printf("Global memory limit for JIT compiler for unprivileged users is %ld bytes\n", res);326 }327 }328}329 330static bool read_next_kernel_config_option(gzFile file, char *buf, size_t n,331 char **value)332{333 char *sep;334 335 while (gzgets(file, buf, n)) {336 if (strncmp(buf, "CONFIG_", 7))337 continue;338 339 sep = strchr(buf, '=');340 if (!sep)341 continue;342 343 /* Trim ending '\n' */344 buf[strlen(buf) - 1] = '\0';345 346 /* Split on '=' and ensure that a value is present. */347 *sep = '\0';348 if (!sep[1])349 continue;350 351 *value = sep + 1;352 return true;353 }354 355 return false;356}357 358static void probe_kernel_image_config(const char *define_prefix)359{360 static const struct {361 const char * const name;362 bool macro_dump;363 } options[] = {364 /* Enable BPF */365 { "CONFIG_BPF", },366 /* Enable bpf() syscall */367 { "CONFIG_BPF_SYSCALL", },368 /* Does selected architecture support eBPF JIT compiler */369 { "CONFIG_HAVE_EBPF_JIT", },370 /* Compile eBPF JIT compiler */371 { "CONFIG_BPF_JIT", },372 /* Avoid compiling eBPF interpreter (use JIT only) */373 { "CONFIG_BPF_JIT_ALWAYS_ON", },374 /* Kernel BTF debug information available */375 { "CONFIG_DEBUG_INFO_BTF", },376 /* Kernel module BTF debug information available */377 { "CONFIG_DEBUG_INFO_BTF_MODULES", },378 379 /* cgroups */380 { "CONFIG_CGROUPS", },381 /* BPF programs attached to cgroups */382 { "CONFIG_CGROUP_BPF", },383 /* bpf_get_cgroup_classid() helper */384 { "CONFIG_CGROUP_NET_CLASSID", },385 /* bpf_skb_{,ancestor_}cgroup_id() helpers */386 { "CONFIG_SOCK_CGROUP_DATA", },387 388 /* Tracing: attach BPF to kprobes, tracepoints, etc. */389 { "CONFIG_BPF_EVENTS", },390 /* Kprobes */391 { "CONFIG_KPROBE_EVENTS", },392 /* Uprobes */393 { "CONFIG_UPROBE_EVENTS", },394 /* Tracepoints */395 { "CONFIG_TRACING", },396 /* Syscall tracepoints */397 { "CONFIG_FTRACE_SYSCALLS", },398 /* bpf_override_return() helper support for selected arch */399 { "CONFIG_FUNCTION_ERROR_INJECTION", },400 /* bpf_override_return() helper */401 { "CONFIG_BPF_KPROBE_OVERRIDE", },402 403 /* Network */404 { "CONFIG_NET", },405 /* AF_XDP sockets */406 { "CONFIG_XDP_SOCKETS", },407 /* BPF_PROG_TYPE_LWT_* and related helpers */408 { "CONFIG_LWTUNNEL_BPF", },409 /* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */410 { "CONFIG_NET_ACT_BPF", },411 /* BPF_PROG_TYPE_SCHED_CLS, TC filters */412 { "CONFIG_NET_CLS_BPF", },413 /* TC clsact qdisc */414 { "CONFIG_NET_CLS_ACT", },415 /* Ingress filtering with TC */416 { "CONFIG_NET_SCH_INGRESS", },417 /* bpf_skb_get_xfrm_state() helper */418 { "CONFIG_XFRM", },419 /* bpf_get_route_realm() helper */420 { "CONFIG_IP_ROUTE_CLASSID", },421 /* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */422 { "CONFIG_IPV6_SEG6_BPF", },423 /* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */424 { "CONFIG_BPF_LIRC_MODE2", },425 /* BPF stream parser and BPF socket maps */426 { "CONFIG_BPF_STREAM_PARSER", },427 /* xt_bpf module for passing BPF programs to netfilter */428 { "CONFIG_NETFILTER_XT_MATCH_BPF", },429 430 /* test_bpf module for BPF tests */431 { "CONFIG_TEST_BPF", },432 433 /* Misc configs useful in BPF C programs */434 /* jiffies <-> sec conversion for bpf_jiffies64() helper */435 { "CONFIG_HZ", true, }436 };437 char *values[ARRAY_SIZE(options)] = { };438 struct utsname utsn;439 char path[PATH_MAX];440 gzFile file = NULL;441 char buf[4096];442 char *value;443 size_t i;444 445 if (!uname(&utsn)) {446 snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);447 448 /* gzopen also accepts uncompressed files. */449 file = gzopen(path, "r");450 }451 452 if (!file) {453 /* Some distributions build with CONFIG_IKCONFIG=y and put the454 * config file at /proc/config.gz.455 */456 file = gzopen("/proc/config.gz", "r");457 }458 if (!file) {459 p_info("skipping kernel config, can't open file: %s",460 strerror(errno));461 goto end_parse;462 }463 /* Sanity checks */464 if (!gzgets(file, buf, sizeof(buf)) ||465 !gzgets(file, buf, sizeof(buf))) {466 p_info("skipping kernel config, can't read from file: %s",467 strerror(errno));468 goto end_parse;469 }470 if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {471 p_info("skipping kernel config, can't find correct file");472 goto end_parse;473 }474 475 while (read_next_kernel_config_option(file, buf, sizeof(buf), &value)) {476 for (i = 0; i < ARRAY_SIZE(options); i++) {477 if ((define_prefix && !options[i].macro_dump) ||478 values[i] || strcmp(buf, options[i].name))479 continue;480 481 values[i] = strdup(value);482 }483 }484 485 for (i = 0; i < ARRAY_SIZE(options); i++) {486 if (define_prefix && !options[i].macro_dump)487 continue;488 print_kernel_option(options[i].name, values[i], define_prefix);489 free(values[i]);490 }491 492end_parse:493 if (file)494 gzclose(file);495}496 497static bool probe_bpf_syscall(const char *define_prefix)498{499 bool res;500 501 bpf_prog_load(BPF_PROG_TYPE_UNSPEC, NULL, NULL, NULL, 0, NULL);502 res = (errno != ENOSYS);503 504 print_bool_feature("have_bpf_syscall",505 "bpf() syscall",506 "BPF_SYSCALL",507 res, define_prefix);508 509 return res;510}511 512static bool513probe_prog_load_ifindex(enum bpf_prog_type prog_type,514 const struct bpf_insn *insns, size_t insns_cnt,515 char *log_buf, size_t log_buf_sz,516 __u32 ifindex)517{518 LIBBPF_OPTS(bpf_prog_load_opts, opts,519 .log_buf = log_buf,520 .log_size = log_buf_sz,521 .log_level = log_buf ? 1 : 0,522 .prog_ifindex = ifindex,523 );524 int fd;525 526 errno = 0;527 fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);528 if (fd >= 0)529 close(fd);530 531 return fd >= 0 && errno != EINVAL && errno != EOPNOTSUPP;532}533 534static bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex)535{536 /* nfp returns -EINVAL on exit(0) with TC offload */537 struct bpf_insn insns[2] = {538 BPF_MOV64_IMM(BPF_REG_0, 2),539 BPF_EXIT_INSN()540 };541 542 return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns),543 NULL, 0, ifindex);544}545 546static void547probe_prog_type(enum bpf_prog_type prog_type, const char *prog_type_str,548 bool *supported_types, const char *define_prefix, __u32 ifindex)549{550 char feat_name[128], plain_desc[128], define_name[128];551 const char *plain_comment = "eBPF program_type ";552 size_t maxlen;553 bool res;554 555 if (ifindex) {556 switch (prog_type) {557 case BPF_PROG_TYPE_SCHED_CLS:558 case BPF_PROG_TYPE_XDP:559 break;560 default:561 return;562 }563 564 res = probe_prog_type_ifindex(prog_type, ifindex);565 } else {566 res = libbpf_probe_bpf_prog_type(prog_type, NULL) > 0;567 }568 569#ifdef USE_LIBCAP570 /* Probe may succeed even if program load fails, for unprivileged users571 * check that we did not fail because of insufficient permissions572 */573 if (run_as_unprivileged && errno == EPERM)574 res = false;575#endif576 577 supported_types[prog_type] |= res;578 579 maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;580 if (strlen(prog_type_str) > maxlen) {581 p_info("program type name too long");582 return;583 }584 585 sprintf(feat_name, "have_%s_prog_type", prog_type_str);586 sprintf(define_name, "%s_prog_type", prog_type_str);587 uppercase(define_name, sizeof(define_name));588 sprintf(plain_desc, "%s%s", plain_comment, prog_type_str);589 print_bool_feature(feat_name, plain_desc, define_name, res,590 define_prefix);591}592 593static bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex)594{595 LIBBPF_OPTS(bpf_map_create_opts, opts);596 int key_size, value_size, max_entries;597 int fd;598 599 opts.map_ifindex = ifindex;600 601 key_size = sizeof(__u32);602 value_size = sizeof(__u32);603 max_entries = 1;604 605 fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries,606 &opts);607 if (fd >= 0)608 close(fd);609 610 return fd >= 0;611}612 613static void614probe_map_type(enum bpf_map_type map_type, char const *map_type_str,615 const char *define_prefix, __u32 ifindex)616{617 char feat_name[128], plain_desc[128], define_name[128];618 const char *plain_comment = "eBPF map_type ";619 size_t maxlen;620 bool res;621 622 if (ifindex) {623 switch (map_type) {624 case BPF_MAP_TYPE_HASH:625 case BPF_MAP_TYPE_ARRAY:626 break;627 default:628 return;629 }630 631 res = probe_map_type_ifindex(map_type, ifindex);632 } else {633 res = libbpf_probe_bpf_map_type(map_type, NULL) > 0;634 }635 636 /* Probe result depends on the success of map creation, no additional637 * check required for unprivileged users638 */639 640 maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;641 if (strlen(map_type_str) > maxlen) {642 p_info("map type name too long");643 return;644 }645 646 sprintf(feat_name, "have_%s_map_type", map_type_str);647 sprintf(define_name, "%s_map_type", map_type_str);648 uppercase(define_name, sizeof(define_name));649 sprintf(plain_desc, "%s%s", plain_comment, map_type_str);650 print_bool_feature(feat_name, plain_desc, define_name, res,651 define_prefix);652}653 654static bool655probe_helper_ifindex(enum bpf_func_id id, enum bpf_prog_type prog_type,656 __u32 ifindex)657{658 struct bpf_insn insns[2] = {659 BPF_EMIT_CALL(id),660 BPF_EXIT_INSN()661 };662 char buf[4096] = {};663 bool res;664 665 probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), buf,666 sizeof(buf), ifindex);667 res = !grep(buf, "invalid func ") && !grep(buf, "unknown func ") &&668 !grep(buf, "program of this type cannot use helper ");669 670 switch (get_vendor_id(ifindex)) {671 case 0x19ee: /* Netronome specific */672 res = res && !grep(buf, "not supported by FW") &&673 !grep(buf, "unsupported function id");674 break;675 default:676 break;677 }678 679 return res;680}681 682static bool683probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,684 const char *define_prefix, unsigned int id,685 const char *ptype_name, __u32 ifindex)686{687 bool res = false;688 689 if (supported_type) {690 if (ifindex)691 res = probe_helper_ifindex(id, prog_type, ifindex);692 else693 res = libbpf_probe_bpf_helper(prog_type, id, NULL) > 0;694#ifdef USE_LIBCAP695 /* Probe may succeed even if program load fails, for696 * unprivileged users check that we did not fail because of697 * insufficient permissions698 */699 if (run_as_unprivileged && errno == EPERM)700 res = false;701#endif702 }703 704 if (json_output) {705 if (res)706 jsonw_string(json_wtr, helper_name[id]);707 } else if (define_prefix) {708 printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n",709 define_prefix, ptype_name, helper_name[id],710 res ? "1" : "0");711 } else {712 if (res)713 printf("\n\t- %s", helper_name[id]);714 }715 716 return res;717}718 719static void720probe_helpers_for_progtype(enum bpf_prog_type prog_type,721 const char *prog_type_str, bool supported_type,722 const char *define_prefix, __u32 ifindex)723{724 char feat_name[128];725 unsigned int id;726 bool probe_res = false;727 728 if (ifindex)729 /* Only test helpers for offload-able program types */730 switch (prog_type) {731 case BPF_PROG_TYPE_SCHED_CLS:732 case BPF_PROG_TYPE_XDP:733 break;734 default:735 return;736 }737 738 if (json_output) {739 sprintf(feat_name, "%s_available_helpers", prog_type_str);740 jsonw_name(json_wtr, feat_name);741 jsonw_start_array(json_wtr);742 } else if (!define_prefix) {743 printf("eBPF helpers supported for program type %s:",744 prog_type_str);745 }746 747 for (id = 1; id < ARRAY_SIZE(helper_name); id++) {748 /* Skip helper functions which emit dmesg messages when not in749 * the full mode.750 */751 switch (id) {752 case BPF_FUNC_trace_printk:753 case BPF_FUNC_trace_vprintk:754 case BPF_FUNC_probe_write_user:755 if (!full_mode)756 continue;757 fallthrough;758 default:759 probe_res |= probe_helper_for_progtype(prog_type, supported_type,760 define_prefix, id, prog_type_str,761 ifindex);762 }763 }764 765 if (json_output)766 jsonw_end_array(json_wtr);767 else if (!define_prefix) {768 printf("\n");769 if (!probe_res) {770 if (!supported_type)771 printf("\tProgram type not supported\n");772 else773 printf("\tCould not determine which helpers are available\n");774 }775 }776 777 778}779 780static void781probe_misc_feature(struct bpf_insn *insns, size_t len,782 const char *define_prefix, __u32 ifindex,783 const char *feat_name, const char *plain_name,784 const char *define_name)785{786 LIBBPF_OPTS(bpf_prog_load_opts, opts,787 .prog_ifindex = ifindex,788 );789 bool res;790 int fd;791 792 errno = 0;793 fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",794 insns, len, &opts);795 res = fd >= 0 || !errno;796 797 if (fd >= 0)798 close(fd);799 800 print_bool_feature(feat_name, plain_name, define_name, res,801 define_prefix);802}803 804/*805 * Probe for availability of kernel commit (5.3):806 *807 * c04c0d2b968a ("bpf: increase complexity limit and maximum program size")808 */809static void probe_large_insn_limit(const char *define_prefix, __u32 ifindex)810{811 struct bpf_insn insns[BPF_MAXINSNS + 1];812 int i;813 814 for (i = 0; i < BPF_MAXINSNS; i++)815 insns[i] = BPF_MOV64_IMM(BPF_REG_0, 1);816 insns[BPF_MAXINSNS] = BPF_EXIT_INSN();817 818 probe_misc_feature(insns, ARRAY_SIZE(insns),819 define_prefix, ifindex,820 "have_large_insn_limit",821 "Large program size limit",822 "LARGE_INSN_LIMIT");823}824 825/*826 * Probe for bounded loop support introduced in commit 2589726d12a1827 * ("bpf: introduce bounded loops").828 */829static void830probe_bounded_loops(const char *define_prefix, __u32 ifindex)831{832 struct bpf_insn insns[4] = {833 BPF_MOV64_IMM(BPF_REG_0, 10),834 BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, 1),835 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, -2),836 BPF_EXIT_INSN()837 };838 839 probe_misc_feature(insns, ARRAY_SIZE(insns),840 define_prefix, ifindex,841 "have_bounded_loops",842 "Bounded loop support",843 "BOUNDED_LOOPS");844}845 846/*847 * Probe for the v2 instruction set extension introduced in commit 92b31a9af73b848 * ("bpf: add BPF_J{LT,LE,SLT,SLE} instructions").849 */850static void851probe_v2_isa_extension(const char *define_prefix, __u32 ifindex)852{853 struct bpf_insn insns[4] = {854 BPF_MOV64_IMM(BPF_REG_0, 0),855 BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 0, 1),856 BPF_MOV64_IMM(BPF_REG_0, 1),857 BPF_EXIT_INSN()858 };859 860 probe_misc_feature(insns, ARRAY_SIZE(insns),861 define_prefix, ifindex,862 "have_v2_isa_extension",863 "ISA extension v2",864 "V2_ISA_EXTENSION");865}866 867/*868 * Probe for the v3 instruction set extension introduced in commit 092ed0968bb6869 * ("bpf: verifier support JMP32").870 */871static void872probe_v3_isa_extension(const char *define_prefix, __u32 ifindex)873{874 struct bpf_insn insns[4] = {875 BPF_MOV64_IMM(BPF_REG_0, 0),876 BPF_JMP32_IMM(BPF_JLT, BPF_REG_0, 0, 1),877 BPF_MOV64_IMM(BPF_REG_0, 1),878 BPF_EXIT_INSN()879 };880 881 probe_misc_feature(insns, ARRAY_SIZE(insns),882 define_prefix, ifindex,883 "have_v3_isa_extension",884 "ISA extension v3",885 "V3_ISA_EXTENSION");886}887 888static void889section_system_config(enum probe_component target, const char *define_prefix)890{891 switch (target) {892 case COMPONENT_KERNEL:893 case COMPONENT_UNSPEC:894 print_start_section("system_config",895 "Scanning system configuration...",896 "/*** Misc kernel config items ***/",897 define_prefix);898 if (!define_prefix) {899 if (check_procfs()) {900 probe_unprivileged_disabled();901 probe_jit_enable();902 probe_jit_harden();903 probe_jit_kallsyms();904 probe_jit_limit();905 } else {906 p_info("/* procfs not mounted, skipping related probes */");907 }908 }909 probe_kernel_image_config(define_prefix);910 print_end_section();911 break;912 default:913 break;914 }915}916 917static bool section_syscall_config(const char *define_prefix)918{919 bool res;920 921 print_start_section("syscall_config",922 "Scanning system call availability...",923 "/*** System call availability ***/",924 define_prefix);925 res = probe_bpf_syscall(define_prefix);926 print_end_section();927 928 return res;929}930 931static void932section_program_types(bool *supported_types, const char *define_prefix,933 __u32 ifindex)934{935 unsigned int prog_type = BPF_PROG_TYPE_UNSPEC;936 const char *prog_type_str;937 938 print_start_section("program_types",939 "Scanning eBPF program types...",940 "/*** eBPF program types ***/",941 define_prefix);942 943 while (true) {944 prog_type++;945 prog_type_str = libbpf_bpf_prog_type_str(prog_type);946 /* libbpf will return NULL for variants unknown to it. */947 if (!prog_type_str)948 break;949 950 probe_prog_type(prog_type, prog_type_str, supported_types, define_prefix,951 ifindex);952 }953 954 print_end_section();955}956 957static void section_map_types(const char *define_prefix, __u32 ifindex)958{959 unsigned int map_type = BPF_MAP_TYPE_UNSPEC;960 const char *map_type_str;961 962 print_start_section("map_types",963 "Scanning eBPF map types...",964 "/*** eBPF map types ***/",965 define_prefix);966 967 while (true) {968 map_type++;969 map_type_str = libbpf_bpf_map_type_str(map_type);970 /* libbpf will return NULL for variants unknown to it. */971 if (!map_type_str)972 break;973 974 probe_map_type(map_type, map_type_str, define_prefix, ifindex);975 }976 977 print_end_section();978}979 980static void981section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex)982{983 unsigned int prog_type = BPF_PROG_TYPE_UNSPEC;984 const char *prog_type_str;985 986 print_start_section("helpers",987 "Scanning eBPF helper functions...",988 "/*** eBPF helper functions ***/",989 define_prefix);990 991 if (define_prefix)992 printf("/*\n"993 " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n"994 " * to determine if <helper_name> is available for <prog_type_name>,\n"995 " * e.g.\n"996 " * #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n"997 " * // do stuff with this helper\n"998 " * #elif\n"999 " * // use a workaround\n"1000 " * #endif\n"1001 " */\n"1002 "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper) \\\n"1003 " %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n",1004 define_prefix, define_prefix, define_prefix,1005 define_prefix);1006 while (true) {1007 prog_type++;1008 prog_type_str = libbpf_bpf_prog_type_str(prog_type);1009 /* libbpf will return NULL for variants unknown to it. */1010 if (!prog_type_str)1011 break;1012 1013 probe_helpers_for_progtype(prog_type, prog_type_str,1014 supported_types[prog_type],1015 define_prefix,1016 ifindex);1017 }1018 1019 print_end_section();1020}1021 1022static void section_misc(const char *define_prefix, __u32 ifindex)1023{1024 print_start_section("misc",1025 "Scanning miscellaneous eBPF features...",1026 "/*** eBPF misc features ***/",1027 define_prefix);1028 probe_large_insn_limit(define_prefix, ifindex);1029 probe_bounded_loops(define_prefix, ifindex);1030 probe_v2_isa_extension(define_prefix, ifindex);1031 probe_v3_isa_extension(define_prefix, ifindex);1032 print_end_section();1033}1034 1035#ifdef USE_LIBCAP1036#define capability(c) { c, false, #c }1037#define capability_msg(a, i) a[i].set ? "" : a[i].name, a[i].set ? "" : ", "1038#endif1039 1040static int handle_perms(void)1041{1042#ifdef USE_LIBCAP1043 struct {1044 cap_value_t cap;1045 bool set;1046 char name[14]; /* strlen("CAP_SYS_ADMIN") */1047 } bpf_caps[] = {1048 capability(CAP_SYS_ADMIN),1049#ifdef CAP_BPF1050 capability(CAP_BPF),1051 capability(CAP_NET_ADMIN),1052 capability(CAP_PERFMON),1053#endif1054 };1055 cap_value_t cap_list[ARRAY_SIZE(bpf_caps)];1056 unsigned int i, nb_bpf_caps = 0;1057 bool cap_sys_admin_only = true;1058 cap_flag_value_t val;1059 int res = -1;1060 cap_t caps;1061 1062 caps = cap_get_proc();1063 if (!caps) {1064 p_err("failed to get capabilities for process: %s",1065 strerror(errno));1066 return -1;1067 }1068 1069#ifdef CAP_BPF1070 if (CAP_IS_SUPPORTED(CAP_BPF))1071 cap_sys_admin_only = false;1072#endif1073 1074 for (i = 0; i < ARRAY_SIZE(bpf_caps); i++) {1075 const char *cap_name = bpf_caps[i].name;1076 cap_value_t cap = bpf_caps[i].cap;1077 1078 if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val)) {1079 p_err("bug: failed to retrieve %s status: %s", cap_name,1080 strerror(errno));1081 goto exit_free;1082 }1083 1084 if (val == CAP_SET) {1085 bpf_caps[i].set = true;1086 cap_list[nb_bpf_caps++] = cap;1087 }1088 1089 if (cap_sys_admin_only)1090 /* System does not know about CAP_BPF, meaning that1091 * CAP_SYS_ADMIN is the only capability required. We1092 * just checked it, break.1093 */1094 break;1095 }1096 1097 if ((run_as_unprivileged && !nb_bpf_caps) ||1098 (!run_as_unprivileged && nb_bpf_caps == ARRAY_SIZE(bpf_caps)) ||1099 (!run_as_unprivileged && cap_sys_admin_only && nb_bpf_caps)) {1100 /* We are all good, exit now */1101 res = 0;1102 goto exit_free;1103 }1104 1105 if (!run_as_unprivileged) {1106 if (cap_sys_admin_only)1107 p_err("missing %s, required for full feature probing; run as root or use 'unprivileged'",1108 bpf_caps[0].name);1109 else1110 p_err("missing %s%s%s%s%s%s%s%srequired for full feature probing; run as root or use 'unprivileged'",1111 capability_msg(bpf_caps, 0),1112#ifdef CAP_BPF1113 capability_msg(bpf_caps, 1),1114 capability_msg(bpf_caps, 2),1115 capability_msg(bpf_caps, 3)1116#else1117 "", "", "", "", "", ""1118#endif /* CAP_BPF */1119 );1120 goto exit_free;1121 }1122 1123 /* if (run_as_unprivileged && nb_bpf_caps > 0), drop capabilities. */1124 if (cap_set_flag(caps, CAP_EFFECTIVE, nb_bpf_caps, cap_list,1125 CAP_CLEAR)) {1126 p_err("bug: failed to clear capabilities: %s", strerror(errno));1127 goto exit_free;1128 }1129 1130 if (cap_set_proc(caps)) {1131 p_err("failed to drop capabilities: %s", strerror(errno));1132 goto exit_free;1133 }1134 1135 res = 0;1136 1137exit_free:1138 if (cap_free(caps) && !res) {1139 p_err("failed to clear storage object for capabilities: %s",1140 strerror(errno));1141 res = -1;1142 }1143 1144 return res;1145#else1146 /* Detection assumes user has specific privileges.1147 * We do not use libcap so let's approximate, and restrict usage to1148 * root user only.1149 */1150 if (geteuid()) {1151 p_err("full feature probing requires root privileges");1152 return -1;1153 }1154 1155 return 0;1156#endif /* USE_LIBCAP */1157}1158 1159static int do_probe(int argc, char **argv)1160{1161 enum probe_component target = COMPONENT_UNSPEC;1162 const char *define_prefix = NULL;1163 bool supported_types[128] = {};1164 __u32 ifindex = 0;1165 char *ifname;1166 1167 set_max_rlimit();1168 1169 while (argc) {1170 if (is_prefix(*argv, "kernel")) {1171 if (target != COMPONENT_UNSPEC) {1172 p_err("component to probe already specified");1173 return -1;1174 }1175 target = COMPONENT_KERNEL;1176 NEXT_ARG();1177 } else if (is_prefix(*argv, "dev")) {1178 NEXT_ARG();1179 1180 if (target != COMPONENT_UNSPEC || ifindex) {1181 p_err("component to probe already specified");1182 return -1;1183 }1184 if (!REQ_ARGS(1))1185 return -1;1186 1187 target = COMPONENT_DEVICE;1188 ifname = GET_ARG();1189 ifindex = if_nametoindex(ifname);1190 if (!ifindex) {1191 p_err("unrecognized netdevice '%s': %s", ifname,1192 strerror(errno));1193 return -1;1194 }1195 } else if (is_prefix(*argv, "full")) {1196 full_mode = true;1197 NEXT_ARG();1198 } else if (is_prefix(*argv, "macros") && !define_prefix) {1199 define_prefix = "";1200 NEXT_ARG();1201 } else if (is_prefix(*argv, "prefix")) {1202 if (!define_prefix) {1203 p_err("'prefix' argument can only be use after 'macros'");1204 return -1;1205 }1206 if (strcmp(define_prefix, "")) {1207 p_err("'prefix' already defined");1208 return -1;1209 }1210 NEXT_ARG();1211 1212 if (!REQ_ARGS(1))1213 return -1;1214 define_prefix = GET_ARG();1215 } else if (is_prefix(*argv, "unprivileged")) {1216#ifdef USE_LIBCAP1217 run_as_unprivileged = true;1218 NEXT_ARG();1219#else1220 p_err("unprivileged run not supported, recompile bpftool with libcap");1221 return -1;1222#endif1223 } else {1224 p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",1225 *argv);1226 return -1;1227 }1228 }1229 1230 /* Full feature detection requires specific privileges.1231 * Let's approximate, and warn if user is not root.1232 */1233 if (handle_perms())1234 return -1;1235 1236 if (json_output) {1237 define_prefix = NULL;1238 jsonw_start_object(json_wtr);1239 }1240 1241 section_system_config(target, define_prefix);1242 if (!section_syscall_config(define_prefix))1243 /* bpf() syscall unavailable, don't probe other BPF features */1244 goto exit_close_json;1245 section_program_types(supported_types, define_prefix, ifindex);1246 section_map_types(define_prefix, ifindex);1247 section_helpers(supported_types, define_prefix, ifindex);1248 section_misc(define_prefix, ifindex);1249 1250exit_close_json:1251 if (json_output)1252 /* End root object */1253 jsonw_end_object(json_wtr);1254 1255 return 0;1256}1257 1258static const char *get_helper_name(unsigned int id)1259{1260 if (id >= ARRAY_SIZE(helper_name))1261 return NULL;1262 1263 return helper_name[id];1264}1265 1266static int do_list_builtins(int argc, char **argv)1267{1268 const char *(*get_name)(unsigned int id);1269 unsigned int id = 0;1270 1271 if (argc < 1)1272 usage();1273 1274 if (is_prefix(*argv, "prog_types")) {1275 get_name = (const char *(*)(unsigned int))libbpf_bpf_prog_type_str;1276 } else if (is_prefix(*argv, "map_types")) {1277 get_name = (const char *(*)(unsigned int))libbpf_bpf_map_type_str;1278 } else if (is_prefix(*argv, "attach_types")) {1279 get_name = (const char *(*)(unsigned int))libbpf_bpf_attach_type_str;1280 } else if (is_prefix(*argv, "link_types")) {1281 get_name = (const char *(*)(unsigned int))libbpf_bpf_link_type_str;1282 } else if (is_prefix(*argv, "helpers")) {1283 get_name = get_helper_name;1284 } else {1285 p_err("expected 'prog_types', 'map_types', 'attach_types', 'link_types' or 'helpers', got: %s", *argv);1286 return -1;1287 }1288 1289 if (json_output)1290 jsonw_start_array(json_wtr); /* root array */1291 1292 while (true) {1293 const char *name;1294 1295 name = get_name(id++);1296 if (!name)1297 break;1298 if (json_output)1299 jsonw_string(json_wtr, name);1300 else1301 printf("%s\n", name);1302 }1303 1304 if (json_output)1305 jsonw_end_array(json_wtr); /* root array */1306 1307 return 0;1308}1309 1310static int do_help(int argc, char **argv)1311{1312 if (json_output) {1313 jsonw_null(json_wtr);1314 return 0;1315 }1316 1317 fprintf(stderr,1318 "Usage: %1$s %2$s probe [COMPONENT] [full] [unprivileged] [macros [prefix PREFIX]]\n"1319 " %1$s %2$s list_builtins GROUP\n"1320 " %1$s %2$s help\n"1321 "\n"1322 " COMPONENT := { kernel | dev NAME }\n"1323 " GROUP := { prog_types | map_types | attach_types | link_types | helpers }\n"1324 " " HELP_SPEC_OPTIONS " }\n"1325 "",1326 bin_name, argv[-2]);1327 1328 return 0;1329}1330 1331static const struct cmd cmds[] = {1332 { "probe", do_probe },1333 { "list_builtins", do_list_builtins },1334 { "help", do_help },1335 { 0 }1336};1337 1338int do_feature(int argc, char **argv)1339{1340 return cmd_select(cmds, argc, argv, do_help);1341}1342