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 jsonw_string_field(json_wtr, "attach_flags", 140 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 printf(" %-15s %-15s", attach_flags_str, prog_name); 154 if (attach_btf_name) 155 printf(" %-15s", attach_btf_name); 156 else if (info.attach_btf_id) 157 printf(" attach_btf_obj_id=%d attach_btf_id=%d", 158 info.attach_btf_obj_id, info.attach_btf_id); 159 printf("\n"); 160 } 161 162 close(prog_fd); 163 return 0; 164 } 165 166 static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) 167 { 168 __u32 prog_cnt = 0; 169 int ret; 170 171 ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL, 172 NULL, &prog_cnt); 173 if (ret) 174 return -1; 175 176 return prog_cnt; 177 } 178 179 static int cgroup_has_attached_progs(int cgroup_fd) 180 { 181 enum bpf_attach_type type; 182 bool no_prog = true; 183 184 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { 185 int count = count_attached_bpf_progs(cgroup_fd, type); 186 187 if (count < 0 && errno != EINVAL) 188 return -1; 189 190 if (count > 0) { 191 no_prog = false; 192 break; 193 } 194 } 195 196 return no_prog ? 0 : 1; 197 } 198 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, 199 int level) 200 { 201 LIBBPF_OPTS(bpf_prog_query_opts, p); 202 __u32 prog_attach_flags[1024] = {0}; 203 const char *attach_flags_str; 204 __u32 prog_ids[1024] = {0}; 205 char buf[32]; 206 __u32 iter; 207 int ret; 208 209 p.query_flags = query_flags; 210 p.prog_cnt = ARRAY_SIZE(prog_ids); 211 p.prog_ids = prog_ids; 212 p.prog_attach_flags = prog_attach_flags; 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 __u32 attach_flags; 223 224 attach_flags = prog_attach_flags[iter] ?: p.attach_flags; 225 226 switch (attach_flags) { 227 case BPF_F_ALLOW_MULTI: 228 attach_flags_str = "multi"; 229 break; 230 case BPF_F_ALLOW_OVERRIDE: 231 attach_flags_str = "override"; 232 break; 233 case 0: 234 attach_flags_str = ""; 235 break; 236 default: 237 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags); 238 attach_flags_str = buf; 239 } 240 241 show_bpf_prog(prog_ids[iter], type, 242 attach_flags_str, level); 243 } 244 245 return 0; 246 } 247 248 static int do_show(int argc, char **argv) 249 { 250 enum bpf_attach_type type; 251 int has_attached_progs; 252 const char *path; 253 int cgroup_fd; 254 int ret = -1; 255 256 query_flags = 0; 257 258 if (!REQ_ARGS(1)) 259 return -1; 260 path = GET_ARG(); 261 262 while (argc) { 263 if (is_prefix(*argv, "effective")) { 264 if (query_flags & BPF_F_QUERY_EFFECTIVE) { 265 p_err("duplicated argument: %s", *argv); 266 return -1; 267 } 268 query_flags |= BPF_F_QUERY_EFFECTIVE; 269 NEXT_ARG(); 270 } else { 271 p_err("expected no more arguments, 'effective', got: '%s'?", 272 *argv); 273 return -1; 274 } 275 } 276 277 cgroup_fd = open(path, O_RDONLY); 278 if (cgroup_fd < 0) { 279 p_err("can't open cgroup %s", path); 280 goto exit; 281 } 282 283 has_attached_progs = cgroup_has_attached_progs(cgroup_fd); 284 if (has_attached_progs < 0) { 285 p_err("can't query bpf programs attached to %s: %s", 286 path, strerror(errno)); 287 goto exit_cgroup; 288 } else if (!has_attached_progs) { 289 ret = 0; 290 goto exit_cgroup; 291 } 292 293 if (json_output) 294 jsonw_start_array(json_wtr); 295 else 296 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType", 297 "AttachFlags", "Name"); 298 299 btf_vmlinux = libbpf_find_kernel_btf(); 300 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { 301 /* 302 * Not all attach types may be supported, so it's expected, 303 * that some requests will fail. 304 * If we were able to get the show for at least one 305 * attach type, let's return 0. 306 */ 307 if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0) 308 ret = 0; 309 } 310 311 if (json_output) 312 jsonw_end_array(json_wtr); 313 314 exit_cgroup: 315 close(cgroup_fd); 316 exit: 317 return ret; 318 } 319 320 /* 321 * To distinguish nftw() errors and do_show_tree_fn() errors 322 * and avoid duplicating error messages, let's return -2 323 * from do_show_tree_fn() in case of error. 324 */ 325 #define NFTW_ERR -1 326 #define SHOW_TREE_FN_ERR -2 327 static int do_show_tree_fn(const char *fpath, const struct stat *sb, 328 int typeflag, struct FTW *ftw) 329 { 330 enum bpf_attach_type type; 331 int has_attached_progs; 332 int cgroup_fd; 333 334 if (typeflag != FTW_D) 335 return 0; 336 337 cgroup_fd = open(fpath, O_RDONLY); 338 if (cgroup_fd < 0) { 339 p_err("can't open cgroup %s: %s", fpath, strerror(errno)); 340 return SHOW_TREE_FN_ERR; 341 } 342 343 has_attached_progs = cgroup_has_attached_progs(cgroup_fd); 344 if (has_attached_progs < 0) { 345 p_err("can't query bpf programs attached to %s: %s", 346 fpath, strerror(errno)); 347 close(cgroup_fd); 348 return SHOW_TREE_FN_ERR; 349 } else if (!has_attached_progs) { 350 close(cgroup_fd); 351 return 0; 352 } 353 354 if (json_output) { 355 jsonw_start_object(json_wtr); 356 jsonw_string_field(json_wtr, "cgroup", fpath); 357 jsonw_name(json_wtr, "programs"); 358 jsonw_start_array(json_wtr); 359 } else { 360 printf("%s\n", fpath); 361 } 362 363 btf_vmlinux = libbpf_find_kernel_btf(); 364 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) 365 show_attached_bpf_progs(cgroup_fd, type, ftw->level); 366 367 if (errno == EINVAL) 368 /* Last attach type does not support query. 369 * Do not report an error for this, especially because batch 370 * mode would stop processing commands. 371 */ 372 errno = 0; 373 374 if (json_output) { 375 jsonw_end_array(json_wtr); 376 jsonw_end_object(json_wtr); 377 } 378 379 close(cgroup_fd); 380 381 return 0; 382 } 383 384 static char *find_cgroup_root(void) 385 { 386 struct mntent *mnt; 387 FILE *f; 388 389 f = fopen("/proc/mounts", "r"); 390 if (f == NULL) 391 return NULL; 392 393 while ((mnt = getmntent(f))) { 394 if (strcmp(mnt->mnt_type, "cgroup2") == 0) { 395 fclose(f); 396 return strdup(mnt->mnt_dir); 397 } 398 } 399 400 fclose(f); 401 return NULL; 402 } 403 404 static int do_show_tree(int argc, char **argv) 405 { 406 char *cgroup_root, *cgroup_alloced = NULL; 407 int ret; 408 409 query_flags = 0; 410 411 if (!argc) { 412 cgroup_alloced = find_cgroup_root(); 413 if (!cgroup_alloced) { 414 p_err("cgroup v2 isn't mounted"); 415 return -1; 416 } 417 cgroup_root = cgroup_alloced; 418 } else { 419 cgroup_root = GET_ARG(); 420 421 while (argc) { 422 if (is_prefix(*argv, "effective")) { 423 if (query_flags & BPF_F_QUERY_EFFECTIVE) { 424 p_err("duplicated argument: %s", *argv); 425 return -1; 426 } 427 query_flags |= BPF_F_QUERY_EFFECTIVE; 428 NEXT_ARG(); 429 } else { 430 p_err("expected no more arguments, 'effective', got: '%s'?", 431 *argv); 432 return -1; 433 } 434 } 435 } 436 437 if (json_output) 438 jsonw_start_array(json_wtr); 439 else 440 printf("%s\n" 441 "%-8s %-15s %-15s %-15s\n", 442 "CgroupPath", 443 "ID", "AttachType", "AttachFlags", "Name"); 444 445 switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) { 446 case NFTW_ERR: 447 p_err("can't iterate over %s: %s", cgroup_root, 448 strerror(errno)); 449 ret = -1; 450 break; 451 case SHOW_TREE_FN_ERR: 452 ret = -1; 453 break; 454 default: 455 ret = 0; 456 } 457 458 if (json_output) 459 jsonw_end_array(json_wtr); 460 461 free(cgroup_alloced); 462 463 return ret; 464 } 465 466 static int do_attach(int argc, char **argv) 467 { 468 enum bpf_attach_type attach_type; 469 int cgroup_fd, prog_fd; 470 int attach_flags = 0; 471 int ret = -1; 472 int i; 473 474 if (argc < 4) { 475 p_err("too few parameters for cgroup attach"); 476 goto exit; 477 } 478 479 cgroup_fd = open(argv[0], O_RDONLY); 480 if (cgroup_fd < 0) { 481 p_err("can't open cgroup %s", argv[0]); 482 goto exit; 483 } 484 485 attach_type = parse_attach_type(argv[1]); 486 if (attach_type == __MAX_BPF_ATTACH_TYPE) { 487 p_err("invalid attach type"); 488 goto exit_cgroup; 489 } 490 491 argc -= 2; 492 argv = &argv[2]; 493 prog_fd = prog_parse_fd(&argc, &argv); 494 if (prog_fd < 0) 495 goto exit_cgroup; 496 497 for (i = 0; i < argc; i++) { 498 if (is_prefix(argv[i], "multi")) { 499 attach_flags |= BPF_F_ALLOW_MULTI; 500 } else if (is_prefix(argv[i], "override")) { 501 attach_flags |= BPF_F_ALLOW_OVERRIDE; 502 } else { 503 p_err("unknown option: %s", argv[i]); 504 goto exit_cgroup; 505 } 506 } 507 508 if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) { 509 p_err("failed to attach program"); 510 goto exit_prog; 511 } 512 513 if (json_output) 514 jsonw_null(json_wtr); 515 516 ret = 0; 517 518 exit_prog: 519 close(prog_fd); 520 exit_cgroup: 521 close(cgroup_fd); 522 exit: 523 return ret; 524 } 525 526 static int do_detach(int argc, char **argv) 527 { 528 enum bpf_attach_type attach_type; 529 int prog_fd, cgroup_fd; 530 int ret = -1; 531 532 if (argc < 4) { 533 p_err("too few parameters for cgroup detach"); 534 goto exit; 535 } 536 537 cgroup_fd = open(argv[0], O_RDONLY); 538 if (cgroup_fd < 0) { 539 p_err("can't open cgroup %s", argv[0]); 540 goto exit; 541 } 542 543 attach_type = parse_attach_type(argv[1]); 544 if (attach_type == __MAX_BPF_ATTACH_TYPE) { 545 p_err("invalid attach type"); 546 goto exit_cgroup; 547 } 548 549 argc -= 2; 550 argv = &argv[2]; 551 prog_fd = prog_parse_fd(&argc, &argv); 552 if (prog_fd < 0) 553 goto exit_cgroup; 554 555 if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) { 556 p_err("failed to detach program"); 557 goto exit_prog; 558 } 559 560 if (json_output) 561 jsonw_null(json_wtr); 562 563 ret = 0; 564 565 exit_prog: 566 close(prog_fd); 567 exit_cgroup: 568 close(cgroup_fd); 569 exit: 570 return ret; 571 } 572 573 static int do_help(int argc, char **argv) 574 { 575 if (json_output) { 576 jsonw_null(json_wtr); 577 return 0; 578 } 579 580 fprintf(stderr, 581 "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n" 582 " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n" 583 " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" 584 " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n" 585 " %1$s %2$s help\n" 586 "\n" 587 HELP_SPEC_ATTACH_TYPES "\n" 588 " " HELP_SPEC_ATTACH_FLAGS "\n" 589 " " HELP_SPEC_PROGRAM "\n" 590 " " HELP_SPEC_OPTIONS " |\n" 591 " {-f|--bpffs} }\n" 592 "", 593 bin_name, argv[-2]); 594 595 return 0; 596 } 597 598 static const struct cmd cmds[] = { 599 { "show", do_show }, 600 { "list", do_show }, 601 { "tree", do_show_tree }, 602 { "attach", do_attach }, 603 { "detach", do_detach }, 604 { "help", do_help }, 605 { 0 } 606 }; 607 608 int do_cgroup(int argc, char **argv) 609 { 610 return cmd_select(cmds, argc, argv, do_help); 611 } 612