Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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
 54static 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};
 72
 73static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
 74{
 75	struct timespec real_time_ts, boot_time_ts;
 76	time_t wallclock_secs;
 77	struct tm load_tm;
 78
 79	buf[--size] = '\0';
 80
 81	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
 82	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
 83		perror("Can't read clocks");
 84		snprintf(buf, size, "%llu", nsecs / 1000000000);
 85		return;
 86	}
 87
 88	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
 89		nsecs / 1000000000;
 90
 91	if (!localtime_r(&wallclock_secs, &load_tm)) {
 92		snprintf(buf, size, "%llu", nsecs / 1000000000);
 93		return;
 94	}
 95
 96	strftime(buf, size, "%b %d/%H:%M", &load_tm);
 97}
 98
 99static int prog_fd_by_tag(unsigned char *tag)
100{
101	struct bpf_prog_info info = {};
102	__u32 len = sizeof(info);
103	unsigned int id = 0;
104	int err;
105	int fd;
106
107	while (true) {
108		err = bpf_prog_get_next_id(id, &id);
109		if (err) {
110			p_err("%s", strerror(errno));
111			return -1;
112		}
113
114		fd = bpf_prog_get_fd_by_id(id);
115		if (fd < 0) {
116			p_err("can't get prog by id (%u): %s",
117			      id, strerror(errno));
118			return -1;
119		}
120
121		err = bpf_obj_get_info_by_fd(fd, &info, &len);
122		if (err) {
123			p_err("can't get prog info (%u): %s",
124			      id, strerror(errno));
125			close(fd);
126			return -1;
127		}
128
129		if (!memcmp(tag, info.tag, BPF_TAG_SIZE))
130			return fd;
131
132		close(fd);
133	}
134}
135
136int prog_parse_fd(int *argc, char ***argv)
137{
138	int fd;
139
140	if (is_prefix(**argv, "id")) {
141		unsigned int id;
142		char *endptr;
143
144		NEXT_ARGP();
145
146		id = strtoul(**argv, &endptr, 0);
147		if (*endptr) {
148			p_err("can't parse %s as ID", **argv);
149			return -1;
150		}
151		NEXT_ARGP();
152
153		fd = bpf_prog_get_fd_by_id(id);
154		if (fd < 0)
155			p_err("get by id (%u): %s", id, strerror(errno));
156		return fd;
157	} else if (is_prefix(**argv, "tag")) {
158		unsigned char tag[BPF_TAG_SIZE];
159
160		NEXT_ARGP();
161
162		if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
163			   tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
164		    != BPF_TAG_SIZE) {
165			p_err("can't parse tag");
166			return -1;
167		}
168		NEXT_ARGP();
169
170		return prog_fd_by_tag(tag);
171	} else if (is_prefix(**argv, "pinned")) {
172		char *path;
173
174		NEXT_ARGP();
175
176		path = **argv;
177		NEXT_ARGP();
178
179		return open_obj_pinned_any(path, BPF_OBJ_PROG);
180	}
181
182	p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv);
183	return -1;
184}
185
186static void show_prog_maps(int fd, u32 num_maps)
187{
188	struct bpf_prog_info info = {};
189	__u32 len = sizeof(info);
190	__u32 map_ids[num_maps];
191	unsigned int i;
192	int err;
193
194	info.nr_map_ids = num_maps;
195	info.map_ids = ptr_to_u64(map_ids);
196
197	err = bpf_obj_get_info_by_fd(fd, &info, &len);
198	if (err || !info.nr_map_ids)
199		return;
200
201	if (json_output) {
202		jsonw_name(json_wtr, "map_ids");
203		jsonw_start_array(json_wtr);
204		for (i = 0; i < info.nr_map_ids; i++)
205			jsonw_uint(json_wtr, map_ids[i]);
206		jsonw_end_array(json_wtr);
207	} else {
208		printf("  map_ids ");
209		for (i = 0; i < info.nr_map_ids; i++)
210			printf("%u%s", map_ids[i],
211			       i == info.nr_map_ids - 1 ? "" : ",");
212	}
213}
214
215static void print_prog_json(struct bpf_prog_info *info, int fd)
216{
217	char *memlock;
218
219	jsonw_start_object(json_wtr);
220	jsonw_uint_field(json_wtr, "id", info->id);
221	if (info->type < ARRAY_SIZE(prog_type_name))
222		jsonw_string_field(json_wtr, "type",
223				   prog_type_name[info->type]);
224	else
225		jsonw_uint_field(json_wtr, "type", info->type);
226
227	if (*info->name)
228		jsonw_string_field(json_wtr, "name", info->name);
229
230	jsonw_name(json_wtr, "tag");
231	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
232		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
233		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
234
235	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
236
237	if (info->load_time) {
238		char buf[32];
239
240		print_boot_time(info->load_time, buf, sizeof(buf));
241
242		/* Piggy back on load_time, since 0 uid is a valid one */
243		jsonw_string_field(json_wtr, "loaded_at", buf);
244		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
245	}
246
247	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
248
249	if (info->jited_prog_len) {
250		jsonw_bool_field(json_wtr, "jited", true);
251		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
252	} else {
253		jsonw_bool_field(json_wtr, "jited", false);
254	}
255
256	memlock = get_fdinfo(fd, "memlock");
257	if (memlock)
258		jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
259	free(memlock);
260
261	if (info->nr_map_ids)
262		show_prog_maps(fd, info->nr_map_ids);
263
264	if (!hash_empty(prog_table.table)) {
265		struct pinned_obj *obj;
266
267		jsonw_name(json_wtr, "pinned");
268		jsonw_start_array(json_wtr);
269		hash_for_each_possible(prog_table.table, obj, hash, info->id) {
270			if (obj->id == info->id)
271				jsonw_string(json_wtr, obj->path);
272		}
273		jsonw_end_array(json_wtr);
274	}
275
276	jsonw_end_object(json_wtr);
277}
278
279static void print_prog_plain(struct bpf_prog_info *info, int fd)
280{
281	char *memlock;
282
283	printf("%u: ", info->id);
284	if (info->type < ARRAY_SIZE(prog_type_name))
285		printf("%s  ", prog_type_name[info->type]);
286	else
287		printf("type %u  ", info->type);
288
289	if (*info->name)
290		printf("name %s  ", info->name);
291
292	printf("tag ");
293	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
294	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
295	printf("\n");
296
297	if (info->load_time) {
298		char buf[32];
299
300		print_boot_time(info->load_time, buf, sizeof(buf));
301
302		/* Piggy back on load_time, since 0 uid is a valid one */
303		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
304	}
305
306	printf("\txlated %uB", info->xlated_prog_len);
307
308	if (info->jited_prog_len)
309		printf("  jited %uB", info->jited_prog_len);
310	else
311		printf("  not jited");
312
313	memlock = get_fdinfo(fd, "memlock");
314	if (memlock)
315		printf("  memlock %sB", memlock);
316	free(memlock);
317
318	if (info->nr_map_ids)
319		show_prog_maps(fd, info->nr_map_ids);
320
321	if (!hash_empty(prog_table.table)) {
322		struct pinned_obj *obj;
323
324		printf("\n");
325		hash_for_each_possible(prog_table.table, obj, hash, info->id) {
326			if (obj->id == info->id)
327				printf("\tpinned %s\n", obj->path);
328		}
329	}
330
331	printf("\n");
332}
333
334static int show_prog(int fd)
335{
336	struct bpf_prog_info info = {};
337	__u32 len = sizeof(info);
338	int err;
339
340	err = bpf_obj_get_info_by_fd(fd, &info, &len);
341	if (err) {
342		p_err("can't get prog info: %s", strerror(errno));
343		return -1;
344	}
345
346	if (json_output)
347		print_prog_json(&info, fd);
348	else
349		print_prog_plain(&info, fd);
350
351	return 0;
352}
353
354static int do_show(int argc, char **argv)
355{
356	__u32 id = 0;
357	int err;
358	int fd;
359
360	if (show_pinned)
361		build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
362
363	if (argc == 2) {
364		fd = prog_parse_fd(&argc, &argv);
365		if (fd < 0)
366			return -1;
367
368		return show_prog(fd);
369	}
370
371	if (argc)
372		return BAD_ARG();
373
374	if (json_output)
375		jsonw_start_array(json_wtr);
376	while (true) {
377		err = bpf_prog_get_next_id(id, &id);
378		if (err) {
379			if (errno == ENOENT) {
380				err = 0;
381				break;
382			}
383			p_err("can't get next program: %s%s", strerror(errno),
384			      errno == EINVAL ? " -- kernel too old?" : "");
385			err = -1;
386			break;
387		}
388
389		fd = bpf_prog_get_fd_by_id(id);
390		if (fd < 0) {
391			if (errno == ENOENT)
392				continue;
393			p_err("can't get prog by id (%u): %s",
394			      id, strerror(errno));
395			err = -1;
396			break;
397		}
398
399		err = show_prog(fd);
400		close(fd);
401		if (err)
402			break;
403	}
404
405	if (json_output)
406		jsonw_end_array(json_wtr);
407
408	return err;
409}
410
411static int do_dump(int argc, char **argv)
412{
413	struct bpf_prog_info info = {};
414	struct dump_data dd = {};
415	__u32 len = sizeof(info);
416	unsigned int buf_size;
417	char *filepath = NULL;
418	bool opcodes = false;
419	bool visual = false;
420	unsigned char *buf;
421	__u32 *member_len;
422	__u64 *member_ptr;
423	ssize_t n;
424	int err;
425	int fd;
426
427	if (is_prefix(*argv, "jited")) {
428		member_len = &info.jited_prog_len;
429		member_ptr = &info.jited_prog_insns;
430	} else if (is_prefix(*argv, "xlated")) {
431		member_len = &info.xlated_prog_len;
432		member_ptr = &info.xlated_prog_insns;
433	} else {
434		p_err("expected 'xlated' or 'jited', got: %s", *argv);
435		return -1;
436	}
437	NEXT_ARG();
438
439	if (argc < 2)
440		usage();
441
442	fd = prog_parse_fd(&argc, &argv);
443	if (fd < 0)
444		return -1;
445
446	if (is_prefix(*argv, "file")) {
447		NEXT_ARG();
448		if (!argc) {
449			p_err("expected file path");
450			return -1;
451		}
452
453		filepath = *argv;
454		NEXT_ARG();
455	} else if (is_prefix(*argv, "opcodes")) {
456		opcodes = true;
457		NEXT_ARG();
458	} else if (is_prefix(*argv, "visual")) {
459		visual = true;
460		NEXT_ARG();
461	}
462
463	if (argc) {
464		usage();
465		return -1;
466	}
467
468	err = bpf_obj_get_info_by_fd(fd, &info, &len);
469	if (err) {
470		p_err("can't get prog info: %s", strerror(errno));
471		return -1;
472	}
473
474	if (!*member_len) {
475		p_info("no instructions returned");
476		close(fd);
477		return 0;
478	}
479
480	buf_size = *member_len;
481
482	buf = malloc(buf_size);
483	if (!buf) {
484		p_err("mem alloc failed");
485		close(fd);
486		return -1;
487	}
488
489	memset(&info, 0, sizeof(info));
490
491	*member_ptr = ptr_to_u64(buf);
492	*member_len = buf_size;
493
494	err = bpf_obj_get_info_by_fd(fd, &info, &len);
495	close(fd);
496	if (err) {
497		p_err("can't get prog info: %s", strerror(errno));
498		goto err_free;
499	}
500
501	if (*member_len > buf_size) {
502		p_err("too many instructions returned");
503		goto err_free;
504	}
505
506	if ((member_len == &info.jited_prog_len &&
507	     info.jited_prog_insns == 0) ||
508	    (member_len == &info.xlated_prog_len &&
509	     info.xlated_prog_insns == 0)) {
510		p_err("error retrieving insn dump: kernel.kptr_restrict set?");
511		goto err_free;
512	}
513
514	if (filepath) {
515		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
516		if (fd < 0) {
517			p_err("can't open file %s: %s", filepath,
518			      strerror(errno));
519			goto err_free;
520		}
521
522		n = write(fd, buf, *member_len);
523		close(fd);
524		if (n != *member_len) {
525			p_err("error writing output file: %s",
526			      n < 0 ? strerror(errno) : "short write");
527			goto err_free;
528		}
529
530		if (json_output)
531			jsonw_null(json_wtr);
532	} else if (member_len == &info.jited_prog_len) {
533		const char *name = NULL;
534
535		if (info.ifindex) {
536			name = ifindex_to_bfd_name_ns(info.ifindex,
537						      info.netns_dev,
538						      info.netns_ino);
539			if (!name)
540				goto err_free;
541		}
542
543		disasm_print_insn(buf, *member_len, opcodes, name);
544	} else if (visual) {
545		if (json_output)
546			jsonw_null(json_wtr);
547		else
548			dump_xlated_cfg(buf, *member_len);
549	} else {
550		kernel_syms_load(&dd);
551		if (json_output)
552			dump_xlated_json(&dd, buf, *member_len, opcodes);
553		else
554			dump_xlated_plain(&dd, buf, *member_len, opcodes);
555		kernel_syms_destroy(&dd);
556	}
557
558	free(buf);
559	return 0;
560
561err_free:
562	free(buf);
563	return -1;
564}
565
566static int do_pin(int argc, char **argv)
567{
568	int err;
569
570	err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
571	if (!err && json_output)
572		jsonw_null(json_wtr);
573	return err;
574}
575
576static int do_load(int argc, char **argv)
577{
578	struct bpf_object *obj;
579	int prog_fd;
580
581	if (argc != 2)
582		usage();
583
584	if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
585		p_err("failed to load program");
586		return -1;
587	}
588
589	if (do_pin_fd(prog_fd, argv[1])) {
590		p_err("failed to pin program");
591		return -1;
592	}
593
594	if (json_output)
595		jsonw_null(json_wtr);
596
597	return 0;
598}
599
600static int do_help(int argc, char **argv)
601{
602	if (json_output) {
603		jsonw_null(json_wtr);
604		return 0;
605	}
606
607	fprintf(stderr,
608		"Usage: %s %s { show | list } [PROG]\n"
609		"       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
610		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
611		"       %s %s pin   PROG FILE\n"
612		"       %s %s load  OBJ  FILE\n"
613		"       %s %s help\n"
614		"\n"
615		"       " HELP_SPEC_PROGRAM "\n"
616		"       " HELP_SPEC_OPTIONS "\n"
617		"",
618		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
619		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
620
621	return 0;
622}
623
624static const struct cmd cmds[] = {
625	{ "show",	do_show },
626	{ "list",	do_show },
627	{ "help",	do_help },
628	{ "dump",	do_dump },
629	{ "pin",	do_pin },
630	{ "load",	do_load },
631	{ 0 }
632};
633
634int do_prog(int argc, char **argv)
635{
636	return cmd_select(cmds, argc, argv, do_help);
637}