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