linux/tools/bpf/bpftool/link.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2020 Facebook */
   3
   4#include <errno.h>
   5#include <net/if.h>
   6#include <stdio.h>
   7#include <unistd.h>
   8
   9#include <bpf/bpf.h>
  10
  11#include "json_writer.h"
  12#include "main.h"
  13
  14static const char * const link_type_name[] = {
  15        [BPF_LINK_TYPE_UNSPEC]                  = "unspec",
  16        [BPF_LINK_TYPE_RAW_TRACEPOINT]          = "raw_tracepoint",
  17        [BPF_LINK_TYPE_TRACING]                 = "tracing",
  18        [BPF_LINK_TYPE_CGROUP]                  = "cgroup",
  19        [BPF_LINK_TYPE_ITER]                    = "iter",
  20        [BPF_LINK_TYPE_NETNS]                   = "netns",
  21};
  22
  23static int link_parse_fd(int *argc, char ***argv)
  24{
  25        int fd;
  26
  27        if (is_prefix(**argv, "id")) {
  28                unsigned int id;
  29                char *endptr;
  30
  31                NEXT_ARGP();
  32
  33                id = strtoul(**argv, &endptr, 0);
  34                if (*endptr) {
  35                        p_err("can't parse %s as ID", **argv);
  36                        return -1;
  37                }
  38                NEXT_ARGP();
  39
  40                fd = bpf_link_get_fd_by_id(id);
  41                if (fd < 0)
  42                        p_err("failed to get link with ID %d: %s", id, strerror(errno));
  43                return fd;
  44        } else if (is_prefix(**argv, "pinned")) {
  45                char *path;
  46
  47                NEXT_ARGP();
  48
  49                path = **argv;
  50                NEXT_ARGP();
  51
  52                return open_obj_pinned_any(path, BPF_OBJ_LINK);
  53        }
  54
  55        p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
  56        return -1;
  57}
  58
  59static void
  60show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
  61{
  62        jsonw_uint_field(wtr, "id", info->id);
  63        if (info->type < ARRAY_SIZE(link_type_name))
  64                jsonw_string_field(wtr, "type", link_type_name[info->type]);
  65        else
  66                jsonw_uint_field(wtr, "type", info->type);
  67
  68        jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
  69}
  70
  71static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
  72{
  73        if (attach_type < ARRAY_SIZE(attach_type_name))
  74                jsonw_string_field(wtr, "attach_type",
  75                                   attach_type_name[attach_type]);
  76        else
  77                jsonw_uint_field(wtr, "attach_type", attach_type);
  78}
  79
  80static bool is_iter_map_target(const char *target_name)
  81{
  82        return strcmp(target_name, "bpf_map_elem") == 0 ||
  83               strcmp(target_name, "bpf_sk_storage_map") == 0;
  84}
  85
  86static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
  87{
  88        const char *target_name = u64_to_ptr(info->iter.target_name);
  89
  90        jsonw_string_field(wtr, "target_name", target_name);
  91
  92        if (is_iter_map_target(target_name))
  93                jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
  94}
  95
  96static int get_prog_info(int prog_id, struct bpf_prog_info *info)
  97{
  98        __u32 len = sizeof(*info);
  99        int err, prog_fd;
 100
 101        prog_fd = bpf_prog_get_fd_by_id(prog_id);
 102        if (prog_fd < 0)
 103                return prog_fd;
 104
 105        memset(info, 0, sizeof(*info));
 106        err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
 107        if (err)
 108                p_err("can't get prog info: %s", strerror(errno));
 109        close(prog_fd);
 110        return err;
 111}
 112
 113static int show_link_close_json(int fd, struct bpf_link_info *info)
 114{
 115        struct bpf_prog_info prog_info;
 116        int err;
 117
 118        jsonw_start_object(json_wtr);
 119
 120        show_link_header_json(info, json_wtr);
 121
 122        switch (info->type) {
 123        case BPF_LINK_TYPE_RAW_TRACEPOINT:
 124                jsonw_string_field(json_wtr, "tp_name",
 125                                   u64_to_ptr(info->raw_tracepoint.tp_name));
 126                break;
 127        case BPF_LINK_TYPE_TRACING:
 128                err = get_prog_info(info->prog_id, &prog_info);
 129                if (err)
 130                        return err;
 131
 132                if (prog_info.type < prog_type_name_size)
 133                        jsonw_string_field(json_wtr, "prog_type",
 134                                           prog_type_name[prog_info.type]);
 135                else
 136                        jsonw_uint_field(json_wtr, "prog_type",
 137                                         prog_info.type);
 138
 139                show_link_attach_type_json(info->tracing.attach_type,
 140                                           json_wtr);
 141                break;
 142        case BPF_LINK_TYPE_CGROUP:
 143                jsonw_lluint_field(json_wtr, "cgroup_id",
 144                                   info->cgroup.cgroup_id);
 145                show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
 146                break;
 147        case BPF_LINK_TYPE_ITER:
 148                show_iter_json(info, json_wtr);
 149                break;
 150        case BPF_LINK_TYPE_NETNS:
 151                jsonw_uint_field(json_wtr, "netns_ino",
 152                                 info->netns.netns_ino);
 153                show_link_attach_type_json(info->netns.attach_type, json_wtr);
 154                break;
 155        default:
 156                break;
 157        }
 158
 159        if (!hash_empty(link_table.table)) {
 160                struct pinned_obj *obj;
 161
 162                jsonw_name(json_wtr, "pinned");
 163                jsonw_start_array(json_wtr);
 164                hash_for_each_possible(link_table.table, obj, hash, info->id) {
 165                        if (obj->id == info->id)
 166                                jsonw_string(json_wtr, obj->path);
 167                }
 168                jsonw_end_array(json_wtr);
 169        }
 170
 171        emit_obj_refs_json(&refs_table, info->id, json_wtr);
 172
 173        jsonw_end_object(json_wtr);
 174
 175        return 0;
 176}
 177
 178static void show_link_header_plain(struct bpf_link_info *info)
 179{
 180        printf("%u: ", info->id);
 181        if (info->type < ARRAY_SIZE(link_type_name))
 182                printf("%s  ", link_type_name[info->type]);
 183        else
 184                printf("type %u  ", info->type);
 185
 186        printf("prog %u  ", info->prog_id);
 187}
 188
 189static void show_link_attach_type_plain(__u32 attach_type)
 190{
 191        if (attach_type < ARRAY_SIZE(attach_type_name))
 192                printf("attach_type %s  ", attach_type_name[attach_type]);
 193        else
 194                printf("attach_type %u  ", attach_type);
 195}
 196
 197static void show_iter_plain(struct bpf_link_info *info)
 198{
 199        const char *target_name = u64_to_ptr(info->iter.target_name);
 200
 201        printf("target_name %s  ", target_name);
 202
 203        if (is_iter_map_target(target_name))
 204                printf("map_id %u  ", info->iter.map.map_id);
 205}
 206
 207static int show_link_close_plain(int fd, struct bpf_link_info *info)
 208{
 209        struct bpf_prog_info prog_info;
 210        int err;
 211
 212        show_link_header_plain(info);
 213
 214        switch (info->type) {
 215        case BPF_LINK_TYPE_RAW_TRACEPOINT:
 216                printf("\n\ttp '%s'  ",
 217                       (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
 218                break;
 219        case BPF_LINK_TYPE_TRACING:
 220                err = get_prog_info(info->prog_id, &prog_info);
 221                if (err)
 222                        return err;
 223
 224                if (prog_info.type < prog_type_name_size)
 225                        printf("\n\tprog_type %s  ",
 226                               prog_type_name[prog_info.type]);
 227                else
 228                        printf("\n\tprog_type %u  ", prog_info.type);
 229
 230                show_link_attach_type_plain(info->tracing.attach_type);
 231                break;
 232        case BPF_LINK_TYPE_CGROUP:
 233                printf("\n\tcgroup_id %zu  ", (size_t)info->cgroup.cgroup_id);
 234                show_link_attach_type_plain(info->cgroup.attach_type);
 235                break;
 236        case BPF_LINK_TYPE_ITER:
 237                show_iter_plain(info);
 238                break;
 239        case BPF_LINK_TYPE_NETNS:
 240                printf("\n\tnetns_ino %u  ", info->netns.netns_ino);
 241                show_link_attach_type_plain(info->netns.attach_type);
 242                break;
 243        default:
 244                break;
 245        }
 246
 247        if (!hash_empty(link_table.table)) {
 248                struct pinned_obj *obj;
 249
 250                hash_for_each_possible(link_table.table, obj, hash, info->id) {
 251                        if (obj->id == info->id)
 252                                printf("\n\tpinned %s", obj->path);
 253                }
 254        }
 255        emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
 256
 257        printf("\n");
 258
 259        return 0;
 260}
 261
 262static int do_show_link(int fd)
 263{
 264        struct bpf_link_info info;
 265        __u32 len = sizeof(info);
 266        char buf[256];
 267        int err;
 268
 269        memset(&info, 0, sizeof(info));
 270again:
 271        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 272        if (err) {
 273                p_err("can't get link info: %s",
 274                      strerror(errno));
 275                close(fd);
 276                return err;
 277        }
 278        if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
 279            !info.raw_tracepoint.tp_name) {
 280                info.raw_tracepoint.tp_name = (unsigned long)&buf;
 281                info.raw_tracepoint.tp_name_len = sizeof(buf);
 282                goto again;
 283        }
 284        if (info.type == BPF_LINK_TYPE_ITER &&
 285            !info.iter.target_name) {
 286                info.iter.target_name = (unsigned long)&buf;
 287                info.iter.target_name_len = sizeof(buf);
 288                goto again;
 289        }
 290
 291        if (json_output)
 292                show_link_close_json(fd, &info);
 293        else
 294                show_link_close_plain(fd, &info);
 295
 296        close(fd);
 297        return 0;
 298}
 299
 300static int do_show(int argc, char **argv)
 301{
 302        __u32 id = 0;
 303        int err, fd;
 304
 305        if (show_pinned)
 306                build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
 307        build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
 308
 309        if (argc == 2) {
 310                fd = link_parse_fd(&argc, &argv);
 311                if (fd < 0)
 312                        return fd;
 313                return do_show_link(fd);
 314        }
 315
 316        if (argc)
 317                return BAD_ARG();
 318
 319        if (json_output)
 320                jsonw_start_array(json_wtr);
 321        while (true) {
 322                err = bpf_link_get_next_id(id, &id);
 323                if (err) {
 324                        if (errno == ENOENT)
 325                                break;
 326                        p_err("can't get next link: %s%s", strerror(errno),
 327                              errno == EINVAL ? " -- kernel too old?" : "");
 328                        break;
 329                }
 330
 331                fd = bpf_link_get_fd_by_id(id);
 332                if (fd < 0) {
 333                        if (errno == ENOENT)
 334                                continue;
 335                        p_err("can't get link by id (%u): %s",
 336                              id, strerror(errno));
 337                        break;
 338                }
 339
 340                err = do_show_link(fd);
 341                if (err)
 342                        break;
 343        }
 344        if (json_output)
 345                jsonw_end_array(json_wtr);
 346
 347        delete_obj_refs_table(&refs_table);
 348
 349        return errno == ENOENT ? 0 : -1;
 350}
 351
 352static int do_pin(int argc, char **argv)
 353{
 354        int err;
 355
 356        err = do_pin_any(argc, argv, link_parse_fd);
 357        if (!err && json_output)
 358                jsonw_null(json_wtr);
 359        return err;
 360}
 361
 362static int do_detach(int argc, char **argv)
 363{
 364        int err, fd;
 365
 366        if (argc != 2) {
 367                p_err("link specifier is invalid or missing\n");
 368                return 1;
 369        }
 370
 371        fd = link_parse_fd(&argc, &argv);
 372        if (fd < 0)
 373                return 1;
 374
 375        err = bpf_link_detach(fd);
 376        if (err)
 377                err = -errno;
 378        close(fd);
 379        if (err) {
 380                p_err("failed link detach: %s", strerror(-err));
 381                return 1;
 382        }
 383
 384        if (json_output)
 385                jsonw_null(json_wtr);
 386
 387        return 0;
 388}
 389
 390static int do_help(int argc, char **argv)
 391{
 392        if (json_output) {
 393                jsonw_null(json_wtr);
 394                return 0;
 395        }
 396
 397        fprintf(stderr,
 398                "Usage: %1$s %2$s { show | list }   [LINK]\n"
 399                "       %1$s %2$s pin        LINK  FILE\n"
 400                "       %1$s %2$s detach     LINK\n"
 401                "       %1$s %2$s help\n"
 402                "\n"
 403                "       " HELP_SPEC_LINK "\n"
 404                "       " HELP_SPEC_OPTIONS " |\n"
 405                "                    {-f|--bpffs} | {-n|--nomount} }\n"
 406                "",
 407                bin_name, argv[-2]);
 408
 409        return 0;
 410}
 411
 412static const struct cmd cmds[] = {
 413        { "show",       do_show },
 414        { "list",       do_show },
 415        { "help",       do_help },
 416        { "pin",        do_pin },
 417        { "detach",     do_detach },
 418        { 0 }
 419};
 420
 421int do_link(int argc, char **argv)
 422{
 423        return cmd_select(cmds, argc, argv, do_help);
 424}
 425