xref: /openbmc/linux/tools/bpf/bpftool/link.c (revision ccc319dc)
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 
16 static 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 
29 static struct hashmap *link_table;
30 
31 static 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 
67 static void
68 show_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 
79 static 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 
88 static 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 
94 static 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 
104 static 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 
121 static 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 
185 static 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 
196 static 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 
204 static 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 
214 static 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 
268 static 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));
276 again:
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 
306 static 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 
368 static 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 
378 static 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 
406 static 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 
428 static 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 
437 int do_link(int argc, char **argv)
438 {
439 	return cmd_select(cmds, argc, argv, do_help);
440 }
441