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