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