1907b2236SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
25ccda64dSRoman Gushchin // Copyright (C) 2017 Facebook
35ccda64dSRoman Gushchin // Author: Roman Gushchin <guro@fb.com>
45ccda64dSRoman Gushchin
52058b383SRoman Gushchin #define _XOPEN_SOURCE 500
62058b383SRoman Gushchin #include <errno.h>
75ccda64dSRoman Gushchin #include <fcntl.h>
82058b383SRoman Gushchin #include <ftw.h>
92058b383SRoman Gushchin #include <mntent.h>
102058b383SRoman Gushchin #include <stdio.h>
115ccda64dSRoman Gushchin #include <stdlib.h>
125ccda64dSRoman Gushchin #include <string.h>
135ccda64dSRoman Gushchin #include <sys/stat.h>
145ccda64dSRoman Gushchin #include <sys/types.h>
155ccda64dSRoman Gushchin #include <unistd.h>
165ccda64dSRoman Gushchin
17229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
18596f5fb2SStanislav Fomichev #include <bpf/btf.h>
195ccda64dSRoman Gushchin
205ccda64dSRoman Gushchin #include "main.h"
215ccda64dSRoman Gushchin
225ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_FLAGS \
235ccda64dSRoman Gushchin "ATTACH_FLAGS := { multi | override }"
245ccda64dSRoman Gushchin
255ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_TYPES \
261ba5ad36SDaniel Müller " ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
271ba5ad36SDaniel Müller " cgroup_inet_sock_create | cgroup_sock_ops |\n" \
281ba5ad36SDaniel Müller " cgroup_device | cgroup_inet4_bind |\n" \
291ba5ad36SDaniel Müller " cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
301ba5ad36SDaniel Müller " cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
311ba5ad36SDaniel Müller " cgroup_inet6_connect | cgroup_inet4_getpeername |\n" \
321ba5ad36SDaniel Müller " cgroup_inet6_getpeername | cgroup_inet4_getsockname |\n" \
331ba5ad36SDaniel Müller " cgroup_inet6_getsockname | cgroup_udp4_sendmsg |\n" \
341ba5ad36SDaniel Müller " cgroup_udp6_sendmsg | cgroup_udp4_recvmsg |\n" \
351ba5ad36SDaniel Müller " cgroup_udp6_recvmsg | cgroup_sysctl |\n" \
361ba5ad36SDaniel Müller " cgroup_getsockopt | cgroup_setsockopt |\n" \
371ba5ad36SDaniel Müller " cgroup_inet_sock_release }"
385ccda64dSRoman Gushchin
39a98bf573SJakub Kicinski static unsigned int query_flags;
40596f5fb2SStanislav Fomichev static struct btf *btf_vmlinux;
41596f5fb2SStanislav Fomichev static __u32 btf_vmlinux_id;
42a98bf573SJakub Kicinski
parse_attach_type(const char * str)435ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
445ccda64dSRoman Gushchin {
451ba5ad36SDaniel Müller const char *attach_type_str;
465ccda64dSRoman Gushchin enum bpf_attach_type type;
475ccda64dSRoman Gushchin
481ba5ad36SDaniel Müller for (type = 0; ; type++) {
491ba5ad36SDaniel Müller attach_type_str = libbpf_bpf_attach_type_str(type);
501ba5ad36SDaniel Müller if (!attach_type_str)
511ba5ad36SDaniel Müller break;
521ba5ad36SDaniel Müller if (!strcmp(str, attach_type_str))
531ba5ad36SDaniel Müller return type;
541ba5ad36SDaniel Müller }
551ba5ad36SDaniel Müller
561ba5ad36SDaniel Müller /* Also check traditionally used attach type strings. For these we keep
571ba5ad36SDaniel Müller * allowing prefixed usage.
581ba5ad36SDaniel Müller */
591ba5ad36SDaniel Müller for (type = 0; ; type++) {
601ba5ad36SDaniel Müller attach_type_str = bpf_attach_type_input_str(type);
611ba5ad36SDaniel Müller if (!attach_type_str)
621ba5ad36SDaniel Müller break;
631ba5ad36SDaniel Müller if (is_prefix(str, attach_type_str))
645ccda64dSRoman Gushchin return type;
655ccda64dSRoman Gushchin }
665ccda64dSRoman Gushchin
675ccda64dSRoman Gushchin return __MAX_BPF_ATTACH_TYPE;
685ccda64dSRoman Gushchin }
695ccda64dSRoman Gushchin
guess_vmlinux_btf_id(__u32 attach_btf_obj_id)70596f5fb2SStanislav Fomichev static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
71596f5fb2SStanislav Fomichev {
72596f5fb2SStanislav Fomichev struct bpf_btf_info btf_info = {};
73596f5fb2SStanislav Fomichev __u32 btf_len = sizeof(btf_info);
74596f5fb2SStanislav Fomichev char name[16] = {};
75596f5fb2SStanislav Fomichev int err;
76596f5fb2SStanislav Fomichev int fd;
77596f5fb2SStanislav Fomichev
78596f5fb2SStanislav Fomichev btf_info.name = ptr_to_u64(name);
79596f5fb2SStanislav Fomichev btf_info.name_len = sizeof(name);
80596f5fb2SStanislav Fomichev
81596f5fb2SStanislav Fomichev fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
82596f5fb2SStanislav Fomichev if (fd < 0)
83596f5fb2SStanislav Fomichev return;
84596f5fb2SStanislav Fomichev
85*38f0408eSIlya Leoshkevich err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);
86596f5fb2SStanislav Fomichev if (err)
87596f5fb2SStanislav Fomichev goto out;
88596f5fb2SStanislav Fomichev
89596f5fb2SStanislav Fomichev if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
90596f5fb2SStanislav Fomichev btf_vmlinux_id = btf_info.id;
91596f5fb2SStanislav Fomichev
92596f5fb2SStanislav Fomichev out:
93596f5fb2SStanislav Fomichev close(fd);
94596f5fb2SStanislav Fomichev }
95596f5fb2SStanislav Fomichev
show_bpf_prog(int id,enum bpf_attach_type attach_type,const char * attach_flags_str,int level)9650325b17SAndrii Nakryiko static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
972058b383SRoman Gushchin const char *attach_flags_str,
982058b383SRoman Gushchin int level)
995ccda64dSRoman Gushchin {
100b662000aSRaman Shukhau char prog_name[MAX_PROG_FULL_NAME];
101596f5fb2SStanislav Fomichev const char *attach_btf_name = NULL;
1025ccda64dSRoman Gushchin struct bpf_prog_info info = {};
1031ba5ad36SDaniel Müller const char *attach_type_str;
1045ccda64dSRoman Gushchin __u32 info_len = sizeof(info);
1055ccda64dSRoman Gushchin int prog_fd;
1065ccda64dSRoman Gushchin
1075ccda64dSRoman Gushchin prog_fd = bpf_prog_get_fd_by_id(id);
1085ccda64dSRoman Gushchin if (prog_fd < 0)
1095ccda64dSRoman Gushchin return -1;
1105ccda64dSRoman Gushchin
111*38f0408eSIlya Leoshkevich if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {
1125ccda64dSRoman Gushchin close(prog_fd);
1135ccda64dSRoman Gushchin return -1;
1145ccda64dSRoman Gushchin }
1155ccda64dSRoman Gushchin
1161ba5ad36SDaniel Müller attach_type_str = libbpf_bpf_attach_type_str(attach_type);
117596f5fb2SStanislav Fomichev
118596f5fb2SStanislav Fomichev if (btf_vmlinux) {
119596f5fb2SStanislav Fomichev if (!btf_vmlinux_id)
120596f5fb2SStanislav Fomichev guess_vmlinux_btf_id(info.attach_btf_obj_id);
121596f5fb2SStanislav Fomichev
122596f5fb2SStanislav Fomichev if (btf_vmlinux_id == info.attach_btf_obj_id &&
123596f5fb2SStanislav Fomichev info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
124596f5fb2SStanislav Fomichev const struct btf_type *t =
125596f5fb2SStanislav Fomichev btf__type_by_id(btf_vmlinux, info.attach_btf_id);
126596f5fb2SStanislav Fomichev attach_btf_name =
127596f5fb2SStanislav Fomichev btf__name_by_offset(btf_vmlinux, t->name_off);
128596f5fb2SStanislav Fomichev }
129596f5fb2SStanislav Fomichev }
130596f5fb2SStanislav Fomichev
131b662000aSRaman Shukhau get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
1325ccda64dSRoman Gushchin if (json_output) {
1335ccda64dSRoman Gushchin jsonw_start_object(json_wtr);
1345ccda64dSRoman Gushchin jsonw_uint_field(json_wtr, "id", info.id);
1351ba5ad36SDaniel Müller if (attach_type_str)
1361ba5ad36SDaniel Müller jsonw_string_field(json_wtr, "attach_type", attach_type_str);
13750325b17SAndrii Nakryiko else
13850325b17SAndrii Nakryiko jsonw_uint_field(json_wtr, "attach_type", attach_type);
139bdcee1b0SPu Lehui if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
140bdcee1b0SPu Lehui jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
141b662000aSRaman Shukhau jsonw_string_field(json_wtr, "name", prog_name);
142596f5fb2SStanislav Fomichev if (attach_btf_name)
143596f5fb2SStanislav Fomichev jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
144596f5fb2SStanislav Fomichev jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
145596f5fb2SStanislav Fomichev jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
1465ccda64dSRoman Gushchin jsonw_end_object(json_wtr);
1475ccda64dSRoman Gushchin } else {
14850325b17SAndrii Nakryiko printf("%s%-8u ", level ? " " : "", info.id);
1491ba5ad36SDaniel Müller if (attach_type_str)
1501ba5ad36SDaniel Müller printf("%-15s", attach_type_str);
15150325b17SAndrii Nakryiko else
15250325b17SAndrii Nakryiko printf("type %-10u", attach_type);
153bdcee1b0SPu Lehui if (query_flags & BPF_F_QUERY_EFFECTIVE)
154bdcee1b0SPu Lehui printf(" %-15s", prog_name);
155bdcee1b0SPu Lehui else
156596f5fb2SStanislav Fomichev printf(" %-15s %-15s", attach_flags_str, prog_name);
157596f5fb2SStanislav Fomichev if (attach_btf_name)
158596f5fb2SStanislav Fomichev printf(" %-15s", attach_btf_name);
159596f5fb2SStanislav Fomichev else if (info.attach_btf_id)
160596f5fb2SStanislav Fomichev printf(" attach_btf_obj_id=%d attach_btf_id=%d",
161596f5fb2SStanislav Fomichev info.attach_btf_obj_id, info.attach_btf_id);
162596f5fb2SStanislav Fomichev printf("\n");
1635ccda64dSRoman Gushchin }
1645ccda64dSRoman Gushchin
1655ccda64dSRoman Gushchin close(prog_fd);
1665ccda64dSRoman Gushchin return 0;
1675ccda64dSRoman Gushchin }
1685ccda64dSRoman Gushchin
count_attached_bpf_progs(int cgroup_fd,enum bpf_attach_type type)1692058b383SRoman Gushchin static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
1702058b383SRoman Gushchin {
1712058b383SRoman Gushchin __u32 prog_cnt = 0;
1722058b383SRoman Gushchin int ret;
1732058b383SRoman Gushchin
174a98bf573SJakub Kicinski ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
175a98bf573SJakub Kicinski NULL, &prog_cnt);
1762058b383SRoman Gushchin if (ret)
1772058b383SRoman Gushchin return -1;
1782058b383SRoman Gushchin
1792058b383SRoman Gushchin return prog_cnt;
1802058b383SRoman Gushchin }
1812058b383SRoman Gushchin
cgroup_has_attached_progs(int cgroup_fd)1821162f844SHechao Li static int cgroup_has_attached_progs(int cgroup_fd)
1831162f844SHechao Li {
1841162f844SHechao Li enum bpf_attach_type type;
1851162f844SHechao Li bool no_prog = true;
1861162f844SHechao Li
1871162f844SHechao Li for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
1881162f844SHechao Li int count = count_attached_bpf_progs(cgroup_fd, type);
1891162f844SHechao Li
1901162f844SHechao Li if (count < 0 && errno != EINVAL)
1911162f844SHechao Li return -1;
1921162f844SHechao Li
1931162f844SHechao Li if (count > 0) {
1941162f844SHechao Li no_prog = false;
1951162f844SHechao Li break;
1961162f844SHechao Li }
1971162f844SHechao Li }
1981162f844SHechao Li
1991162f844SHechao Li return no_prog ? 0 : 1;
2001162f844SHechao Li }
201bdcee1b0SPu Lehui
show_effective_bpf_progs(int cgroup_fd,enum bpf_attach_type type,int level)202bdcee1b0SPu Lehui static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
203bdcee1b0SPu Lehui int level)
204bdcee1b0SPu Lehui {
205bdcee1b0SPu Lehui LIBBPF_OPTS(bpf_prog_query_opts, p);
206bdcee1b0SPu Lehui __u32 prog_ids[1024] = {0};
207bdcee1b0SPu Lehui __u32 iter;
208bdcee1b0SPu Lehui int ret;
209bdcee1b0SPu Lehui
210bdcee1b0SPu Lehui p.query_flags = query_flags;
211bdcee1b0SPu Lehui p.prog_cnt = ARRAY_SIZE(prog_ids);
212bdcee1b0SPu Lehui p.prog_ids = prog_ids;
213bdcee1b0SPu Lehui
214bdcee1b0SPu Lehui ret = bpf_prog_query_opts(cgroup_fd, type, &p);
215bdcee1b0SPu Lehui if (ret)
216bdcee1b0SPu Lehui return ret;
217bdcee1b0SPu Lehui
218bdcee1b0SPu Lehui if (p.prog_cnt == 0)
219bdcee1b0SPu Lehui return 0;
220bdcee1b0SPu Lehui
221bdcee1b0SPu Lehui for (iter = 0; iter < p.prog_cnt; iter++)
222bdcee1b0SPu Lehui show_bpf_prog(prog_ids[iter], type, NULL, level);
223bdcee1b0SPu Lehui
224bdcee1b0SPu Lehui return 0;
225bdcee1b0SPu Lehui }
226bdcee1b0SPu Lehui
show_attached_bpf_progs(int cgroup_fd,enum bpf_attach_type type,int level)2272058b383SRoman Gushchin static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
2282058b383SRoman Gushchin int level)
2295ccda64dSRoman Gushchin {
230596f5fb2SStanislav Fomichev LIBBPF_OPTS(bpf_prog_query_opts, p);
231596f5fb2SStanislav Fomichev __u32 prog_attach_flags[1024] = {0};
232a9436dcaSQuentin Monnet const char *attach_flags_str;
2335ccda64dSRoman Gushchin __u32 prog_ids[1024] = {0};
2345ccda64dSRoman Gushchin char buf[32];
235596f5fb2SStanislav Fomichev __u32 iter;
2365ccda64dSRoman Gushchin int ret;
2375ccda64dSRoman Gushchin
238596f5fb2SStanislav Fomichev p.query_flags = query_flags;
239596f5fb2SStanislav Fomichev p.prog_cnt = ARRAY_SIZE(prog_ids);
240596f5fb2SStanislav Fomichev p.prog_ids = prog_ids;
241596f5fb2SStanislav Fomichev p.prog_attach_flags = prog_attach_flags;
242596f5fb2SStanislav Fomichev
243596f5fb2SStanislav Fomichev ret = bpf_prog_query_opts(cgroup_fd, type, &p);
2445ccda64dSRoman Gushchin if (ret)
2455ccda64dSRoman Gushchin return ret;
2465ccda64dSRoman Gushchin
247596f5fb2SStanislav Fomichev if (p.prog_cnt == 0)
2485ccda64dSRoman Gushchin return 0;
2495ccda64dSRoman Gushchin
250596f5fb2SStanislav Fomichev for (iter = 0; iter < p.prog_cnt; iter++) {
251596f5fb2SStanislav Fomichev __u32 attach_flags;
252596f5fb2SStanislav Fomichev
253596f5fb2SStanislav Fomichev attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
254596f5fb2SStanislav Fomichev
2555ccda64dSRoman Gushchin switch (attach_flags) {
2565ccda64dSRoman Gushchin case BPF_F_ALLOW_MULTI:
2575ccda64dSRoman Gushchin attach_flags_str = "multi";
2585ccda64dSRoman Gushchin break;
2595ccda64dSRoman Gushchin case BPF_F_ALLOW_OVERRIDE:
2605ccda64dSRoman Gushchin attach_flags_str = "override";
2615ccda64dSRoman Gushchin break;
2625ccda64dSRoman Gushchin case 0:
2635ccda64dSRoman Gushchin attach_flags_str = "";
2645ccda64dSRoman Gushchin break;
2655ccda64dSRoman Gushchin default:
2665ccda64dSRoman Gushchin snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
2675ccda64dSRoman Gushchin attach_flags_str = buf;
2685ccda64dSRoman Gushchin }
2695ccda64dSRoman Gushchin
27050325b17SAndrii Nakryiko show_bpf_prog(prog_ids[iter], type,
2712058b383SRoman Gushchin attach_flags_str, level);
272596f5fb2SStanislav Fomichev }
2735ccda64dSRoman Gushchin
2745ccda64dSRoman Gushchin return 0;
2755ccda64dSRoman Gushchin }
2765ccda64dSRoman Gushchin
show_bpf_progs(int cgroup_fd,enum bpf_attach_type type,int level)277bdcee1b0SPu Lehui static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
278bdcee1b0SPu Lehui int level)
279bdcee1b0SPu Lehui {
280bdcee1b0SPu Lehui return query_flags & BPF_F_QUERY_EFFECTIVE ?
281bdcee1b0SPu Lehui show_effective_bpf_progs(cgroup_fd, type, level) :
282bdcee1b0SPu Lehui show_attached_bpf_progs(cgroup_fd, type, level);
283bdcee1b0SPu Lehui }
284bdcee1b0SPu Lehui
do_show(int argc,char ** argv)28565b875bcSJakub Kicinski static int do_show(int argc, char **argv)
2865ccda64dSRoman Gushchin {
2875ccda64dSRoman Gushchin enum bpf_attach_type type;
2881162f844SHechao Li int has_attached_progs;
289a98bf573SJakub Kicinski const char *path;
2905ccda64dSRoman Gushchin int cgroup_fd;
2915ccda64dSRoman Gushchin int ret = -1;
2925ccda64dSRoman Gushchin
293a98bf573SJakub Kicinski query_flags = 0;
294a98bf573SJakub Kicinski
295a98bf573SJakub Kicinski if (!REQ_ARGS(1))
296a98bf573SJakub Kicinski return -1;
297a98bf573SJakub Kicinski path = GET_ARG();
298a98bf573SJakub Kicinski
299a98bf573SJakub Kicinski while (argc) {
300a98bf573SJakub Kicinski if (is_prefix(*argv, "effective")) {
301a98bf573SJakub Kicinski if (query_flags & BPF_F_QUERY_EFFECTIVE) {
302a98bf573SJakub Kicinski p_err("duplicated argument: %s", *argv);
303a98bf573SJakub Kicinski return -1;
304a98bf573SJakub Kicinski }
305a98bf573SJakub Kicinski query_flags |= BPF_F_QUERY_EFFECTIVE;
306a98bf573SJakub Kicinski NEXT_ARG();
307a98bf573SJakub Kicinski } else {
308a98bf573SJakub Kicinski p_err("expected no more arguments, 'effective', got: '%s'?",
309a98bf573SJakub Kicinski *argv);
310a98bf573SJakub Kicinski return -1;
311a98bf573SJakub Kicinski }
3125ccda64dSRoman Gushchin }
3135ccda64dSRoman Gushchin
314a98bf573SJakub Kicinski cgroup_fd = open(path, O_RDONLY);
3155ccda64dSRoman Gushchin if (cgroup_fd < 0) {
316a98bf573SJakub Kicinski p_err("can't open cgroup %s", path);
3175ccda64dSRoman Gushchin goto exit;
3185ccda64dSRoman Gushchin }
3195ccda64dSRoman Gushchin
3201162f844SHechao Li has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
3211162f844SHechao Li if (has_attached_progs < 0) {
3221162f844SHechao Li p_err("can't query bpf programs attached to %s: %s",
3231162f844SHechao Li path, strerror(errno));
3241162f844SHechao Li goto exit_cgroup;
3251162f844SHechao Li } else if (!has_attached_progs) {
3261162f844SHechao Li ret = 0;
3271162f844SHechao Li goto exit_cgroup;
3281162f844SHechao Li }
3291162f844SHechao Li
3305ccda64dSRoman Gushchin if (json_output)
3315ccda64dSRoman Gushchin jsonw_start_array(json_wtr);
332bdcee1b0SPu Lehui else if (query_flags & BPF_F_QUERY_EFFECTIVE)
333bdcee1b0SPu Lehui printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
3345ccda64dSRoman Gushchin else
3355ccda64dSRoman Gushchin printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
3365ccda64dSRoman Gushchin "AttachFlags", "Name");
3375ccda64dSRoman Gushchin
338596f5fb2SStanislav Fomichev btf_vmlinux = libbpf_find_kernel_btf();
3395ccda64dSRoman Gushchin for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
3405ccda64dSRoman Gushchin /*
3415ccda64dSRoman Gushchin * Not all attach types may be supported, so it's expected,
3425ccda64dSRoman Gushchin * that some requests will fail.
34365b875bcSJakub Kicinski * If we were able to get the show for at least one
3445ccda64dSRoman Gushchin * attach type, let's return 0.
3455ccda64dSRoman Gushchin */
346bdcee1b0SPu Lehui if (show_bpf_progs(cgroup_fd, type, 0) == 0)
3475ccda64dSRoman Gushchin ret = 0;
3485ccda64dSRoman Gushchin }
3495ccda64dSRoman Gushchin
3505ccda64dSRoman Gushchin if (json_output)
3515ccda64dSRoman Gushchin jsonw_end_array(json_wtr);
3525ccda64dSRoman Gushchin
3531162f844SHechao Li exit_cgroup:
3545ccda64dSRoman Gushchin close(cgroup_fd);
3555ccda64dSRoman Gushchin exit:
3565ccda64dSRoman Gushchin return ret;
3575ccda64dSRoman Gushchin }
3585ccda64dSRoman Gushchin
3592058b383SRoman Gushchin /*
3602058b383SRoman Gushchin * To distinguish nftw() errors and do_show_tree_fn() errors
3612058b383SRoman Gushchin * and avoid duplicating error messages, let's return -2
3622058b383SRoman Gushchin * from do_show_tree_fn() in case of error.
3632058b383SRoman Gushchin */
3642058b383SRoman Gushchin #define NFTW_ERR -1
3652058b383SRoman Gushchin #define SHOW_TREE_FN_ERR -2
do_show_tree_fn(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftw)3662058b383SRoman Gushchin static int do_show_tree_fn(const char *fpath, const struct stat *sb,
3672058b383SRoman Gushchin int typeflag, struct FTW *ftw)
3682058b383SRoman Gushchin {
3692058b383SRoman Gushchin enum bpf_attach_type type;
3701162f844SHechao Li int has_attached_progs;
3712058b383SRoman Gushchin int cgroup_fd;
3722058b383SRoman Gushchin
3732058b383SRoman Gushchin if (typeflag != FTW_D)
3742058b383SRoman Gushchin return 0;
3752058b383SRoman Gushchin
3762058b383SRoman Gushchin cgroup_fd = open(fpath, O_RDONLY);
3772058b383SRoman Gushchin if (cgroup_fd < 0) {
3782058b383SRoman Gushchin p_err("can't open cgroup %s: %s", fpath, strerror(errno));
3792058b383SRoman Gushchin return SHOW_TREE_FN_ERR;
3802058b383SRoman Gushchin }
3812058b383SRoman Gushchin
3821162f844SHechao Li has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
3831162f844SHechao Li if (has_attached_progs < 0) {
3842058b383SRoman Gushchin p_err("can't query bpf programs attached to %s: %s",
3852058b383SRoman Gushchin fpath, strerror(errno));
3862058b383SRoman Gushchin close(cgroup_fd);
3872058b383SRoman Gushchin return SHOW_TREE_FN_ERR;
3881162f844SHechao Li } else if (!has_attached_progs) {
3892058b383SRoman Gushchin close(cgroup_fd);
3902058b383SRoman Gushchin return 0;
3912058b383SRoman Gushchin }
3922058b383SRoman Gushchin
3932058b383SRoman Gushchin if (json_output) {
3942058b383SRoman Gushchin jsonw_start_object(json_wtr);
3952058b383SRoman Gushchin jsonw_string_field(json_wtr, "cgroup", fpath);
3962058b383SRoman Gushchin jsonw_name(json_wtr, "programs");
3972058b383SRoman Gushchin jsonw_start_array(json_wtr);
3982058b383SRoman Gushchin } else {
3992058b383SRoman Gushchin printf("%s\n", fpath);
4002058b383SRoman Gushchin }
4012058b383SRoman Gushchin
402596f5fb2SStanislav Fomichev btf_vmlinux = libbpf_find_kernel_btf();
4032058b383SRoman Gushchin for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
404bdcee1b0SPu Lehui show_bpf_progs(cgroup_fd, type, ftw->level);
4052058b383SRoman Gushchin
40639c9f106SQuentin Monnet if (errno == EINVAL)
40739c9f106SQuentin Monnet /* Last attach type does not support query.
40839c9f106SQuentin Monnet * Do not report an error for this, especially because batch
40939c9f106SQuentin Monnet * mode would stop processing commands.
41039c9f106SQuentin Monnet */
41139c9f106SQuentin Monnet errno = 0;
41239c9f106SQuentin Monnet
4132058b383SRoman Gushchin if (json_output) {
4142058b383SRoman Gushchin jsonw_end_array(json_wtr);
4152058b383SRoman Gushchin jsonw_end_object(json_wtr);
4162058b383SRoman Gushchin }
4172058b383SRoman Gushchin
4182058b383SRoman Gushchin close(cgroup_fd);
4192058b383SRoman Gushchin
4202058b383SRoman Gushchin return 0;
4212058b383SRoman Gushchin }
4222058b383SRoman Gushchin
find_cgroup_root(void)4232058b383SRoman Gushchin static char *find_cgroup_root(void)
4242058b383SRoman Gushchin {
4252058b383SRoman Gushchin struct mntent *mnt;
4262058b383SRoman Gushchin FILE *f;
4272058b383SRoman Gushchin
4282058b383SRoman Gushchin f = fopen("/proc/mounts", "r");
4292058b383SRoman Gushchin if (f == NULL)
4302058b383SRoman Gushchin return NULL;
4312058b383SRoman Gushchin
4322058b383SRoman Gushchin while ((mnt = getmntent(f))) {
4332058b383SRoman Gushchin if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
4342058b383SRoman Gushchin fclose(f);
4352058b383SRoman Gushchin return strdup(mnt->mnt_dir);
4362058b383SRoman Gushchin }
4372058b383SRoman Gushchin }
4382058b383SRoman Gushchin
4392058b383SRoman Gushchin fclose(f);
4402058b383SRoman Gushchin return NULL;
4412058b383SRoman Gushchin }
4422058b383SRoman Gushchin
do_show_tree(int argc,char ** argv)4432058b383SRoman Gushchin static int do_show_tree(int argc, char **argv)
4442058b383SRoman Gushchin {
445a98bf573SJakub Kicinski char *cgroup_root, *cgroup_alloced = NULL;
4462058b383SRoman Gushchin int ret;
4472058b383SRoman Gushchin
448a98bf573SJakub Kicinski query_flags = 0;
449a98bf573SJakub Kicinski
450a98bf573SJakub Kicinski if (!argc) {
451a98bf573SJakub Kicinski cgroup_alloced = find_cgroup_root();
452a98bf573SJakub Kicinski if (!cgroup_alloced) {
4532058b383SRoman Gushchin p_err("cgroup v2 isn't mounted");
4542058b383SRoman Gushchin return -1;
4552058b383SRoman Gushchin }
456a98bf573SJakub Kicinski cgroup_root = cgroup_alloced;
457a98bf573SJakub Kicinski } else {
458a98bf573SJakub Kicinski cgroup_root = GET_ARG();
459a98bf573SJakub Kicinski
460a98bf573SJakub Kicinski while (argc) {
461a98bf573SJakub Kicinski if (is_prefix(*argv, "effective")) {
462a98bf573SJakub Kicinski if (query_flags & BPF_F_QUERY_EFFECTIVE) {
463a98bf573SJakub Kicinski p_err("duplicated argument: %s", *argv);
4642058b383SRoman Gushchin return -1;
4652058b383SRoman Gushchin }
466a98bf573SJakub Kicinski query_flags |= BPF_F_QUERY_EFFECTIVE;
467a98bf573SJakub Kicinski NEXT_ARG();
468a98bf573SJakub Kicinski } else {
469a98bf573SJakub Kicinski p_err("expected no more arguments, 'effective', got: '%s'?",
470a98bf573SJakub Kicinski *argv);
471a98bf573SJakub Kicinski return -1;
472a98bf573SJakub Kicinski }
473a98bf573SJakub Kicinski }
474a98bf573SJakub Kicinski }
4752058b383SRoman Gushchin
4762058b383SRoman Gushchin if (json_output)
4772058b383SRoman Gushchin jsonw_start_array(json_wtr);
478bdcee1b0SPu Lehui else if (query_flags & BPF_F_QUERY_EFFECTIVE)
479bdcee1b0SPu Lehui printf("%s\n"
480bdcee1b0SPu Lehui "%-8s %-15s %-15s\n",
481bdcee1b0SPu Lehui "CgroupPath",
482bdcee1b0SPu Lehui "ID", "AttachType", "Name");
4832058b383SRoman Gushchin else
4842058b383SRoman Gushchin printf("%s\n"
4852058b383SRoman Gushchin "%-8s %-15s %-15s %-15s\n",
4862058b383SRoman Gushchin "CgroupPath",
4872058b383SRoman Gushchin "ID", "AttachType", "AttachFlags", "Name");
4882058b383SRoman Gushchin
4892058b383SRoman Gushchin switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
4902058b383SRoman Gushchin case NFTW_ERR:
4912058b383SRoman Gushchin p_err("can't iterate over %s: %s", cgroup_root,
4922058b383SRoman Gushchin strerror(errno));
4932058b383SRoman Gushchin ret = -1;
4942058b383SRoman Gushchin break;
4952058b383SRoman Gushchin case SHOW_TREE_FN_ERR:
4962058b383SRoman Gushchin ret = -1;
4972058b383SRoman Gushchin break;
4982058b383SRoman Gushchin default:
4992058b383SRoman Gushchin ret = 0;
5002058b383SRoman Gushchin }
5012058b383SRoman Gushchin
5022058b383SRoman Gushchin if (json_output)
5032058b383SRoman Gushchin jsonw_end_array(json_wtr);
5042058b383SRoman Gushchin
505a98bf573SJakub Kicinski free(cgroup_alloced);
5062058b383SRoman Gushchin
5072058b383SRoman Gushchin return ret;
5082058b383SRoman Gushchin }
5092058b383SRoman Gushchin
do_attach(int argc,char ** argv)5105ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
5115ccda64dSRoman Gushchin {
5125ccda64dSRoman Gushchin enum bpf_attach_type attach_type;
5135ccda64dSRoman Gushchin int cgroup_fd, prog_fd;
5145ccda64dSRoman Gushchin int attach_flags = 0;
5155ccda64dSRoman Gushchin int ret = -1;
5165ccda64dSRoman Gushchin int i;
5175ccda64dSRoman Gushchin
5185ccda64dSRoman Gushchin if (argc < 4) {
519b4fac96dSJakub Kicinski p_err("too few parameters for cgroup attach");
5205ccda64dSRoman Gushchin goto exit;
5215ccda64dSRoman Gushchin }
5225ccda64dSRoman Gushchin
5235ccda64dSRoman Gushchin cgroup_fd = open(argv[0], O_RDONLY);
5245ccda64dSRoman Gushchin if (cgroup_fd < 0) {
5256c6874f4SJakub Kicinski p_err("can't open cgroup %s", argv[0]);
5265ccda64dSRoman Gushchin goto exit;
5275ccda64dSRoman Gushchin }
5285ccda64dSRoman Gushchin
5295ccda64dSRoman Gushchin attach_type = parse_attach_type(argv[1]);
5305ccda64dSRoman Gushchin if (attach_type == __MAX_BPF_ATTACH_TYPE) {
531b4fac96dSJakub Kicinski p_err("invalid attach type");
5325ccda64dSRoman Gushchin goto exit_cgroup;
5335ccda64dSRoman Gushchin }
5345ccda64dSRoman Gushchin
5355ccda64dSRoman Gushchin argc -= 2;
5365ccda64dSRoman Gushchin argv = &argv[2];
5375ccda64dSRoman Gushchin prog_fd = prog_parse_fd(&argc, &argv);
5385ccda64dSRoman Gushchin if (prog_fd < 0)
5395ccda64dSRoman Gushchin goto exit_cgroup;
5405ccda64dSRoman Gushchin
5415ccda64dSRoman Gushchin for (i = 0; i < argc; i++) {
5425ccda64dSRoman Gushchin if (is_prefix(argv[i], "multi")) {
5435ccda64dSRoman Gushchin attach_flags |= BPF_F_ALLOW_MULTI;
5445ccda64dSRoman Gushchin } else if (is_prefix(argv[i], "override")) {
5455ccda64dSRoman Gushchin attach_flags |= BPF_F_ALLOW_OVERRIDE;
5465ccda64dSRoman Gushchin } else {
547b4fac96dSJakub Kicinski p_err("unknown option: %s", argv[i]);
5485ccda64dSRoman Gushchin goto exit_cgroup;
5495ccda64dSRoman Gushchin }
5505ccda64dSRoman Gushchin }
5515ccda64dSRoman Gushchin
5525ccda64dSRoman Gushchin if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
5535ccda64dSRoman Gushchin p_err("failed to attach program");
5545ccda64dSRoman Gushchin goto exit_prog;
5555ccda64dSRoman Gushchin }
5565ccda64dSRoman Gushchin
5575ccda64dSRoman Gushchin if (json_output)
5585ccda64dSRoman Gushchin jsonw_null(json_wtr);
5595ccda64dSRoman Gushchin
5605ccda64dSRoman Gushchin ret = 0;
5615ccda64dSRoman Gushchin
5625ccda64dSRoman Gushchin exit_prog:
5635ccda64dSRoman Gushchin close(prog_fd);
5645ccda64dSRoman Gushchin exit_cgroup:
5655ccda64dSRoman Gushchin close(cgroup_fd);
5665ccda64dSRoman Gushchin exit:
5675ccda64dSRoman Gushchin return ret;
5685ccda64dSRoman Gushchin }
5695ccda64dSRoman Gushchin
do_detach(int argc,char ** argv)5705ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
5715ccda64dSRoman Gushchin {
5725ccda64dSRoman Gushchin enum bpf_attach_type attach_type;
5735ccda64dSRoman Gushchin int prog_fd, cgroup_fd;
5745ccda64dSRoman Gushchin int ret = -1;
5755ccda64dSRoman Gushchin
5765ccda64dSRoman Gushchin if (argc < 4) {
577b4fac96dSJakub Kicinski p_err("too few parameters for cgroup detach");
5785ccda64dSRoman Gushchin goto exit;
5795ccda64dSRoman Gushchin }
5805ccda64dSRoman Gushchin
5815ccda64dSRoman Gushchin cgroup_fd = open(argv[0], O_RDONLY);
5825ccda64dSRoman Gushchin if (cgroup_fd < 0) {
5836c6874f4SJakub Kicinski p_err("can't open cgroup %s", argv[0]);
5845ccda64dSRoman Gushchin goto exit;
5855ccda64dSRoman Gushchin }
5865ccda64dSRoman Gushchin
5875ccda64dSRoman Gushchin attach_type = parse_attach_type(argv[1]);
5885ccda64dSRoman Gushchin if (attach_type == __MAX_BPF_ATTACH_TYPE) {
5895ccda64dSRoman Gushchin p_err("invalid attach type");
5905ccda64dSRoman Gushchin goto exit_cgroup;
5915ccda64dSRoman Gushchin }
5925ccda64dSRoman Gushchin
5935ccda64dSRoman Gushchin argc -= 2;
5945ccda64dSRoman Gushchin argv = &argv[2];
5955ccda64dSRoman Gushchin prog_fd = prog_parse_fd(&argc, &argv);
5965ccda64dSRoman Gushchin if (prog_fd < 0)
5975ccda64dSRoman Gushchin goto exit_cgroup;
5985ccda64dSRoman Gushchin
5995ccda64dSRoman Gushchin if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
6005ccda64dSRoman Gushchin p_err("failed to detach program");
6015ccda64dSRoman Gushchin goto exit_prog;
6025ccda64dSRoman Gushchin }
6035ccda64dSRoman Gushchin
6045ccda64dSRoman Gushchin if (json_output)
6055ccda64dSRoman Gushchin jsonw_null(json_wtr);
6065ccda64dSRoman Gushchin
6075ccda64dSRoman Gushchin ret = 0;
6085ccda64dSRoman Gushchin
6095ccda64dSRoman Gushchin exit_prog:
6105ccda64dSRoman Gushchin close(prog_fd);
6115ccda64dSRoman Gushchin exit_cgroup:
6125ccda64dSRoman Gushchin close(cgroup_fd);
6135ccda64dSRoman Gushchin exit:
6145ccda64dSRoman Gushchin return ret;
6155ccda64dSRoman Gushchin }
6165ccda64dSRoman Gushchin
do_help(int argc,char ** argv)6175ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
6185ccda64dSRoman Gushchin {
6195ccda64dSRoman Gushchin if (json_output) {
6205ccda64dSRoman Gushchin jsonw_null(json_wtr);
6215ccda64dSRoman Gushchin return 0;
6225ccda64dSRoman Gushchin }
6235ccda64dSRoman Gushchin
6245ccda64dSRoman Gushchin fprintf(stderr,
62590040351SQuentin Monnet "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
62690040351SQuentin Monnet " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
62790040351SQuentin Monnet " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
62890040351SQuentin Monnet " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
62990040351SQuentin Monnet " %1$s %2$s help\n"
6305ccda64dSRoman Gushchin "\n"
631393de512SAndrey Ignatov HELP_SPEC_ATTACH_TYPES "\n"
6325ccda64dSRoman Gushchin " " HELP_SPEC_ATTACH_FLAGS "\n"
6335ccda64dSRoman Gushchin " " HELP_SPEC_PROGRAM "\n"
634c07ba629SQuentin Monnet " " HELP_SPEC_OPTIONS " |\n"
6358cc8c635SQuentin Monnet " {-f|--bpffs} }\n"
6365ccda64dSRoman Gushchin "",
63790040351SQuentin Monnet bin_name, argv[-2]);
6385ccda64dSRoman Gushchin
6395ccda64dSRoman Gushchin return 0;
6405ccda64dSRoman Gushchin }
6415ccda64dSRoman Gushchin
6425ccda64dSRoman Gushchin static const struct cmd cmds[] = {
6436ebe6dbdSJakub Kicinski { "show", do_show },
64465b875bcSJakub Kicinski { "list", do_show },
6452058b383SRoman Gushchin { "tree", do_show_tree },
6465ccda64dSRoman Gushchin { "attach", do_attach },
6475ccda64dSRoman Gushchin { "detach", do_detach },
6485ccda64dSRoman Gushchin { "help", do_help },
6495ccda64dSRoman Gushchin { 0 }
6505ccda64dSRoman Gushchin };
6515ccda64dSRoman Gushchin
do_cgroup(int argc,char ** argv)6525ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
6535ccda64dSRoman Gushchin {
6545ccda64dSRoman Gushchin return cmd_select(cmds, argc, argv, do_help);
6555ccda64dSRoman Gushchin }
656