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