xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision 1ba5ad36)
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 #define _XOPEN_SOURCE 500
6 #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 
19 #include "main.h"
20 
21 #define HELP_SPEC_ATTACH_FLAGS						\
22 	"ATTACH_FLAGS := { multi | override }"
23 
24 #define HELP_SPEC_ATTACH_TYPES						\
25 	"       ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
26 	"                        cgroup_inet_sock_create | cgroup_sock_ops |\n" \
27 	"                        cgroup_device | cgroup_inet4_bind |\n" \
28 	"                        cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
29 	"                        cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
30 	"                        cgroup_inet6_connect | cgroup_inet4_getpeername |\n" \
31 	"                        cgroup_inet6_getpeername | cgroup_inet4_getsockname |\n" \
32 	"                        cgroup_inet6_getsockname | cgroup_udp4_sendmsg |\n" \
33 	"                        cgroup_udp6_sendmsg | cgroup_udp4_recvmsg |\n" \
34 	"                        cgroup_udp6_recvmsg | cgroup_sysctl |\n" \
35 	"                        cgroup_getsockopt | cgroup_setsockopt |\n" \
36 	"                        cgroup_inet_sock_release }"
37 
38 static unsigned int query_flags;
39 
40 static enum bpf_attach_type parse_attach_type(const char *str)
41 {
42 	const char *attach_type_str;
43 	enum bpf_attach_type type;
44 
45 	for (type = 0; ; type++) {
46 		attach_type_str = libbpf_bpf_attach_type_str(type);
47 		if (!attach_type_str)
48 			break;
49 		if (!strcmp(str, attach_type_str))
50 			return type;
51 	}
52 
53 	/* Also check traditionally used attach type strings. For these we keep
54 	 * allowing prefixed usage.
55 	 */
56 	for (type = 0; ; type++) {
57 		attach_type_str = bpf_attach_type_input_str(type);
58 		if (!attach_type_str)
59 			break;
60 		if (is_prefix(str, attach_type_str))
61 			return type;
62 	}
63 
64 	return __MAX_BPF_ATTACH_TYPE;
65 }
66 
67 static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
68 			 const char *attach_flags_str,
69 			 int level)
70 {
71 	char prog_name[MAX_PROG_FULL_NAME];
72 	struct bpf_prog_info info = {};
73 	const char *attach_type_str;
74 	__u32 info_len = sizeof(info);
75 	int prog_fd;
76 
77 	prog_fd = bpf_prog_get_fd_by_id(id);
78 	if (prog_fd < 0)
79 		return -1;
80 
81 	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
82 		close(prog_fd);
83 		return -1;
84 	}
85 
86 	attach_type_str = libbpf_bpf_attach_type_str(attach_type);
87 	get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
88 	if (json_output) {
89 		jsonw_start_object(json_wtr);
90 		jsonw_uint_field(json_wtr, "id", info.id);
91 		if (attach_type_str)
92 			jsonw_string_field(json_wtr, "attach_type", attach_type_str);
93 		else
94 			jsonw_uint_field(json_wtr, "attach_type", attach_type);
95 		jsonw_string_field(json_wtr, "attach_flags",
96 				   attach_flags_str);
97 		jsonw_string_field(json_wtr, "name", prog_name);
98 		jsonw_end_object(json_wtr);
99 	} else {
100 		printf("%s%-8u ", level ? "    " : "", info.id);
101 		if (attach_type_str)
102 			printf("%-15s", attach_type_str);
103 		else
104 			printf("type %-10u", attach_type);
105 		printf(" %-15s %-15s\n", attach_flags_str, prog_name);
106 	}
107 
108 	close(prog_fd);
109 	return 0;
110 }
111 
112 static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
113 {
114 	__u32 prog_cnt = 0;
115 	int ret;
116 
117 	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
118 			     NULL, &prog_cnt);
119 	if (ret)
120 		return -1;
121 
122 	return prog_cnt;
123 }
124 
125 static int cgroup_has_attached_progs(int cgroup_fd)
126 {
127 	enum bpf_attach_type type;
128 	bool no_prog = true;
129 
130 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
131 		int count = count_attached_bpf_progs(cgroup_fd, type);
132 
133 		if (count < 0 && errno != EINVAL)
134 			return -1;
135 
136 		if (count > 0) {
137 			no_prog = false;
138 			break;
139 		}
140 	}
141 
142 	return no_prog ? 0 : 1;
143 }
144 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
145 				   int level)
146 {
147 	const char *attach_flags_str;
148 	__u32 prog_ids[1024] = {0};
149 	__u32 prog_cnt, iter;
150 	__u32 attach_flags;
151 	char buf[32];
152 	int ret;
153 
154 	prog_cnt = ARRAY_SIZE(prog_ids);
155 	ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags,
156 			     prog_ids, &prog_cnt);
157 	if (ret)
158 		return ret;
159 
160 	if (prog_cnt == 0)
161 		return 0;
162 
163 	switch (attach_flags) {
164 	case BPF_F_ALLOW_MULTI:
165 		attach_flags_str = "multi";
166 		break;
167 	case BPF_F_ALLOW_OVERRIDE:
168 		attach_flags_str = "override";
169 		break;
170 	case 0:
171 		attach_flags_str = "";
172 		break;
173 	default:
174 		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
175 		attach_flags_str = buf;
176 	}
177 
178 	for (iter = 0; iter < prog_cnt; iter++)
179 		show_bpf_prog(prog_ids[iter], type,
180 			      attach_flags_str, level);
181 
182 	return 0;
183 }
184 
185 static int do_show(int argc, char **argv)
186 {
187 	enum bpf_attach_type type;
188 	int has_attached_progs;
189 	const char *path;
190 	int cgroup_fd;
191 	int ret = -1;
192 
193 	query_flags = 0;
194 
195 	if (!REQ_ARGS(1))
196 		return -1;
197 	path = GET_ARG();
198 
199 	while (argc) {
200 		if (is_prefix(*argv, "effective")) {
201 			if (query_flags & BPF_F_QUERY_EFFECTIVE) {
202 				p_err("duplicated argument: %s", *argv);
203 				return -1;
204 			}
205 			query_flags |= BPF_F_QUERY_EFFECTIVE;
206 			NEXT_ARG();
207 		} else {
208 			p_err("expected no more arguments, 'effective', got: '%s'?",
209 			      *argv);
210 			return -1;
211 		}
212 	}
213 
214 	cgroup_fd = open(path, O_RDONLY);
215 	if (cgroup_fd < 0) {
216 		p_err("can't open cgroup %s", path);
217 		goto exit;
218 	}
219 
220 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
221 	if (has_attached_progs < 0) {
222 		p_err("can't query bpf programs attached to %s: %s",
223 		      path, strerror(errno));
224 		goto exit_cgroup;
225 	} else if (!has_attached_progs) {
226 		ret = 0;
227 		goto exit_cgroup;
228 	}
229 
230 	if (json_output)
231 		jsonw_start_array(json_wtr);
232 	else
233 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
234 		       "AttachFlags", "Name");
235 
236 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
237 		/*
238 		 * Not all attach types may be supported, so it's expected,
239 		 * that some requests will fail.
240 		 * If we were able to get the show for at least one
241 		 * attach type, let's return 0.
242 		 */
243 		if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
244 			ret = 0;
245 	}
246 
247 	if (json_output)
248 		jsonw_end_array(json_wtr);
249 
250 exit_cgroup:
251 	close(cgroup_fd);
252 exit:
253 	return ret;
254 }
255 
256 /*
257  * To distinguish nftw() errors and do_show_tree_fn() errors
258  * and avoid duplicating error messages, let's return -2
259  * from do_show_tree_fn() in case of error.
260  */
261 #define NFTW_ERR		-1
262 #define SHOW_TREE_FN_ERR	-2
263 static int do_show_tree_fn(const char *fpath, const struct stat *sb,
264 			   int typeflag, struct FTW *ftw)
265 {
266 	enum bpf_attach_type type;
267 	int has_attached_progs;
268 	int cgroup_fd;
269 
270 	if (typeflag != FTW_D)
271 		return 0;
272 
273 	cgroup_fd = open(fpath, O_RDONLY);
274 	if (cgroup_fd < 0) {
275 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
276 		return SHOW_TREE_FN_ERR;
277 	}
278 
279 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
280 	if (has_attached_progs < 0) {
281 		p_err("can't query bpf programs attached to %s: %s",
282 		      fpath, strerror(errno));
283 		close(cgroup_fd);
284 		return SHOW_TREE_FN_ERR;
285 	} else if (!has_attached_progs) {
286 		close(cgroup_fd);
287 		return 0;
288 	}
289 
290 	if (json_output) {
291 		jsonw_start_object(json_wtr);
292 		jsonw_string_field(json_wtr, "cgroup", fpath);
293 		jsonw_name(json_wtr, "programs");
294 		jsonw_start_array(json_wtr);
295 	} else {
296 		printf("%s\n", fpath);
297 	}
298 
299 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
300 		show_attached_bpf_progs(cgroup_fd, type, ftw->level);
301 
302 	if (errno == EINVAL)
303 		/* Last attach type does not support query.
304 		 * Do not report an error for this, especially because batch
305 		 * mode would stop processing commands.
306 		 */
307 		errno = 0;
308 
309 	if (json_output) {
310 		jsonw_end_array(json_wtr);
311 		jsonw_end_object(json_wtr);
312 	}
313 
314 	close(cgroup_fd);
315 
316 	return 0;
317 }
318 
319 static char *find_cgroup_root(void)
320 {
321 	struct mntent *mnt;
322 	FILE *f;
323 
324 	f = fopen("/proc/mounts", "r");
325 	if (f == NULL)
326 		return NULL;
327 
328 	while ((mnt = getmntent(f))) {
329 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
330 			fclose(f);
331 			return strdup(mnt->mnt_dir);
332 		}
333 	}
334 
335 	fclose(f);
336 	return NULL;
337 }
338 
339 static int do_show_tree(int argc, char **argv)
340 {
341 	char *cgroup_root, *cgroup_alloced = NULL;
342 	int ret;
343 
344 	query_flags = 0;
345 
346 	if (!argc) {
347 		cgroup_alloced = find_cgroup_root();
348 		if (!cgroup_alloced) {
349 			p_err("cgroup v2 isn't mounted");
350 			return -1;
351 		}
352 		cgroup_root = cgroup_alloced;
353 	} else {
354 		cgroup_root = GET_ARG();
355 
356 		while (argc) {
357 			if (is_prefix(*argv, "effective")) {
358 				if (query_flags & BPF_F_QUERY_EFFECTIVE) {
359 					p_err("duplicated argument: %s", *argv);
360 					return -1;
361 				}
362 				query_flags |= BPF_F_QUERY_EFFECTIVE;
363 				NEXT_ARG();
364 			} else {
365 				p_err("expected no more arguments, 'effective', got: '%s'?",
366 				      *argv);
367 				return -1;
368 			}
369 		}
370 	}
371 
372 	if (json_output)
373 		jsonw_start_array(json_wtr);
374 	else
375 		printf("%s\n"
376 		       "%-8s %-15s %-15s %-15s\n",
377 		       "CgroupPath",
378 		       "ID", "AttachType", "AttachFlags", "Name");
379 
380 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
381 	case NFTW_ERR:
382 		p_err("can't iterate over %s: %s", cgroup_root,
383 		      strerror(errno));
384 		ret = -1;
385 		break;
386 	case SHOW_TREE_FN_ERR:
387 		ret = -1;
388 		break;
389 	default:
390 		ret = 0;
391 	}
392 
393 	if (json_output)
394 		jsonw_end_array(json_wtr);
395 
396 	free(cgroup_alloced);
397 
398 	return ret;
399 }
400 
401 static int do_attach(int argc, char **argv)
402 {
403 	enum bpf_attach_type attach_type;
404 	int cgroup_fd, prog_fd;
405 	int attach_flags = 0;
406 	int ret = -1;
407 	int i;
408 
409 	if (argc < 4) {
410 		p_err("too few parameters for cgroup attach");
411 		goto exit;
412 	}
413 
414 	cgroup_fd = open(argv[0], O_RDONLY);
415 	if (cgroup_fd < 0) {
416 		p_err("can't open cgroup %s", argv[0]);
417 		goto exit;
418 	}
419 
420 	attach_type = parse_attach_type(argv[1]);
421 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
422 		p_err("invalid attach type");
423 		goto exit_cgroup;
424 	}
425 
426 	argc -= 2;
427 	argv = &argv[2];
428 	prog_fd = prog_parse_fd(&argc, &argv);
429 	if (prog_fd < 0)
430 		goto exit_cgroup;
431 
432 	for (i = 0; i < argc; i++) {
433 		if (is_prefix(argv[i], "multi")) {
434 			attach_flags |= BPF_F_ALLOW_MULTI;
435 		} else if (is_prefix(argv[i], "override")) {
436 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
437 		} else {
438 			p_err("unknown option: %s", argv[i]);
439 			goto exit_cgroup;
440 		}
441 	}
442 
443 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
444 		p_err("failed to attach program");
445 		goto exit_prog;
446 	}
447 
448 	if (json_output)
449 		jsonw_null(json_wtr);
450 
451 	ret = 0;
452 
453 exit_prog:
454 	close(prog_fd);
455 exit_cgroup:
456 	close(cgroup_fd);
457 exit:
458 	return ret;
459 }
460 
461 static int do_detach(int argc, char **argv)
462 {
463 	enum bpf_attach_type attach_type;
464 	int prog_fd, cgroup_fd;
465 	int ret = -1;
466 
467 	if (argc < 4) {
468 		p_err("too few parameters for cgroup detach");
469 		goto exit;
470 	}
471 
472 	cgroup_fd = open(argv[0], O_RDONLY);
473 	if (cgroup_fd < 0) {
474 		p_err("can't open cgroup %s", argv[0]);
475 		goto exit;
476 	}
477 
478 	attach_type = parse_attach_type(argv[1]);
479 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
480 		p_err("invalid attach type");
481 		goto exit_cgroup;
482 	}
483 
484 	argc -= 2;
485 	argv = &argv[2];
486 	prog_fd = prog_parse_fd(&argc, &argv);
487 	if (prog_fd < 0)
488 		goto exit_cgroup;
489 
490 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
491 		p_err("failed to detach program");
492 		goto exit_prog;
493 	}
494 
495 	if (json_output)
496 		jsonw_null(json_wtr);
497 
498 	ret = 0;
499 
500 exit_prog:
501 	close(prog_fd);
502 exit_cgroup:
503 	close(cgroup_fd);
504 exit:
505 	return ret;
506 }
507 
508 static int do_help(int argc, char **argv)
509 {
510 	if (json_output) {
511 		jsonw_null(json_wtr);
512 		return 0;
513 	}
514 
515 	fprintf(stderr,
516 		"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
517 		"       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
518 		"       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
519 		"       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
520 		"       %1$s %2$s help\n"
521 		"\n"
522 		HELP_SPEC_ATTACH_TYPES "\n"
523 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
524 		"       " HELP_SPEC_PROGRAM "\n"
525 		"       " HELP_SPEC_OPTIONS " |\n"
526 		"                    {-f|--bpffs} }\n"
527 		"",
528 		bin_name, argv[-2]);
529 
530 	return 0;
531 }
532 
533 static const struct cmd cmds[] = {
534 	{ "show",	do_show },
535 	{ "list",	do_show },
536 	{ "tree",       do_show_tree },
537 	{ "attach",	do_attach },
538 	{ "detach",	do_detach },
539 	{ "help",	do_help },
540 	{ 0 }
541 };
542 
543 int do_cgroup(int argc, char **argv)
544 {
545 	return cmd_select(cmds, argc, argv, do_help);
546 }
547