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