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 (errno == EINVAL) 252 /* Last attach type does not support query. 253 * Do not report an error for this, especially because batch 254 * mode would stop processing commands. 255 */ 256 errno = 0; 257 258 if (json_output) { 259 jsonw_end_array(json_wtr); 260 jsonw_end_object(json_wtr); 261 } 262 263 close(cgroup_fd); 264 265 return 0; 266 } 267 268 static char *find_cgroup_root(void) 269 { 270 struct mntent *mnt; 271 FILE *f; 272 273 f = fopen("/proc/mounts", "r"); 274 if (f == NULL) 275 return NULL; 276 277 while ((mnt = getmntent(f))) { 278 if (strcmp(mnt->mnt_type, "cgroup2") == 0) { 279 fclose(f); 280 return strdup(mnt->mnt_dir); 281 } 282 } 283 284 fclose(f); 285 return NULL; 286 } 287 288 static int do_show_tree(int argc, char **argv) 289 { 290 char *cgroup_root; 291 int ret; 292 293 switch (argc) { 294 case 0: 295 cgroup_root = find_cgroup_root(); 296 if (!cgroup_root) { 297 p_err("cgroup v2 isn't mounted"); 298 return -1; 299 } 300 break; 301 case 1: 302 cgroup_root = argv[0]; 303 break; 304 default: 305 p_err("too many parameters for cgroup tree"); 306 return -1; 307 } 308 309 310 if (json_output) 311 jsonw_start_array(json_wtr); 312 else 313 printf("%s\n" 314 "%-8s %-15s %-15s %-15s\n", 315 "CgroupPath", 316 "ID", "AttachType", "AttachFlags", "Name"); 317 318 switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) { 319 case NFTW_ERR: 320 p_err("can't iterate over %s: %s", cgroup_root, 321 strerror(errno)); 322 ret = -1; 323 break; 324 case SHOW_TREE_FN_ERR: 325 ret = -1; 326 break; 327 default: 328 ret = 0; 329 } 330 331 if (json_output) 332 jsonw_end_array(json_wtr); 333 334 if (argc == 0) 335 free(cgroup_root); 336 337 return ret; 338 } 339 340 static int do_attach(int argc, char **argv) 341 { 342 enum bpf_attach_type attach_type; 343 int cgroup_fd, prog_fd; 344 int attach_flags = 0; 345 int ret = -1; 346 int i; 347 348 if (argc < 4) { 349 p_err("too few parameters for cgroup attach"); 350 goto exit; 351 } 352 353 cgroup_fd = open(argv[0], O_RDONLY); 354 if (cgroup_fd < 0) { 355 p_err("can't open cgroup %s", argv[1]); 356 goto exit; 357 } 358 359 attach_type = parse_attach_type(argv[1]); 360 if (attach_type == __MAX_BPF_ATTACH_TYPE) { 361 p_err("invalid attach type"); 362 goto exit_cgroup; 363 } 364 365 argc -= 2; 366 argv = &argv[2]; 367 prog_fd = prog_parse_fd(&argc, &argv); 368 if (prog_fd < 0) 369 goto exit_cgroup; 370 371 for (i = 0; i < argc; i++) { 372 if (is_prefix(argv[i], "multi")) { 373 attach_flags |= BPF_F_ALLOW_MULTI; 374 } else if (is_prefix(argv[i], "override")) { 375 attach_flags |= BPF_F_ALLOW_OVERRIDE; 376 } else { 377 p_err("unknown option: %s", argv[i]); 378 goto exit_cgroup; 379 } 380 } 381 382 if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) { 383 p_err("failed to attach program"); 384 goto exit_prog; 385 } 386 387 if (json_output) 388 jsonw_null(json_wtr); 389 390 ret = 0; 391 392 exit_prog: 393 close(prog_fd); 394 exit_cgroup: 395 close(cgroup_fd); 396 exit: 397 return ret; 398 } 399 400 static int do_detach(int argc, char **argv) 401 { 402 enum bpf_attach_type attach_type; 403 int prog_fd, cgroup_fd; 404 int ret = -1; 405 406 if (argc < 4) { 407 p_err("too few parameters for cgroup detach"); 408 goto exit; 409 } 410 411 cgroup_fd = open(argv[0], O_RDONLY); 412 if (cgroup_fd < 0) { 413 p_err("can't open cgroup %s", argv[1]); 414 goto exit; 415 } 416 417 attach_type = parse_attach_type(argv[1]); 418 if (attach_type == __MAX_BPF_ATTACH_TYPE) { 419 p_err("invalid attach type"); 420 goto exit_cgroup; 421 } 422 423 argc -= 2; 424 argv = &argv[2]; 425 prog_fd = prog_parse_fd(&argc, &argv); 426 if (prog_fd < 0) 427 goto exit_cgroup; 428 429 if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) { 430 p_err("failed to detach program"); 431 goto exit_prog; 432 } 433 434 if (json_output) 435 jsonw_null(json_wtr); 436 437 ret = 0; 438 439 exit_prog: 440 close(prog_fd); 441 exit_cgroup: 442 close(cgroup_fd); 443 exit: 444 return ret; 445 } 446 447 static int do_help(int argc, char **argv) 448 { 449 if (json_output) { 450 jsonw_null(json_wtr); 451 return 0; 452 } 453 454 fprintf(stderr, 455 "Usage: %s %s { show | list } CGROUP\n" 456 " %s %s tree [CGROUP_ROOT]\n" 457 " %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" 458 " %s %s detach CGROUP ATTACH_TYPE PROG\n" 459 " %s %s help\n" 460 "\n" 461 HELP_SPEC_ATTACH_TYPES "\n" 462 " " HELP_SPEC_ATTACH_FLAGS "\n" 463 " " HELP_SPEC_PROGRAM "\n" 464 " " HELP_SPEC_OPTIONS "\n" 465 "", 466 bin_name, argv[-2], 467 bin_name, argv[-2], bin_name, argv[-2], 468 bin_name, argv[-2], bin_name, argv[-2]); 469 470 return 0; 471 } 472 473 static const struct cmd cmds[] = { 474 { "show", do_show }, 475 { "list", do_show }, 476 { "tree", do_show_tree }, 477 { "attach", do_attach }, 478 { "detach", do_detach }, 479 { "help", do_help }, 480 { 0 } 481 }; 482 483 int do_cgroup(int argc, char **argv) 484 { 485 return cmd_select(cmds, argc, argv, do_help); 486 } 487