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