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 | sendmsg4 | sendmsg6 |\n" \
29 " recvmsg4 | recvmsg6 | sysctl |\n" \
30 " getsockopt | setsockopt }"
32 static unsigned int query_flags
;
34 static const char * const attach_type_strings
[] = {
35 [BPF_CGROUP_INET_INGRESS
] = "ingress",
36 [BPF_CGROUP_INET_EGRESS
] = "egress",
37 [BPF_CGROUP_INET_SOCK_CREATE
] = "sock_create",
38 [BPF_CGROUP_SOCK_OPS
] = "sock_ops",
39 [BPF_CGROUP_DEVICE
] = "device",
40 [BPF_CGROUP_INET4_BIND
] = "bind4",
41 [BPF_CGROUP_INET6_BIND
] = "bind6",
42 [BPF_CGROUP_INET4_CONNECT
] = "connect4",
43 [BPF_CGROUP_INET6_CONNECT
] = "connect6",
44 [BPF_CGROUP_INET4_POST_BIND
] = "post_bind4",
45 [BPF_CGROUP_INET6_POST_BIND
] = "post_bind6",
46 [BPF_CGROUP_UDP4_SENDMSG
] = "sendmsg4",
47 [BPF_CGROUP_UDP6_SENDMSG
] = "sendmsg6",
48 [BPF_CGROUP_SYSCTL
] = "sysctl",
49 [BPF_CGROUP_UDP4_RECVMSG
] = "recvmsg4",
50 [BPF_CGROUP_UDP6_RECVMSG
] = "recvmsg6",
51 [BPF_CGROUP_GETSOCKOPT
] = "getsockopt",
52 [BPF_CGROUP_SETSOCKOPT
] = "setsockopt",
53 [__MAX_BPF_ATTACH_TYPE
] = NULL
,
56 static enum bpf_attach_type
parse_attach_type(const char *str
)
58 enum bpf_attach_type type
;
60 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
61 if (attach_type_strings
[type
] &&
62 is_prefix(str
, attach_type_strings
[type
]))
66 return __MAX_BPF_ATTACH_TYPE
;
69 static int show_bpf_prog(int id
, const char *attach_type_str
,
70 const char *attach_flags_str
,
73 struct bpf_prog_info info
= {};
74 __u32 info_len
= sizeof(info
);
77 prog_fd
= bpf_prog_get_fd_by_id(id
);
81 if (bpf_obj_get_info_by_fd(prog_fd
, &info
, &info_len
)) {
87 jsonw_start_object(json_wtr
);
88 jsonw_uint_field(json_wtr
, "id", info
.id
);
89 jsonw_string_field(json_wtr
, "attach_type",
91 jsonw_string_field(json_wtr
, "attach_flags",
93 jsonw_string_field(json_wtr
, "name", info
.name
);
94 jsonw_end_object(json_wtr
);
96 printf("%s%-8u %-15s %-15s %-15s\n", level
? " " : "",
107 static int count_attached_bpf_progs(int cgroup_fd
, enum bpf_attach_type type
)
112 ret
= bpf_prog_query(cgroup_fd
, type
, query_flags
, NULL
,
120 static int cgroup_has_attached_progs(int cgroup_fd
)
122 enum bpf_attach_type type
;
125 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
126 int count
= count_attached_bpf_progs(cgroup_fd
, type
);
128 if (count
< 0 && errno
!= EINVAL
)
137 return no_prog
? 0 : 1;
139 static int show_attached_bpf_progs(int cgroup_fd
, enum bpf_attach_type type
,
142 const char *attach_flags_str
;
143 __u32 prog_ids
[1024] = {0};
144 __u32 prog_cnt
, iter
;
149 prog_cnt
= ARRAY_SIZE(prog_ids
);
150 ret
= bpf_prog_query(cgroup_fd
, type
, query_flags
, &attach_flags
,
151 prog_ids
, &prog_cnt
);
158 switch (attach_flags
) {
159 case BPF_F_ALLOW_MULTI
:
160 attach_flags_str
= "multi";
162 case BPF_F_ALLOW_OVERRIDE
:
163 attach_flags_str
= "override";
166 attach_flags_str
= "";
169 snprintf(buf
, sizeof(buf
), "unknown(%x)", attach_flags
);
170 attach_flags_str
= buf
;
173 for (iter
= 0; iter
< prog_cnt
; iter
++)
174 show_bpf_prog(prog_ids
[iter
], attach_type_strings
[type
],
175 attach_flags_str
, level
);
180 static int do_show(int argc
, char **argv
)
182 enum bpf_attach_type type
;
183 int has_attached_progs
;
195 if (is_prefix(*argv
, "effective")) {
196 if (query_flags
& BPF_F_QUERY_EFFECTIVE
) {
197 p_err("duplicated argument: %s", *argv
);
200 query_flags
|= BPF_F_QUERY_EFFECTIVE
;
203 p_err("expected no more arguments, 'effective', got: '%s'?",
209 cgroup_fd
= open(path
, O_RDONLY
);
211 p_err("can't open cgroup %s", path
);
215 has_attached_progs
= cgroup_has_attached_progs(cgroup_fd
);
216 if (has_attached_progs
< 0) {
217 p_err("can't query bpf programs attached to %s: %s",
218 path
, strerror(errno
));
220 } else if (!has_attached_progs
) {
226 jsonw_start_array(json_wtr
);
228 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
229 "AttachFlags", "Name");
231 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
233 * Not all attach types may be supported, so it's expected,
234 * that some requests will fail.
235 * If we were able to get the show for at least one
236 * attach type, let's return 0.
238 if (show_attached_bpf_progs(cgroup_fd
, type
, 0) == 0)
243 jsonw_end_array(json_wtr
);
252 * To distinguish nftw() errors and do_show_tree_fn() errors
253 * and avoid duplicating error messages, let's return -2
254 * from do_show_tree_fn() in case of error.
257 #define SHOW_TREE_FN_ERR -2
258 static int do_show_tree_fn(const char *fpath
, const struct stat
*sb
,
259 int typeflag
, struct FTW
*ftw
)
261 enum bpf_attach_type type
;
262 int has_attached_progs
;
265 if (typeflag
!= FTW_D
)
268 cgroup_fd
= open(fpath
, O_RDONLY
);
270 p_err("can't open cgroup %s: %s", fpath
, strerror(errno
));
271 return SHOW_TREE_FN_ERR
;
274 has_attached_progs
= cgroup_has_attached_progs(cgroup_fd
);
275 if (has_attached_progs
< 0) {
276 p_err("can't query bpf programs attached to %s: %s",
277 fpath
, strerror(errno
));
279 return SHOW_TREE_FN_ERR
;
280 } else if (!has_attached_progs
) {
286 jsonw_start_object(json_wtr
);
287 jsonw_string_field(json_wtr
, "cgroup", fpath
);
288 jsonw_name(json_wtr
, "programs");
289 jsonw_start_array(json_wtr
);
291 printf("%s\n", fpath
);
294 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++)
295 show_attached_bpf_progs(cgroup_fd
, type
, ftw
->level
);
298 /* Last attach type does not support query.
299 * Do not report an error for this, especially because batch
300 * mode would stop processing commands.
305 jsonw_end_array(json_wtr
);
306 jsonw_end_object(json_wtr
);
314 static char *find_cgroup_root(void)
319 f
= fopen("/proc/mounts", "r");
323 while ((mnt
= getmntent(f
))) {
324 if (strcmp(mnt
->mnt_type
, "cgroup2") == 0) {
326 return strdup(mnt
->mnt_dir
);
334 static int do_show_tree(int argc
, char **argv
)
336 char *cgroup_root
, *cgroup_alloced
= NULL
;
342 cgroup_alloced
= find_cgroup_root();
343 if (!cgroup_alloced
) {
344 p_err("cgroup v2 isn't mounted");
347 cgroup_root
= cgroup_alloced
;
349 cgroup_root
= GET_ARG();
352 if (is_prefix(*argv
, "effective")) {
353 if (query_flags
& BPF_F_QUERY_EFFECTIVE
) {
354 p_err("duplicated argument: %s", *argv
);
357 query_flags
|= BPF_F_QUERY_EFFECTIVE
;
360 p_err("expected no more arguments, 'effective', got: '%s'?",
368 jsonw_start_array(json_wtr
);
371 "%-8s %-15s %-15s %-15s\n",
373 "ID", "AttachType", "AttachFlags", "Name");
375 switch (nftw(cgroup_root
, do_show_tree_fn
, 1024, FTW_MOUNT
)) {
377 p_err("can't iterate over %s: %s", cgroup_root
,
381 case SHOW_TREE_FN_ERR
:
389 jsonw_end_array(json_wtr
);
391 free(cgroup_alloced
);
396 static int do_attach(int argc
, char **argv
)
398 enum bpf_attach_type attach_type
;
399 int cgroup_fd
, prog_fd
;
400 int attach_flags
= 0;
405 p_err("too few parameters for cgroup attach");
409 cgroup_fd
= open(argv
[0], O_RDONLY
);
411 p_err("can't open cgroup %s", argv
[0]);
415 attach_type
= parse_attach_type(argv
[1]);
416 if (attach_type
== __MAX_BPF_ATTACH_TYPE
) {
417 p_err("invalid attach type");
423 prog_fd
= prog_parse_fd(&argc
, &argv
);
427 for (i
= 0; i
< argc
; i
++) {
428 if (is_prefix(argv
[i
], "multi")) {
429 attach_flags
|= BPF_F_ALLOW_MULTI
;
430 } else if (is_prefix(argv
[i
], "override")) {
431 attach_flags
|= BPF_F_ALLOW_OVERRIDE
;
433 p_err("unknown option: %s", argv
[i
]);
438 if (bpf_prog_attach(prog_fd
, cgroup_fd
, attach_type
, attach_flags
)) {
439 p_err("failed to attach program");
444 jsonw_null(json_wtr
);
456 static int do_detach(int argc
, char **argv
)
458 enum bpf_attach_type attach_type
;
459 int prog_fd
, cgroup_fd
;
463 p_err("too few parameters for cgroup detach");
467 cgroup_fd
= open(argv
[0], O_RDONLY
);
469 p_err("can't open cgroup %s", argv
[0]);
473 attach_type
= parse_attach_type(argv
[1]);
474 if (attach_type
== __MAX_BPF_ATTACH_TYPE
) {
475 p_err("invalid attach type");
481 prog_fd
= prog_parse_fd(&argc
, &argv
);
485 if (bpf_prog_detach2(prog_fd
, cgroup_fd
, attach_type
)) {
486 p_err("failed to detach program");
491 jsonw_null(json_wtr
);
503 static int do_help(int argc
, char **argv
)
506 jsonw_null(json_wtr
);
511 "Usage: %s %s { show | list } CGROUP [**effective**]\n"
512 " %s %s tree [CGROUP_ROOT] [**effective**]\n"
513 " %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
514 " %s %s detach CGROUP ATTACH_TYPE PROG\n"
517 HELP_SPEC_ATTACH_TYPES
"\n"
518 " " HELP_SPEC_ATTACH_FLAGS
"\n"
519 " " HELP_SPEC_PROGRAM
"\n"
520 " " HELP_SPEC_OPTIONS
"\n"
523 bin_name
, argv
[-2], bin_name
, argv
[-2],
524 bin_name
, argv
[-2], bin_name
, argv
[-2]);
529 static const struct cmd cmds
[] = {
532 { "tree", do_show_tree
},
533 { "attach", do_attach
},
534 { "detach", do_detach
},
539 int do_cgroup(int argc
, char **argv
)
541 return cmd_select(cmds
, argc
, argv
, do_help
);