cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

link.c (9860B)


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