Commit d7be143b authored by Andrey Ignatov's avatar Andrey Ignatov Committed by Daniel Borkmann
Browse files

libbpf: Support expected_attach_type at prog load



Support setting `expected_attach_type` at prog load time in both
`bpf/bpf.h` and `bpf/libbpf.h`.

Since both headers already have API to load programs, new functions are
added not to break backward compatibility for existing ones:
* `bpf_load_program_xattr()` is added to `bpf/bpf.h`;
* `bpf_prog_load_xattr()` is added to `bpf/libbpf.h`.

Both new functions accept structures, `struct bpf_load_program_attr` and
`struct bpf_prog_load_attr` correspondingly, where new fields can be
added in the future w/o changing the API.

Standard `_xattr` suffix is used to name the new API functions.

Since `bpf_load_program_name()` is not used as heavily as
`bpf_load_program()`, it was removed in favor of more generic
`bpf_load_program_xattr()`.

Signed-off-by: default avatarAndrey Ignatov <rdna@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent 5e43f899
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -296,6 +296,11 @@ union bpf_attr {
		__u32		prog_flags;
		__u32		prog_flags;
		char		prog_name[BPF_OBJ_NAME_LEN];
		char		prog_name[BPF_OBJ_NAME_LEN];
		__u32		prog_ifindex;	/* ifindex of netdev to prep for */
		__u32		prog_ifindex;	/* ifindex of netdev to prep for */
		/* For some prog types expected attach type must be known at
		 * load time to verify attach type specific parts of prog
		 * (context accesses, allowed helpers, etc).
		 */
		__u32		expected_attach_type;
	};
	};


	struct { /* anonymous struct used by BPF_OBJ_* commands */
	struct { /* anonymous struct used by BPF_OBJ_* commands */
+29 −15
Original line number Original line Diff line number Diff line
@@ -146,26 +146,30 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
					  -1);
					  -1);
}
}


int bpf_load_program_name(enum bpf_prog_type type, const char *name,
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
			  const struct bpf_insn *insns,
			   char *log_buf, size_t log_buf_sz)
			  size_t insns_cnt, const char *license,
			  __u32 kern_version, char *log_buf,
			  size_t log_buf_sz)
{
{
	int fd;
	union bpf_attr attr;
	union bpf_attr attr;
	__u32 name_len = name ? strlen(name) : 0;
	__u32 name_len;
	int fd;

	if (!load_attr)
		return -EINVAL;

	name_len = load_attr->name ? strlen(load_attr->name) : 0;


	bzero(&attr, sizeof(attr));
	bzero(&attr, sizeof(attr));
	attr.prog_type = type;
	attr.prog_type = load_attr->prog_type;
	attr.insn_cnt = (__u32)insns_cnt;
	attr.expected_attach_type = load_attr->expected_attach_type;
	attr.insns = ptr_to_u64(insns);
	attr.insn_cnt = (__u32)load_attr->insns_cnt;
	attr.license = ptr_to_u64(license);
	attr.insns = ptr_to_u64(load_attr->insns);
	attr.license = ptr_to_u64(load_attr->license);
	attr.log_buf = ptr_to_u64(NULL);
	attr.log_buf = ptr_to_u64(NULL);
	attr.log_size = 0;
	attr.log_size = 0;
	attr.log_level = 0;
	attr.log_level = 0;
	attr.kern_version = kern_version;
	attr.kern_version = load_attr->kern_version;
	memcpy(attr.prog_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
	memcpy(attr.prog_name, load_attr->name,
	       min(name_len, BPF_OBJ_NAME_LEN - 1));


	fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
	fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
	if (fd >= 0 || !log_buf || !log_buf_sz)
	if (fd >= 0 || !log_buf || !log_buf_sz)
@@ -184,8 +188,18 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
		     __u32 kern_version, char *log_buf,
		     __u32 kern_version, char *log_buf,
		     size_t log_buf_sz)
		     size_t log_buf_sz)
{
{
	return bpf_load_program_name(type, NULL, insns, insns_cnt, license,
	struct bpf_load_program_attr load_attr;
				     kern_version, log_buf, log_buf_sz);

	memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
	load_attr.prog_type = type;
	load_attr.expected_attach_type = 0;
	load_attr.name = NULL;
	load_attr.insns = insns;
	load_attr.insns_cnt = insns_cnt;
	load_attr.license = license;
	load_attr.kern_version = kern_version;

	return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
}
}


int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+12 −5
Original line number Original line Diff line number Diff line
@@ -41,13 +41,20 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
			  int key_size, int inner_map_fd, int max_entries,
			  int key_size, int inner_map_fd, int max_entries,
			  __u32 map_flags);
			  __u32 map_flags);


