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