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