brintos

brintos / linux-shallow public Read only

0
0
Text · 31.4 KiB · 4dbc4fc Raw
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