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