diff options
Diffstat (limited to 'tools/lib/bpf/libbpf.c')
| -rw-r--r-- | tools/lib/bpf/libbpf.c | 446 | 
1 files changed, 348 insertions, 98 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 514b1a524abb..8f480e29a6b0 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -24,6 +24,7 @@  #include <endian.h>  #include <fcntl.h>  #include <errno.h> +#include <ctype.h>  #include <asm/unistd.h>  #include <linux/err.h>  #include <linux/kernel.h> @@ -177,6 +178,8 @@ struct bpf_capabilities {  	__u32 array_mmap:1;  	/* BTF_FUNC_GLOBAL is supported */  	__u32 btf_func_global:1; +	/* kernel support for expected_attach_type in BPF_PROG_LOAD */ +	__u32 exp_attach_type:1;  };  enum reloc_type { @@ -193,6 +196,22 @@ struct reloc_desc {  	int sym_off;  }; +struct bpf_sec_def; + +typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec, +					struct bpf_program *prog); + +struct bpf_sec_def { +	const char *sec; +	size_t len; +	enum bpf_prog_type prog_type; +	enum bpf_attach_type expected_attach_type; +	bool is_exp_attach_type_optional; +	bool is_attachable; +	bool is_attach_btf; +	attach_fn_t attach_fn; +}; +  /*   * bpf_prog should be a better name but it has been used in   * linux/filter.h. @@ -203,6 +222,7 @@ struct bpf_program {  	char *name;  	int prog_ifindex;  	char *section_name; +	const struct bpf_sec_def *sec_def;  	/* section_name with / replaced by _; makes recursive pinning  	 * in bpf_object__pin_programs easier  	 */ @@ -1283,7 +1303,7 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map)  static char *internal_map_name(struct bpf_object *obj,  			       enum libbpf_map_type type)  { -	char map_name[BPF_OBJ_NAME_LEN]; +	char map_name[BPF_OBJ_NAME_LEN], *p;  	const char *sfx = libbpf_type_to_btf_name[type];  	int sfx_len = max((size_t)7, strlen(sfx));  	int pfx_len = min((size_t)BPF_OBJ_NAME_LEN - sfx_len - 1, @@ -1292,6 +1312,11 @@ static char *internal_map_name(struct bpf_object *obj,  	snprintf(map_name, sizeof(map_name), "%.*s%.*s", pfx_len, obj->name,  		 sfx_len, libbpf_type_to_btf_name[type]); +	/* sanitise map name to characters allowed by kernel */ +	for (p = map_name; *p && p < map_name + sizeof(map_name); p++) +		if (!isalnum(*p) && *p != '_' && *p != '.') +			*p = '_'; +  	return strdup(map_name);  } @@ -1839,7 +1864,6 @@ resolve_func_ptr(const struct btf *btf, __u32 id, __u32 *res_id)   * type definition, while using only sizeof(void *) space in ELF data section.   */  static bool get_map_field_int(const char *map_name, const struct btf *btf, -			      const struct btf_type *def,  			      const struct btf_member *m, __u32 *res)  {  	const struct btf_type *t = skip_mods_and_typedefs(btf, m->type, NULL); @@ -1966,19 +1990,19 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,  			return -EINVAL;  		}  		if (strcmp(name, "type") == 0) { -			if (!get_map_field_int(map_name, obj->btf, def, m, +			if (!get_map_field_int(map_name, obj->btf, m,  					       &map->def.type))  				return -EINVAL;  			pr_debug("map '%s': found type = %u.\n",  				 map_name, map->def.type);  		} else if (strcmp(name, "max_entries") == 0) { -			if (!get_map_field_int(map_name, obj->btf, def, m, +			if (!get_map_field_int(map_name, obj->btf, m,  					       &map->def.max_entries))  				return -EINVAL;  			pr_debug("map '%s': found max_entries = %u.\n",  				 map_name, map->def.max_entries);  		} else if (strcmp(name, "map_flags") == 0) { -			if (!get_map_field_int(map_name, obj->btf, def, m, +			if (!get_map_field_int(map_name, obj->btf, m,  					       &map->def.map_flags))  				return -EINVAL;  			pr_debug("map '%s': found map_flags = %u.\n", @@ -1986,8 +2010,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,  		} else if (strcmp(name, "key_size") == 0) {  			__u32 sz; -			if (!get_map_field_int(map_name, obj->btf, def, m, -					       &sz)) +			if (!get_map_field_int(map_name, obj->btf, m, &sz))  				return -EINVAL;  			pr_debug("map '%s': found key_size = %u.\n",  				 map_name, sz); @@ -2029,8 +2052,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,  		} else if (strcmp(name, "value_size") == 0) {  			__u32 sz; -			if (!get_map_field_int(map_name, obj->btf, def, m, -					       &sz)) +			if (!get_map_field_int(map_name, obj->btf, m, &sz))  				return -EINVAL;  			pr_debug("map '%s': found value_size = %u.\n",  				 map_name, sz); @@ -2073,8 +2095,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,  			__u32 val;  			int err; -			if (!get_map_field_int(map_name, obj->btf, def, m, -					       &val)) +			if (!get_map_field_int(map_name, obj->btf, m, &val))  				return -EINVAL;  			pr_debug("map '%s': found pinning = %u.\n",  				 map_name, val); @@ -2278,11 +2299,16 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)  	}  } -static bool bpf_object__is_btf_mandatory(const struct bpf_object *obj) +static bool libbpf_needs_btf(const struct bpf_object *obj)  {  	return obj->efile.btf_maps_shndx >= 0 || -		obj->efile.st_ops_shndx >= 0 || -		obj->nr_extern > 0; +	       obj->efile.st_ops_shndx >= 0 || +	       obj->nr_extern > 0; +} + +static bool kernel_needs_btf(const struct bpf_object *obj) +{ +	return obj->efile.st_ops_shndx >= 0;  }  static int bpf_object__init_btf(struct bpf_object *obj, @@ -2318,7 +2344,7 @@ static int bpf_object__init_btf(struct bpf_object *obj,  		}  	}  out: -	if (err && bpf_object__is_btf_mandatory(obj)) { +	if (err && libbpf_needs_btf(obj)) {  		pr_warn("BTF is required, but is missing or corrupted.\n");  		return err;  	} @@ -2342,7 +2368,7 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)  	btf_ext__free(obj->btf_ext);  	obj->btf_ext = NULL; -	if (bpf_object__is_btf_mandatory(obj)) { +	if (libbpf_needs_btf(obj)) {  		pr_warn("BTF is required, but is missing or corrupted.\n");  		return -ENOENT;  	} @@ -2351,7 +2377,8 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)  static inline bool libbpf_prog_needs_vmlinux_btf(struct bpf_program *prog)  { -	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) +	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS || +	    prog->type == BPF_PROG_TYPE_LSM)  		return true;  	/* BPF_PROG_TYPE_TRACING programs which do not attach to other programs @@ -2406,7 +2433,7 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)  			obj->btf_ext = NULL;  		} -		if (bpf_object__is_btf_mandatory(obj)) +		if (kernel_needs_btf(obj))  			return err;  	}  	return 0; @@ -3308,6 +3335,37 @@ static int bpf_object__probe_array_mmap(struct bpf_object *obj)  }  static int +bpf_object__probe_exp_attach_type(struct bpf_object *obj) +{ +	struct bpf_load_program_attr attr; +	struct bpf_insn insns[] = { +		BPF_MOV64_IMM(BPF_REG_0, 0), +		BPF_EXIT_INSN(), +	}; +	int fd; + +	memset(&attr, 0, sizeof(attr)); +	/* use any valid combination of program type and (optional) +	 * non-zero expected attach type (i.e., not a BPF_CGROUP_INET_INGRESS) +	 * to see if kernel supports expected_attach_type field for +	 * BPF_PROG_LOAD command +	 */ +	attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK; +	attr.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE; +	attr.insns = insns; +	attr.insns_cnt = ARRAY_SIZE(insns); +	attr.license = "GPL"; + +	fd = bpf_load_program_xattr(&attr, NULL, 0); +	if (fd >= 0) { +		obj->caps.exp_attach_type = 1; +		close(fd); +		return 1; +	} +	return 0; +} + +static int  bpf_object__probe_caps(struct bpf_object *obj)  {  	int (*probe_fn[])(struct bpf_object *obj) = { @@ -3317,6 +3375,7 @@ bpf_object__probe_caps(struct bpf_object *obj)  		bpf_object__probe_btf_func_global,  		bpf_object__probe_btf_datasec,  		bpf_object__probe_array_mmap, +		bpf_object__probe_exp_attach_type,  	};  	int i, ret; @@ -3862,6 +3921,10 @@ static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf,  		if (str_is_empty(targ_name))  			continue; +		t = skip_mods_and_typedefs(targ_btf, i, NULL); +		if (!btf_is_composite(t) && !btf_is_array(t)) +			continue; +  		targ_essent_len = bpf_core_essential_name_len(targ_name);  		if (targ_essent_len != local_essent_len)  			continue; @@ -4840,8 +4903,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,  {  	struct bpf_load_program_attr load_attr;  	char *cp, errmsg[STRERR_BUFSIZE]; -	int log_buf_size = BPF_LOG_BUF_SIZE; -	char *log_buf; +	size_t log_buf_size = 0; +	char *log_buf = NULL;  	int btf_fd, ret;  	if (!insns || !insns_cnt) @@ -4849,13 +4912,19 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,  	memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));  	load_attr.prog_type = prog->type; -	load_attr.expected_attach_type = prog->expected_attach_type; +	/* old kernels might not support specifying expected_attach_type */ +	if (!prog->caps->exp_attach_type && prog->sec_def && +	    prog->sec_def->is_exp_attach_type_optional) +		load_attr.expected_attach_type = 0; +	else +		load_attr.expected_attach_type = prog->expected_attach_type;  	if (prog->caps->name)  		load_attr.name = prog->name;  	load_attr.insns = insns;  	load_attr.insns_cnt = insns_cnt;  	load_attr.license = license; -	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) { +	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS || +	    prog->type == BPF_PROG_TYPE_LSM) {  		load_attr.attach_btf_id = prog->attach_btf_id;  	} else if (prog->type == BPF_PROG_TYPE_TRACING ||  		   prog->type == BPF_PROG_TYPE_EXT) { @@ -4881,22 +4950,28 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,  	load_attr.prog_flags = prog->prog_flags;  retry_load: -	log_buf = malloc(log_buf_size); -	if (!log_buf) -		pr_warn("Alloc log buffer for bpf loader error, continue without log\n"); +	if (log_buf_size) { +		log_buf = malloc(log_buf_size); +		if (!log_buf) +			return -ENOMEM; + +		*log_buf = 0; +	}  	ret = bpf_load_program_xattr(&load_attr, log_buf, log_buf_size);  	if (ret >= 0) { -		if (load_attr.log_level) +		if (log_buf && load_attr.log_level)  			pr_debug("verifier log:\n%s", log_buf);  		*pfd = ret;  		ret = 0;  		goto out;  	} -	if (errno == ENOSPC) { -		log_buf_size <<= 1; +	if (!log_buf || errno == ENOSPC) { +		log_buf_size = max((size_t)BPF_LOG_BUF_SIZE, +				   log_buf_size << 1); +  		free(log_buf);  		goto retry_load;  	} @@ -4939,8 +5014,9 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)  {  	int err = 0, fd, i, btf_id; -	if (prog->type == BPF_PROG_TYPE_TRACING || -	    prog->type == BPF_PROG_TYPE_EXT) { +	if ((prog->type == BPF_PROG_TYPE_TRACING || +	     prog->type == BPF_PROG_TYPE_LSM || +	     prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {  		btf_id = libbpf_find_attach_btf_id(prog);  		if (btf_id <= 0)  			return btf_id; @@ -5042,6 +5118,8 @@ bpf_object__load_progs(struct bpf_object *obj, int log_level)  	return 0;  } +static const struct bpf_sec_def *find_sec_def(const char *sec_name); +  static struct bpf_object *  __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,  		   const struct bpf_object_open_opts *opts) @@ -5097,24 +5175,17 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,  	bpf_object__elf_finish(obj);  	bpf_object__for_each_program(prog, obj) { -		enum bpf_prog_type prog_type; -		enum bpf_attach_type attach_type; - -		if (prog->type != BPF_PROG_TYPE_UNSPEC) -			continue; - -		err = libbpf_prog_type_by_name(prog->section_name, &prog_type, -					       &attach_type); -		if (err == -ESRCH) +		prog->sec_def = find_sec_def(prog->section_name); +		if (!prog->sec_def)  			/* couldn't guess, but user might manually specify */  			continue; -		if (err) -			goto out; -		bpf_program__set_type(prog, prog_type); -		bpf_program__set_expected_attach_type(prog, attach_type); -		if (prog_type == BPF_PROG_TYPE_TRACING || -		    prog_type == BPF_PROG_TYPE_EXT) +		bpf_program__set_type(prog, prog->sec_def->prog_type); +		bpf_program__set_expected_attach_type(prog, +				prog->sec_def->expected_attach_type); + +		if (prog->sec_def->prog_type == BPF_PROG_TYPE_TRACING || +		    prog->sec_def->prog_type == BPF_PROG_TYPE_EXT)  			prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);  	} @@ -6179,6 +6250,7 @@ bool bpf_program__is_##NAME(const struct bpf_program *prog)	\  }								\  BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER); +BPF_PROG_TYPE_FNS(lsm, BPF_PROG_TYPE_LSM);  BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);  BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);  BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT); @@ -6202,23 +6274,32 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog,  	prog->expected_attach_type = type;  } -#define BPF_PROG_SEC_IMPL(string, ptype, eatype, is_attachable, btf, atype) \ -	{ string, sizeof(string) - 1, ptype, eatype, is_attachable, btf, atype } +#define BPF_PROG_SEC_IMPL(string, ptype, eatype, eatype_optional,	    \ +			  attachable, attach_btf)			    \ +	{								    \ +		.sec = string,						    \ +		.len = sizeof(string) - 1,				    \ +		.prog_type = ptype,					    \ +		.expected_attach_type = eatype,				    \ +		.is_exp_attach_type_optional = eatype_optional,		    \ +		.is_attachable = attachable,				    \ +		.is_attach_btf = attach_btf,				    \ +	}  /* Programs that can NOT be attached. */  #define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_IMPL(string, ptype, 0, 0, 0, 0)  /* Programs that can be attached. */  #define BPF_APROG_SEC(string, ptype, atype) \ -	BPF_PROG_SEC_IMPL(string, ptype, 0, 1, 0, atype) +	BPF_PROG_SEC_IMPL(string, ptype, atype, true, 1, 0)  /* Programs that must specify expected attach type at load time. */  #define BPF_EAPROG_SEC(string, ptype, eatype) \ -	BPF_PROG_SEC_IMPL(string, ptype, eatype, 1, 0, eatype) +	BPF_PROG_SEC_IMPL(string, ptype, eatype, false, 1, 0)  /* Programs that use BTF to identify attach point */  #define BPF_PROG_BTF(string, ptype, eatype) \ -	BPF_PROG_SEC_IMPL(string, ptype, eatype, 0, 1, 0) +	BPF_PROG_SEC_IMPL(string, ptype, eatype, false, 0, 1)  /* Programs that can be attached but attach type can't be identified by section   * name. Kept for backward compatibility. @@ -6232,11 +6313,6 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog,  	__VA_ARGS__							    \  } -struct bpf_sec_def; - -typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec, -					struct bpf_program *prog); -  static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec,  				      struct bpf_program *prog);  static struct bpf_link *attach_tp(const struct bpf_sec_def *sec, @@ -6245,17 +6321,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,  				      struct bpf_program *prog);  static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,  				     struct bpf_program *prog); - -struct bpf_sec_def { -	const char *sec; -	size_t len; -	enum bpf_prog_type prog_type; -	enum bpf_attach_type expected_attach_type; -	bool is_attachable; -	bool is_attach_btf; -	enum bpf_attach_type attach_type; -	attach_fn_t attach_fn; -}; +static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec, +				   struct bpf_program *prog);  static const struct bpf_sec_def section_defs[] = {  	BPF_PROG_SEC("socket",			BPF_PROG_TYPE_SOCKET_FILTER), @@ -6284,6 +6351,10 @@ static const struct bpf_sec_def section_defs[] = {  		.expected_attach_type = BPF_TRACE_FENTRY,  		.is_attach_btf = true,  		.attach_fn = attach_trace), +	SEC_DEF("fmod_ret/", TRACING, +		.expected_attach_type = BPF_MODIFY_RETURN, +		.is_attach_btf = true, +		.attach_fn = attach_trace),  	SEC_DEF("fexit/", TRACING,  		.expected_attach_type = BPF_TRACE_FEXIT,  		.is_attach_btf = true, @@ -6291,6 +6362,10 @@ static const struct bpf_sec_def section_defs[] = {  	SEC_DEF("freplace/", EXT,  		.is_attach_btf = true,  		.attach_fn = attach_trace), +	SEC_DEF("lsm/", LSM, +		.is_attach_btf = true, +		.expected_attach_type = BPF_LSM_MAC, +		.attach_fn = attach_lsm),  	BPF_PROG_SEC("xdp",			BPF_PROG_TYPE_XDP),  	BPF_PROG_SEC("perf_event",		BPF_PROG_TYPE_PERF_EVENT),  	BPF_PROG_SEC("lwt_in",			BPF_PROG_TYPE_LWT_IN), @@ -6553,6 +6628,7 @@ invalid_prog:  }  #define BTF_TRACE_PREFIX "btf_trace_" +#define BTF_LSM_PREFIX "bpf_lsm_"  #define BTF_MAX_NAME_SIZE 128  static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix, @@ -6580,9 +6656,15 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name,  	if (attach_type == BPF_TRACE_RAW_TP)  		err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name,  					      BTF_KIND_TYPEDEF); +	else if (attach_type == BPF_LSM_MAC) +		err = find_btf_by_prefix_kind(btf, BTF_LSM_PREFIX, name, +					      BTF_KIND_FUNC);  	else  		err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); +	if (err <= 0) +		pr_warn("%s is not found in vmlinux BTF\n", name); +  	return err;  } @@ -6655,8 +6737,6 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog)  			err = __find_vmlinux_btf_id(prog->obj->btf_vmlinux,  						    name + section_defs[i].len,  						    attach_type); -		if (err <= 0) -			pr_warn("%s is not found in vmlinux BTF\n", name);  		return err;  	}  	pr_warn("failed to identify btf_id based on ELF section name '%s'\n", name); @@ -6677,7 +6757,7 @@ int libbpf_attach_type_by_name(const char *name,  			continue;  		if (!section_defs[i].is_attachable)  			return -EINVAL; -		*attach_type = section_defs[i].attach_type; +		*attach_type = section_defs[i].expected_attach_type;  		return 0;  	}  	pr_debug("failed to guess attach type based on ELF section name '%s'\n", name); @@ -6736,6 +6816,17 @@ void *bpf_map__priv(const struct bpf_map *map)  	return map ? map->priv : ERR_PTR(-EINVAL);  } +int bpf_map__set_initial_value(struct bpf_map *map, +			       const void *data, size_t size) +{ +	if (!map->mmaped || map->libbpf_type == LIBBPF_MAP_KCONFIG || +	    size != map->def.value_size || map->fd >= 0) +		return -EINVAL; + +	memcpy(map->mmaped, data, size); +	return 0; +} +  bool bpf_map__is_offload_neutral(const struct bpf_map *map)  {  	return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY; @@ -6926,9 +7017,17 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,  struct bpf_link {  	int (*detach)(struct bpf_link *link);  	int (*destroy)(struct bpf_link *link); +	char *pin_path;		/* NULL, if not pinned */ +	int fd;			/* hook FD, -1 if not applicable */  	bool disconnected;  }; +/* Replace link's underlying BPF program with the new one */ +int bpf_link__update_program(struct bpf_link *link, struct bpf_program *prog) +{ +	return bpf_link_update(bpf_link__fd(link), bpf_program__fd(prog), NULL); +} +  /* Release "ownership" of underlying BPF resource (typically, BPF program   * attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected   * link, when destructed through bpf_link__destroy() call won't attempt to @@ -6955,26 +7054,109 @@ int bpf_link__destroy(struct bpf_link *link)  		err = link->detach(link);  	if (link->destroy)  		link->destroy(link); +	if (link->pin_path) +		free(link->pin_path);  	free(link);  	return err;  } -struct bpf_link_fd { -	struct bpf_link link; /* has to be at the top of struct */ -	int fd; /* hook FD */ -}; +int bpf_link__fd(const struct bpf_link *link) +{ +	return link->fd; +} + +const char *bpf_link__pin_path(const struct bpf_link *link) +{ +	return link->pin_path; +} + +static int bpf_link__detach_fd(struct bpf_link *link) +{ +	return close(link->fd); +} + +struct bpf_link *bpf_link__open(const char *path) +{ +	struct bpf_link *link; +	int fd; + +	fd = bpf_obj_get(path); +	if (fd < 0) { +		fd = -errno; +		pr_warn("failed to open link at %s: %d\n", path, fd); +		return ERR_PTR(fd); +	} + +	link = calloc(1, sizeof(*link)); +	if (!link) { +		close(fd); +		return ERR_PTR(-ENOMEM); +	} +	link->detach = &bpf_link__detach_fd; +	link->fd = fd; + +	link->pin_path = strdup(path); +	if (!link->pin_path) { +		bpf_link__destroy(link); +		return ERR_PTR(-ENOMEM); +	} + +	return link; +} + +int bpf_link__pin(struct bpf_link *link, const char *path) +{ +	int err; + +	if (link->pin_path) +		return -EBUSY; +	err = make_parent_dir(path); +	if (err) +		return err; +	err = check_path(path); +	if (err) +		return err; + +	link->pin_path = strdup(path); +	if (!link->pin_path) +		return -ENOMEM; + +	if (bpf_obj_pin(link->fd, link->pin_path)) { +		err = -errno; +		zfree(&link->pin_path); +		return err; +	} + +	pr_debug("link fd=%d: pinned at %s\n", link->fd, link->pin_path); +	return 0; +} + +int bpf_link__unpin(struct bpf_link *link) +{ +	int err; + +	if (!link->pin_path) +		return -EINVAL; + +	err = unlink(link->pin_path); +	if (err != 0) +		return -errno; + +	pr_debug("link fd=%d: unpinned from %s\n", link->fd, link->pin_path); +	zfree(&link->pin_path); +	return 0; +}  static int bpf_link__detach_perf_event(struct bpf_link *link)  { -	struct bpf_link_fd *l = (void *)link;  	int err; -	err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0); +	err = ioctl(link->fd, PERF_EVENT_IOC_DISABLE, 0);  	if (err)  		err = -errno; -	close(l->fd); +	close(link->fd);  	return err;  } @@ -6982,7 +7164,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,  						int pfd)  {  	char errmsg[STRERR_BUFSIZE]; -	struct bpf_link_fd *link; +	struct bpf_link *link;  	int prog_fd, err;  	if (pfd < 0) { @@ -7000,7 +7182,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,  	link = calloc(1, sizeof(*link));  	if (!link)  		return ERR_PTR(-ENOMEM); -	link->link.detach = &bpf_link__detach_perf_event; +	link->detach = &bpf_link__detach_perf_event;  	link->fd = pfd;  	if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { @@ -7019,7 +7201,7 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,  			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));  		return ERR_PTR(err);  	} -	return (struct bpf_link *)link; +	return link;  }  /* @@ -7307,18 +7489,11 @@ out:  	return link;  } -static int bpf_link__detach_fd(struct bpf_link *link) -{ -	struct bpf_link_fd *l = (void *)link; - -	return close(l->fd); -} -  struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,  						    const char *tp_name)  {  	char errmsg[STRERR_BUFSIZE]; -	struct bpf_link_fd *link; +	struct bpf_link *link;  	int prog_fd, pfd;  	prog_fd = bpf_program__fd(prog); @@ -7331,7 +7506,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,  	link = calloc(1, sizeof(*link));  	if (!link)  		return ERR_PTR(-ENOMEM); -	link->link.detach = &bpf_link__detach_fd; +	link->detach = &bpf_link__detach_fd;  	pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);  	if (pfd < 0) { @@ -7343,7 +7518,7 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,  		return ERR_PTR(pfd);  	}  	link->fd = pfd; -	return (struct bpf_link *)link; +	return link;  }  static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, @@ -7354,10 +7529,11 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,  	return bpf_program__attach_raw_tracepoint(prog, tp_name);  } -struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) +/* Common logic for all BPF program types that attach to a btf_id */ +static struct bpf_link *bpf_program__attach_btf_id(struct bpf_program *prog)  {  	char errmsg[STRERR_BUFSIZE]; -	struct bpf_link_fd *link; +	struct bpf_link *link;  	int prog_fd, pfd;  	prog_fd = bpf_program__fd(prog); @@ -7370,13 +7546,13 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)  	link = calloc(1, sizeof(*link));  	if (!link)  		return ERR_PTR(-ENOMEM); -	link->link.detach = &bpf_link__detach_fd; +	link->detach = &bpf_link__detach_fd;  	pfd = bpf_raw_tracepoint_open(NULL, prog_fd);  	if (pfd < 0) {  		pfd = -errno;  		free(link); -		pr_warn("program '%s': failed to attach to trace: %s\n", +		pr_warn("program '%s': failed to attach: %s\n",  			bpf_program__title(prog, false),  			libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));  		return ERR_PTR(pfd); @@ -7385,12 +7561,62 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)  	return (struct bpf_link *)link;  } +struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) +{ +	return bpf_program__attach_btf_id(prog); +} + +struct bpf_link *bpf_program__attach_lsm(struct bpf_program *prog) +{ +	return bpf_program__attach_btf_id(prog); +} +  static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,  				     struct bpf_program *prog)  {  	return bpf_program__attach_trace(prog);  } +static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec, +				   struct bpf_program *prog) +{ +	return bpf_program__attach_lsm(prog); +} + +struct bpf_link * +bpf_program__attach_cgroup(struct bpf_program *prog, int cgroup_fd) +{ +	enum bpf_attach_type attach_type; +	char errmsg[STRERR_BUFSIZE]; +	struct bpf_link *link; +	int prog_fd, link_fd; + +	prog_fd = bpf_program__fd(prog); +	if (prog_fd < 0) { +		pr_warn("program '%s': can't attach before loaded\n", +			bpf_program__title(prog, false)); +		return ERR_PTR(-EINVAL); +	} + +	link = calloc(1, sizeof(*link)); +	if (!link) +		return ERR_PTR(-ENOMEM); +	link->detach = &bpf_link__detach_fd; + +	attach_type = bpf_program__get_expected_attach_type(prog); +	link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, NULL); +	if (link_fd < 0) { +		link_fd = -errno; +		free(link); +		pr_warn("program '%s': failed to attach to cgroup: %s\n", +			bpf_program__title(prog, false), +			libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg))); +		return ERR_PTR(link_fd); +	} +	link->fd = link_fd; +	return link; +} +  struct bpf_link *bpf_program__attach(struct bpf_program *prog)  {  	const struct bpf_sec_def *sec_def; @@ -7404,10 +7630,9 @@ struct bpf_link *bpf_program__attach(struct bpf_program *prog)  static int bpf_link__detach_struct_ops(struct bpf_link *link)  { -	struct bpf_link_fd *l = (void *)link;  	__u32 zero = 0; -	if (bpf_map_delete_elem(l->fd, &zero)) +	if (bpf_map_delete_elem(link->fd, &zero))  		return -errno;  	return 0; @@ -7416,7 +7641,7 @@ static int bpf_link__detach_struct_ops(struct bpf_link *link)  struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map)  {  	struct bpf_struct_ops *st_ops; -	struct bpf_link_fd *link; +	struct bpf_link *link;  	__u32 i, zero = 0;  	int err; @@ -7448,10 +7673,10 @@ struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map)  		return ERR_PTR(err);  	} -	link->link.detach = bpf_link__detach_struct_ops; +	link->detach = bpf_link__detach_struct_ops;  	link->fd = map->fd; -	return (struct bpf_link *)link; +	return link;  }  enum bpf_perf_event_ret @@ -8132,6 +8357,31 @@ void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)  	}  } +int bpf_program__set_attach_target(struct bpf_program *prog, +				   int attach_prog_fd, +				   const char *attach_func_name) +{ +	int btf_id; + +	if (!prog || attach_prog_fd < 0 || !attach_func_name) +		return -EINVAL; + +	if (attach_prog_fd) +		btf_id = libbpf_find_prog_btf_id(attach_func_name, +						 attach_prog_fd); +	else +		btf_id = __find_vmlinux_btf_id(prog->obj->btf_vmlinux, +					       attach_func_name, +					       prog->expected_attach_type); + +	if (btf_id < 0) +		return btf_id; + +	prog->attach_btf_id = btf_id; +	prog->attach_prog_fd = attach_prog_fd; +	return 0; +} +  int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz)  {  	int err = 0, n, len, start, end = -1;  |