diff options
Diffstat (limited to 'tools/bpf/bpftool/prog.c')
| -rw-r--r-- | tools/bpf/bpftool/prog.c | 261 | 
1 files changed, 247 insertions, 14 deletions
| diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index a4f435203fef..dce960d22106 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2017 Netronome Systems, Inc. + * Copyright (C) 2017-2018 Netronome Systems, Inc.   *   * This software is dual licensed under the GNU General License Version 2,   * June 1991 as shown in the file COPYING in the top-level directory of this @@ -31,8 +31,7 @@   * SOFTWARE.   */ -/* Author: Jakub Kicinski <[email protected]> */ - +#define _GNU_SOURCE  #include <errno.h>  #include <fcntl.h>  #include <stdarg.h> @@ -41,9 +40,12 @@  #include <string.h>  #include <time.h>  #include <unistd.h> +#include <net/if.h>  #include <sys/types.h>  #include <sys/stat.h> +#include <linux/err.h> +  #include <bpf.h>  #include <libbpf.h> @@ -90,7 +92,9 @@ static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)  	}  	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + -		nsecs / 1000000000; +		(real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) / +		1000000000; +  	if (!localtime_r(&wallclock_secs, &load_tm)) {  		snprintf(buf, size, "%llu", nsecs / 1000000000); @@ -679,28 +683,248 @@ static int do_pin(int argc, char **argv)  	return err;  } +struct map_replace { +	int idx; +	int fd; +	char *name; +}; + +int map_replace_compar(const void *p1, const void *p2) +{ +	const struct map_replace *a = p1, *b = p2; + +	return a->idx - b->idx; +} +  static int do_load(int argc, char **argv)  { +	enum bpf_attach_type expected_attach_type; +	struct bpf_object_open_attr attr = { +		.prog_type	= BPF_PROG_TYPE_UNSPEC, +	}; +	struct map_replace *map_replace = NULL; +	unsigned int old_map_fds = 0; +	struct bpf_program *prog;  	struct bpf_object *obj; -	int prog_fd; - -	if (argc != 2) -		usage(); +	struct bpf_map *map; +	const char *pinfile; +	unsigned int i, j; +	__u32 ifindex = 0; +	int idx, err; -	if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { -		p_err("failed to load program"); +	if (!REQ_ARGS(2))  		return -1; +	attr.file = GET_ARG(); +	pinfile = GET_ARG(); + +	while (argc) { +		if (is_prefix(*argv, "type")) { +			char *type; + +			NEXT_ARG(); + +			if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) { +				p_err("program type already specified"); +				goto err_free_reuse_maps; +			} +			if (!REQ_ARGS(1)) +				goto err_free_reuse_maps; + +			/* Put a '/' at the end of type to appease libbpf */ +			type = malloc(strlen(*argv) + 2); +			if (!type) { +				p_err("mem alloc failed"); +				goto err_free_reuse_maps; +			} +			*type = 0; +			strcat(type, *argv); +			strcat(type, "/"); + +			err = libbpf_prog_type_by_name(type, &attr.prog_type, +						       &expected_attach_type); +			free(type); +			if (err < 0) { +				p_err("unknown program type '%s'", *argv); +				goto err_free_reuse_maps; +			} +			NEXT_ARG(); +		} else if (is_prefix(*argv, "map")) { +			char *endptr, *name; +			int fd; + +			NEXT_ARG(); + +			if (!REQ_ARGS(4)) +				goto err_free_reuse_maps; + +			if (is_prefix(*argv, "idx")) { +				NEXT_ARG(); + +				idx = strtoul(*argv, &endptr, 0); +				if (*endptr) { +					p_err("can't parse %s as IDX", *argv); +					goto err_free_reuse_maps; +				} +				name = NULL; +			} else if (is_prefix(*argv, "name")) { +				NEXT_ARG(); + +				name = *argv; +				idx = -1; +			} else { +				p_err("expected 'idx' or 'name', got: '%s'?", +				      *argv); +				goto err_free_reuse_maps; +			} +			NEXT_ARG(); + +			fd = map_parse_fd(&argc, &argv); +			if (fd < 0) +				goto err_free_reuse_maps; + +			map_replace = reallocarray(map_replace, old_map_fds + 1, +						   sizeof(*map_replace)); +			if (!map_replace) { +				p_err("mem alloc failed"); +				goto err_free_reuse_maps; +			} +			map_replace[old_map_fds].idx = idx; +			map_replace[old_map_fds].name = name; +			map_replace[old_map_fds].fd = fd; +			old_map_fds++; +		} else if (is_prefix(*argv, "dev")) { +			NEXT_ARG(); + +			if (ifindex) { +				p_err("offload device already specified"); +				goto err_free_reuse_maps; +			} +			if (!REQ_ARGS(1)) +				goto err_free_reuse_maps; + +			ifindex = if_nametoindex(*argv); +			if (!ifindex) { +				p_err("unrecognized netdevice '%s': %s", +				      *argv, strerror(errno)); +				goto err_free_reuse_maps; +			} +			NEXT_ARG(); +		} else { +			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", +			      *argv); +			goto err_free_reuse_maps; +		}  	} -	if (do_pin_fd(prog_fd, argv[1])) { -		p_err("failed to pin program"); -		return -1; +	obj = bpf_object__open_xattr(&attr); +	if (IS_ERR_OR_NULL(obj)) { +		p_err("failed to open object file"); +		goto err_free_reuse_maps; +	} + +	prog = bpf_program__next(NULL, obj); +	if (!prog) { +		p_err("object file doesn't contain any bpf program"); +		goto err_close_obj; +	} + +	bpf_program__set_ifindex(prog, ifindex); +	if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) { +		const char *sec_name = bpf_program__title(prog, false); + +		err = libbpf_prog_type_by_name(sec_name, &attr.prog_type, +					       &expected_attach_type); +		if (err < 0) { +			p_err("failed to guess program type based on section name %s\n", +			      sec_name); +			goto err_close_obj; +		} +	} +	bpf_program__set_type(prog, attr.prog_type); +	bpf_program__set_expected_attach_type(prog, expected_attach_type); + +	qsort(map_replace, old_map_fds, sizeof(*map_replace), +	      map_replace_compar); + +	/* After the sort maps by name will be first on the list, because they +	 * have idx == -1.  Resolve them. +	 */ +	j = 0; +	while (j < old_map_fds && map_replace[j].name) { +		i = 0; +		bpf_map__for_each(map, obj) { +			if (!strcmp(bpf_map__name(map), map_replace[j].name)) { +				map_replace[j].idx = i; +				break; +			} +			i++; +		} +		if (map_replace[j].idx == -1) { +			p_err("unable to find map '%s'", map_replace[j].name); +			goto err_close_obj; +		} +		j++; +	} +	/* Resort if any names were resolved */ +	if (j) +		qsort(map_replace, old_map_fds, sizeof(*map_replace), +		      map_replace_compar); + +	/* Set ifindex and name reuse */ +	j = 0; +	idx = 0; +	bpf_map__for_each(map, obj) { +		if (!bpf_map__is_offload_neutral(map)) +			bpf_map__set_ifindex(map, ifindex); + +		if (j < old_map_fds && idx == map_replace[j].idx) { +			err = bpf_map__reuse_fd(map, map_replace[j++].fd); +			if (err) { +				p_err("unable to set up map reuse: %d", err); +				goto err_close_obj; +			} + +			/* Next reuse wants to apply to the same map */ +			if (j < old_map_fds && map_replace[j].idx == idx) { +				p_err("replacement for map idx %d specified more than once", +				      idx); +				goto err_close_obj; +			} +		} + +		idx++; +	} +	if (j < old_map_fds) { +		p_err("map idx '%d' not used", map_replace[j].idx); +		goto err_close_obj;  	} +	err = bpf_object__load(obj); +	if (err) { +		p_err("failed to load object file"); +		goto err_close_obj; +	} + +	if (do_pin_fd(bpf_program__fd(prog), pinfile)) +		goto err_close_obj; +  	if (json_output)  		jsonw_null(json_wtr); +	bpf_object__close(obj); +	for (i = 0; i < old_map_fds; i++) +		close(map_replace[i].fd); +	free(map_replace); +  	return 0; + +err_close_obj: +	bpf_object__close(obj); +err_free_reuse_maps: +	for (i = 0; i < old_map_fds; i++) +		close(map_replace[i].fd); +	free(map_replace); +	return -1;  }  static int do_help(int argc, char **argv) @@ -715,10 +939,19 @@ static int do_help(int argc, char **argv)  		"       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"  		"       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"  		"       %s %s pin   PROG FILE\n" -		"       %s %s load  OBJ  FILE\n" +		"       %s %s load  OBJ  FILE [type TYPE] [dev NAME] \\\n" +		"                         [map { idx IDX | name NAME } MAP]\n"  		"       %s %s help\n"  		"\n" +		"       " HELP_SPEC_MAP "\n"  		"       " HELP_SPEC_PROGRAM "\n" +		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n" +		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" +		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n" +		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n" +		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" +		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" +		"                 cgroup/sendmsg4 | cgroup/sendmsg6 }\n"  		"       " HELP_SPEC_OPTIONS "\n"  		"",  		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], |