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