xref: /linux/tools/bpf/bpftool/prog.c (revision 393de512e719a5fbd6712fc392a571ab287eb8ab)
1 /*
2  * Copyright (C) 2017 Netronome Systems, Inc.
3  *
4  * This software is dual licensed under the GNU General License Version 2,
5  * June 1991 as shown in the file COPYING in the top-level directory of this
6  * source tree or the BSD 2-Clause License provided below.  You have the
7  * option to license this software under the complete terms of either license.
8  *
9  * The BSD 2-Clause License:
10  *
11  *     Redistribution and use in source and binary forms, with or
12  *     without modification, are permitted provided that the following
13  *     conditions are met:
14  *
15  *      1. Redistributions of source code must retain the above
16  *         copyright notice, this list of conditions and the following
17  *         disclaimer.
18  *
19  *      2. Redistributions in binary form must reproduce the above
20  *         copyright notice, this list of conditions and the following
21  *         disclaimer in the documentation and/or other materials
22  *         provided with the distribution.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31  * SOFTWARE.
32  */
33 
34 /* Author: Jakub Kicinski <kubakici@wp.pl> */
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 
47 #include <bpf.h>
48 #include <libbpf.h>
49 
50 #include "cfg.h"
51 #include "main.h"
52 #include "xlated_dumper.h"
53 
54 static const char * const prog_type_name[] = {
55 	[BPF_PROG_TYPE_UNSPEC]		= "unspec",
56 	[BPF_PROG_TYPE_SOCKET_FILTER]	= "socket_filter",
57 	[BPF_PROG_TYPE_KPROBE]		= "kprobe",
58 	[BPF_PROG_TYPE_SCHED_CLS]	= "sched_cls",
59 	[BPF_PROG_TYPE_SCHED_ACT]	= "sched_act",
60 	[BPF_PROG_TYPE_TRACEPOINT]	= "tracepoint",
61 	[BPF_PROG_TYPE_XDP]		= "xdp",
62 	[BPF_PROG_TYPE_PERF_EVENT]	= "perf_event",
63 	[BPF_PROG_TYPE_CGROUP_SKB]	= "cgroup_skb",
64 	[BPF_PROG_TYPE_CGROUP_SOCK]	= "cgroup_sock",
65 	[BPF_PROG_TYPE_LWT_IN]		= "lwt_in",
66 	[BPF_PROG_TYPE_LWT_OUT]		= "lwt_out",
67 	[BPF_PROG_TYPE_LWT_XMIT]	= "lwt_xmit",
68 	[BPF_PROG_TYPE_SOCK_OPS]	= "sock_ops",
69 	[BPF_PROG_TYPE_SK_SKB]		= "sk_skb",
70 	[BPF_PROG_TYPE_CGROUP_DEVICE]	= "cgroup_device",
71 	[BPF_PROG_TYPE_SK_MSG]		= "sk_msg",
72 	[BPF_PROG_TYPE_RAW_TRACEPOINT]	= "raw_tracepoint",
73 	[BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr",
74 };
75 
76 static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
77 {
78 	struct timespec real_time_ts, boot_time_ts;
79 	time_t wallclock_secs;
80 	struct tm load_tm;
81 
82 	buf[--size] = '\0';
83 
84 	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
85 	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
86 		perror("Can't read clocks");
87 		snprintf(buf, size, "%llu", nsecs / 1000000000);
88 		return;
89 	}
90 
91 	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
92 		nsecs / 1000000000;
93 
94 	if (!localtime_r(&wallclock_secs, &load_tm)) {
95 		snprintf(buf, size, "%llu", nsecs / 1000000000);
96 		return;
97 	}
98 
99 	strftime(buf, size, "%b %d/%H:%M", &load_tm);
100 }
101 
102 static int prog_fd_by_tag(unsigned char *tag)
103 {
104 	struct bpf_prog_info info = {};
105 	__u32 len = sizeof(info);
106 	unsigned int id = 0;
107 	int err;
108 	int fd;
109 
110 	while (true) {
111 		err = bpf_prog_get_next_id(id, &id);
112 		if (err) {
113 			p_err("%s", strerror(errno));
114 			return -1;
115 		}
116 
117 		fd = bpf_prog_get_fd_by_id(id);
118 		if (fd < 0) {
119 			p_err("can't get prog by id (%u): %s",
120 			      id, strerror(errno));
121 			return -1;
122 		}
123 
124 		err = bpf_obj_get_info_by_fd(fd, &info, &len);
125 		if (err) {
126 			p_err("can't get prog info (%u): %s",
127 			      id, strerror(errno));
128 			close(fd);
129 			return -1;
130 		}
131 
132 		if (!memcmp(tag, info.tag, BPF_TAG_SIZE))
133 			return fd;
134 
135 		close(fd);
136 	}
137 }
138 
139 int prog_parse_fd(int *argc, char ***argv)
140 {
141 	int fd;
142 
143 	if (is_prefix(**argv, "id")) {
144 		unsigned int id;
145 		char *endptr;
146 
147 		NEXT_ARGP();
148 
149 		id = strtoul(**argv, &endptr, 0);
150 		if (*endptr) {
151 			p_err("can't parse %s as ID", **argv);
152 			return -1;
153 		}
154 		NEXT_ARGP();
155 
156 		fd = bpf_prog_get_fd_by_id(id);
157 		if (fd < 0)
158 			p_err("get by id (%u): %s", id, strerror(errno));
159 		return fd;
160 	} else if (is_prefix(**argv, "tag")) {
161 		unsigned char tag[BPF_TAG_SIZE];
162 
163 		NEXT_ARGP();
164 
165 		if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
166 			   tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
167 		    != BPF_TAG_SIZE) {
168 			p_err("can't parse tag");
169 			return -1;
170 		}
171 		NEXT_ARGP();
172 
173 		return prog_fd_by_tag(tag);
174 	} else if (is_prefix(**argv, "pinned")) {
175 		char *path;
176 
177 		NEXT_ARGP();
178 
179 		path = **argv;
180 		NEXT_ARGP();
181 
182 		return open_obj_pinned_any(path, BPF_OBJ_PROG);
183 	}
184 
185 	p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv);
186 	return -1;
187 }
188 
189 static void show_prog_maps(int fd, u32 num_maps)
190 {
191 	struct bpf_prog_info info = {};
192 	__u32 len = sizeof(info);
193 	__u32 map_ids[num_maps];
194 	unsigned int i;
195 	int err;
196 
197 	info.nr_map_ids = num_maps;
198 	info.map_ids = ptr_to_u64(map_ids);
199 
200 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
201 	if (err || !info.nr_map_ids)
202 		return;
203 
204 	if (json_output) {
205 		jsonw_name(json_wtr, "map_ids");
206 		jsonw_start_array(json_wtr);
207 		for (i = 0; i < info.nr_map_ids; i++)
208 			jsonw_uint(json_wtr, map_ids[i]);
209 		jsonw_end_array(json_wtr);
210 	} else {
211 		printf("  map_ids ");
212 		for (i = 0; i < info.nr_map_ids; i++)
213 			printf("%u%s", map_ids[i],
214 			       i == info.nr_map_ids - 1 ? "" : ",");
215 	}
216 }
217 
218 static void print_prog_json(struct bpf_prog_info *info, int fd)
219 {
220 	char *memlock;
221 
222 	jsonw_start_object(json_wtr);
223 	jsonw_uint_field(json_wtr, "id", info->id);
224 	if (info->type < ARRAY_SIZE(prog_type_name))
225 		jsonw_string_field(json_wtr, "type",
226 				   prog_type_name[info->type]);
227 	else
228 		jsonw_uint_field(json_wtr, "type", info->type);
229 
230 	if (*info->name)
231 		jsonw_string_field(json_wtr, "name", info->name);
232 
233 	jsonw_name(json_wtr, "tag");
234 	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
235 		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
236 		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
237 
238 	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
239 
240 	if (info->load_time) {
241 		char buf[32];
242 
243 		print_boot_time(info->load_time, buf, sizeof(buf));
244 
245 		/* Piggy back on load_time, since 0 uid is a valid one */
246 		jsonw_string_field(json_wtr, "loaded_at", buf);
247 		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
248 	}
249 
250 	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
251 
252 	if (info->jited_prog_len) {
253 		jsonw_bool_field(json_wtr, "jited", true);
254 		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
255 	} else {
256 		jsonw_bool_field(json_wtr, "jited", false);
257 	}
258 
259 	memlock = get_fdinfo(fd, "memlock");
260 	if (memlock)
261 		jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
262 	free(memlock);
263 
264 	if (info->nr_map_ids)
265 		show_prog_maps(fd, info->nr_map_ids);
266 
267 	if (!hash_empty(prog_table.table)) {
268 		struct pinned_obj *obj;
269 
270 		jsonw_name(json_wtr, "pinned");
271 		jsonw_start_array(json_wtr);
272 		hash_for_each_possible(prog_table.table, obj, hash, info->id) {
273 			if (obj->id == info->id)
274 				jsonw_string(json_wtr, obj->path);
275 		}
276 		jsonw_end_array(json_wtr);
277 	}
278 
279 	jsonw_end_object(json_wtr);
280 }
281 
282 static void print_prog_plain(struct bpf_prog_info *info, int fd)
283 {
284 	char *memlock;
285 
286 	printf("%u: ", info->id);
287 	if (info->type < ARRAY_SIZE(prog_type_name))
288 		printf("%s  ", prog_type_name[info->type]);
289 	else
290 		printf("type %u  ", info->type);
291 
292 	if (*info->name)
293 		printf("name %s  ", info->name);
294 
295 	printf("tag ");
296 	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
297 	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
298 	printf("\n");
299 
300 	if (info->load_time) {
301 		char buf[32];
302 
303 		print_boot_time(info->load_time, buf, sizeof(buf));
304 
305 		/* Piggy back on load_time, since 0 uid is a valid one */
306 		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
307 	}
308 
309 	printf("\txlated %uB", info->xlated_prog_len);
310 
311 	if (info->jited_prog_len)
312 		printf("  jited %uB", info->jited_prog_len);
313 	else
314 		printf("  not jited");
315 
316 	memlock = get_fdinfo(fd, "memlock");
317 	if (memlock)
318 		printf("  memlock %sB", memlock);
319 	free(memlock);
320 
321 	if (info->nr_map_ids)
322 		show_prog_maps(fd, info->nr_map_ids);
323 
324 	if (!hash_empty(prog_table.table)) {
325 		struct pinned_obj *obj;
326 
327 		printf("\n");
328 		hash_for_each_possible(prog_table.table, obj, hash, info->id) {
329 			if (obj->id == info->id)
330 				printf("\tpinned %s\n", obj->path);
331 		}
332 	}
333 
334 	printf("\n");
335 }
336 
337 static int show_prog(int fd)
338 {
339 	struct bpf_prog_info info = {};
340 	__u32 len = sizeof(info);
341 	int err;
342 
343 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
344 	if (err) {
345 		p_err("can't get prog info: %s", strerror(errno));
346 		return -1;
347 	}
348 
349 	if (json_output)
350 		print_prog_json(&info, fd);
351 	else
352 		print_prog_plain(&info, fd);
353 
354 	return 0;
355 }
356 
357 static int do_show(int argc, char **argv)
358 {
359 	__u32 id = 0;
360 	int err;
361 	int fd;
362 
363 	if (show_pinned)
364 		build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
365 
366 	if (argc == 2) {
367 		fd = prog_parse_fd(&argc, &argv);
368 		if (fd < 0)
369 			return -1;
370 
371 		return show_prog(fd);
372 	}
373 
374 	if (argc)
375 		return BAD_ARG();
376 
377 	if (json_output)
378 		jsonw_start_array(json_wtr);
379 	while (true) {
380 		err = bpf_prog_get_next_id(id, &id);
381 		if (err) {
382 			if (errno == ENOENT) {
383 				err = 0;
384 				break;
385 			}
386 			p_err("can't get next program: %s%s", strerror(errno),
387 			      errno == EINVAL ? " -- kernel too old?" : "");
388 			err = -1;
389 			break;
390 		}
391 
392 		fd = bpf_prog_get_fd_by_id(id);
393 		if (fd < 0) {
394 			if (errno == ENOENT)
395 				continue;
396 			p_err("can't get prog by id (%u): %s",
397 			      id, strerror(errno));
398 			err = -1;
399 			break;
400 		}
401 
402 		err = show_prog(fd);
403 		close(fd);
404 		if (err)
405 			break;
406 	}
407 
408 	if (json_output)
409 		jsonw_end_array(json_wtr);
410 
411 	return err;
412 }
413 
414 static int do_dump(int argc, char **argv)
415 {
416 	struct bpf_prog_info info = {};
417 	struct dump_data dd = {};
418 	__u32 len = sizeof(info);
419 	unsigned int buf_size;
420 	char *filepath = NULL;
421 	bool opcodes = false;
422 	bool visual = false;
423 	unsigned char *buf;
424 	__u32 *member_len;
425 	__u64 *member_ptr;
426 	ssize_t n;
427 	int err;
428 	int fd;
429 
430 	if (is_prefix(*argv, "jited")) {
431 		member_len = &info.jited_prog_len;
432 		member_ptr = &info.jited_prog_insns;
433 	} else if (is_prefix(*argv, "xlated")) {
434 		member_len = &info.xlated_prog_len;
435 		member_ptr = &info.xlated_prog_insns;
436 	} else {
437 		p_err("expected 'xlated' or 'jited', got: %s", *argv);
438 		return -1;
439 	}
440 	NEXT_ARG();
441 
442 	if (argc < 2)
443 		usage();
444 
445 	fd = prog_parse_fd(&argc, &argv);
446 	if (fd < 0)
447 		return -1;
448 
449 	if (is_prefix(*argv, "file")) {
450 		NEXT_ARG();
451 		if (!argc) {
452 			p_err("expected file path");
453 			return -1;
454 		}
455 
456 		filepath = *argv;
457 		NEXT_ARG();
458 	} else if (is_prefix(*argv, "opcodes")) {
459 		opcodes = true;
460 		NEXT_ARG();
461 	} else if (is_prefix(*argv, "visual")) {
462 		visual = true;
463 		NEXT_ARG();
464 	}
465 
466 	if (argc) {
467 		usage();
468 		return -1;
469 	}
470 
471 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
472 	if (err) {
473 		p_err("can't get prog info: %s", strerror(errno));
474 		return -1;
475 	}
476 
477 	if (!*member_len) {
478 		p_info("no instructions returned");
479 		close(fd);
480 		return 0;
481 	}
482 
483 	buf_size = *member_len;
484 
485 	buf = malloc(buf_size);
486 	if (!buf) {
487 		p_err("mem alloc failed");
488 		close(fd);
489 		return -1;
490 	}
491 
492 	memset(&info, 0, sizeof(info));
493 
494 	*member_ptr = ptr_to_u64(buf);
495 	*member_len = buf_size;
496 
497 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
498 	close(fd);
499 	if (err) {
500 		p_err("can't get prog info: %s", strerror(errno));
501 		goto err_free;
502 	}
503 
504 	if (*member_len > buf_size) {
505 		p_err("too many instructions returned");
506 		goto err_free;
507 	}
508 
509 	if ((member_len == &info.jited_prog_len &&
510 	     info.jited_prog_insns == 0) ||
511 	    (member_len == &info.xlated_prog_len &&
512 	     info.xlated_prog_insns == 0)) {
513 		p_err("error retrieving insn dump: kernel.kptr_restrict set?");
514 		goto err_free;
515 	}
516 
517 	if (filepath) {
518 		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
519 		if (fd < 0) {
520 			p_err("can't open file %s: %s", filepath,
521 			      strerror(errno));
522 			goto err_free;
523 		}
524 
525 		n = write(fd, buf, *member_len);
526 		close(fd);
527 		if (n != *member_len) {
528 			p_err("error writing output file: %s",
529 			      n < 0 ? strerror(errno) : "short write");
530 			goto err_free;
531 		}
532 
533 		if (json_output)
534 			jsonw_null(json_wtr);
535 	} else if (member_len == &info.jited_prog_len) {
536 		const char *name = NULL;
537 
538 		if (info.ifindex) {
539 			name = ifindex_to_bfd_name_ns(info.ifindex,
540 						      info.netns_dev,
541 						      info.netns_ino);
542 			if (!name)
543 				goto err_free;
544 		}
545 
546 		disasm_print_insn(buf, *member_len, opcodes, name);
547 	} else if (visual) {
548 		if (json_output)
549 			jsonw_null(json_wtr);
550 		else
551 			dump_xlated_cfg(buf, *member_len);
552 	} else {
553 		kernel_syms_load(&dd);
554 		if (json_output)
555 			dump_xlated_json(&dd, buf, *member_len, opcodes);
556 		else
557 			dump_xlated_plain(&dd, buf, *member_len, opcodes);
558 		kernel_syms_destroy(&dd);
559 	}
560 
561 	free(buf);
562 	return 0;
563 
564 err_free:
565 	free(buf);
566 	return -1;
567 }
568 
569 static int do_pin(int argc, char **argv)
570 {
571 	int err;
572 
573 	err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
574 	if (!err && json_output)
575 		jsonw_null(json_wtr);
576 	return err;
577 }
578 
579 static int do_load(int argc, char **argv)
580 {
581 	struct bpf_object *obj;
582 	int prog_fd;
583 
584 	if (argc != 2)
585 		usage();
586 
587 	if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
588 		p_err("failed to load program");
589 		return -1;
590 	}
591 
592 	if (do_pin_fd(prog_fd, argv[1])) {
593 		p_err("failed to pin program");
594 		return -1;
595 	}
596 
597 	if (json_output)
598 		jsonw_null(json_wtr);
599 
600 	return 0;
601 }
602 
603 static int do_help(int argc, char **argv)
604 {
605 	if (json_output) {
606 		jsonw_null(json_wtr);
607 		return 0;
608 	}
609 
610 	fprintf(stderr,
611 		"Usage: %s %s { show | list } [PROG]\n"
612 		"       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
613 		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
614 		"       %s %s pin   PROG FILE\n"
615 		"       %s %s load  OBJ  FILE\n"
616 		"       %s %s help\n"
617 		"\n"
618 		"       " HELP_SPEC_PROGRAM "\n"
619 		"       " HELP_SPEC_OPTIONS "\n"
620 		"",
621 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
622 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
623 
624 	return 0;
625 }
626 
627 static const struct cmd cmds[] = {
628 	{ "show",	do_show },
629 	{ "list",	do_show },
630 	{ "help",	do_help },
631 	{ "dump",	do_dump },
632 	{ "pin",	do_pin },
633 	{ "load",	do_load },
634 	{ 0 }
635 };
636 
637 int do_prog(int argc, char **argv)
638 {
639 	return cmd_select(cmds, argc, argv, do_help);
640 }
641