0001
0002
0003
0004
0005 #define _XOPEN_SOURCE 500
0006 #include <errno.h>
0007 #include <fcntl.h>
0008 #include <ftw.h>
0009 #include <mntent.h>
0010 #include <stdio.h>
0011 #include <stdlib.h>
0012 #include <string.h>
0013 #include <sys/stat.h>
0014 #include <sys/types.h>
0015 #include <unistd.h>
0016
0017 #include <bpf/bpf.h>
0018 #include <bpf/btf.h>
0019
0020 #include "main.h"
0021
0022 #define HELP_SPEC_ATTACH_FLAGS \
0023 "ATTACH_FLAGS := { multi | override }"
0024
0025 #define HELP_SPEC_ATTACH_TYPES \
0026 " ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
0027 " cgroup_inet_sock_create | cgroup_sock_ops |\n" \
0028 " cgroup_device | cgroup_inet4_bind |\n" \
0029 " cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
0030 " cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
0031 " cgroup_inet6_connect | cgroup_inet4_getpeername |\n" \
0032 " cgroup_inet6_getpeername | cgroup_inet4_getsockname |\n" \
0033 " cgroup_inet6_getsockname | cgroup_udp4_sendmsg |\n" \
0034 " cgroup_udp6_sendmsg | cgroup_udp4_recvmsg |\n" \
0035 " cgroup_udp6_recvmsg | cgroup_sysctl |\n" \
0036 " cgroup_getsockopt | cgroup_setsockopt |\n" \
0037 " cgroup_inet_sock_release }"
0038
0039 static unsigned int query_flags;
0040 static struct btf *btf_vmlinux;
0041 static __u32 btf_vmlinux_id;
0042
0043 static enum bpf_attach_type parse_attach_type(const char *str)
0044 {
0045 const char *attach_type_str;
0046 enum bpf_attach_type type;
0047
0048 for (type = 0; ; type++) {
0049 attach_type_str = libbpf_bpf_attach_type_str(type);
0050 if (!attach_type_str)
0051 break;
0052 if (!strcmp(str, attach_type_str))
0053 return type;
0054 }
0055
0056
0057
0058
0059 for (type = 0; ; type++) {
0060 attach_type_str = bpf_attach_type_input_str(type);
0061 if (!attach_type_str)
0062 break;
0063 if (is_prefix(str, attach_type_str))
0064 return type;
0065 }
0066
0067 return __MAX_BPF_ATTACH_TYPE;
0068 }
0069
0070 static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
0071 {
0072 struct bpf_btf_info btf_info = {};
0073 __u32 btf_len = sizeof(btf_info);
0074 char name[16] = {};
0075 int err;
0076 int fd;
0077
0078 btf_info.name = ptr_to_u64(name);
0079 btf_info.name_len = sizeof(name);
0080
0081 fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
0082 if (fd < 0)
0083 return;
0084
0085 err = bpf_obj_get_info_by_fd(fd, &btf_info, &btf_len);
0086 if (err)
0087 goto out;
0088
0089 if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
0090 btf_vmlinux_id = btf_info.id;
0091
0092 out:
0093 close(fd);
0094 }
0095
0096 static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
0097 const char *attach_flags_str,
0098 int level)
0099 {
0100 char prog_name[MAX_PROG_FULL_NAME];
0101 const char *attach_btf_name = NULL;
0102 struct bpf_prog_info info = {};
0103 const char *attach_type_str;
0104 __u32 info_len = sizeof(info);
0105 int prog_fd;
0106
0107 prog_fd = bpf_prog_get_fd_by_id(id);
0108 if (prog_fd < 0)
0109 return -1;
0110
0111 if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
0112 close(prog_fd);
0113 return -1;
0114 }
0115
0116 attach_type_str = libbpf_bpf_attach_type_str(attach_type);
0117
0118 if (btf_vmlinux) {
0119 if (!btf_vmlinux_id)
0120 guess_vmlinux_btf_id(info.attach_btf_obj_id);
0121
0122 if (btf_vmlinux_id == info.attach_btf_obj_id &&
0123 info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
0124 const struct btf_type *t =
0125 btf__type_by_id(btf_vmlinux, info.attach_btf_id);
0126 attach_btf_name =
0127 btf__name_by_offset(btf_vmlinux, t->name_off);
0128 }
0129 }
0130
0131 get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
0132 if (json_output) {
0133 jsonw_start_object(json_wtr);
0134 jsonw_uint_field(json_wtr, "id", info.id);
0135 if (attach_type_str)
0136 jsonw_string_field(json_wtr, "attach_type", attach_type_str);
0137 else
0138 jsonw_uint_field(json_wtr, "attach_type", attach_type);
0139 jsonw_string_field(json_wtr, "attach_flags",
0140 attach_flags_str);
0141 jsonw_string_field(json_wtr, "name", prog_name);
0142 if (attach_btf_name)
0143 jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
0144 jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
0145 jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
0146 jsonw_end_object(json_wtr);
0147 } else {
0148 printf("%s%-8u ", level ? " " : "", info.id);
0149 if (attach_type_str)
0150 printf("%-15s", attach_type_str);
0151 else
0152 printf("type %-10u", attach_type);
0153 printf(" %-15s %-15s", attach_flags_str, prog_name);
0154 if (attach_btf_name)
0155 printf(" %-15s", attach_btf_name);
0156 else if (info.attach_btf_id)
0157 printf(" attach_btf_obj_id=%d attach_btf_id=%d",
0158 info.attach_btf_obj_id, info.attach_btf_id);
0159 printf("\n");
0160 }
0161
0162 close(prog_fd);
0163 return 0;
0164 }
0165
0166 static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
0167 {
0168 __u32 prog_cnt = 0;
0169 int ret;
0170
0171 ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
0172 NULL, &prog_cnt);
0173 if (ret)
0174 return -1;
0175
0176 return prog_cnt;
0177 }
0178
0179 static int cgroup_has_attached_progs(int cgroup_fd)
0180 {
0181 enum bpf_attach_type type;
0182 bool no_prog = true;
0183
0184 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
0185 int count = count_attached_bpf_progs(cgroup_fd, type);
0186
0187 if (count < 0 && errno != EINVAL)
0188 return -1;
0189
0190 if (count > 0) {
0191 no_prog = false;
0192 break;
0193 }
0194 }
0195
0196 return no_prog ? 0 : 1;
0197 }
0198 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
0199 int level)
0200 {
0201 LIBBPF_OPTS(bpf_prog_query_opts, p);
0202 __u32 prog_attach_flags[1024] = {0};
0203 const char *attach_flags_str;
0204 __u32 prog_ids[1024] = {0};
0205 char buf[32];
0206 __u32 iter;
0207 int ret;
0208
0209 p.query_flags = query_flags;
0210 p.prog_cnt = ARRAY_SIZE(prog_ids);
0211 p.prog_ids = prog_ids;
0212 p.prog_attach_flags = prog_attach_flags;
0213
0214 ret = bpf_prog_query_opts(cgroup_fd, type, &p);
0215 if (ret)
0216 return ret;
0217
0218 if (p.prog_cnt == 0)
0219 return 0;
0220
0221 for (iter = 0; iter < p.prog_cnt; iter++) {
0222 __u32 attach_flags;
0223
0224 attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
0225
0226 switch (attach_flags) {
0227 case BPF_F_ALLOW_MULTI:
0228 attach_flags_str = "multi";
0229 break;
0230 case BPF_F_ALLOW_OVERRIDE:
0231 attach_flags_str = "override";
0232 break;
0233 case 0:
0234 attach_flags_str = "";
0235 break;
0236 default:
0237 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
0238 attach_flags_str = buf;
0239 }
0240
0241 show_bpf_prog(prog_ids[iter], type,
0242 attach_flags_str, level);
0243 }
0244
0245 return 0;
0246 }
0247
0248 static int do_show(int argc, char **argv)
0249 {
0250 enum bpf_attach_type type;
0251 int has_attached_progs;
0252 const char *path;
0253 int cgroup_fd;
0254 int ret = -1;
0255
0256 query_flags = 0;
0257
0258 if (!REQ_ARGS(1))
0259 return -1;
0260 path = GET_ARG();
0261
0262 while (argc) {
0263 if (is_prefix(*argv, "effective")) {
0264 if (query_flags & BPF_F_QUERY_EFFECTIVE) {
0265 p_err("duplicated argument: %s", *argv);
0266 return -1;
0267 }
0268 query_flags |= BPF_F_QUERY_EFFECTIVE;
0269 NEXT_ARG();
0270 } else {
0271 p_err("expected no more arguments, 'effective', got: '%s'?",
0272 *argv);
0273 return -1;
0274 }
0275 }
0276
0277 cgroup_fd = open(path, O_RDONLY);
0278 if (cgroup_fd < 0) {
0279 p_err("can't open cgroup %s", path);
0280 goto exit;
0281 }
0282
0283 has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
0284 if (has_attached_progs < 0) {
0285 p_err("can't query bpf programs attached to %s: %s",
0286 path, strerror(errno));
0287 goto exit_cgroup;
0288 } else if (!has_attached_progs) {
0289 ret = 0;
0290 goto exit_cgroup;
0291 }
0292
0293 if (json_output)
0294 jsonw_start_array(json_wtr);
0295 else
0296 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
0297 "AttachFlags", "Name");
0298
0299 btf_vmlinux = libbpf_find_kernel_btf();
0300 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
0301
0302
0303
0304
0305
0306
0307 if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
0308 ret = 0;
0309 }
0310
0311 if (json_output)
0312 jsonw_end_array(json_wtr);
0313
0314 exit_cgroup:
0315 close(cgroup_fd);
0316 exit:
0317 return ret;
0318 }
0319
0320
0321
0322
0323
0324
0325 #define NFTW_ERR -1
0326 #define SHOW_TREE_FN_ERR -2
0327 static int do_show_tree_fn(const char *fpath, const struct stat *sb,
0328 int typeflag, struct FTW *ftw)
0329 {
0330 enum bpf_attach_type type;
0331 int has_attached_progs;
0332 int cgroup_fd;
0333
0334 if (typeflag != FTW_D)
0335 return 0;
0336
0337 cgroup_fd = open(fpath, O_RDONLY);
0338 if (cgroup_fd < 0) {
0339 p_err("can't open cgroup %s: %s", fpath, strerror(errno));
0340 return SHOW_TREE_FN_ERR;
0341 }
0342
0343 has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
0344 if (has_attached_progs < 0) {
0345 p_err("can't query bpf programs attached to %s: %s",
0346 fpath, strerror(errno));
0347 close(cgroup_fd);
0348 return SHOW_TREE_FN_ERR;
0349 } else if (!has_attached_progs) {
0350 close(cgroup_fd);
0351 return 0;
0352 }
0353
0354 if (json_output) {
0355 jsonw_start_object(json_wtr);
0356 jsonw_string_field(json_wtr, "cgroup", fpath);
0357 jsonw_name(json_wtr, "programs");
0358 jsonw_start_array(json_wtr);
0359 } else {
0360 printf("%s\n", fpath);
0361 }
0362
0363 btf_vmlinux = libbpf_find_kernel_btf();
0364 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
0365 show_attached_bpf_progs(cgroup_fd, type, ftw->level);
0366
0367 if (errno == EINVAL)
0368
0369
0370
0371
0372 errno = 0;
0373
0374 if (json_output) {
0375 jsonw_end_array(json_wtr);
0376 jsonw_end_object(json_wtr);
0377 }
0378
0379 close(cgroup_fd);
0380
0381 return 0;
0382 }
0383
0384 static char *find_cgroup_root(void)
0385 {
0386 struct mntent *mnt;
0387 FILE *f;
0388
0389 f = fopen("/proc/mounts", "r");
0390 if (f == NULL)
0391 return NULL;
0392
0393 while ((mnt = getmntent(f))) {
0394 if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
0395 fclose(f);
0396 return strdup(mnt->mnt_dir);
0397 }
0398 }
0399
0400 fclose(f);
0401 return NULL;
0402 }
0403
0404 static int do_show_tree(int argc, char **argv)
0405 {
0406 char *cgroup_root, *cgroup_alloced = NULL;
0407 int ret;
0408
0409 query_flags = 0;
0410
0411 if (!argc) {
0412 cgroup_alloced = find_cgroup_root();
0413 if (!cgroup_alloced) {
0414 p_err("cgroup v2 isn't mounted");
0415 return -1;
0416 }
0417 cgroup_root = cgroup_alloced;
0418 } else {
0419 cgroup_root = GET_ARG();
0420
0421 while (argc) {
0422 if (is_prefix(*argv, "effective")) {
0423 if (query_flags & BPF_F_QUERY_EFFECTIVE) {
0424 p_err("duplicated argument: %s", *argv);
0425 return -1;
0426 }
0427 query_flags |= BPF_F_QUERY_EFFECTIVE;
0428 NEXT_ARG();
0429 } else {
0430 p_err("expected no more arguments, 'effective', got: '%s'?",
0431 *argv);
0432 return -1;
0433 }
0434 }
0435 }
0436
0437 if (json_output)
0438 jsonw_start_array(json_wtr);
0439 else
0440 printf("%s\n"
0441 "%-8s %-15s %-15s %-15s\n",
0442 "CgroupPath",
0443 "ID", "AttachType", "AttachFlags", "Name");
0444
0445 switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
0446 case NFTW_ERR:
0447 p_err("can't iterate over %s: %s", cgroup_root,
0448 strerror(errno));
0449 ret = -1;
0450 break;
0451 case SHOW_TREE_FN_ERR:
0452 ret = -1;
0453 break;
0454 default:
0455 ret = 0;
0456 }
0457
0458 if (json_output)
0459 jsonw_end_array(json_wtr);
0460
0461 free(cgroup_alloced);
0462
0463 return ret;
0464 }
0465
0466 static int do_attach(int argc, char **argv)
0467 {
0468 enum bpf_attach_type attach_type;
0469 int cgroup_fd, prog_fd;
0470 int attach_flags = 0;
0471 int ret = -1;
0472 int i;
0473
0474 if (argc < 4) {
0475 p_err("too few parameters for cgroup attach");
0476 goto exit;
0477 }
0478
0479 cgroup_fd = open(argv[0], O_RDONLY);
0480 if (cgroup_fd < 0) {
0481 p_err("can't open cgroup %s", argv[0]);
0482 goto exit;
0483 }
0484
0485 attach_type = parse_attach_type(argv[1]);
0486 if (attach_type == __MAX_BPF_ATTACH_TYPE) {
0487 p_err("invalid attach type");
0488 goto exit_cgroup;
0489 }
0490
0491 argc -= 2;
0492 argv = &argv[2];
0493 prog_fd = prog_parse_fd(&argc, &argv);
0494 if (prog_fd < 0)
0495 goto exit_cgroup;
0496
0497 for (i = 0; i < argc; i++) {
0498 if (is_prefix(argv[i], "multi")) {
0499 attach_flags |= BPF_F_ALLOW_MULTI;
0500 } else if (is_prefix(argv[i], "override")) {
0501 attach_flags |= BPF_F_ALLOW_OVERRIDE;
0502 } else {
0503 p_err("unknown option: %s", argv[i]);
0504 goto exit_cgroup;
0505 }
0506 }
0507
0508 if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
0509 p_err("failed to attach program");
0510 goto exit_prog;
0511 }
0512
0513 if (json_output)
0514 jsonw_null(json_wtr);
0515
0516 ret = 0;
0517
0518 exit_prog:
0519 close(prog_fd);
0520 exit_cgroup:
0521 close(cgroup_fd);
0522 exit:
0523 return ret;
0524 }
0525
0526 static int do_detach(int argc, char **argv)
0527 {
0528 enum bpf_attach_type attach_type;
0529 int prog_fd, cgroup_fd;
0530 int ret = -1;
0531
0532 if (argc < 4) {
0533 p_err("too few parameters for cgroup detach");
0534 goto exit;
0535 }
0536
0537 cgroup_fd = open(argv[0], O_RDONLY);
0538 if (cgroup_fd < 0) {
0539 p_err("can't open cgroup %s", argv[0]);
0540 goto exit;
0541 }
0542
0543 attach_type = parse_attach_type(argv[1]);
0544 if (attach_type == __MAX_BPF_ATTACH_TYPE) {
0545 p_err("invalid attach type");
0546 goto exit_cgroup;
0547 }
0548
0549 argc -= 2;
0550 argv = &argv[2];
0551 prog_fd = prog_parse_fd(&argc, &argv);
0552 if (prog_fd < 0)
0553 goto exit_cgroup;
0554
0555 if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
0556 p_err("failed to detach program");
0557 goto exit_prog;
0558 }
0559
0560 if (json_output)
0561 jsonw_null(json_wtr);
0562
0563 ret = 0;
0564
0565 exit_prog:
0566 close(prog_fd);
0567 exit_cgroup:
0568 close(cgroup_fd);
0569 exit:
0570 return ret;
0571 }
0572
0573 static int do_help(int argc, char **argv)
0574 {
0575 if (json_output) {
0576 jsonw_null(json_wtr);
0577 return 0;
0578 }
0579
0580 fprintf(stderr,
0581 "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
0582 " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
0583 " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
0584 " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
0585 " %1$s %2$s help\n"
0586 "\n"
0587 HELP_SPEC_ATTACH_TYPES "\n"
0588 " " HELP_SPEC_ATTACH_FLAGS "\n"
0589 " " HELP_SPEC_PROGRAM "\n"
0590 " " HELP_SPEC_OPTIONS " |\n"
0591 " {-f|--bpffs} }\n"
0592 "",
0593 bin_name, argv[-2]);
0594
0595 return 0;
0596 }
0597
0598 static const struct cmd cmds[] = {
0599 { "show", do_show },
0600 { "list", do_show },
0601 { "tree", do_show_tree },
0602 { "attach", do_attach },
0603 { "detach", do_detach },
0604 { "help", do_help },
0605 { 0 }
0606 };
0607
0608 int do_cgroup(int argc, char **argv)
0609 {
0610 return cmd_select(cmds, argc, argv, do_help);
0611 }