struct bpf_load_program_attr {
	enum bpf_prog_type prog_type;
	enum bpf_attach_type expected_attach_type;
	const char *name;
	const struct bpf_insn *insns;
	size_t insns_cnt;
	const char *license;
	__u32 kern_version;
};

/* Recommend log buffer size */
/* Recommend log buffer size */
#define BPF_LOG_BUF_SIZE (256 * 1024)
#define BPF_LOG_BUF_SIZE (256 * 1024)
int bpf_load_program_name(enum bpf_prog_type type, const char *name,
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
			  const struct bpf_insn *insns,
			   char *log_buf, size_t log_buf_sz);
			  size_t insns_cnt, const char *license,
			  __u32 kern_version, char *log_buf,
			  size_t log_buf_sz);
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
		     size_t insns_cnt, const char *license,
		     size_t insns_cnt, const char *license,
		     __u32 kern_version, char *log_buf,
		     __u32 kern_version, char *log_buf,
+79 −26
Original line number Original line Diff line number Diff line
@@ -203,6 +203,8 @@ struct bpf_program {
	struct bpf_object *obj;
	struct bpf_object *obj;
	void *priv;
	void *priv;
	bpf_program_clear_priv_t clear_priv;
	bpf_program_clear_priv_t clear_priv;

	enum bpf_attach_type expected_attach_type;
};
};


struct bpf_map {
struct bpf_map {
@@ -1162,21 +1164,31 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
}
}


