1244 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/* Copyright (C) 2020 Facebook */3 4#include <errno.h>5#include <linux/err.h>6#include <linux/netfilter.h>7#include <linux/netfilter_arp.h>8#include <linux/perf_event.h>9#include <net/if.h>10#include <stdio.h>11#include <unistd.h>12 13#include <bpf/bpf.h>14#include <bpf/hashmap.h>15 16#include "json_writer.h"17#include "main.h"18#include "xlated_dumper.h"19 20#define PERF_HW_CACHE_LEN 12821 22static struct hashmap *link_table;23static struct dump_data dd;24 25static const char *perf_type_name[PERF_TYPE_MAX] = {26 [PERF_TYPE_HARDWARE] = "hardware",27 [PERF_TYPE_SOFTWARE] = "software",28 [PERF_TYPE_TRACEPOINT] = "tracepoint",29 [PERF_TYPE_HW_CACHE] = "hw-cache",30 [PERF_TYPE_RAW] = "raw",31 [PERF_TYPE_BREAKPOINT] = "breakpoint",32};33 34const char *event_symbols_hw[PERF_COUNT_HW_MAX] = {35 [PERF_COUNT_HW_CPU_CYCLES] = "cpu-cycles",36 [PERF_COUNT_HW_INSTRUCTIONS] = "instructions",37 [PERF_COUNT_HW_CACHE_REFERENCES] = "cache-references",38 [PERF_COUNT_HW_CACHE_MISSES] = "cache-misses",39 [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = "branch-instructions",40 [PERF_COUNT_HW_BRANCH_MISSES] = "branch-misses",41 [PERF_COUNT_HW_BUS_CYCLES] = "bus-cycles",42 [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = "stalled-cycles-frontend",43 [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = "stalled-cycles-backend",44 [PERF_COUNT_HW_REF_CPU_CYCLES] = "ref-cycles",45};46 47const char *event_symbols_sw[PERF_COUNT_SW_MAX] = {48 [PERF_COUNT_SW_CPU_CLOCK] = "cpu-clock",49 [PERF_COUNT_SW_TASK_CLOCK] = "task-clock",50 [PERF_COUNT_SW_PAGE_FAULTS] = "page-faults",51 [PERF_COUNT_SW_CONTEXT_SWITCHES] = "context-switches",52 [PERF_COUNT_SW_CPU_MIGRATIONS] = "cpu-migrations",53 [PERF_COUNT_SW_PAGE_FAULTS_MIN] = "minor-faults",54 [PERF_COUNT_SW_PAGE_FAULTS_MAJ] = "major-faults",55 [PERF_COUNT_SW_ALIGNMENT_FAULTS] = "alignment-faults",56 [PERF_COUNT_SW_EMULATION_FAULTS] = "emulation-faults",57 [PERF_COUNT_SW_DUMMY] = "dummy",58 [PERF_COUNT_SW_BPF_OUTPUT] = "bpf-output",59 [PERF_COUNT_SW_CGROUP_SWITCHES] = "cgroup-switches",60};61 62const char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] = {63 [PERF_COUNT_HW_CACHE_L1D] = "L1-dcache",64 [PERF_COUNT_HW_CACHE_L1I] = "L1-icache",65 [PERF_COUNT_HW_CACHE_LL] = "LLC",66 [PERF_COUNT_HW_CACHE_DTLB] = "dTLB",67 [PERF_COUNT_HW_CACHE_ITLB] = "iTLB",68 [PERF_COUNT_HW_CACHE_BPU] = "branch",69 [PERF_COUNT_HW_CACHE_NODE] = "node",70};71 72const char *evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] = {73 [PERF_COUNT_HW_CACHE_OP_READ] = "load",74 [PERF_COUNT_HW_CACHE_OP_WRITE] = "store",75 [PERF_COUNT_HW_CACHE_OP_PREFETCH] = "prefetch",76};77 78const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] = {79 [PERF_COUNT_HW_CACHE_RESULT_ACCESS] = "refs",80 [PERF_COUNT_HW_CACHE_RESULT_MISS] = "misses",81};82 83#define perf_event_name(array, id) ({ \84 const char *event_str = NULL; \85 \86 if ((id) < ARRAY_SIZE(array)) \87 event_str = array[id]; \88 event_str; \89})90 91static int link_parse_fd(int *argc, char ***argv)92{93 int fd;94 95 if (is_prefix(**argv, "id")) {96 unsigned int id;97 char *endptr;98 99 NEXT_ARGP();100 101 id = strtoul(**argv, &endptr, 0);102 if (*endptr) {103 p_err("can't parse %s as ID", **argv);104 return -1;105 }106 NEXT_ARGP();107 108 fd = bpf_link_get_fd_by_id(id);109 if (fd < 0)110 p_err("failed to get link with ID %d: %s", id, strerror(errno));111 return fd;112 } else if (is_prefix(**argv, "pinned")) {113 char *path;114 115 NEXT_ARGP();116 117 path = **argv;118 NEXT_ARGP();119 120 return open_obj_pinned_any(path, BPF_OBJ_LINK);121 }122 123 p_err("expected 'id' or 'pinned', got: '%s'?", **argv);124 return -1;125}126 127static void128show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)129{130 const char *link_type_str;131 132 jsonw_uint_field(wtr, "id", info->id);133 link_type_str = libbpf_bpf_link_type_str(info->type);134 if (link_type_str)135 jsonw_string_field(wtr, "type", link_type_str);136 else137 jsonw_uint_field(wtr, "type", info->type);138 139 jsonw_uint_field(json_wtr, "prog_id", info->prog_id);140}141 142static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)143{144 const char *attach_type_str;145 146 attach_type_str = libbpf_bpf_attach_type_str(attach_type);147 if (attach_type_str)148 jsonw_string_field(wtr, "attach_type", attach_type_str);149 else150 jsonw_uint_field(wtr, "attach_type", attach_type);151}152 153static void show_link_ifindex_json(__u32 ifindex, json_writer_t *wtr)154{155 char devname[IF_NAMESIZE] = "(unknown)";156 157 if (ifindex)158 if_indextoname(ifindex, devname);159 else160 snprintf(devname, sizeof(devname), "(detached)");161 jsonw_string_field(wtr, "devname", devname);162 jsonw_uint_field(wtr, "ifindex", ifindex);163}164 165static bool is_iter_map_target(const char *target_name)166{167 return strcmp(target_name, "bpf_map_elem") == 0 ||168 strcmp(target_name, "bpf_sk_storage_map") == 0;169}170 171static bool is_iter_cgroup_target(const char *target_name)172{173 return strcmp(target_name, "cgroup") == 0;174}175 176static const char *cgroup_order_string(__u32 order)177{178 switch (order) {179 case BPF_CGROUP_ITER_ORDER_UNSPEC:180 return "order_unspec";181 case BPF_CGROUP_ITER_SELF_ONLY:182 return "self_only";183 case BPF_CGROUP_ITER_DESCENDANTS_PRE:184 return "descendants_pre";185 case BPF_CGROUP_ITER_DESCENDANTS_POST:186 return "descendants_post";187 case BPF_CGROUP_ITER_ANCESTORS_UP:188 return "ancestors_up";189 default: /* won't happen */190 return "unknown";191 }192}193 194static bool is_iter_task_target(const char *target_name)195{196 return strcmp(target_name, "task") == 0 ||197 strcmp(target_name, "task_file") == 0 ||198 strcmp(target_name, "task_vma") == 0;199}200 201static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)202{203 const char *target_name = u64_to_ptr(info->iter.target_name);204 205 jsonw_string_field(wtr, "target_name", target_name);206 207 if (is_iter_map_target(target_name))208 jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);209 else if (is_iter_task_target(target_name)) {210 if (info->iter.task.tid)211 jsonw_uint_field(wtr, "tid", info->iter.task.tid);212 else if (info->iter.task.pid)213 jsonw_uint_field(wtr, "pid", info->iter.task.pid);214 }215 216 if (is_iter_cgroup_target(target_name)) {217 jsonw_lluint_field(wtr, "cgroup_id", info->iter.cgroup.cgroup_id);218 jsonw_string_field(wtr, "order",219 cgroup_order_string(info->iter.cgroup.order));220 }221}222 223void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr)224{225 jsonw_uint_field(json_wtr, "pf",226 info->netfilter.pf);227 jsonw_uint_field(json_wtr, "hook",228 info->netfilter.hooknum);229 jsonw_int_field(json_wtr, "prio",230 info->netfilter.priority);231 jsonw_uint_field(json_wtr, "flags",232 info->netfilter.flags);233}234 235static int get_prog_info(int prog_id, struct bpf_prog_info *info)236{237 __u32 len = sizeof(*info);238 int err, prog_fd;239 240 prog_fd = bpf_prog_get_fd_by_id(prog_id);241 if (prog_fd < 0)242 return prog_fd;243 244 memset(info, 0, sizeof(*info));245 err = bpf_prog_get_info_by_fd(prog_fd, info, &len);246 if (err)247 p_err("can't get prog info: %s", strerror(errno));248 close(prog_fd);249 return err;250}251 252struct addr_cookie {253 __u64 addr;254 __u64 cookie;255};256 257static int cmp_addr_cookie(const void *A, const void *B)258{259 const struct addr_cookie *a = A, *b = B;260 261 if (a->addr == b->addr)262 return 0;263 return a->addr < b->addr ? -1 : 1;264}265 266static struct addr_cookie *267get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)268{269 struct addr_cookie *data;270 __u32 i;271 272 data = calloc(count, sizeof(data[0]));273 if (!data) {274 p_err("mem alloc failed");275 return NULL;276 }277 for (i = 0; i < count; i++) {278 data[i].addr = addrs[i];279 data[i].cookie = cookies[i];280 }281 qsort(data, count, sizeof(data[0]), cmp_addr_cookie);282 return data;283}284 285static void286show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)287{288 struct addr_cookie *data;289 __u32 i, j = 0;290 291 jsonw_bool_field(json_wtr, "retprobe",292 info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN);293 jsonw_uint_field(json_wtr, "func_cnt", info->kprobe_multi.count);294 jsonw_uint_field(json_wtr, "missed", info->kprobe_multi.missed);295 jsonw_name(json_wtr, "funcs");296 jsonw_start_array(json_wtr);297 data = get_addr_cookie_array(u64_to_ptr(info->kprobe_multi.addrs),298 u64_to_ptr(info->kprobe_multi.cookies),299 info->kprobe_multi.count);300 if (!data)301 return;302 303 /* Load it once for all. */304 if (!dd.sym_count)305 kernel_syms_load(&dd);306 if (!dd.sym_count)307 goto error;308 309 for (i = 0; i < dd.sym_count; i++) {310 if (dd.sym_mapping[i].address != data[j].addr)311 continue;312 jsonw_start_object(json_wtr);313 jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);314 jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);315 /* Print null if it is vmlinux */316 if (dd.sym_mapping[i].module[0] == '\0') {317 jsonw_name(json_wtr, "module");318 jsonw_null(json_wtr);319 } else {320 jsonw_string_field(json_wtr, "module", dd.sym_mapping[i].module);321 }322 jsonw_uint_field(json_wtr, "cookie", data[j].cookie);323 jsonw_end_object(json_wtr);324 if (j++ == info->kprobe_multi.count)325 break;326 }327 jsonw_end_array(json_wtr);328error:329 free(data);330}331 332static __u64 *u64_to_arr(__u64 val)333{334 return (__u64 *) u64_to_ptr(val);335}336 337static void338show_uprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)339{340 __u32 i;341 342 jsonw_bool_field(json_wtr, "retprobe",343 info->uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN);344 jsonw_string_field(json_wtr, "path", (char *) u64_to_ptr(info->uprobe_multi.path));345 jsonw_uint_field(json_wtr, "func_cnt", info->uprobe_multi.count);346 jsonw_int_field(json_wtr, "pid", (int) info->uprobe_multi.pid);347 jsonw_name(json_wtr, "funcs");348 jsonw_start_array(json_wtr);349 350 for (i = 0; i < info->uprobe_multi.count; i++) {351 jsonw_start_object(json_wtr);352 jsonw_uint_field(json_wtr, "offset",353 u64_to_arr(info->uprobe_multi.offsets)[i]);354 jsonw_uint_field(json_wtr, "ref_ctr_offset",355 u64_to_arr(info->uprobe_multi.ref_ctr_offsets)[i]);356 jsonw_uint_field(json_wtr, "cookie",357 u64_to_arr(info->uprobe_multi.cookies)[i]);358 jsonw_end_object(json_wtr);359 }360 jsonw_end_array(json_wtr);361}362 363static void364show_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr)365{366 jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_KRETPROBE);367 jsonw_uint_field(wtr, "addr", info->perf_event.kprobe.addr);368 jsonw_string_field(wtr, "func",369 u64_to_ptr(info->perf_event.kprobe.func_name));370 jsonw_uint_field(wtr, "offset", info->perf_event.kprobe.offset);371 jsonw_uint_field(wtr, "missed", info->perf_event.kprobe.missed);372 jsonw_uint_field(wtr, "cookie", info->perf_event.kprobe.cookie);373}374 375static void376show_perf_event_uprobe_json(struct bpf_link_info *info, json_writer_t *wtr)377{378 jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_URETPROBE);379 jsonw_string_field(wtr, "file",380 u64_to_ptr(info->perf_event.uprobe.file_name));381 jsonw_uint_field(wtr, "offset", info->perf_event.uprobe.offset);382 jsonw_uint_field(wtr, "cookie", info->perf_event.uprobe.cookie);383}384 385static void386show_perf_event_tracepoint_json(struct bpf_link_info *info, json_writer_t *wtr)387{388 jsonw_string_field(wtr, "tracepoint",389 u64_to_ptr(info->perf_event.tracepoint.tp_name));390 jsonw_uint_field(wtr, "cookie", info->perf_event.tracepoint.cookie);391}392 393static char *perf_config_hw_cache_str(__u64 config)394{395 const char *hw_cache, *result, *op;396 char *str = malloc(PERF_HW_CACHE_LEN);397 398 if (!str) {399 p_err("mem alloc failed");400 return NULL;401 }402 403 hw_cache = perf_event_name(evsel__hw_cache, config & 0xff);404 if (hw_cache)405 snprintf(str, PERF_HW_CACHE_LEN, "%s-", hw_cache);406 else407 snprintf(str, PERF_HW_CACHE_LEN, "%lld-", config & 0xff);408 409 op = perf_event_name(evsel__hw_cache_op, (config >> 8) & 0xff);410 if (op)411 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),412 "%s-", op);413 else414 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),415 "%lld-", (config >> 8) & 0xff);416 417 result = perf_event_name(evsel__hw_cache_result, config >> 16);418 if (result)419 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),420 "%s", result);421 else422 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),423 "%lld", config >> 16);424 return str;425}426 427static const char *perf_config_str(__u32 type, __u64 config)428{429 const char *perf_config;430 431 switch (type) {432 case PERF_TYPE_HARDWARE:433 perf_config = perf_event_name(event_symbols_hw, config);434 break;435 case PERF_TYPE_SOFTWARE:436 perf_config = perf_event_name(event_symbols_sw, config);437 break;438 case PERF_TYPE_HW_CACHE:439 perf_config = perf_config_hw_cache_str(config);440 break;441 default:442 perf_config = NULL;443 break;444 }445 return perf_config;446}447 448static void449show_perf_event_event_json(struct bpf_link_info *info, json_writer_t *wtr)450{451 __u64 config = info->perf_event.event.config;452 __u32 type = info->perf_event.event.type;453 const char *perf_type, *perf_config;454 455 perf_type = perf_event_name(perf_type_name, type);456 if (perf_type)457 jsonw_string_field(wtr, "event_type", perf_type);458 else459 jsonw_uint_field(wtr, "event_type", type);460 461 perf_config = perf_config_str(type, config);462 if (perf_config)463 jsonw_string_field(wtr, "event_config", perf_config);464 else465 jsonw_uint_field(wtr, "event_config", config);466 467 jsonw_uint_field(wtr, "cookie", info->perf_event.event.cookie);468 469 if (type == PERF_TYPE_HW_CACHE && perf_config)470 free((void *)perf_config);471}472 473static int show_link_close_json(int fd, struct bpf_link_info *info)474{475 struct bpf_prog_info prog_info;476 const char *prog_type_str;477 int err;478 479 jsonw_start_object(json_wtr);480 481 show_link_header_json(info, json_wtr);482 483 switch (info->type) {484 case BPF_LINK_TYPE_RAW_TRACEPOINT:485 jsonw_string_field(json_wtr, "tp_name",486 u64_to_ptr(info->raw_tracepoint.tp_name));487 break;488 case BPF_LINK_TYPE_TRACING:489 err = get_prog_info(info->prog_id, &prog_info);490 if (err)491 return err;492 493 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);494 /* libbpf will return NULL for variants unknown to it. */495 if (prog_type_str)496 jsonw_string_field(json_wtr, "prog_type", prog_type_str);497 else498 jsonw_uint_field(json_wtr, "prog_type", prog_info.type);499 500 show_link_attach_type_json(info->tracing.attach_type,501 json_wtr);502 jsonw_uint_field(json_wtr, "target_obj_id", info->tracing.target_obj_id);503 jsonw_uint_field(json_wtr, "target_btf_id", info->tracing.target_btf_id);504 break;505 case BPF_LINK_TYPE_CGROUP:506 jsonw_lluint_field(json_wtr, "cgroup_id",507 info->cgroup.cgroup_id);508 show_link_attach_type_json(info->cgroup.attach_type, json_wtr);509 break;510 case BPF_LINK_TYPE_ITER:511 show_iter_json(info, json_wtr);512 break;513 case BPF_LINK_TYPE_NETNS:514 jsonw_uint_field(json_wtr, "netns_ino",515 info->netns.netns_ino);516 show_link_attach_type_json(info->netns.attach_type, json_wtr);517 break;518 case BPF_LINK_TYPE_NETFILTER:519 netfilter_dump_json(info, json_wtr);520 break;521 case BPF_LINK_TYPE_TCX:522 show_link_ifindex_json(info->tcx.ifindex, json_wtr);523 show_link_attach_type_json(info->tcx.attach_type, json_wtr);524 break;525 case BPF_LINK_TYPE_NETKIT:526 show_link_ifindex_json(info->netkit.ifindex, json_wtr);527 show_link_attach_type_json(info->netkit.attach_type, json_wtr);528 break;529 case BPF_LINK_TYPE_SOCKMAP:530 jsonw_uint_field(json_wtr, "map_id", info->sockmap.map_id);531 show_link_attach_type_json(info->sockmap.attach_type, json_wtr);532 break;533 case BPF_LINK_TYPE_XDP:534 show_link_ifindex_json(info->xdp.ifindex, json_wtr);535 break;536 case BPF_LINK_TYPE_STRUCT_OPS:537 jsonw_uint_field(json_wtr, "map_id",538 info->struct_ops.map_id);539 break;540 case BPF_LINK_TYPE_KPROBE_MULTI:541 show_kprobe_multi_json(info, json_wtr);542 break;543 case BPF_LINK_TYPE_UPROBE_MULTI:544 show_uprobe_multi_json(info, json_wtr);545 break;546 case BPF_LINK_TYPE_PERF_EVENT:547 switch (info->perf_event.type) {548 case BPF_PERF_EVENT_EVENT:549 show_perf_event_event_json(info, json_wtr);550 break;551 case BPF_PERF_EVENT_TRACEPOINT:552 show_perf_event_tracepoint_json(info, json_wtr);553 break;554 case BPF_PERF_EVENT_KPROBE:555 case BPF_PERF_EVENT_KRETPROBE:556 show_perf_event_kprobe_json(info, json_wtr);557 break;558 case BPF_PERF_EVENT_UPROBE:559 case BPF_PERF_EVENT_URETPROBE:560 show_perf_event_uprobe_json(info, json_wtr);561 break;562 default:563 break;564 }565 break;566 default:567 break;568 }569 570 if (!hashmap__empty(link_table)) {571 struct hashmap_entry *entry;572 573 jsonw_name(json_wtr, "pinned");574 jsonw_start_array(json_wtr);575 hashmap__for_each_key_entry(link_table, entry, info->id)576 jsonw_string(json_wtr, entry->pvalue);577 jsonw_end_array(json_wtr);578 }579 580 emit_obj_refs_json(refs_table, info->id, json_wtr);581 582 jsonw_end_object(json_wtr);583 584 return 0;585}586 587static void show_link_header_plain(struct bpf_link_info *info)588{589 const char *link_type_str;590 591 printf("%u: ", info->id);592 link_type_str = libbpf_bpf_link_type_str(info->type);593 if (link_type_str)594 printf("%s ", link_type_str);595 else596 printf("type %u ", info->type);597 598 if (info->type == BPF_LINK_TYPE_STRUCT_OPS)599 printf("map %u ", info->struct_ops.map_id);600 else601 printf("prog %u ", info->prog_id);602}603 604static void show_link_attach_type_plain(__u32 attach_type)605{606 const char *attach_type_str;607 608 attach_type_str = libbpf_bpf_attach_type_str(attach_type);609 if (attach_type_str)610 printf("attach_type %s ", attach_type_str);611 else612 printf("attach_type %u ", attach_type);613}614 615static void show_link_ifindex_plain(__u32 ifindex)616{617 char devname[IF_NAMESIZE * 2] = "(unknown)";618 char tmpname[IF_NAMESIZE];619 char *ret = NULL;620 621 if (ifindex)622 ret = if_indextoname(ifindex, tmpname);623 else624 snprintf(devname, sizeof(devname), "(detached)");625 if (ret)626 snprintf(devname, sizeof(devname), "%s(%d)",627 tmpname, ifindex);628 printf("ifindex %s ", devname);629}630 631static void show_iter_plain(struct bpf_link_info *info)632{633 const char *target_name = u64_to_ptr(info->iter.target_name);634 635 printf("target_name %s ", target_name);636 637 if (is_iter_map_target(target_name))638 printf("map_id %u ", info->iter.map.map_id);639 else if (is_iter_task_target(target_name)) {640 if (info->iter.task.tid)641 printf("tid %u ", info->iter.task.tid);642 else if (info->iter.task.pid)643 printf("pid %u ", info->iter.task.pid);644 }645 646 if (is_iter_cgroup_target(target_name)) {647 printf("cgroup_id %llu ", info->iter.cgroup.cgroup_id);648 printf("order %s ",649 cgroup_order_string(info->iter.cgroup.order));650 }651}652 653static const char * const pf2name[] = {654 [NFPROTO_INET] = "inet",655 [NFPROTO_IPV4] = "ip",656 [NFPROTO_ARP] = "arp",657 [NFPROTO_NETDEV] = "netdev",658 [NFPROTO_BRIDGE] = "bridge",659 [NFPROTO_IPV6] = "ip6",660};661 662static const char * const inethook2name[] = {663 [NF_INET_PRE_ROUTING] = "prerouting",664 [NF_INET_LOCAL_IN] = "input",665 [NF_INET_FORWARD] = "forward",666 [NF_INET_LOCAL_OUT] = "output",667 [NF_INET_POST_ROUTING] = "postrouting",668};669 670static const char * const arphook2name[] = {671 [NF_ARP_IN] = "input",672 [NF_ARP_OUT] = "output",673};674 675void netfilter_dump_plain(const struct bpf_link_info *info)676{677 const char *hookname = NULL, *pfname = NULL;678 unsigned int hook = info->netfilter.hooknum;679 unsigned int pf = info->netfilter.pf;680 681 if (pf < ARRAY_SIZE(pf2name))682 pfname = pf2name[pf];683 684 switch (pf) {685 case NFPROTO_BRIDGE: /* bridge shares numbers with enum nf_inet_hooks */686 case NFPROTO_IPV4:687 case NFPROTO_IPV6:688 case NFPROTO_INET:689 if (hook < ARRAY_SIZE(inethook2name))690 hookname = inethook2name[hook];691 break;692 case NFPROTO_ARP:693 if (hook < ARRAY_SIZE(arphook2name))694 hookname = arphook2name[hook];695 default:696 break;697 }698 699 if (pfname)700 printf("\n\t%s", pfname);701 else702 printf("\n\tpf: %d", pf);703 704 if (hookname)705 printf(" %s", hookname);706 else707 printf(", hook %u,", hook);708 709 printf(" prio %d", info->netfilter.priority);710 711 if (info->netfilter.flags)712 printf(" flags 0x%x", info->netfilter.flags);713}714 715static void show_kprobe_multi_plain(struct bpf_link_info *info)716{717 struct addr_cookie *data;718 __u32 i, j = 0;719 720 if (!info->kprobe_multi.count)721 return;722 723 if (info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN)724 printf("\n\tkretprobe.multi ");725 else726 printf("\n\tkprobe.multi ");727 printf("func_cnt %u ", info->kprobe_multi.count);728 if (info->kprobe_multi.missed)729 printf("missed %llu ", info->kprobe_multi.missed);730 data = get_addr_cookie_array(u64_to_ptr(info->kprobe_multi.addrs),731 u64_to_ptr(info->kprobe_multi.cookies),732 info->kprobe_multi.count);733 if (!data)734 return;735 736 /* Load it once for all. */737 if (!dd.sym_count)738 kernel_syms_load(&dd);739 if (!dd.sym_count)740 goto error;741 742 printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");743 for (i = 0; i < dd.sym_count; i++) {744 if (dd.sym_mapping[i].address != data[j].addr)745 continue;746 printf("\n\t%016lx %-16llx %s",747 dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);748 if (dd.sym_mapping[i].module[0] != '\0')749 printf(" [%s] ", dd.sym_mapping[i].module);750 else751 printf(" ");752 753 if (j++ == info->kprobe_multi.count)754 break;755 }756error:757 free(data);758}759 760static void show_uprobe_multi_plain(struct bpf_link_info *info)761{762 __u32 i;763 764 if (!info->uprobe_multi.count)765 return;766 767 if (info->uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN)768 printf("\n\turetprobe.multi ");769 else770 printf("\n\tuprobe.multi ");771 772 printf("path %s ", (char *) u64_to_ptr(info->uprobe_multi.path));773 printf("func_cnt %u ", info->uprobe_multi.count);774 775 if (info->uprobe_multi.pid)776 printf("pid %d ", info->uprobe_multi.pid);777 778 printf("\n\t%-16s %-16s %-16s", "offset", "ref_ctr_offset", "cookies");779 for (i = 0; i < info->uprobe_multi.count; i++) {780 printf("\n\t0x%-16llx 0x%-16llx 0x%-16llx",781 u64_to_arr(info->uprobe_multi.offsets)[i],782 u64_to_arr(info->uprobe_multi.ref_ctr_offsets)[i],783 u64_to_arr(info->uprobe_multi.cookies)[i]);784 }785}786 787static void show_perf_event_kprobe_plain(struct bpf_link_info *info)788{789 const char *buf;790 791 buf = u64_to_ptr(info->perf_event.kprobe.func_name);792 if (buf[0] == '\0' && !info->perf_event.kprobe.addr)793 return;794 795 if (info->perf_event.type == BPF_PERF_EVENT_KRETPROBE)796 printf("\n\tkretprobe ");797 else798 printf("\n\tkprobe ");799 if (info->perf_event.kprobe.addr)800 printf("%llx ", info->perf_event.kprobe.addr);801 printf("%s", buf);802 if (info->perf_event.kprobe.offset)803 printf("+%#x", info->perf_event.kprobe.offset);804 if (info->perf_event.kprobe.missed)805 printf(" missed %llu", info->perf_event.kprobe.missed);806 if (info->perf_event.kprobe.cookie)807 printf(" cookie %llu", info->perf_event.kprobe.cookie);808 printf(" ");809}810 811static void show_perf_event_uprobe_plain(struct bpf_link_info *info)812{813 const char *buf;814 815 buf = u64_to_ptr(info->perf_event.uprobe.file_name);816 if (buf[0] == '\0')817 return;818 819 if (info->perf_event.type == BPF_PERF_EVENT_URETPROBE)820 printf("\n\turetprobe ");821 else822 printf("\n\tuprobe ");823 printf("%s+%#x ", buf, info->perf_event.uprobe.offset);824 if (info->perf_event.uprobe.cookie)825 printf("cookie %llu ", info->perf_event.uprobe.cookie);826}827 828static void show_perf_event_tracepoint_plain(struct bpf_link_info *info)829{830 const char *buf;831 832 buf = u64_to_ptr(info->perf_event.tracepoint.tp_name);833 if (buf[0] == '\0')834 return;835 836 printf("\n\ttracepoint %s ", buf);837 if (info->perf_event.tracepoint.cookie)838 printf("cookie %llu ", info->perf_event.tracepoint.cookie);839}840 841static void show_perf_event_event_plain(struct bpf_link_info *info)842{843 __u64 config = info->perf_event.event.config;844 __u32 type = info->perf_event.event.type;845 const char *perf_type, *perf_config;846 847 printf("\n\tevent ");848 perf_type = perf_event_name(perf_type_name, type);849 if (perf_type)850 printf("%s:", perf_type);851 else852 printf("%u :", type);853 854 perf_config = perf_config_str(type, config);855 if (perf_config)856 printf("%s ", perf_config);857 else858 printf("%llu ", config);859 860 if (info->perf_event.event.cookie)861 printf("cookie %llu ", info->perf_event.event.cookie);862 863 if (type == PERF_TYPE_HW_CACHE && perf_config)864 free((void *)perf_config);865}866 867static int show_link_close_plain(int fd, struct bpf_link_info *info)868{869 struct bpf_prog_info prog_info;870 const char *prog_type_str;871 int err;872 873 show_link_header_plain(info);874 875 switch (info->type) {876 case BPF_LINK_TYPE_RAW_TRACEPOINT:877 printf("\n\ttp '%s' ",878 (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));879 break;880 case BPF_LINK_TYPE_TRACING:881 err = get_prog_info(info->prog_id, &prog_info);882 if (err)883 return err;884 885 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);886 /* libbpf will return NULL for variants unknown to it. */887 if (prog_type_str)888 printf("\n\tprog_type %s ", prog_type_str);889 else890 printf("\n\tprog_type %u ", prog_info.type);891 892 show_link_attach_type_plain(info->tracing.attach_type);893 if (info->tracing.target_obj_id || info->tracing.target_btf_id)894 printf("\n\ttarget_obj_id %u target_btf_id %u ",895 info->tracing.target_obj_id,896 info->tracing.target_btf_id);897 break;898 case BPF_LINK_TYPE_CGROUP:899 printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id);900 show_link_attach_type_plain(info->cgroup.attach_type);901 break;902 case BPF_LINK_TYPE_ITER:903 show_iter_plain(info);904 break;905 case BPF_LINK_TYPE_NETNS:906 printf("\n\tnetns_ino %u ", info->netns.netns_ino);907 show_link_attach_type_plain(info->netns.attach_type);908 break;909 case BPF_LINK_TYPE_NETFILTER:910 netfilter_dump_plain(info);911 break;912 case BPF_LINK_TYPE_TCX:913 printf("\n\t");914 show_link_ifindex_plain(info->tcx.ifindex);915 show_link_attach_type_plain(info->tcx.attach_type);916 break;917 case BPF_LINK_TYPE_NETKIT:918 printf("\n\t");919 show_link_ifindex_plain(info->netkit.ifindex);920 show_link_attach_type_plain(info->netkit.attach_type);921 break;922 case BPF_LINK_TYPE_SOCKMAP:923 printf("\n\t");924 printf("map_id %u ", info->sockmap.map_id);925 show_link_attach_type_plain(info->sockmap.attach_type);926 break;927 case BPF_LINK_TYPE_XDP:928 printf("\n\t");929 show_link_ifindex_plain(info->xdp.ifindex);930 break;931 case BPF_LINK_TYPE_KPROBE_MULTI:932 show_kprobe_multi_plain(info);933 break;934 case BPF_LINK_TYPE_UPROBE_MULTI:935 show_uprobe_multi_plain(info);936 break;937 case BPF_LINK_TYPE_PERF_EVENT:938 switch (info->perf_event.type) {939 case BPF_PERF_EVENT_EVENT:940 show_perf_event_event_plain(info);941 break;942 case BPF_PERF_EVENT_TRACEPOINT:943 show_perf_event_tracepoint_plain(info);944 break;945 case BPF_PERF_EVENT_KPROBE:946 case BPF_PERF_EVENT_KRETPROBE:947 show_perf_event_kprobe_plain(info);948 break;949 case BPF_PERF_EVENT_UPROBE:950 case BPF_PERF_EVENT_URETPROBE:951 show_perf_event_uprobe_plain(info);952 break;953 default:954 break;955 }956 break;957 default:958 break;959 }960 961 if (!hashmap__empty(link_table)) {962 struct hashmap_entry *entry;963 964 hashmap__for_each_key_entry(link_table, entry, info->id)965 printf("\n\tpinned %s", (char *)entry->pvalue);966 }967 emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");968 969 printf("\n");970 971 return 0;972}973 974static int do_show_link(int fd)975{976 __u64 *ref_ctr_offsets = NULL, *offsets = NULL, *cookies = NULL;977 struct bpf_link_info info;978 __u32 len = sizeof(info);979 char path_buf[PATH_MAX];980 __u64 *addrs = NULL;981 char buf[PATH_MAX];982 int count;983 int err;984 985 memset(&info, 0, sizeof(info));986 buf[0] = '\0';987again:988 err = bpf_link_get_info_by_fd(fd, &info, &len);989 if (err) {990 p_err("can't get link info: %s",991 strerror(errno));992 close(fd);993 return err;994 }995 if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&996 !info.raw_tracepoint.tp_name) {997 info.raw_tracepoint.tp_name = ptr_to_u64(&buf);998 info.raw_tracepoint.tp_name_len = sizeof(buf);999 goto again;1000 }1001 if (info.type == BPF_LINK_TYPE_ITER &&1002 !info.iter.target_name) {1003 info.iter.target_name = ptr_to_u64(&buf);1004 info.iter.target_name_len = sizeof(buf);1005 goto again;1006 }1007 if (info.type == BPF_LINK_TYPE_KPROBE_MULTI &&1008 !info.kprobe_multi.addrs) {1009 count = info.kprobe_multi.count;1010 if (count) {1011 addrs = calloc(count, sizeof(__u64));1012 if (!addrs) {1013 p_err("mem alloc failed");1014 close(fd);1015 return -ENOMEM;1016 }1017 info.kprobe_multi.addrs = ptr_to_u64(addrs);1018 cookies = calloc(count, sizeof(__u64));1019 if (!cookies) {1020 p_err("mem alloc failed");1021 free(addrs);1022 close(fd);1023 return -ENOMEM;1024 }1025 info.kprobe_multi.cookies = ptr_to_u64(cookies);1026 goto again;1027 }1028 }1029 if (info.type == BPF_LINK_TYPE_UPROBE_MULTI &&1030 !info.uprobe_multi.offsets) {1031 count = info.uprobe_multi.count;1032 if (count) {1033 offsets = calloc(count, sizeof(__u64));1034 if (!offsets) {1035 p_err("mem alloc failed");1036 close(fd);1037 return -ENOMEM;1038 }1039 info.uprobe_multi.offsets = ptr_to_u64(offsets);1040 ref_ctr_offsets = calloc(count, sizeof(__u64));1041 if (!ref_ctr_offsets) {1042 p_err("mem alloc failed");1043 free(offsets);1044 close(fd);1045 return -ENOMEM;1046 }1047 info.uprobe_multi.ref_ctr_offsets = ptr_to_u64(ref_ctr_offsets);1048 cookies = calloc(count, sizeof(__u64));1049 if (!cookies) {1050 p_err("mem alloc failed");1051 free(ref_ctr_offsets);1052 free(offsets);1053 close(fd);1054 return -ENOMEM;1055 }1056 info.uprobe_multi.cookies = ptr_to_u64(cookies);1057 info.uprobe_multi.path = ptr_to_u64(path_buf);1058 info.uprobe_multi.path_size = sizeof(path_buf);1059 goto again;1060 }1061 }1062 if (info.type == BPF_LINK_TYPE_PERF_EVENT) {1063 switch (info.perf_event.type) {1064 case BPF_PERF_EVENT_TRACEPOINT:1065 if (!info.perf_event.tracepoint.tp_name) {1066 info.perf_event.tracepoint.tp_name = ptr_to_u64(&buf);1067 info.perf_event.tracepoint.name_len = sizeof(buf);1068 goto again;1069 }1070 break;1071 case BPF_PERF_EVENT_KPROBE:1072 case BPF_PERF_EVENT_KRETPROBE:1073 if (!info.perf_event.kprobe.func_name) {1074 info.perf_event.kprobe.func_name = ptr_to_u64(&buf);1075 info.perf_event.kprobe.name_len = sizeof(buf);1076 goto again;1077 }1078 break;1079 case BPF_PERF_EVENT_UPROBE:1080 case BPF_PERF_EVENT_URETPROBE:1081 if (!info.perf_event.uprobe.file_name) {1082 info.perf_event.uprobe.file_name = ptr_to_u64(&buf);1083 info.perf_event.uprobe.name_len = sizeof(buf);1084 goto again;1085 }1086 break;1087 default:1088 break;1089 }1090 }1091 1092 if (json_output)1093 show_link_close_json(fd, &info);1094 else1095 show_link_close_plain(fd, &info);1096 1097 free(ref_ctr_offsets);1098 free(cookies);1099 free(offsets);1100 free(addrs);1101 close(fd);1102 return 0;1103}1104 1105static int do_show(int argc, char **argv)1106{1107 __u32 id = 0;1108 int err, fd;1109 1110 if (show_pinned) {1111 link_table = hashmap__new(hash_fn_for_key_as_id,1112 equal_fn_for_key_as_id, NULL);1113 if (IS_ERR(link_table)) {1114 p_err("failed to create hashmap for pinned paths");1115 return -1;1116 }1117 build_pinned_obj_table(link_table, BPF_OBJ_LINK);1118 }1119 build_obj_refs_table(&refs_table, BPF_OBJ_LINK);1120 1121 if (argc == 2) {1122 fd = link_parse_fd(&argc, &argv);1123 if (fd < 0)1124 return fd;1125 do_show_link(fd);1126 goto out;1127 }1128 1129 if (argc)1130 return BAD_ARG();1131 1132 if (json_output)1133 jsonw_start_array(json_wtr);1134 while (true) {1135 err = bpf_link_get_next_id(id, &id);1136 if (err) {1137 if (errno == ENOENT)1138 break;1139 p_err("can't get next link: %s%s", strerror(errno),1140 errno == EINVAL ? " -- kernel too old?" : "");1141 break;1142 }1143 1144 fd = bpf_link_get_fd_by_id(id);1145 if (fd < 0) {1146 if (errno == ENOENT)1147 continue;1148 p_err("can't get link by id (%u): %s",1149 id, strerror(errno));1150 break;1151 }1152 1153 err = do_show_link(fd);1154 if (err)1155 break;1156 }1157 if (json_output)1158 jsonw_end_array(json_wtr);1159 1160 delete_obj_refs_table(refs_table);1161 1162 if (show_pinned)1163 delete_pinned_obj_table(link_table);1164 1165out:1166 if (dd.sym_count)1167 kernel_syms_destroy(&dd);1168 return errno == ENOENT ? 0 : -1;1169}1170 1171static int do_pin(int argc, char **argv)1172{1173 int err;1174 1175 err = do_pin_any(argc, argv, link_parse_fd);1176 if (!err && json_output)1177 jsonw_null(json_wtr);1178 return err;1179}1180 1181static int do_detach(int argc, char **argv)1182{1183 int err, fd;1184 1185 if (argc != 2) {1186 p_err("link specifier is invalid or missing\n");1187 return 1;1188 }1189 1190 fd = link_parse_fd(&argc, &argv);1191 if (fd < 0)1192 return 1;1193 1194 err = bpf_link_detach(fd);1195 if (err)1196 err = -errno;1197 close(fd);1198 if (err) {1199 p_err("failed link detach: %s", strerror(-err));1200 return 1;1201 }1202 1203 if (json_output)1204 jsonw_null(json_wtr);1205 1206 return 0;1207}1208 1209static int do_help(int argc, char **argv)1210{1211 if (json_output) {1212 jsonw_null(json_wtr);1213 return 0;1214 }1215 1216 fprintf(stderr,1217 "Usage: %1$s %2$s { show | list } [LINK]\n"1218 " %1$s %2$s pin LINK FILE\n"1219 " %1$s %2$s detach LINK\n"1220 " %1$s %2$s help\n"1221 "\n"1222 " " HELP_SPEC_LINK "\n"1223 " " HELP_SPEC_OPTIONS " |\n"1224 " {-f|--bpffs} | {-n|--nomount} }\n"1225 "",1226 bin_name, argv[-2]);1227 1228 return 0;1229}1230 1231static const struct cmd cmds[] = {1232 { "show", do_show },1233 { "list", do_show },1234 { "help", do_help },1235 { "pin", do_pin },1236 { "detach", do_detach },1237 { 0 }1238};1239 1240int do_link(int argc, char **argv)1241{1242 return cmd_select(cmds, argc, argv, do_help);1243}1244