690 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2// Copyright (C) 2017 Facebook3// Author: Roman Gushchin <guro@fb.com>4 5#define _XOPEN_SOURCE 5006#include <errno.h>7#include <fcntl.h>8#include <ftw.h>9#include <mntent.h>10#include <stdio.h>11#include <stdlib.h>12#include <string.h>13#include <sys/stat.h>14#include <sys/types.h>15#include <unistd.h>16 17#include <bpf/bpf.h>18#include <bpf/btf.h>19 20#include "main.h"21 22static const int cgroup_attach_types[] = {23 BPF_CGROUP_INET_INGRESS,24 BPF_CGROUP_INET_EGRESS,25 BPF_CGROUP_INET_SOCK_CREATE,26 BPF_CGROUP_INET_SOCK_RELEASE,27 BPF_CGROUP_INET4_BIND,28 BPF_CGROUP_INET6_BIND,29 BPF_CGROUP_INET4_POST_BIND,30 BPF_CGROUP_INET6_POST_BIND,31 BPF_CGROUP_INET4_CONNECT,32 BPF_CGROUP_INET6_CONNECT,33 BPF_CGROUP_UNIX_CONNECT,34 BPF_CGROUP_INET4_GETPEERNAME,35 BPF_CGROUP_INET6_GETPEERNAME,36 BPF_CGROUP_UNIX_GETPEERNAME,37 BPF_CGROUP_INET4_GETSOCKNAME,38 BPF_CGROUP_INET6_GETSOCKNAME,39 BPF_CGROUP_UNIX_GETSOCKNAME,40 BPF_CGROUP_UDP4_SENDMSG,41 BPF_CGROUP_UDP6_SENDMSG,42 BPF_CGROUP_UNIX_SENDMSG,43 BPF_CGROUP_UDP4_RECVMSG,44 BPF_CGROUP_UDP6_RECVMSG,45 BPF_CGROUP_UNIX_RECVMSG,46 BPF_CGROUP_SOCK_OPS,47 BPF_CGROUP_DEVICE,48 BPF_CGROUP_SYSCTL,49 BPF_CGROUP_GETSOCKOPT,50 BPF_CGROUP_SETSOCKOPT,51 BPF_LSM_CGROUP52};53 54#define HELP_SPEC_ATTACH_FLAGS \55 "ATTACH_FLAGS := { multi | override }"56 57#define HELP_SPEC_ATTACH_TYPES \58 " ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \59 " cgroup_inet_sock_create | cgroup_sock_ops |\n" \60 " cgroup_device | cgroup_inet4_bind |\n" \61 " cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \62 " cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \63 " cgroup_inet6_connect | cgroup_unix_connect |\n" \64 " cgroup_inet4_getpeername | cgroup_inet6_getpeername |\n" \65 " cgroup_unix_getpeername | cgroup_inet4_getsockname |\n" \66 " cgroup_inet6_getsockname | cgroup_unix_getsockname |\n" \67 " cgroup_udp4_sendmsg | cgroup_udp6_sendmsg |\n" \68 " cgroup_unix_sendmsg | cgroup_udp4_recvmsg |\n" \69 " cgroup_udp6_recvmsg | cgroup_unix_recvmsg |\n" \70 " cgroup_sysctl | cgroup_getsockopt |\n" \71 " cgroup_setsockopt | cgroup_inet_sock_release }"72 73static unsigned int query_flags;74static struct btf *btf_vmlinux;75static __u32 btf_vmlinux_id;76 77static enum bpf_attach_type parse_attach_type(const char *str)78{79 const char *attach_type_str;80 enum bpf_attach_type type;81 82 for (type = 0; ; type++) {83 attach_type_str = libbpf_bpf_attach_type_str(type);84 if (!attach_type_str)85 break;86 if (!strcmp(str, attach_type_str))87 return type;88 }89 90 /* Also check traditionally used attach type strings. For these we keep91 * allowing prefixed usage.92 */93 for (type = 0; ; type++) {94 attach_type_str = bpf_attach_type_input_str(type);95 if (!attach_type_str)96 break;97 if (is_prefix(str, attach_type_str))98 return type;99 }100 101 return __MAX_BPF_ATTACH_TYPE;102}103 104static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)105{106 struct bpf_btf_info btf_info = {};107 __u32 btf_len = sizeof(btf_info);108 char name[16] = {};109 int err;110 int fd;111 112 btf_info.name = ptr_to_u64(name);113 btf_info.name_len = sizeof(name);114 115 fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);116 if (fd < 0)117 return;118 119 err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);120 if (err)121 goto out;122 123 if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)124 btf_vmlinux_id = btf_info.id;125 126out:127 close(fd);128}129 130static int show_bpf_prog(int id, enum bpf_attach_type attach_type,131 const char *attach_flags_str,132 int level)133{134 char prog_name[MAX_PROG_FULL_NAME];135 const char *attach_btf_name = NULL;136 struct bpf_prog_info info = {};137 const char *attach_type_str;138 __u32 info_len = sizeof(info);139 int prog_fd;140 141 prog_fd = bpf_prog_get_fd_by_id(id);142 if (prog_fd < 0)143 return -1;144 145 if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {146 close(prog_fd);147 return -1;148 }149 150 attach_type_str = libbpf_bpf_attach_type_str(attach_type);151 152 if (btf_vmlinux) {153 if (!btf_vmlinux_id)154 guess_vmlinux_btf_id(info.attach_btf_obj_id);155 156 if (btf_vmlinux_id == info.attach_btf_obj_id &&157 info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {158 const struct btf_type *t =159 btf__type_by_id(btf_vmlinux, info.attach_btf_id);160 attach_btf_name =161 btf__name_by_offset(btf_vmlinux, t->name_off);162 }163 }164 165 get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));166 if (json_output) {167 jsonw_start_object(json_wtr);168 jsonw_uint_field(json_wtr, "id", info.id);169 if (attach_type_str)170 jsonw_string_field(json_wtr, "attach_type", attach_type_str);171 else172 jsonw_uint_field(json_wtr, "attach_type", attach_type);173 if (!(query_flags & BPF_F_QUERY_EFFECTIVE))174 jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);175 jsonw_string_field(json_wtr, "name", prog_name);176 if (attach_btf_name)177 jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);178 jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);179 jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);180 jsonw_end_object(json_wtr);181 } else {182 printf("%s%-8u ", level ? " " : "", info.id);183 if (attach_type_str)184 printf("%-15s", attach_type_str);185 else186 printf("type %-10u", attach_type);187 if (query_flags & BPF_F_QUERY_EFFECTIVE)188 printf(" %-15s", prog_name);189 else190 printf(" %-15s %-15s", attach_flags_str, prog_name);191 if (attach_btf_name)192 printf(" %-15s", attach_btf_name);193 else if (info.attach_btf_id)194 printf(" attach_btf_obj_id=%d attach_btf_id=%d",195 info.attach_btf_obj_id, info.attach_btf_id);196 printf("\n");197 }198 199 close(prog_fd);200 return 0;201}202 203static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)204{205 __u32 prog_cnt = 0;206 int ret;207 208 ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,209 NULL, &prog_cnt);210 if (ret)211 return -1;212 213 return prog_cnt;214}215 216static int cgroup_has_attached_progs(int cgroup_fd)217{218 unsigned int i = 0;219 bool no_prog = true;220 221 for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) {222 int count = count_attached_bpf_progs(cgroup_fd, cgroup_attach_types[i]);223 224 if (count < 0)225 return -1;226 227 if (count > 0) {228 no_prog = false;229 break;230 }231 }232 233 return no_prog ? 0 : 1;234}235 236static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,237 int level)238{239 LIBBPF_OPTS(bpf_prog_query_opts, p);240 __u32 prog_ids[1024] = {0};241 __u32 iter;242 int ret;243 244 p.query_flags = query_flags;245 p.prog_cnt = ARRAY_SIZE(prog_ids);246 p.prog_ids = prog_ids;247 248 ret = bpf_prog_query_opts(cgroup_fd, type, &p);249 if (ret)250 return ret;251 252 if (p.prog_cnt == 0)253 return 0;254 255 for (iter = 0; iter < p.prog_cnt; iter++)256 show_bpf_prog(prog_ids[iter], type, NULL, level);257 258 return 0;259}260 261static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,262 int level)263{264 LIBBPF_OPTS(bpf_prog_query_opts, p);265 __u32 prog_attach_flags[1024] = {0};266 const char *attach_flags_str;267 __u32 prog_ids[1024] = {0};268 char buf[32];269 __u32 iter;270 int ret;271 272 p.query_flags = query_flags;273 p.prog_cnt = ARRAY_SIZE(prog_ids);274 p.prog_ids = prog_ids;275 p.prog_attach_flags = prog_attach_flags;276 277 ret = bpf_prog_query_opts(cgroup_fd, type, &p);278 if (ret)279 return ret;280 281 if (p.prog_cnt == 0)282 return 0;283 284 for (iter = 0; iter < p.prog_cnt; iter++) {285 __u32 attach_flags;286 287 attach_flags = prog_attach_flags[iter] ?: p.attach_flags;288 289 switch (attach_flags) {290 case BPF_F_ALLOW_MULTI:291 attach_flags_str = "multi";292 break;293 case BPF_F_ALLOW_OVERRIDE:294 attach_flags_str = "override";295 break;296 case 0:297 attach_flags_str = "";298 break;299 default:300 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);301 attach_flags_str = buf;302 }303 304 show_bpf_prog(prog_ids[iter], type,305 attach_flags_str, level);306 }307 308 return 0;309}310 311static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,312 int level)313{314 return query_flags & BPF_F_QUERY_EFFECTIVE ?315 show_effective_bpf_progs(cgroup_fd, type, level) :316 show_attached_bpf_progs(cgroup_fd, type, level);317}318 319static int do_show(int argc, char **argv)320{321 enum bpf_attach_type type;322 int has_attached_progs;323 const char *path;324 int cgroup_fd;325 int ret = -1;326 327 query_flags = 0;328 329 if (!REQ_ARGS(1))330 return -1;331 path = GET_ARG();332 333 while (argc) {334 if (is_prefix(*argv, "effective")) {335 if (query_flags & BPF_F_QUERY_EFFECTIVE) {336 p_err("duplicated argument: %s", *argv);337 return -1;338 }339 query_flags |= BPF_F_QUERY_EFFECTIVE;340 NEXT_ARG();341 } else {342 p_err("expected no more arguments, 'effective', got: '%s'?",343 *argv);344 return -1;345 }346 }347 348 cgroup_fd = open(path, O_RDONLY);349 if (cgroup_fd < 0) {350 p_err("can't open cgroup %s", path);351 goto exit;352 }353 354 has_attached_progs = cgroup_has_attached_progs(cgroup_fd);355 if (has_attached_progs < 0) {356 p_err("can't query bpf programs attached to %s: %s",357 path, strerror(errno));358 goto exit_cgroup;359 } else if (!has_attached_progs) {360 ret = 0;361 goto exit_cgroup;362 }363 364 if (json_output)365 jsonw_start_array(json_wtr);366 else if (query_flags & BPF_F_QUERY_EFFECTIVE)367 printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");368 else369 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",370 "AttachFlags", "Name");371 372 btf_vmlinux = libbpf_find_kernel_btf();373 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {374 /*375 * Not all attach types may be supported, so it's expected,376 * that some requests will fail.377 * If we were able to get the show for at least one378 * attach type, let's return 0.379 */380 if (show_bpf_progs(cgroup_fd, type, 0) == 0)381 ret = 0;382 }383 384 if (json_output)385 jsonw_end_array(json_wtr);386 387exit_cgroup:388 close(cgroup_fd);389exit:390 return ret;391}392 393/*394 * To distinguish nftw() errors and do_show_tree_fn() errors395 * and avoid duplicating error messages, let's return -2396 * from do_show_tree_fn() in case of error.397 */398#define NFTW_ERR -1399#define SHOW_TREE_FN_ERR -2400static int do_show_tree_fn(const char *fpath, const struct stat *sb,401 int typeflag, struct FTW *ftw)402{403 enum bpf_attach_type type;404 int has_attached_progs;405 int cgroup_fd;406 407 if (typeflag != FTW_D)408 return 0;409 410 cgroup_fd = open(fpath, O_RDONLY);411 if (cgroup_fd < 0) {412 p_err("can't open cgroup %s: %s", fpath, strerror(errno));413 return SHOW_TREE_FN_ERR;414 }415 416 has_attached_progs = cgroup_has_attached_progs(cgroup_fd);417 if (has_attached_progs < 0) {418 p_err("can't query bpf programs attached to %s: %s",419 fpath, strerror(errno));420 close(cgroup_fd);421 return SHOW_TREE_FN_ERR;422 } else if (!has_attached_progs) {423 close(cgroup_fd);424 return 0;425 }426 427 if (json_output) {428 jsonw_start_object(json_wtr);429 jsonw_string_field(json_wtr, "cgroup", fpath);430 jsonw_name(json_wtr, "programs");431 jsonw_start_array(json_wtr);432 } else {433 printf("%s\n", fpath);434 }435 436 btf_vmlinux = libbpf_find_kernel_btf();437 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)438 show_bpf_progs(cgroup_fd, type, ftw->level);439 440 if (errno == EINVAL)441 /* Last attach type does not support query.442 * Do not report an error for this, especially because batch443 * mode would stop processing commands.444 */445 errno = 0;446 447 if (json_output) {448 jsonw_end_array(json_wtr);449 jsonw_end_object(json_wtr);450 }451 452 close(cgroup_fd);453 454 return 0;455}456 457static char *find_cgroup_root(void)458{459 struct mntent *mnt;460 FILE *f;461 462 f = fopen("/proc/mounts", "r");463 if (f == NULL)464 return NULL;465 466 while ((mnt = getmntent(f))) {467 if (strcmp(mnt->mnt_type, "cgroup2") == 0) {468 fclose(f);469 return strdup(mnt->mnt_dir);470 }471 }472 473 fclose(f);474 return NULL;475}476 477static int do_show_tree(int argc, char **argv)478{479 char *cgroup_root, *cgroup_alloced = NULL;480 int ret;481 482 query_flags = 0;483 484 if (!argc) {485 cgroup_alloced = find_cgroup_root();486 if (!cgroup_alloced) {487 p_err("cgroup v2 isn't mounted");488 return -1;489 }490 cgroup_root = cgroup_alloced;491 } else {492 cgroup_root = GET_ARG();493 494 while (argc) {495 if (is_prefix(*argv, "effective")) {496 if (query_flags & BPF_F_QUERY_EFFECTIVE) {497 p_err("duplicated argument: %s", *argv);498 return -1;499 }500 query_flags |= BPF_F_QUERY_EFFECTIVE;501 NEXT_ARG();502 } else {503 p_err("expected no more arguments, 'effective', got: '%s'?",504 *argv);505 return -1;506 }507 }508 }509 510 if (json_output)511 jsonw_start_array(json_wtr);512 else if (query_flags & BPF_F_QUERY_EFFECTIVE)513 printf("%s\n"514 "%-8s %-15s %-15s\n",515 "CgroupPath",516 "ID", "AttachType", "Name");517 else518 printf("%s\n"519 "%-8s %-15s %-15s %-15s\n",520 "CgroupPath",521 "ID", "AttachType", "AttachFlags", "Name");522 523 switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {524 case NFTW_ERR:525 p_err("can't iterate over %s: %s", cgroup_root,526 strerror(errno));527 ret = -1;528 break;529 case SHOW_TREE_FN_ERR:530 ret = -1;531 break;532 default:533 ret = 0;534 }535 536 if (json_output)537 jsonw_end_array(json_wtr);538 539 free(cgroup_alloced);540 541 return ret;542}543 544static int do_attach(int argc, char **argv)545{546 enum bpf_attach_type attach_type;547 int cgroup_fd, prog_fd;548 int attach_flags = 0;549 int ret = -1;550 int i;551 552 if (argc < 4) {553 p_err("too few parameters for cgroup attach");554 goto exit;555 }556 557 cgroup_fd = open(argv[0], O_RDONLY);558 if (cgroup_fd < 0) {559 p_err("can't open cgroup %s", argv[0]);560 goto exit;561 }562 563 attach_type = parse_attach_type(argv[1]);564 if (attach_type == __MAX_BPF_ATTACH_TYPE) {565 p_err("invalid attach type");566 goto exit_cgroup;567 }568 569 argc -= 2;570 argv = &argv[2];571 prog_fd = prog_parse_fd(&argc, &argv);572 if (prog_fd < 0)573 goto exit_cgroup;574 575 for (i = 0; i < argc; i++) {576 if (is_prefix(argv[i], "multi")) {577 attach_flags |= BPF_F_ALLOW_MULTI;578 } else if (is_prefix(argv[i], "override")) {579 attach_flags |= BPF_F_ALLOW_OVERRIDE;580 } else {581 p_err("unknown option: %s", argv[i]);582 goto exit_cgroup;583 }584 }585 586 if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {587 p_err("failed to attach program");588 goto exit_prog;589 }590 591 if (json_output)592 jsonw_null(json_wtr);593 594 ret = 0;595 596exit_prog:597 close(prog_fd);598exit_cgroup:599 close(cgroup_fd);600exit:601 return ret;602}603 604static int do_detach(int argc, char **argv)605{606 enum bpf_attach_type attach_type;607 int prog_fd, cgroup_fd;608 int ret = -1;609 610 if (argc < 4) {611 p_err("too few parameters for cgroup detach");612 goto exit;613 }614 615 cgroup_fd = open(argv[0], O_RDONLY);616 if (cgroup_fd < 0) {617 p_err("can't open cgroup %s", argv[0]);618 goto exit;619 }620 621 attach_type = parse_attach_type(argv[1]);622 if (attach_type == __MAX_BPF_ATTACH_TYPE) {623 p_err("invalid attach type");624 goto exit_cgroup;625 }626 627 argc -= 2;628 argv = &argv[2];629 prog_fd = prog_parse_fd(&argc, &argv);630 if (prog_fd < 0)631 goto exit_cgroup;632 633 if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {634 p_err("failed to detach program");635 goto exit_prog;636 }637 638 if (json_output)639 jsonw_null(json_wtr);640 641 ret = 0;642 643exit_prog:644 close(prog_fd);645exit_cgroup:646 close(cgroup_fd);647exit:648 return ret;649}650 651static int do_help(int argc, char **argv)652{653 if (json_output) {654 jsonw_null(json_wtr);655 return 0;656 }657 658 fprintf(stderr,659 "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"660 " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"661 " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"662 " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"663 " %1$s %2$s help\n"664 "\n"665 HELP_SPEC_ATTACH_TYPES "\n"666 " " HELP_SPEC_ATTACH_FLAGS "\n"667 " " HELP_SPEC_PROGRAM "\n"668 " " HELP_SPEC_OPTIONS " |\n"669 " {-f|--bpffs} }\n"670 "",671 bin_name, argv[-2]);672 673 return 0;674}675 676static const struct cmd cmds[] = {677 { "show", do_show },678 { "list", do_show },679 { "tree", do_show_tree },680 { "attach", do_attach },681 { "detach", do_detach },682 { "help", do_help },683 { 0 }684};685 686int do_cgroup(int argc, char **argv)687{688 return cmd_select(cmds, argc, argv, do_help);689}690