static int
static int
load_program(enum bpf_prog_type type, const char *name, struct bpf_insn *insns,
load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
	     int insns_cnt, char *license, u32 kern_version, int *pfd)
	     const char *name, struct bpf_insn *insns, int insns_cnt,
	     char *license, u32 kern_version, int *pfd)
{
{
	int ret;
	struct bpf_load_program_attr load_attr;
	char *log_buf;
	char *log_buf;
	int ret;


	if (!insns || !insns_cnt)
	memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
	load_attr.prog_type = type;
	load_attr.expected_attach_type = expected_attach_type;
	load_attr.name = name;
	load_attr.insns = insns;
	load_attr.insns_cnt = insns_cnt;
	load_attr.license = license;
	load_attr.kern_version = kern_version;

	if (!load_attr.insns || !load_attr.insns_cnt)
		return -EINVAL;
		return -EINVAL;


	log_buf = malloc(BPF_LOG_BUF_SIZE);
	log_buf = malloc(BPF_LOG_BUF_SIZE);
	if (!log_buf)
	if (!log_buf)
		pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
		pr_warning("Alloc log buffer for bpf loader error, continue without log\n");


	ret = bpf_load_program_name(type, name, insns, insns_cnt, license,
	ret = bpf_load_program_xattr(&load_attr, log_buf, BPF_LOG_BUF_SIZE);
				    kern_version, log_buf, BPF_LOG_BUF_SIZE);


	if (ret >= 0) {
	if (ret >= 0) {
		*pfd = ret;
		*pfd = ret;
@@ -1192,18 +1204,18 @@ load_program(enum bpf_prog_type type, const char *name, struct bpf_insn *insns,
		pr_warning("-- BEGIN DUMP LOG ---\n");
		pr_warning("-- BEGIN DUMP LOG ---\n");
		pr_warning("\n%s\n", log_buf);
		pr_warning("\n%s\n", log_buf);
		pr_warning("-- END LOG --\n");
		pr_warning("-- END LOG --\n");
	} else if (insns_cnt >= BPF_MAXINSNS) {
	} else if (load_attr.insns_cnt >= BPF_MAXINSNS) {
		pr_warning("Program too large (%d insns), at most %d insns\n",
		pr_warning("Program too large (%zu insns), at most %d insns\n",
			   insns_cnt, BPF_MAXINSNS);
			   load_attr.insns_cnt, BPF_MAXINSNS);
		ret = -LIBBPF_ERRNO__PROG2BIG;
		ret = -LIBBPF_ERRNO__PROG2BIG;
	} else {
	} else {
		/* Wrong program type? */
		/* Wrong program type? */
		if (type != BPF_PROG_TYPE_KPROBE) {
		if (load_attr.prog_type != BPF_PROG_TYPE_KPROBE) {
			int fd;
			int fd;


			fd = bpf_load_program_name(BPF_PROG_TYPE_KPROBE, name,
			load_attr.prog_type = BPF_PROG_TYPE_KPROBE;
						   insns, insns_cnt, license,
			load_attr.expected_attach_type = 0;
						   kern_version, NULL, 0);
			fd = bpf_load_program_xattr(&load_attr, NULL, 0);
			if (fd >= 0) {
			if (fd >= 0) {
				close(fd);
				close(fd);
				ret = -LIBBPF_ERRNO__PROGTYPE;
				ret = -LIBBPF_ERRNO__PROGTYPE;
@@ -1247,8 +1259,9 @@ bpf_program__load(struct bpf_program *prog,
			pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
			pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
				   prog->section_name, prog->instances.nr);
				   prog->section_name, prog->instances.nr);
		}
		}
		err = load_program(prog->type, prog->name, prog->insns,
		err = load_program(prog->type, prog->expected_attach_type,
				   prog->insns_cnt, license, kern_version, &fd);
				   prog->name, prog->insns, prog->insns_cnt,
				   license, kern_version, &fd);
		if (!err)
		if (!err)
			prog->instances.fds[0] = fd;
			prog->instances.fds[0] = fd;
		goto out;
		goto out;
@@ -1276,8 +1289,8 @@ bpf_program__load(struct bpf_program *prog,
			continue;
			continue;
		}
		}


		err = load_program(prog->type, prog->name,
		err = load_program(prog->type, prog->expected_attach_type,
				   result.new_insn_ptr,
				   prog->name, result.new_insn_ptr,
				   result.new_insn_cnt,
				   result.new_insn_cnt,
				   license, kern_version, &fd);
				   license, kern_version, &fd);


@@ -1835,11 +1848,22 @@ BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);


#define BPF_PROG_SEC(string, type) { string, sizeof(string) - 1, type }
static void bpf_program__set_expected_attach_type(struct bpf_program *prog,
						 enum bpf_attach_type type)
{
	prog->expected_attach_type = type;
}

#define BPF_PROG_SEC_FULL(string, ptype, atype) \
	{ string, sizeof(string) - 1, ptype, atype }

#define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_FULL(string, ptype, 0)

static const struct {
static const struct {
	const char *sec;
	const char *sec;
	size_t len;
	size_t len;
	enum bpf_prog_type prog_type;
	enum bpf_prog_type prog_type;
	enum bpf_attach_type expected_attach_type;
} section_names[] = {
} section_names[] = {
	BPF_PROG_SEC("socket",		BPF_PROG_TYPE_SOCKET_FILTER),
	BPF_PROG_SEC("socket",		BPF_PROG_TYPE_SOCKET_FILTER),
	BPF_PROG_SEC("kprobe/",		BPF_PROG_TYPE_KPROBE),
	BPF_PROG_SEC("kprobe/",		BPF_PROG_TYPE_KPROBE),
@@ -1859,9 +1883,11 @@ static const struct {
	BPF_PROG_SEC("sk_skb",		BPF_PROG_TYPE_SK_SKB),
	BPF_PROG_SEC("sk_skb",		BPF_PROG_TYPE_SK_SKB),
	BPF_PROG_SEC("sk_msg",		BPF_PROG_TYPE_SK_MSG),
	BPF_PROG_SEC("sk_msg",		BPF_PROG_TYPE_SK_MSG),
};
};

#undef BPF_PROG_SEC
#undef BPF_PROG_SEC
#undef BPF_PROG_SEC_FULL


static enum bpf_prog_type bpf_program__guess_type(struct bpf_program *prog)
static int bpf_program__identify_section(struct bpf_program *prog)
{
{
	int i;
	int i;


@@ -1871,13 +1897,13 @@ static enum bpf_prog_type bpf_program__guess_type(struct bpf_program *prog)
	for (i = 0; i < ARRAY_SIZE(section_names); i++)
	for (i = 0; i < ARRAY_SIZE(section_names); i++)
		if (strncmp(prog->section_name, section_names[i].sec,
		if (strncmp(prog->section_name, section_names[i].sec,
			    section_names[i].len) == 0)
			    section_names[i].len) == 0)
			return section_names[i].prog_type;
			return i;


err:
err:
	pr_warning("failed to guess program type based on section name %s\n",
	pr_warning("failed to guess program type based on section name %s\n",
		   prog->section_name);
		   prog->section_name);


	return BPF_PROG_TYPE_UNSPEC;
	return -1;
}
}


int bpf_map__fd(struct bpf_map *map)
int bpf_map__fd(struct bpf_map *map)
@@ -1976,12 +2002,31 @@ long libbpf_get_error(const void *ptr)


int bpf_prog_load(const char *file, enum bpf_prog_type type,
int bpf_prog_load(const char *file, enum bpf_prog_type type,
		  struct bpf_object **pobj, int *prog_fd)
		  struct bpf_object **pobj, int *prog_fd)
{
	struct bpf_prog_load_attr attr;

	memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
	attr.file = file;
	attr.prog_type = type;
	attr.expected_attach_type = 0;

	return bpf_prog_load_xattr(&attr, pobj, prog_fd);
}

int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
			struct bpf_object **pobj, int *prog_fd)
{
{
	struct bpf_program *prog, *first_prog = NULL;
	struct bpf_program *prog, *first_prog = NULL;
	enum bpf_attach_type expected_attach_type;
	enum bpf_prog_type prog_type;
	struct bpf_object *obj;
	struct bpf_object *obj;
	int section_idx;
	int err;
	int err;


	obj = bpf_object__open(file);
	if (!attr)
		return -EINVAL;

	obj = bpf_object__open(attr->file);
	if (IS_ERR(obj))
	if (IS_ERR(obj))
		return -ENOENT;
		return -ENOENT;


@@ -1990,15 +2035,23 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
		 * If type is not specified, try to guess it based on
		 * If type is not specified, try to guess it based on
		 * section name.
		 * section name.
		 */
		 */
		if (type == BPF_PROG_TYPE_UNSPEC) {
		prog_type = attr->prog_type;
			type = bpf_program__guess_type(prog);
		expected_attach_type = attr->expected_attach_type;
			if (type == BPF_PROG_TYPE_UNSPEC) {
		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
			section_idx = bpf_program__identify_section(prog);
			if (section_idx < 0) {
				bpf_object__close(obj);
				bpf_object__close(obj);
				return -EINVAL;
				return -EINVAL;
			}
			}
			prog_type = section_names[section_idx].prog_type;
			expected_attach_type =
				section_names[section_idx].expected_attach_type;
		}
		}


		bpf_program__set_type(prog, type);
		bpf_program__set_type(prog, prog_type);
		bpf_program__set_expected_attach_type(prog,
						      expected_attach_type);

		if (prog->idx != obj->efile.text_shndx && !first_prog)
		if (prog->idx != obj->efile.text_shndx && !first_prog)
			first_prog = prog;
			first_prog = prog;
	}
	}
+8 −0
Original line number Original line Diff line number Diff line
@@ -248,6 +248,14 @@ int bpf_map__pin(struct bpf_map *map, const char *path);


long libbpf_get_error(const void *ptr);
long libbpf_get_error(const void *ptr);


struct bpf_prog_load_attr {
	const char *file;
	enum bpf_prog_type prog_type;
	enum bpf_attach_type expected_attach_type;
};

int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
			struct bpf_object **pobj, int *prog_fd);
int bpf_prog_load(const char *file, enum bpf_prog_type type,
int bpf_prog_load(const char *file, enum bpf_prog_type type,
		  struct bpf_object **pobj, int *prog_fd);
		  struct bpf_object **pobj, int *prog_fd);