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