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