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