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