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