File Index
Files and directories
Sign in
FILE

cgroup.c

File preview with syntax highlighting.

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