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