xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision 393de512)
15ccda64dSRoman Gushchin // SPDX-License-Identifier: GPL-2.0+
25ccda64dSRoman Gushchin // Copyright (C) 2017 Facebook
35ccda64dSRoman Gushchin // Author: Roman Gushchin <guro@fb.com>
45ccda64dSRoman Gushchin 
55ccda64dSRoman Gushchin #include <fcntl.h>
65ccda64dSRoman Gushchin #include <stdlib.h>
75ccda64dSRoman Gushchin #include <string.h>
85ccda64dSRoman Gushchin #include <sys/stat.h>
95ccda64dSRoman Gushchin #include <sys/types.h>
105ccda64dSRoman Gushchin #include <unistd.h>
115ccda64dSRoman Gushchin 
125ccda64dSRoman Gushchin #include <bpf.h>
135ccda64dSRoman Gushchin 
145ccda64dSRoman Gushchin #include "main.h"
155ccda64dSRoman Gushchin 
165ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_FLAGS						\
175ccda64dSRoman Gushchin 	"ATTACH_FLAGS := { multi | override }"
185ccda64dSRoman Gushchin 
195ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_TYPES						       \
20393de512SAndrey Ignatov 	"       ATTACH_TYPE := { ingress | egress | sock_create |\n"	       \
21393de512SAndrey Ignatov 	"                        sock_ops | device | bind4 | bind6 |\n"	       \
22393de512SAndrey Ignatov 	"                        post_bind4 | post_bind6 | connect4 |\n"       \
23393de512SAndrey Ignatov 	"                        connect6 }"
245ccda64dSRoman Gushchin 
255ccda64dSRoman Gushchin static const char * const attach_type_strings[] = {
265ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_INGRESS] = "ingress",
275ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_EGRESS] = "egress",
285ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
295ccda64dSRoman Gushchin 	[BPF_CGROUP_SOCK_OPS] = "sock_ops",
305ccda64dSRoman Gushchin 	[BPF_CGROUP_DEVICE] = "device",
31393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_BIND] = "bind4",
32393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_BIND] = "bind6",
33393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_CONNECT] = "connect4",
34393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_CONNECT] = "connect6",
35393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
36393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
375ccda64dSRoman Gushchin 	[__MAX_BPF_ATTACH_TYPE] = NULL,
385ccda64dSRoman Gushchin };
395ccda64dSRoman Gushchin 
405ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
415ccda64dSRoman Gushchin {
425ccda64dSRoman Gushchin 	enum bpf_attach_type type;
435ccda64dSRoman Gushchin 
445ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
455ccda64dSRoman Gushchin 		if (attach_type_strings[type] &&
465ccda64dSRoman Gushchin 		    is_prefix(str, attach_type_strings[type]))
475ccda64dSRoman Gushchin 			return type;
485ccda64dSRoman Gushchin 	}
495ccda64dSRoman Gushchin 
505ccda64dSRoman Gushchin 	return __MAX_BPF_ATTACH_TYPE;
515ccda64dSRoman Gushchin }
525ccda64dSRoman Gushchin 
5365b875bcSJakub Kicinski static int show_bpf_prog(int id, const char *attach_type_str,
545ccda64dSRoman Gushchin 			 const char *attach_flags_str)
555ccda64dSRoman Gushchin {
565ccda64dSRoman Gushchin 	struct bpf_prog_info info = {};
575ccda64dSRoman Gushchin 	__u32 info_len = sizeof(info);
585ccda64dSRoman Gushchin 	int prog_fd;
595ccda64dSRoman Gushchin 
605ccda64dSRoman Gushchin 	prog_fd = bpf_prog_get_fd_by_id(id);
615ccda64dSRoman Gushchin 	if (prog_fd < 0)
625ccda64dSRoman Gushchin 		return -1;
635ccda64dSRoman Gushchin 
645ccda64dSRoman Gushchin 	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
655ccda64dSRoman Gushchin 		close(prog_fd);
665ccda64dSRoman Gushchin 		return -1;
675ccda64dSRoman Gushchin 	}
685ccda64dSRoman Gushchin 
695ccda64dSRoman Gushchin 	if (json_output) {
705ccda64dSRoman Gushchin 		jsonw_start_object(json_wtr);
715ccda64dSRoman Gushchin 		jsonw_uint_field(json_wtr, "id", info.id);
725ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_type",
735ccda64dSRoman Gushchin 				   attach_type_str);
745ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_flags",
755ccda64dSRoman Gushchin 				   attach_flags_str);
765ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "name", info.name);
775ccda64dSRoman Gushchin 		jsonw_end_object(json_wtr);
785ccda64dSRoman Gushchin 	} else {
795ccda64dSRoman Gushchin 		printf("%-8u %-15s %-15s %-15s\n", info.id,
805ccda64dSRoman Gushchin 		       attach_type_str,
815ccda64dSRoman Gushchin 		       attach_flags_str,
825ccda64dSRoman Gushchin 		       info.name);
835ccda64dSRoman Gushchin 	}
845ccda64dSRoman Gushchin 
855ccda64dSRoman Gushchin 	close(prog_fd);
865ccda64dSRoman Gushchin 	return 0;
875ccda64dSRoman Gushchin }
885ccda64dSRoman Gushchin 
8965b875bcSJakub Kicinski static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
905ccda64dSRoman Gushchin {
915ccda64dSRoman Gushchin 	__u32 prog_ids[1024] = {0};
925ccda64dSRoman Gushchin 	char *attach_flags_str;
935ccda64dSRoman Gushchin 	__u32 prog_cnt, iter;
945ccda64dSRoman Gushchin 	__u32 attach_flags;
955ccda64dSRoman Gushchin 	char buf[32];
965ccda64dSRoman Gushchin 	int ret;
975ccda64dSRoman Gushchin 
985ccda64dSRoman Gushchin 	prog_cnt = ARRAY_SIZE(prog_ids);
995ccda64dSRoman Gushchin 	ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
1005ccda64dSRoman Gushchin 			     &prog_cnt);
1015ccda64dSRoman Gushchin 	if (ret)
1025ccda64dSRoman Gushchin 		return ret;
1035ccda64dSRoman Gushchin 
1045ccda64dSRoman Gushchin 	if (prog_cnt == 0)
1055ccda64dSRoman Gushchin 		return 0;
1065ccda64dSRoman Gushchin 
1075ccda64dSRoman Gushchin 	switch (attach_flags) {
1085ccda64dSRoman Gushchin 	case BPF_F_ALLOW_MULTI:
1095ccda64dSRoman Gushchin 		attach_flags_str = "multi";
1105ccda64dSRoman Gushchin 		break;
1115ccda64dSRoman Gushchin 	case BPF_F_ALLOW_OVERRIDE:
1125ccda64dSRoman Gushchin 		attach_flags_str = "override";
1135ccda64dSRoman Gushchin 		break;
1145ccda64dSRoman Gushchin 	case 0:
1155ccda64dSRoman Gushchin 		attach_flags_str = "";
1165ccda64dSRoman Gushchin 		break;
1175ccda64dSRoman Gushchin 	default:
1185ccda64dSRoman Gushchin 		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
1195ccda64dSRoman Gushchin 		attach_flags_str = buf;
1205ccda64dSRoman Gushchin 	}
1215ccda64dSRoman Gushchin 
1225ccda64dSRoman Gushchin 	for (iter = 0; iter < prog_cnt; iter++)
12365b875bcSJakub Kicinski 		show_bpf_prog(prog_ids[iter], attach_type_strings[type],
1245ccda64dSRoman Gushchin 			      attach_flags_str);
1255ccda64dSRoman Gushchin 
1265ccda64dSRoman Gushchin 	return 0;
1275ccda64dSRoman Gushchin }
1285ccda64dSRoman Gushchin 
12965b875bcSJakub Kicinski static int do_show(int argc, char **argv)
1305ccda64dSRoman Gushchin {
1315ccda64dSRoman Gushchin 	enum bpf_attach_type type;
1325ccda64dSRoman Gushchin 	int cgroup_fd;
1335ccda64dSRoman Gushchin 	int ret = -1;
1345ccda64dSRoman Gushchin 
1355ccda64dSRoman Gushchin 	if (argc < 1) {
136b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup show");
1375ccda64dSRoman Gushchin 		goto exit;
1385ccda64dSRoman Gushchin 	} else if (argc > 1) {
139b4fac96dSJakub Kicinski 		p_err("too many parameters for cgroup show");
1405ccda64dSRoman Gushchin 		goto exit;
1415ccda64dSRoman Gushchin 	}
1425ccda64dSRoman Gushchin 
1435ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
1445ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
145b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
1465ccda64dSRoman Gushchin 		goto exit;
1475ccda64dSRoman Gushchin 	}
1485ccda64dSRoman Gushchin 
1495ccda64dSRoman Gushchin 	if (json_output)
1505ccda64dSRoman Gushchin 		jsonw_start_array(json_wtr);
1515ccda64dSRoman Gushchin 	else
1525ccda64dSRoman Gushchin 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
1535ccda64dSRoman Gushchin 		       "AttachFlags", "Name");
1545ccda64dSRoman Gushchin 
1555ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
1565ccda64dSRoman Gushchin 		/*
1575ccda64dSRoman Gushchin 		 * Not all attach types may be supported, so it's expected,
1585ccda64dSRoman Gushchin 		 * that some requests will fail.
15965b875bcSJakub Kicinski 		 * If we were able to get the show for at least one
1605ccda64dSRoman Gushchin 		 * attach type, let's return 0.
1615ccda64dSRoman Gushchin 		 */
16265b875bcSJakub Kicinski 		if (show_attached_bpf_progs(cgroup_fd, type) == 0)
1635ccda64dSRoman Gushchin 			ret = 0;
1645ccda64dSRoman Gushchin 	}
1655ccda64dSRoman Gushchin 
1665ccda64dSRoman Gushchin 	if (json_output)
1675ccda64dSRoman Gushchin 		jsonw_end_array(json_wtr);
1685ccda64dSRoman Gushchin 
1695ccda64dSRoman Gushchin 	close(cgroup_fd);
1705ccda64dSRoman Gushchin exit:
1715ccda64dSRoman Gushchin 	return ret;
1725ccda64dSRoman Gushchin }
1735ccda64dSRoman Gushchin 
1745ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
1755ccda64dSRoman Gushchin {
1765ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
1775ccda64dSRoman Gushchin 	int cgroup_fd, prog_fd;
1785ccda64dSRoman Gushchin 	int attach_flags = 0;
1795ccda64dSRoman Gushchin 	int ret = -1;
1805ccda64dSRoman Gushchin 	int i;
1815ccda64dSRoman Gushchin 
1825ccda64dSRoman Gushchin 	if (argc < 4) {
183b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup attach");
1845ccda64dSRoman Gushchin 		goto exit;
1855ccda64dSRoman Gushchin 	}
1865ccda64dSRoman Gushchin 
1875ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
1885ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
189b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
1905ccda64dSRoman Gushchin 		goto exit;
1915ccda64dSRoman Gushchin 	}
1925ccda64dSRoman Gushchin 
1935ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
1945ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
195b4fac96dSJakub Kicinski 		p_err("invalid attach type");
1965ccda64dSRoman Gushchin 		goto exit_cgroup;
1975ccda64dSRoman Gushchin 	}
1985ccda64dSRoman Gushchin 
1995ccda64dSRoman Gushchin 	argc -= 2;
2005ccda64dSRoman Gushchin 	argv = &argv[2];
2015ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
2025ccda64dSRoman Gushchin 	if (prog_fd < 0)
2035ccda64dSRoman Gushchin 		goto exit_cgroup;
2045ccda64dSRoman Gushchin 
2055ccda64dSRoman Gushchin 	for (i = 0; i < argc; i++) {
2065ccda64dSRoman Gushchin 		if (is_prefix(argv[i], "multi")) {
2075ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_MULTI;
2085ccda64dSRoman Gushchin 		} else if (is_prefix(argv[i], "override")) {
2095ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
2105ccda64dSRoman Gushchin 		} else {
211b4fac96dSJakub Kicinski 			p_err("unknown option: %s", argv[i]);
2125ccda64dSRoman Gushchin 			goto exit_cgroup;
2135ccda64dSRoman Gushchin 		}
2145ccda64dSRoman Gushchin 	}
2155ccda64dSRoman Gushchin 
2165ccda64dSRoman Gushchin 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
2175ccda64dSRoman Gushchin 		p_err("failed to attach program");
2185ccda64dSRoman Gushchin 		goto exit_prog;
2195ccda64dSRoman Gushchin 	}
2205ccda64dSRoman Gushchin 
2215ccda64dSRoman Gushchin 	if (json_output)
2225ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
2235ccda64dSRoman Gushchin 
2245ccda64dSRoman Gushchin 	ret = 0;
2255ccda64dSRoman Gushchin 
2265ccda64dSRoman Gushchin exit_prog:
2275ccda64dSRoman Gushchin 	close(prog_fd);
2285ccda64dSRoman Gushchin exit_cgroup:
2295ccda64dSRoman Gushchin 	close(cgroup_fd);
2305ccda64dSRoman Gushchin exit:
2315ccda64dSRoman Gushchin 	return ret;
2325ccda64dSRoman Gushchin }
2335ccda64dSRoman Gushchin 
2345ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
2355ccda64dSRoman Gushchin {
2365ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
2375ccda64dSRoman Gushchin 	int prog_fd, cgroup_fd;
2385ccda64dSRoman Gushchin 	int ret = -1;
2395ccda64dSRoman Gushchin 
2405ccda64dSRoman Gushchin 	if (argc < 4) {
241b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup detach");
2425ccda64dSRoman Gushchin 		goto exit;
2435ccda64dSRoman Gushchin 	}
2445ccda64dSRoman Gushchin 
2455ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
2465ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
247b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
2485ccda64dSRoman Gushchin 		goto exit;
2495ccda64dSRoman Gushchin 	}
2505ccda64dSRoman Gushchin 
2515ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
2525ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
2535ccda64dSRoman Gushchin 		p_err("invalid attach type");
2545ccda64dSRoman Gushchin 		goto exit_cgroup;
2555ccda64dSRoman Gushchin 	}
2565ccda64dSRoman Gushchin 
2575ccda64dSRoman Gushchin 	argc -= 2;
2585ccda64dSRoman Gushchin 	argv = &argv[2];
2595ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
2605ccda64dSRoman Gushchin 	if (prog_fd < 0)
2615ccda64dSRoman Gushchin 		goto exit_cgroup;
2625ccda64dSRoman Gushchin 
2635ccda64dSRoman Gushchin 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
2645ccda64dSRoman Gushchin 		p_err("failed to detach program");
2655ccda64dSRoman Gushchin 		goto exit_prog;
2665ccda64dSRoman Gushchin 	}
2675ccda64dSRoman Gushchin 
2685ccda64dSRoman Gushchin 	if (json_output)
2695ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
2705ccda64dSRoman Gushchin 
2715ccda64dSRoman Gushchin 	ret = 0;
2725ccda64dSRoman Gushchin 
2735ccda64dSRoman Gushchin exit_prog:
2745ccda64dSRoman Gushchin 	close(prog_fd);
2755ccda64dSRoman Gushchin exit_cgroup:
2765ccda64dSRoman Gushchin 	close(cgroup_fd);
2775ccda64dSRoman Gushchin exit:
2785ccda64dSRoman Gushchin 	return ret;
2795ccda64dSRoman Gushchin }
2805ccda64dSRoman Gushchin 
2815ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
2825ccda64dSRoman Gushchin {
2835ccda64dSRoman Gushchin 	if (json_output) {
2845ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
2855ccda64dSRoman Gushchin 		return 0;
2865ccda64dSRoman Gushchin 	}
2875ccda64dSRoman Gushchin 
2885ccda64dSRoman Gushchin 	fprintf(stderr,
2896ebe6dbdSJakub Kicinski 		"Usage: %s %s { show | list } CGROUP\n"
2905ccda64dSRoman Gushchin 		"       %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
2915ccda64dSRoman Gushchin 		"       %s %s detach CGROUP ATTACH_TYPE PROG\n"
2925ccda64dSRoman Gushchin 		"       %s %s help\n"
2935ccda64dSRoman Gushchin 		"\n"
294393de512SAndrey Ignatov 		HELP_SPEC_ATTACH_TYPES "\n"
2955ccda64dSRoman Gushchin 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
2965ccda64dSRoman Gushchin 		"       " HELP_SPEC_PROGRAM "\n"
2975ccda64dSRoman Gushchin 		"       " HELP_SPEC_OPTIONS "\n"
2985ccda64dSRoman Gushchin 		"",
2995ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2],
3005ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2]);
3015ccda64dSRoman Gushchin 
3025ccda64dSRoman Gushchin 	return 0;
3035ccda64dSRoman Gushchin }
3045ccda64dSRoman Gushchin 
3055ccda64dSRoman Gushchin static const struct cmd cmds[] = {
3066ebe6dbdSJakub Kicinski 	{ "show",	do_show },
30765b875bcSJakub Kicinski 	{ "list",	do_show },
3085ccda64dSRoman Gushchin 	{ "attach",	do_attach },
3095ccda64dSRoman Gushchin 	{ "detach",	do_detach },
3105ccda64dSRoman Gushchin 	{ "help",	do_help },
3115ccda64dSRoman Gushchin 	{ 0 }
3125ccda64dSRoman Gushchin };
3135ccda64dSRoman Gushchin 
3145ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
3155ccda64dSRoman Gushchin {
3165ccda64dSRoman Gushchin 	return cmd_select(cmds, argc, argv, do_help);
3175ccda64dSRoman Gushchin }
318