1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 // Copyright (C) 2017 Facebook
3 // Author: Roman Gushchin <guro@fb.com>
5 #define _XOPEN_SOURCE 500
14 #include <sys/types.h>
21 #define HELP_SPEC_ATTACH_FLAGS \
22 "ATTACH_FLAGS := { multi | override }"
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 | getpeername4 | getpeername6 |\n" \
29 " getsockname4 | getsockname6 | sendmsg4 |\n" \
30 " sendmsg6 | recvmsg4 | recvmsg6 |\n" \
31 " sysctl | getsockopt | setsockopt }"
33 static unsigned int query_flags
;
35 static enum bpf_attach_type
parse_attach_type(const char *str
)
37 enum bpf_attach_type type
;
39 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
40 if (attach_type_name
[type
] &&
41 is_prefix(str
, attach_type_name
[type
]))
45 return __MAX_BPF_ATTACH_TYPE
;
48 static int show_bpf_prog(int id
, enum bpf_attach_type attach_type
,
49 const char *attach_flags_str
,
52 struct bpf_prog_info info
= {};
53 __u32 info_len
= sizeof(info
);
56 prog_fd
= bpf_prog_get_fd_by_id(id
);
60 if (bpf_obj_get_info_by_fd(prog_fd
, &info
, &info_len
)) {
66 jsonw_start_object(json_wtr
);
67 jsonw_uint_field(json_wtr
, "id", info
.id
);
68 if (attach_type
< ARRAY_SIZE(attach_type_name
))
69 jsonw_string_field(json_wtr
, "attach_type",
70 attach_type_name
[attach_type
]);
72 jsonw_uint_field(json_wtr
, "attach_type", attach_type
);
73 jsonw_string_field(json_wtr
, "attach_flags",
75 jsonw_string_field(json_wtr
, "name", info
.name
);
76 jsonw_end_object(json_wtr
);
78 printf("%s%-8u ", level
? " " : "", info
.id
);
79 if (attach_type
< ARRAY_SIZE(attach_type_name
))
80 printf("%-15s", attach_type_name
[attach_type
]);
82 printf("type %-10u", attach_type
);
83 printf(" %-15s %-15s\n", attach_flags_str
, info
.name
);
90 static int count_attached_bpf_progs(int cgroup_fd
, enum bpf_attach_type type
)
95 ret
= bpf_prog_query(cgroup_fd
, type
, query_flags
, NULL
,
103 static int cgroup_has_attached_progs(int cgroup_fd
)
105 enum bpf_attach_type type
;
108 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
109 int count
= count_attached_bpf_progs(cgroup_fd
, type
);
111 if (count
< 0 && errno
!= EINVAL
)
120 return no_prog
? 0 : 1;
122 static int show_attached_bpf_progs(int cgroup_fd
, enum bpf_attach_type type
,
125 const char *attach_flags_str
;
126 __u32 prog_ids
[1024] = {0};
127 __u32 prog_cnt
, iter
;
132 prog_cnt
= ARRAY_SIZE(prog_ids
);
133 ret
= bpf_prog_query(cgroup_fd
, type
, query_flags
, &attach_flags
,
134 prog_ids
, &prog_cnt
);
141 switch (attach_flags
) {
142 case BPF_F_ALLOW_MULTI
:
143 attach_flags_str
= "multi";
145 case BPF_F_ALLOW_OVERRIDE
:
146 attach_flags_str
= "override";
149 attach_flags_str
= "";
152 snprintf(buf
, sizeof(buf
), "unknown(%x)", attach_flags
);
153 attach_flags_str
= buf
;
156 for (iter
= 0; iter
< prog_cnt
; iter
++)
157 show_bpf_prog(prog_ids
[iter
], type
,
158 attach_flags_str
, level
);
163 static int do_show(int argc
, char **argv
)
165 enum bpf_attach_type type
;
166 int has_attached_progs
;
178 if (is_prefix(*argv
, "effective")) {
179 if (query_flags
& BPF_F_QUERY_EFFECTIVE
) {
180 p_err("duplicated argument: %s", *argv
);
183 query_flags
|= BPF_F_QUERY_EFFECTIVE
;
186 p_err("expected no more arguments, 'effective', got: '%s'?",
192 cgroup_fd
= open(path
, O_RDONLY
);
194 p_err("can't open cgroup %s", path
);
198 has_attached_progs
= cgroup_has_attached_progs(cgroup_fd
);
199 if (has_attached_progs
< 0) {
200 p_err("can't query bpf programs attached to %s: %s",
201 path
, strerror(errno
));
203 } else if (!has_attached_progs
) {
209 jsonw_start_array(json_wtr
);
211 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
212 "AttachFlags", "Name");
214 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
216 * Not all attach types may be supported, so it's expected,
217 * that some requests will fail.
218 * If we were able to get the show for at least one
219 * attach type, let's return 0.
221 if (show_attached_bpf_progs(cgroup_fd
, type
, 0) == 0)
226 jsonw_end_array(json_wtr
);
235 * To distinguish nftw() errors and do_show_tree_fn() errors
236 * and avoid duplicating error messages, let's return -2
237 * from do_show_tree_fn() in case of error.
240 #define SHOW_TREE_FN_ERR -2
241 static int do_show_tree_fn(const char *fpath
, const struct stat
*sb
,
242 int typeflag
, struct FTW
*ftw
)
244 enum bpf_attach_type type
;
245 int has_attached_progs
;
248 if (typeflag
!= FTW_D
)
251 cgroup_fd
= open(fpath
, O_RDONLY
);
253 p_err("can't open cgroup %s: %s", fpath
, strerror(errno
));
254 return SHOW_TREE_FN_ERR
;
257 has_attached_progs
= cgroup_has_attached_progs(cgroup_fd
);
258 if (has_attached_progs
< 0) {
259 p_err("can't query bpf programs attached to %s: %s",
260 fpath
, strerror(errno
));
262 return SHOW_TREE_FN_ERR
;
263 } else if (!has_attached_progs
) {
269 jsonw_start_object(json_wtr
);
270 jsonw_string_field(json_wtr
, "cgroup", fpath
);
271 jsonw_name(json_wtr
, "programs");
272 jsonw_start_array(json_wtr
);
274 printf("%s\n", fpath
);
277 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++)
278 show_attached_bpf_progs(cgroup_fd
, type
, ftw
->level
);
281 /* Last attach type does not support query.
282 * Do not report an error for this, especially because batch
283 * mode would stop processing commands.
288 jsonw_end_array(json_wtr
);
289 jsonw_end_object(json_wtr
);
297 static char *find_cgroup_root(void)
302 f
= fopen("/proc/mounts", "r");
306 while ((mnt
= getmntent(f
))) {
307 if (strcmp(mnt
->mnt_type
, "cgroup2") == 0) {
309 return strdup(mnt
->mnt_dir
);
317 static int do_show_tree(int argc
, char **argv
)
319 char *cgroup_root
, *cgroup_alloced
= NULL
;
325 cgroup_alloced
= find_cgroup_root();
326 if (!cgroup_alloced
) {
327 p_err("cgroup v2 isn't mounted");
330 cgroup_root
= cgroup_alloced
;
332 cgroup_root
= GET_ARG();
335 if (is_prefix(*argv
, "effective")) {
336 if (query_flags
& BPF_F_QUERY_EFFECTIVE
) {
337 p_err("duplicated argument: %s", *argv
);
340 query_flags
|= BPF_F_QUERY_EFFECTIVE
;
343 p_err("expected no more arguments, 'effective', got: '%s'?",
351 jsonw_start_array(json_wtr
);
354 "%-8s %-15s %-15s %-15s\n",
356 "ID", "AttachType", "AttachFlags", "Name");
358 switch (nftw(cgroup_root
, do_show_tree_fn
, 1024, FTW_MOUNT
)) {
360 p_err("can't iterate over %s: %s", cgroup_root
,
364 case SHOW_TREE_FN_ERR
:
372 jsonw_end_array(json_wtr
);
374 free(cgroup_alloced
);
379 static int do_attach(int argc
, char **argv
)
381 enum bpf_attach_type attach_type
;
382 int cgroup_fd
, prog_fd
;
383 int attach_flags
= 0;
388 p_err("too few parameters for cgroup attach");
392 cgroup_fd
= open(argv
[0], O_RDONLY
);
394 p_err("can't open cgroup %s", argv
[0]);
398 attach_type
= parse_attach_type(argv
[1]);
399 if (attach_type
== __MAX_BPF_ATTACH_TYPE
) {
400 p_err("invalid attach type");
406 prog_fd
= prog_parse_fd(&argc
, &argv
);
410 for (i
= 0; i
< argc
; i
++) {
411 if (is_prefix(argv
[i
], "multi")) {
412 attach_flags
|= BPF_F_ALLOW_MULTI
;
413 } else if (is_prefix(argv
[i
], "override")) {
414 attach_flags
|= BPF_F_ALLOW_OVERRIDE
;
416 p_err("unknown option: %s", argv
[i
]);
421 if (bpf_prog_attach(prog_fd
, cgroup_fd
, attach_type
, attach_flags
)) {
422 p_err("failed to attach program");
427 jsonw_null(json_wtr
);
439 static int do_detach(int argc
, char **argv
)
441 enum bpf_attach_type attach_type
;
442 int prog_fd
, cgroup_fd
;
446 p_err("too few parameters for cgroup detach");
450 cgroup_fd
= open(argv
[0], O_RDONLY
);
452 p_err("can't open cgroup %s", argv
[0]);
456 attach_type
= parse_attach_type(argv
[1]);
457 if (attach_type
== __MAX_BPF_ATTACH_TYPE
) {
458 p_err("invalid attach type");
464 prog_fd
= prog_parse_fd(&argc
, &argv
);
468 if (bpf_prog_detach2(prog_fd
, cgroup_fd
, attach_type
)) {
469 p_err("failed to detach program");
474 jsonw_null(json_wtr
);
486 static int do_help(int argc
, char **argv
)
489 jsonw_null(json_wtr
);
494 "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
495 " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
496 " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
497 " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
500 HELP_SPEC_ATTACH_TYPES
"\n"
501 " " HELP_SPEC_ATTACH_FLAGS
"\n"
502 " " HELP_SPEC_PROGRAM
"\n"
503 " " HELP_SPEC_OPTIONS
"\n"
510 static const struct cmd cmds
[] = {
513 { "tree", do_show_tree
},
514 { "attach", do_attach
},
515 { "detach", do_detach
},
520 int do_cgroup(int argc
, char **argv
)
522 return cmd_select(cmds
, argc
, argv
, do_help
);