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