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