brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · 9af426d Raw
690 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2// Copyright (C) 2017 Facebook3// Author: Roman Gushchin <guro@fb.com>4 5#define _XOPEN_SOURCE 5006#include <errno.h>7#include <fcntl.h>8#include <ftw.h>9#include <mntent.h>10#include <stdio.h>11#include <stdlib.h>12#include <string.h>13#include <sys/stat.h>14#include <sys/types.h>15#include <unistd.h>16 17#include <bpf/bpf.h>18#include <bpf/btf.h>19 20#include "main.h"21 22static const int cgroup_attach_types[] = {23	BPF_CGROUP_INET_INGRESS,24	BPF_CGROUP_INET_EGRESS,25	BPF_CGROUP_INET_SOCK_CREATE,26	BPF_CGROUP_INET_SOCK_RELEASE,27	BPF_CGROUP_INET4_BIND,28	BPF_CGROUP_INET6_BIND,29	BPF_CGROUP_INET4_POST_BIND,30	BPF_CGROUP_INET6_POST_BIND,31	BPF_CGROUP_INET4_CONNECT,32	BPF_CGROUP_INET6_CONNECT,33	BPF_CGROUP_UNIX_CONNECT,34	BPF_CGROUP_INET4_GETPEERNAME,35	BPF_CGROUP_INET6_GETPEERNAME,36	BPF_CGROUP_UNIX_GETPEERNAME,37	BPF_CGROUP_INET4_GETSOCKNAME,38	BPF_CGROUP_INET6_GETSOCKNAME,39	BPF_CGROUP_UNIX_GETSOCKNAME,40	BPF_CGROUP_UDP4_SENDMSG,41	BPF_CGROUP_UDP6_SENDMSG,42	BPF_CGROUP_UNIX_SENDMSG,43	BPF_CGROUP_UDP4_RECVMSG,44	BPF_CGROUP_UDP6_RECVMSG,45	BPF_CGROUP_UNIX_RECVMSG,46	BPF_CGROUP_SOCK_OPS,47	BPF_CGROUP_DEVICE,48	BPF_CGROUP_SYSCTL,49	BPF_CGROUP_GETSOCKOPT,50	BPF_CGROUP_SETSOCKOPT,51	BPF_LSM_CGROUP52};53 54#define HELP_SPEC_ATTACH_FLAGS						\55	"ATTACH_FLAGS := { multi | override }"56 57#define HELP_SPEC_ATTACH_TYPES						\58	"       ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \59	"                        cgroup_inet_sock_create | cgroup_sock_ops |\n" \60	"                        cgroup_device | cgroup_inet4_bind |\n" \61	"                        cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \62	"                        cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \63	"                        cgroup_inet6_connect | cgroup_unix_connect |\n" \64	"                        cgroup_inet4_getpeername | cgroup_inet6_getpeername |\n" \65	"                        cgroup_unix_getpeername | cgroup_inet4_getsockname |\n" \66	"                        cgroup_inet6_getsockname | cgroup_unix_getsockname |\n" \67	"                        cgroup_udp4_sendmsg | cgroup_udp6_sendmsg |\n" \68	"                        cgroup_unix_sendmsg | cgroup_udp4_recvmsg |\n" \69	"                        cgroup_udp6_recvmsg | cgroup_unix_recvmsg |\n" \70	"                        cgroup_sysctl | cgroup_getsockopt |\n" \71	"                        cgroup_setsockopt | cgroup_inet_sock_release }"72 73static unsigned int query_flags;74static struct btf *btf_vmlinux;75static __u32 btf_vmlinux_id;76 77static enum bpf_attach_type parse_attach_type(const char *str)78{79	const char *attach_type_str;80	enum bpf_attach_type type;81 82	for (type = 0; ; type++) {83		attach_type_str = libbpf_bpf_attach_type_str(type);84		if (!attach_type_str)85			break;86		if (!strcmp(str, attach_type_str))87			return type;88	}89 90	/* Also check traditionally used attach type strings. For these we keep91	 * allowing prefixed usage.92	 */93	for (type = 0; ; type++) {94		attach_type_str = bpf_attach_type_input_str(type);95		if (!attach_type_str)96			break;97		if (is_prefix(str, attach_type_str))98			return type;99	}100 101	return __MAX_BPF_ATTACH_TYPE;102}103 104static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)105{106	struct bpf_btf_info btf_info = {};107	__u32 btf_len = sizeof(btf_info);108	char name[16] = {};109	int err;110	int fd;111 112	btf_info.name = ptr_to_u64(name);113	btf_info.name_len = sizeof(name);114 115	fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);116	if (fd < 0)117		return;118 119	err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);120	if (err)121		goto out;122 123	if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)124		btf_vmlinux_id = btf_info.id;125 126out:127	close(fd);128}129 130static int show_bpf_prog(int id, enum bpf_attach_type attach_type,131			 const char *attach_flags_str,132			 int level)133{134	char prog_name[MAX_PROG_FULL_NAME];135	const char *attach_btf_name = NULL;136	struct bpf_prog_info info = {};137	const char *attach_type_str;138	__u32 info_len = sizeof(info);139	int prog_fd;140 141	prog_fd = bpf_prog_get_fd_by_id(id);142	if (prog_fd < 0)143		return -1;144 145	if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {146		close(prog_fd);147		return -1;148	}149 150	attach_type_str = libbpf_bpf_attach_type_str(attach_type);151 152	if (btf_vmlinux) {153		if (!btf_vmlinux_id)154			guess_vmlinux_btf_id(info.attach_btf_obj_id);155 156		if (btf_vmlinux_id == info.attach_btf_obj_id &&157		    info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {158			const struct btf_type *t =159				btf__type_by_id(btf_vmlinux, info.attach_btf_id);160			attach_btf_name =161				btf__name_by_offset(btf_vmlinux, t->name_off);162		}163	}164 165	get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));166	if (json_output) {167		jsonw_start_object(json_wtr);168		jsonw_uint_field(json_wtr, "id", info.id);169		if (attach_type_str)170			jsonw_string_field(json_wtr, "attach_type", attach_type_str);171		else172			jsonw_uint_field(json_wtr, "attach_type", attach_type);173		if (!(query_flags & BPF_F_QUERY_EFFECTIVE))174			jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);175		jsonw_string_field(json_wtr, "name", prog_name);176		if (attach_btf_name)177			jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);178		jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);179		jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);180		jsonw_end_object(json_wtr);181	} else {182		printf("%s%-8u ", level ? "    " : "", info.id);183		if (attach_type_str)184			printf("%-15s", attach_type_str);185		else186			printf("type %-10u", attach_type);187		if (query_flags & BPF_F_QUERY_EFFECTIVE)188			printf(" %-15s", prog_name);189		else190			printf(" %-15s %-15s", attach_flags_str, prog_name);191		if (attach_btf_name)192			printf(" %-15s", attach_btf_name);193		else if (info.attach_btf_id)194			printf(" attach_btf_obj_id=%d attach_btf_id=%d",195			       info.attach_btf_obj_id, info.attach_btf_id);196		printf("\n");197	}198 199	close(prog_fd);200	return 0;201}202 203static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)204{205	__u32 prog_cnt = 0;206	int ret;207 208	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,209			     NULL, &prog_cnt);210	if (ret)211		return -1;212 213	return prog_cnt;214}215 216static int cgroup_has_attached_progs(int cgroup_fd)217{218	unsigned int i = 0;219	bool no_prog = true;220 221	for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) {222		int count = count_attached_bpf_progs(cgroup_fd, cgroup_attach_types[i]);223 224		if (count < 0)225			return -1;226 227		if (count > 0) {228			no_prog = false;229			break;230		}231	}232 233	return no_prog ? 0 : 1;234}235 236static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,237				    int level)238{239	LIBBPF_OPTS(bpf_prog_query_opts, p);240	__u32 prog_ids[1024] = {0};241	__u32 iter;242	int ret;243 244	p.query_flags = query_flags;245	p.prog_cnt = ARRAY_SIZE(prog_ids);246	p.prog_ids = prog_ids;247 248	ret = bpf_prog_query_opts(cgroup_fd, type, &p);249	if (ret)250		return ret;251 252	if (p.prog_cnt == 0)253		return 0;254 255	for (iter = 0; iter < p.prog_cnt; iter++)256		show_bpf_prog(prog_ids[iter], type, NULL, level);257 258	return 0;259}260 261static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,262				   int level)263{264	LIBBPF_OPTS(bpf_prog_query_opts, p);265	__u32 prog_attach_flags[1024] = {0};266	const char *attach_flags_str;267	__u32 prog_ids[1024] = {0};268	char buf[32];269	__u32 iter;270	int ret;271 272	p.query_flags = query_flags;273	p.prog_cnt = ARRAY_SIZE(prog_ids);274	p.prog_ids = prog_ids;275	p.prog_attach_flags = prog_attach_flags;276 277	ret = bpf_prog_query_opts(cgroup_fd, type, &p);278	if (ret)279		return ret;280 281	if (p.prog_cnt == 0)282		return 0;283 284	for (iter = 0; iter < p.prog_cnt; iter++) {285		__u32 attach_flags;286 287		attach_flags = prog_attach_flags[iter] ?: p.attach_flags;288 289		switch (attach_flags) {290		case BPF_F_ALLOW_MULTI:291			attach_flags_str = "multi";292			break;293		case BPF_F_ALLOW_OVERRIDE:294			attach_flags_str = "override";295			break;296		case 0:297			attach_flags_str = "";298			break;299		default:300			snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);301			attach_flags_str = buf;302		}303 304		show_bpf_prog(prog_ids[iter], type,305			      attach_flags_str, level);306	}307 308	return 0;309}310 311static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,312			  int level)313{314	return query_flags & BPF_F_QUERY_EFFECTIVE ?315	       show_effective_bpf_progs(cgroup_fd, type, level) :316	       show_attached_bpf_progs(cgroup_fd, type, level);317}318 319static int do_show(int argc, char **argv)320{321	enum bpf_attach_type type;322	int has_attached_progs;323	const char *path;324	int cgroup_fd;325	int ret = -1;326 327	query_flags = 0;328 329	if (!REQ_ARGS(1))330		return -1;331	path = GET_ARG();332 333	while (argc) {334		if (is_prefix(*argv, "effective")) {335			if (query_flags & BPF_F_QUERY_EFFECTIVE) {336				p_err("duplicated argument: %s", *argv);337				return -1;338			}339			query_flags |= BPF_F_QUERY_EFFECTIVE;340			NEXT_ARG();341		} else {342			p_err("expected no more arguments, 'effective', got: '%s'?",343			      *argv);344			return -1;345		}346	}347 348	cgroup_fd = open(path, O_RDONLY);349	if (cgroup_fd < 0) {350		p_err("can't open cgroup %s", path);351		goto exit;352	}353 354	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);355	if (has_attached_progs < 0) {356		p_err("can't query bpf programs attached to %s: %s",357		      path, strerror(errno));358		goto exit_cgroup;359	} else if (!has_attached_progs) {360		ret = 0;361		goto exit_cgroup;362	}363 364	if (json_output)365		jsonw_start_array(json_wtr);366	else if (query_flags & BPF_F_QUERY_EFFECTIVE)367		printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");368	else369		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",370		       "AttachFlags", "Name");371 372	btf_vmlinux = libbpf_find_kernel_btf();373	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {374		/*375		 * Not all attach types may be supported, so it's expected,376		 * that some requests will fail.377		 * If we were able to get the show for at least one378		 * attach type, let's return 0.379		 */380		if (show_bpf_progs(cgroup_fd, type, 0) == 0)381			ret = 0;382	}383 384	if (json_output)385		jsonw_end_array(json_wtr);386 387exit_cgroup:388	close(cgroup_fd);389exit:390	return ret;391}392 393/*394 * To distinguish nftw() errors and do_show_tree_fn() errors395 * and avoid duplicating error messages, let's return -2396 * from do_show_tree_fn() in case of error.397 */398#define NFTW_ERR		-1399#define SHOW_TREE_FN_ERR	-2400static int do_show_tree_fn(const char *fpath, const struct stat *sb,401			   int typeflag, struct FTW *ftw)402{403	enum bpf_attach_type type;404	int has_attached_progs;405	int cgroup_fd;406 407	if (typeflag != FTW_D)408		return 0;409 410	cgroup_fd = open(fpath, O_RDONLY);411	if (cgroup_fd < 0) {412		p_err("can't open cgroup %s: %s", fpath, strerror(errno));413		return SHOW_TREE_FN_ERR;414	}415 416	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);417	if (has_attached_progs < 0) {418		p_err("can't query bpf programs attached to %s: %s",419		      fpath, strerror(errno));420		close(cgroup_fd);421		return SHOW_TREE_FN_ERR;422	} else if (!has_attached_progs) {423		close(cgroup_fd);424		return 0;425	}426 427	if (json_output) {428		jsonw_start_object(json_wtr);429		jsonw_string_field(json_wtr, "cgroup", fpath);430		jsonw_name(json_wtr, "programs");431		jsonw_start_array(json_wtr);432	} else {433		printf("%s\n", fpath);434	}435 436	btf_vmlinux = libbpf_find_kernel_btf();437	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)438		show_bpf_progs(cgroup_fd, type, ftw->level);439 440	if (errno == EINVAL)441		/* Last attach type does not support query.442		 * Do not report an error for this, especially because batch443		 * mode would stop processing commands.444		 */445		errno = 0;446 447	if (json_output) {448		jsonw_end_array(json_wtr);449		jsonw_end_object(json_wtr);450	}451 452	close(cgroup_fd);453 454	return 0;455}456 457static char *find_cgroup_root(void)458{459	struct mntent *mnt;460	FILE *f;461 462	f = fopen("/proc/mounts", "r");463	if (f == NULL)464		return NULL;465 466	while ((mnt = getmntent(f))) {467		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {468			fclose(f);469			return strdup(mnt->mnt_dir);470		}471	}472 473	fclose(f);474	return NULL;475}476 477static int do_show_tree(int argc, char **argv)478{479	char *cgroup_root, *cgroup_alloced = NULL;480	int ret;481 482	query_flags = 0;483 484	if (!argc) {485		cgroup_alloced = find_cgroup_root();486		if (!cgroup_alloced) {487			p_err("cgroup v2 isn't mounted");488			return -1;489		}490		cgroup_root = cgroup_alloced;491	} else {492		cgroup_root = GET_ARG();493 494		while (argc) {495			if (is_prefix(*argv, "effective")) {496				if (query_flags & BPF_F_QUERY_EFFECTIVE) {497					p_err("duplicated argument: %s", *argv);498					return -1;499				}500				query_flags |= BPF_F_QUERY_EFFECTIVE;501				NEXT_ARG();502			} else {503				p_err("expected no more arguments, 'effective', got: '%s'?",504				      *argv);505				return -1;506			}507		}508	}509 510	if (json_output)511		jsonw_start_array(json_wtr);512	else if (query_flags & BPF_F_QUERY_EFFECTIVE)513		printf("%s\n"514		       "%-8s %-15s %-15s\n",515		       "CgroupPath",516		       "ID", "AttachType", "Name");517	else518		printf("%s\n"519		       "%-8s %-15s %-15s %-15s\n",520		       "CgroupPath",521		       "ID", "AttachType", "AttachFlags", "Name");522 523	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {524	case NFTW_ERR:525		p_err("can't iterate over %s: %s", cgroup_root,526		      strerror(errno));527		ret = -1;528		break;529	case SHOW_TREE_FN_ERR:530		ret = -1;531		break;532	default:533		ret = 0;534	}535 536	if (json_output)537		jsonw_end_array(json_wtr);538 539	free(cgroup_alloced);540 541	return ret;542}543 544static int do_attach(int argc, char **argv)545{546	enum bpf_attach_type attach_type;547	int cgroup_fd, prog_fd;548	int attach_flags = 0;549	int ret = -1;550	int i;551 552	if (argc < 4) {553		p_err("too few parameters for cgroup attach");554		goto exit;555	}556 557	cgroup_fd = open(argv[0], O_RDONLY);558	if (cgroup_fd < 0) {559		p_err("can't open cgroup %s", argv[0]);560		goto exit;561	}562 563	attach_type = parse_attach_type(argv[1]);564	if (attach_type == __MAX_BPF_ATTACH_TYPE) {565		p_err("invalid attach type");566		goto exit_cgroup;567	}568 569	argc -= 2;570	argv = &argv[2];571	prog_fd = prog_parse_fd(&argc, &argv);572	if (prog_fd < 0)573		goto exit_cgroup;574 575	for (i = 0; i < argc; i++) {576		if (is_prefix(argv[i], "multi")) {577			attach_flags |= BPF_F_ALLOW_MULTI;578		} else if (is_prefix(argv[i], "override")) {579			attach_flags |= BPF_F_ALLOW_OVERRIDE;580		} else {581			p_err("unknown option: %s", argv[i]);582			goto exit_cgroup;583		}584	}585 586	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {587		p_err("failed to attach program");588		goto exit_prog;589	}590 591	if (json_output)592		jsonw_null(json_wtr);593 594	ret = 0;595 596exit_prog:597	close(prog_fd);598exit_cgroup:599	close(cgroup_fd);600exit:601	return ret;602}603 604static int do_detach(int argc, char **argv)605{606	enum bpf_attach_type attach_type;607	int prog_fd, cgroup_fd;608	int ret = -1;609 610	if (argc < 4) {611		p_err("too few parameters for cgroup detach");612		goto exit;613	}614 615	cgroup_fd = open(argv[0], O_RDONLY);616	if (cgroup_fd < 0) {617		p_err("can't open cgroup %s", argv[0]);618		goto exit;619	}620 621	attach_type = parse_attach_type(argv[1]);622	if (attach_type == __MAX_BPF_ATTACH_TYPE) {623		p_err("invalid attach type");624		goto exit_cgroup;625	}626 627	argc -= 2;628	argv = &argv[2];629	prog_fd = prog_parse_fd(&argc, &argv);630	if (prog_fd < 0)631		goto exit_cgroup;632 633	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {634		p_err("failed to detach program");635		goto exit_prog;636	}637 638	if (json_output)639		jsonw_null(json_wtr);640 641	ret = 0;642 643exit_prog:644	close(prog_fd);645exit_cgroup:646	close(cgroup_fd);647exit:648	return ret;649}650 651static int do_help(int argc, char **argv)652{653	if (json_output) {654		jsonw_null(json_wtr);655		return 0;656	}657 658	fprintf(stderr,659		"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"660		"       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"661		"       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"662		"       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"663		"       %1$s %2$s help\n"664		"\n"665		HELP_SPEC_ATTACH_TYPES "\n"666		"       " HELP_SPEC_ATTACH_FLAGS "\n"667		"       " HELP_SPEC_PROGRAM "\n"668		"       " HELP_SPEC_OPTIONS " |\n"669		"                    {-f|--bpffs} }\n"670		"",671		bin_name, argv[-2]);672 673	return 0;674}675 676static const struct cmd cmds[] = {677	{ "show",	do_show },678	{ "list",	do_show },679	{ "tree",       do_show_tree },680	{ "attach",	do_attach },681	{ "detach",	do_detach },682	{ "help",	do_help },683	{ 0 }684};685 686int do_cgroup(int argc, char **argv)687{688	return cmd_select(cmds, argc, argv, do_help);689}690