mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-30 18:07:52 +00:00 
			
		
		
		
	23.05: drop ucode-mod-bpf backport
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
		| @@ -1,40 +0,0 @@ | |||||||
| include $(TOPDIR)/rules.mk |  | ||||||
|  |  | ||||||
| PKG_NAME:=ucode-mod-bpf |  | ||||||
| PKG_RELEASE:=1 |  | ||||||
| PKG_LICENSE:=ISC |  | ||||||
| PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name> |  | ||||||
|  |  | ||||||
| include $(INCLUDE_DIR)/package.mk |  | ||||||
| include $(INCLUDE_DIR)/nls.mk |  | ||||||
|  |  | ||||||
| define Package/ucode-mod-bpf |  | ||||||
|   SECTION:=utils |  | ||||||
|   CATEGORY:=Utilities |  | ||||||
|   TITLE:=ucode eBPF module |  | ||||||
|   DEPENDS:=+libucode +libbpf |  | ||||||
| endef |  | ||||||
|  |  | ||||||
| define Package/ucode-mod-bpf/description |  | ||||||
| The bpf plugin provides functionality for loading and interacting with |  | ||||||
| eBPF modules. |  | ||||||
|  |  | ||||||
| It allows loading full modules and pinned maps/programs and supports |  | ||||||
| interacting with maps and attaching programs as tc classifiers. |  | ||||||
| endef |  | ||||||
|  |  | ||||||
| define Package/ucode-mod-bpf/install |  | ||||||
| 	$(INSTALL_DIR) $(1)/usr/lib/ucode |  | ||||||
| 	$(CP) $(PKG_BUILD_DIR)/bpf.so $(1)/usr/lib/ucode/ |  | ||||||
| endef |  | ||||||
|  |  | ||||||
| define Build/Configure |  | ||||||
| endef |  | ||||||
|  |  | ||||||
| define Build/Compile |  | ||||||
| 	$(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) $(FPIC) \ |  | ||||||
| 		-Wall -ffunction-sections -Wl,--gc-sections -shared -Wl,--no-as-needed -lbpf \ |  | ||||||
| 		-o $(PKG_BUILD_DIR)/bpf.so $(PKG_BUILD_DIR)/bpf.c |  | ||||||
| endef |  | ||||||
|  |  | ||||||
| $(eval $(call BuildPackage,ucode-mod-bpf)) |  | ||||||
| @@ -1,814 +0,0 @@ | |||||||
| #include <sys/resource.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/socket.h> |  | ||||||
| #include <net/if.h> |  | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <errno.h> |  | ||||||
| #include <unistd.h> |  | ||||||
|  |  | ||||||
| #include <bpf/bpf.h> |  | ||||||
| #include <bpf/libbpf.h> |  | ||||||
|  |  | ||||||
| #include "ucode/module.h" |  | ||||||
|  |  | ||||||
| #define err_return_int(err, ...) do { set_error(err, __VA_ARGS__); return -1; } while(0) |  | ||||||
| #define err_return(err, ...) do { set_error(err, __VA_ARGS__); return NULL; } while(0) |  | ||||||
| #define TRUE ucv_boolean_new(true) |  | ||||||
|  |  | ||||||
| static uc_resource_type_t *module_type, *map_type, *map_iter_type, *program_type; |  | ||||||
| static uc_value_t *registry; |  | ||||||
| static uc_vm_t *debug_vm; |  | ||||||
|  |  | ||||||
| static struct { |  | ||||||
| 	int code; |  | ||||||
| 	char *msg; |  | ||||||
| } last_error; |  | ||||||
|  |  | ||||||
| struct uc_bpf_fd { |  | ||||||
| 	int fd; |  | ||||||
| 	bool close; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct uc_bpf_map { |  | ||||||
| 	struct uc_bpf_fd fd; /* must be first */ |  | ||||||
| 	unsigned int key_size, val_size; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct uc_bpf_map_iter { |  | ||||||
| 	int fd; |  | ||||||
| 	unsigned int key_size; |  | ||||||
| 	bool has_next; |  | ||||||
| 	uint8_t key[]; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| __attribute__((format(printf, 2, 3))) static void |  | ||||||
| set_error(int errcode, const char *fmt, ...) |  | ||||||
| { |  | ||||||
| 	va_list ap; |  | ||||||
|  |  | ||||||
| 	free(last_error.msg); |  | ||||||
|  |  | ||||||
| 	last_error.code = errcode; |  | ||||||
| 	last_error.msg = NULL; |  | ||||||
|  |  | ||||||
| 	if (fmt) { |  | ||||||
| 		va_start(ap, fmt); |  | ||||||
| 		xvasprintf(&last_error.msg, fmt, ap); |  | ||||||
| 		va_end(ap); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void init_env(void) |  | ||||||
| { |  | ||||||
| 	static bool init_done = false; |  | ||||||
| 	struct rlimit limit = { |  | ||||||
| 		.rlim_cur = RLIM_INFINITY, |  | ||||||
| 		.rlim_max = RLIM_INFINITY, |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	if (init_done) |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	setrlimit(RLIMIT_MEMLOCK, &limit); |  | ||||||
| 	init_done = true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_error(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	uc_value_t *numeric = uc_fn_arg(0); |  | ||||||
| 	const char *msg = last_error.msg; |  | ||||||
| 	int code = last_error.code; |  | ||||||
| 	uc_stringbuf_t *buf; |  | ||||||
| 	const char *s; |  | ||||||
|  |  | ||||||
| 	if (last_error.code == 0) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	set_error(0, NULL); |  | ||||||
|  |  | ||||||
| 	if (ucv_is_truish(numeric)) |  | ||||||
| 		return ucv_int64_new(code); |  | ||||||
|  |  | ||||||
| 	buf = ucv_stringbuf_new(); |  | ||||||
| 	if (code < 0 && msg) { |  | ||||||
| 		ucv_stringbuf_addstr(buf, msg, strlen(msg)); |  | ||||||
| 	} else { |  | ||||||
| 		s = strerror(code); |  | ||||||
| 		ucv_stringbuf_addstr(buf, s, strlen(s)); |  | ||||||
| 		if (msg) |  | ||||||
| 			ucv_stringbuf_printf(buf, ": %s", msg); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return ucv_stringbuf_finish(buf); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| uc_bpf_module_set_opts(struct bpf_object *obj, uc_value_t *opts) |  | ||||||
| { |  | ||||||
| 	uc_value_t *val; |  | ||||||
|  |  | ||||||
| 	if (!opts) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	if (ucv_type(opts) != UC_OBJECT) |  | ||||||
| 		err_return_int(EINVAL, "options argument"); |  | ||||||
|  |  | ||||||
| 	if ((val = ucv_object_get(opts, "rodata", NULL)) != NULL) { |  | ||||||
| 		struct bpf_map *map = NULL; |  | ||||||
|  |  | ||||||
| 		if (ucv_type(val) != UC_STRING) |  | ||||||
| 			err_return_int(EINVAL, "rodata type"); |  | ||||||
|  |  | ||||||
| 		while ((map = bpf_object__next_map(obj, map)) != NULL) { |  | ||||||
| 			if (!strstr(bpf_map__name(map), ".rodata")) |  | ||||||
| 				continue; |  | ||||||
|  |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!map) |  | ||||||
| 			err_return_int(errno, "rodata map"); |  | ||||||
|  |  | ||||||
| 		if (bpf_map__set_initial_value(map, ucv_string_get(val), |  | ||||||
| 					       ucv_string_length(val))) |  | ||||||
| 			err_return_int(errno, "rodata"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if ((val = ucv_object_get(opts, "program-type", NULL)) != NULL) { |  | ||||||
| 		if (ucv_type(val) != UC_OBJECT) |  | ||||||
| 			err_return_int(EINVAL, "prog_types argument"); |  | ||||||
|  |  | ||||||
| 		ucv_object_foreach(val, name, type) { |  | ||||||
| 			struct bpf_program *prog; |  | ||||||
|  |  | ||||||
| 			if (ucv_type(type) != UC_INTEGER) |  | ||||||
| 				err_return_int(EINVAL, "program %s type", name); |  | ||||||
|  |  | ||||||
| 			prog = bpf_object__find_program_by_name(obj, name); |  | ||||||
| 			if (!prog) |  | ||||||
| 				err_return_int(-1, "program %s not found", name); |  | ||||||
|  |  | ||||||
| 			bpf_program__set_type(prog, ucv_int64_get(type)); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_open_module(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, bpf_opts); |  | ||||||
| 	uc_value_t *path = uc_fn_arg(0); |  | ||||||
| 	uc_value_t *opts = uc_fn_arg(1); |  | ||||||
| 	struct bpf_object *obj; |  | ||||||
|  |  | ||||||
| 	if (ucv_type(path) != UC_STRING) |  | ||||||
| 		err_return(EINVAL, "module path"); |  | ||||||
|  |  | ||||||
| 	init_env(); |  | ||||||
| 	obj = bpf_object__open_file(ucv_string_get(path), &bpf_opts); |  | ||||||
| 	if (libbpf_get_error(obj)) |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
|  |  | ||||||
| 	if (uc_bpf_module_set_opts(obj, opts)) { |  | ||||||
| 		bpf_object__close(obj); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (bpf_object__load(obj)) { |  | ||||||
| 		bpf_object__close(obj); |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return uc_resource_new(module_type, obj); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_create(int fd, unsigned int key_size, unsigned int val_size, bool close) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *uc_map; |  | ||||||
|  |  | ||||||
| 	uc_map = xalloc(sizeof(*uc_map)); |  | ||||||
| 	uc_map->fd.fd = fd; |  | ||||||
| 	uc_map->key_size = key_size; |  | ||||||
| 	uc_map->val_size = val_size; |  | ||||||
| 	uc_map->fd.close = close; |  | ||||||
|  |  | ||||||
| 	return uc_resource_new(map_type, uc_map); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_open_map(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct bpf_map_info info; |  | ||||||
| 	uc_value_t *path = uc_fn_arg(0); |  | ||||||
| 	__u32 len = sizeof(info); |  | ||||||
| 	int err; |  | ||||||
| 	int fd; |  | ||||||
|  |  | ||||||
| 	if (ucv_type(path) != UC_STRING) |  | ||||||
| 		err_return(EINVAL, "module path"); |  | ||||||
|  |  | ||||||
| 	fd = bpf_obj_get(ucv_string_get(path)); |  | ||||||
| 	if (fd < 0) |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
|  |  | ||||||
| 	err = bpf_obj_get_info_by_fd(fd, &info, &len); |  | ||||||
| 	if (err) { |  | ||||||
| 		close(fd); |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return uc_bpf_map_create(fd, info.key_size, info.value_size, true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_open_program(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	uc_value_t *path = uc_fn_arg(0); |  | ||||||
| 	struct uc_bpf_fd *f; |  | ||||||
| 	int fd; |  | ||||||
|  |  | ||||||
| 	if (ucv_type(path) != UC_STRING) |  | ||||||
| 		err_return(EINVAL, "module path"); |  | ||||||
|  |  | ||||||
| 	fd = bpf_obj_get(ucv_string_get(path)); |  | ||||||
| 	if (fd < 0) |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
|  |  | ||||||
| 	f = xalloc(sizeof(*f)); |  | ||||||
| 	f->fd = fd; |  | ||||||
| 	f->close = true; |  | ||||||
|  |  | ||||||
| 	return uc_resource_new(program_type, f); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_module_get_maps(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct bpf_object *obj = uc_fn_thisval("bpf.module"); |  | ||||||
| 	struct bpf_map *map = NULL; |  | ||||||
| 	uc_value_t *rv; |  | ||||||
| 	int i = 0; |  | ||||||
|  |  | ||||||
| 	if (!obj) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	rv = ucv_array_new(vm); |  | ||||||
| 	bpf_object__for_each_map(map, obj) |  | ||||||
| 		ucv_array_set(rv, i++, ucv_string_new(bpf_map__name(map))); |  | ||||||
|  |  | ||||||
| 	return rv; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_module_get_map(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct bpf_object *obj = uc_fn_thisval("bpf.module"); |  | ||||||
| 	struct bpf_map *map; |  | ||||||
| 	uc_value_t *name = uc_fn_arg(0); |  | ||||||
| 	int fd; |  | ||||||
|  |  | ||||||
| 	if (!obj || ucv_type(name) != UC_STRING) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	map = bpf_object__find_map_by_name(obj, ucv_string_get(name)); |  | ||||||
| 	if (!map) |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
|  |  | ||||||
| 	fd = bpf_map__fd(map); |  | ||||||
| 	if (fd < 0) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	return uc_bpf_map_create(fd, bpf_map__key_size(map), bpf_map__value_size(map), false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_module_get_programs(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct bpf_object *obj = uc_fn_thisval("bpf.module"); |  | ||||||
| 	struct bpf_program *prog = NULL; |  | ||||||
| 	uc_value_t *rv; |  | ||||||
| 	int i = 0; |  | ||||||
|  |  | ||||||
| 	if (!obj) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	rv = ucv_array_new(vm); |  | ||||||
| 	bpf_object__for_each_program(prog, obj) |  | ||||||
| 		ucv_array_set(rv, i++, ucv_string_new(bpf_program__name(prog))); |  | ||||||
|  |  | ||||||
| 	return rv; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_module_get_program(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct bpf_object *obj = uc_fn_thisval("bpf.module"); |  | ||||||
| 	struct bpf_program *prog; |  | ||||||
| 	uc_value_t *name = uc_fn_arg(0); |  | ||||||
| 	struct uc_bpf_fd *f; |  | ||||||
| 	int fd; |  | ||||||
|  |  | ||||||
| 	if (!obj || !name || ucv_type(name) != UC_STRING) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	prog = bpf_object__find_program_by_name(obj, ucv_string_get(name)); |  | ||||||
| 	if (!prog) |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
|  |  | ||||||
| 	fd = bpf_program__fd(prog); |  | ||||||
| 	if (fd < 0) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	f = xalloc(sizeof(*f)); |  | ||||||
| 	f->fd = fd; |  | ||||||
|  |  | ||||||
| 	return uc_resource_new(program_type, f); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void * |  | ||||||
| uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size) |  | ||||||
| { |  | ||||||
| 	static union { |  | ||||||
| 		uint32_t u32; |  | ||||||
| 		uint64_t u64; |  | ||||||
| 	} val_int; |  | ||||||
|  |  | ||||||
| 	switch (ucv_type(val)) { |  | ||||||
| 	case UC_INTEGER: |  | ||||||
| 		if (size == 4) |  | ||||||
| 			val_int.u32 = ucv_int64_get(val); |  | ||||||
| 		else if (size == 8) |  | ||||||
| 			val_int.u64 = ucv_int64_get(val); |  | ||||||
| 		else |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		return &val_int; |  | ||||||
| 	case UC_STRING: |  | ||||||
| 		if (size != ucv_string_length(val)) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		return ucv_string_get(val); |  | ||||||
| 	default: |  | ||||||
| 		err_return(EINVAL, "%s type", kind); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err_return(EINVAL, "%s size mismatch (expected: %d)", kind, size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_get(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); |  | ||||||
| 	uc_value_t *a_key = uc_fn_arg(0); |  | ||||||
| 	void *key, *val; |  | ||||||
|  |  | ||||||
| 	if (!map) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	key = uc_bpf_map_arg(a_key, "key", map->key_size); |  | ||||||
| 	if (!key) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	val = alloca(map->val_size); |  | ||||||
| 	if (bpf_map_lookup_elem(map->fd.fd, key, val)) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	return ucv_string_new_length(val, map->val_size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_set(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); |  | ||||||
| 	uc_value_t *a_key = uc_fn_arg(0); |  | ||||||
| 	uc_value_t *a_val = uc_fn_arg(1); |  | ||||||
| 	uc_value_t *a_flags = uc_fn_arg(2); |  | ||||||
| 	uint64_t flags; |  | ||||||
| 	void *key, *val; |  | ||||||
|  |  | ||||||
| 	if (!map) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	key = uc_bpf_map_arg(a_key, "key", map->key_size); |  | ||||||
| 	if (!key) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	val = uc_bpf_map_arg(a_val, "value", map->val_size); |  | ||||||
| 	if (!val) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	if (!a_flags) |  | ||||||
| 		flags = BPF_ANY; |  | ||||||
| 	else if (ucv_type(a_flags) != UC_INTEGER) |  | ||||||
| 		err_return(EINVAL, "flags"); |  | ||||||
| 	else |  | ||||||
| 		flags = ucv_int64_get(a_flags); |  | ||||||
|  |  | ||||||
| 	if (bpf_map_update_elem(map->fd.fd, key, val, flags)) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	return ucv_string_new_length(val, map->val_size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_delete(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); |  | ||||||
| 	uc_value_t *a_key = uc_fn_arg(0); |  | ||||||
| 	uc_value_t *a_return = uc_fn_arg(1); |  | ||||||
| 	void *key, *val = NULL; |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	if (!map) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	key = uc_bpf_map_arg(a_key, "key", map->key_size); |  | ||||||
| 	if (!key) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	if (!ucv_is_truish(a_return)) { |  | ||||||
| 		ret = bpf_map_delete_elem(map->fd.fd, key); |  | ||||||
|  |  | ||||||
| 		return ucv_boolean_new(ret == 0); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	val = alloca(map->val_size); |  | ||||||
| 	if (bpf_map_lookup_and_delete_elem(map->fd.fd, key, val)) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	return ucv_string_new_length(val, map->val_size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_delete_all(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); |  | ||||||
| 	uc_value_t *filter = uc_fn_arg(0); |  | ||||||
| 	bool has_next; |  | ||||||
| 	void *key, *next; |  | ||||||
|  |  | ||||||
| 	if (!map) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	key = alloca(map->key_size); |  | ||||||
| 	next = alloca(map->key_size); |  | ||||||
| 	has_next = !bpf_map_get_next_key(map->fd.fd, NULL, next); |  | ||||||
| 	while (has_next) { |  | ||||||
| 		bool skip = false; |  | ||||||
|  |  | ||||||
| 		memcpy(key, next, map->key_size); |  | ||||||
| 		has_next = !bpf_map_get_next_key(map->fd.fd, next, next); |  | ||||||
|  |  | ||||||
| 		if (ucv_is_callable(filter)) { |  | ||||||
| 			uc_value_t *rv; |  | ||||||
|  |  | ||||||
| 			uc_value_push(ucv_get(filter)); |  | ||||||
| 			uc_value_push(ucv_string_new_length((const char *)key, map->key_size)); |  | ||||||
| 			if (uc_call(1) != EXCEPTION_NONE) |  | ||||||
| 				break; |  | ||||||
|  |  | ||||||
| 			rv = uc_vm_stack_pop(vm); |  | ||||||
| 			if (!rv) |  | ||||||
| 				break; |  | ||||||
|  |  | ||||||
| 			skip = !ucv_is_truish(rv); |  | ||||||
| 			ucv_put(rv); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!skip) |  | ||||||
| 			bpf_map_delete_elem(map->fd.fd, key); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return TRUE; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_iterator(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); |  | ||||||
| 	struct uc_bpf_map_iter *iter; |  | ||||||
|  |  | ||||||
| 	if (!map) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	iter = xalloc(sizeof(*iter) + map->key_size); |  | ||||||
| 	iter->fd = map->fd.fd; |  | ||||||
| 	iter->key_size = map->key_size; |  | ||||||
| 	iter->has_next = !bpf_map_get_next_key(iter->fd, NULL, &iter->key); |  | ||||||
|  |  | ||||||
| 	return uc_resource_new(map_iter_type, iter); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_iter_next(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map_iter *iter = uc_fn_thisval("bpf.map_iter"); |  | ||||||
| 	uc_value_t *rv; |  | ||||||
|  |  | ||||||
| 	if (!iter->has_next) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	rv = ucv_string_new_length((const char *)iter->key, iter->key_size); |  | ||||||
| 	iter->has_next = !bpf_map_get_next_key(iter->fd, &iter->key, &iter->key); |  | ||||||
|  |  | ||||||
| 	return rv; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_iter_next_int(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map_iter *iter = uc_fn_thisval("bpf.map_iter"); |  | ||||||
| 	uint64_t intval; |  | ||||||
| 	uc_value_t *rv; |  | ||||||
|  |  | ||||||
| 	if (!iter->has_next) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	if (iter->key_size == 4) |  | ||||||
| 		intval = *(uint32_t *)iter->key; |  | ||||||
| 	else if (iter->key_size == 8) |  | ||||||
| 		intval = *(uint64_t *)iter->key; |  | ||||||
| 	else |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	rv = ucv_int64_new(intval); |  | ||||||
| 	iter->has_next = !bpf_map_get_next_key(iter->fd, &iter->key, &iter->key); |  | ||||||
|  |  | ||||||
| 	return rv; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_foreach(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); |  | ||||||
| 	uc_value_t *func = uc_fn_arg(0); |  | ||||||
| 	bool has_next; |  | ||||||
| 	void *key, *next; |  | ||||||
| 	bool ret = false; |  | ||||||
|  |  | ||||||
| 	key = alloca(map->key_size); |  | ||||||
| 	next = alloca(map->key_size); |  | ||||||
| 	has_next = !bpf_map_get_next_key(map->fd.fd, NULL, next); |  | ||||||
|  |  | ||||||
| 	while (has_next) { |  | ||||||
| 		uc_value_t *rv; |  | ||||||
| 		bool stop; |  | ||||||
|  |  | ||||||
| 		memcpy(key, next, map->key_size); |  | ||||||
| 		has_next = !bpf_map_get_next_key(map->fd.fd, next, next); |  | ||||||
|  |  | ||||||
| 		uc_value_push(ucv_get(func)); |  | ||||||
| 		uc_value_push(ucv_string_new_length((const char *)key, map->key_size)); |  | ||||||
|  |  | ||||||
| 		if (uc_call(1) != EXCEPTION_NONE) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		rv = uc_vm_stack_pop(vm); |  | ||||||
| 		stop = (ucv_type(rv) == UC_BOOLEAN && !ucv_boolean_get(rv)); |  | ||||||
| 		ucv_put(rv); |  | ||||||
|  |  | ||||||
| 		if (stop) |  | ||||||
| 			break; |  | ||||||
|  |  | ||||||
| 		ret = true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return ucv_boolean_new(ret); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_obj_pin(uc_vm_t *vm, size_t nargs, const char *type) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_fd *f = uc_fn_thisval(type); |  | ||||||
| 	uc_value_t *path = uc_fn_arg(0); |  | ||||||
|  |  | ||||||
| 	if (ucv_type(path) != UC_STRING) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	if (bpf_obj_pin(f->fd, ucv_string_get(path))) |  | ||||||
| 		err_return(errno, NULL); |  | ||||||
|  |  | ||||||
| 	return TRUE; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_program_pin(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	return uc_bpf_obj_pin(vm, nargs, "bpf.program"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_map_pin(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	return uc_bpf_obj_pin(vm, nargs, "bpf.map"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_set_tc_hook(uc_value_t *ifname, uc_value_t *type, uc_value_t *prio, |  | ||||||
| 		   int fd) |  | ||||||
| { |  | ||||||
| 	DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook); |  | ||||||
| 	DECLARE_LIBBPF_OPTS(bpf_tc_opts, attach_tc, |  | ||||||
| 			    .handle = 1); |  | ||||||
| 	const char *type_str; |  | ||||||
| 	uint64_t prio_val; |  | ||||||
|  |  | ||||||
| 	if (ucv_type(ifname) != UC_STRING || ucv_type(type) != UC_STRING || |  | ||||||
| 	    ucv_type(prio) != UC_INTEGER) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	prio_val = ucv_int64_get(prio); |  | ||||||
| 	if (prio_val > 0xffff) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	type_str = ucv_string_get(type); |  | ||||||
| 	if (!strcmp(type_str, "ingress")) |  | ||||||
| 		hook.attach_point = BPF_TC_INGRESS; |  | ||||||
| 	else if (!strcmp(type_str, "egress")) |  | ||||||
| 		hook.attach_point = BPF_TC_EGRESS; |  | ||||||
| 	else |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	hook.ifindex = if_nametoindex(ucv_string_get(ifname)); |  | ||||||
| 	if (!hook.ifindex) |  | ||||||
| 		goto error; |  | ||||||
|  |  | ||||||
| 	bpf_tc_hook_create(&hook); |  | ||||||
| 	attach_tc.priority = prio_val; |  | ||||||
| 	if (bpf_tc_detach(&hook, &attach_tc) < 0 && fd < 0) |  | ||||||
| 		goto error; |  | ||||||
|  |  | ||||||
| 	if (fd < 0) |  | ||||||
| 		goto out; |  | ||||||
|  |  | ||||||
| 	attach_tc.prog_fd = fd; |  | ||||||
| 	if (bpf_tc_attach(&hook, &attach_tc) < 0) |  | ||||||
| 		goto error; |  | ||||||
|  |  | ||||||
| out: |  | ||||||
| 	return TRUE; |  | ||||||
|  |  | ||||||
| error: |  | ||||||
| 	if (fd >= 0) |  | ||||||
| 		err_return(ENOENT, NULL); |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_program_tc_attach(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_fd *f = uc_fn_thisval("bpf.program"); |  | ||||||
| 	uc_value_t *ifname = uc_fn_arg(0); |  | ||||||
| 	uc_value_t *type = uc_fn_arg(1); |  | ||||||
| 	uc_value_t *prio = uc_fn_arg(2); |  | ||||||
|  |  | ||||||
| 	if (!f) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	return uc_bpf_set_tc_hook(ifname, type, prio, f->fd); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_tc_detach(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	uc_value_t *ifname = uc_fn_arg(0); |  | ||||||
| 	uc_value_t *type = uc_fn_arg(1); |  | ||||||
| 	uc_value_t *prio = uc_fn_arg(2); |  | ||||||
|  |  | ||||||
| 	return uc_bpf_set_tc_hook(ifname, type, prio, -1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| uc_bpf_debug_print(enum libbpf_print_level level, const char *format, |  | ||||||
| 		   va_list args) |  | ||||||
| { |  | ||||||
| 	char buf[256], *str = NULL; |  | ||||||
| 	uc_value_t *val; |  | ||||||
| 	va_list ap; |  | ||||||
| 	int size; |  | ||||||
|  |  | ||||||
| 	va_copy(ap, args); |  | ||||||
| 	size = vsnprintf(buf, sizeof(buf), format, ap); |  | ||||||
| 	va_end(ap); |  | ||||||
|  |  | ||||||
| 	if (size > 0 && (unsigned long)size < ARRAY_SIZE(buf) - 1) { |  | ||||||
| 		val = ucv_string_new(buf); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (vasprintf(&str, format, args) < 0) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	val = ucv_string_new(str); |  | ||||||
| 	free(str); |  | ||||||
|  |  | ||||||
| out: |  | ||||||
| 	uc_vm_stack_push(debug_vm, ucv_get(ucv_array_get(registry, 0))); |  | ||||||
| 	uc_vm_stack_push(debug_vm, ucv_int64_new(level)); |  | ||||||
| 	uc_vm_stack_push(debug_vm, val); |  | ||||||
| 	if (uc_vm_call(debug_vm, false, 2) == EXCEPTION_NONE) |  | ||||||
| 		ucv_put(uc_vm_stack_pop(debug_vm)); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uc_value_t * |  | ||||||
| uc_bpf_set_debug_handler(uc_vm_t *vm, size_t nargs) |  | ||||||
| { |  | ||||||
| 	uc_value_t *handler = uc_fn_arg(0); |  | ||||||
|  |  | ||||||
| 	if (handler && !ucv_is_callable(handler)) |  | ||||||
| 		err_return(EINVAL, NULL); |  | ||||||
|  |  | ||||||
| 	debug_vm = vm; |  | ||||||
| 	libbpf_set_print(handler ? uc_bpf_debug_print : NULL); |  | ||||||
|  |  | ||||||
| 	ucv_array_set(registry, 0, ucv_get(handler)); |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| register_constants(uc_vm_t *vm, uc_value_t *scope) |  | ||||||
| { |  | ||||||
| #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x)) |  | ||||||
| 	ADD_CONST(BPF_PROG_TYPE_SCHED_CLS); |  | ||||||
| 	ADD_CONST(BPF_PROG_TYPE_SCHED_ACT); |  | ||||||
|  |  | ||||||
| 	ADD_CONST(BPF_ANY); |  | ||||||
| 	ADD_CONST(BPF_NOEXIST); |  | ||||||
| 	ADD_CONST(BPF_EXIST); |  | ||||||
| 	ADD_CONST(BPF_F_LOCK); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const uc_function_list_t module_fns[] = { |  | ||||||
| 	{ "get_map",			uc_bpf_module_get_map }, |  | ||||||
| 	{ "get_maps",			uc_bpf_module_get_maps }, |  | ||||||
| 	{ "get_programs",		uc_bpf_module_get_programs }, |  | ||||||
| 	{ "get_program",		uc_bpf_module_get_program }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void module_free(void *ptr) |  | ||||||
| { |  | ||||||
| 	struct bpf_object *obj = ptr; |  | ||||||
|  |  | ||||||
| 	bpf_object__close(obj); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const uc_function_list_t map_fns[] = { |  | ||||||
| 	{ "pin",			uc_bpf_map_pin }, |  | ||||||
| 	{ "get",			uc_bpf_map_get }, |  | ||||||
| 	{ "set",			uc_bpf_map_set }, |  | ||||||
| 	{ "delete",			uc_bpf_map_delete }, |  | ||||||
| 	{ "delete_all",			uc_bpf_map_delete_all }, |  | ||||||
| 	{ "foreach",			uc_bpf_map_foreach }, |  | ||||||
| 	{ "iterator",			uc_bpf_map_iterator }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void uc_bpf_fd_free(void *ptr) |  | ||||||
| { |  | ||||||
| 	struct uc_bpf_fd *f = ptr; |  | ||||||
|  |  | ||||||
| 	if (f->close) |  | ||||||
| 		close(f->fd); |  | ||||||
| 	free(f); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const uc_function_list_t map_iter_fns[] = { |  | ||||||
| 	{ "next",			uc_bpf_map_iter_next }, |  | ||||||
| 	{ "next_int",			uc_bpf_map_iter_next_int }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const uc_function_list_t prog_fns[] = { |  | ||||||
| 	{ "pin",			uc_bpf_program_pin }, |  | ||||||
| 	{ "tc_attach",			uc_bpf_program_tc_attach }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const uc_function_list_t global_fns[] = { |  | ||||||
| 	{ "error",			uc_bpf_error }, |  | ||||||
| 	{ "set_debug_handler",		uc_bpf_set_debug_handler }, |  | ||||||
| 	{ "open_module",		uc_bpf_open_module }, |  | ||||||
| 	{ "open_map",			uc_bpf_open_map }, |  | ||||||
| 	{ "open_program",		uc_bpf_open_program }, |  | ||||||
| 	{ "tc_detach",			uc_bpf_tc_detach }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void uc_module_init(uc_vm_t *vm, uc_value_t *scope) |  | ||||||
| { |  | ||||||
| 	uc_function_list_register(scope, global_fns); |  | ||||||
| 	register_constants(vm, scope); |  | ||||||
|  |  | ||||||
| 	registry = ucv_array_new(vm); |  | ||||||
| 	uc_vm_registry_set(vm, "bpf.registry", registry); |  | ||||||
|  |  | ||||||
| 	module_type = uc_type_declare(vm, "bpf.module", module_fns, module_free); |  | ||||||
| 	map_type = uc_type_declare(vm, "bpf.map", map_fns, uc_bpf_fd_free); |  | ||||||
| 	map_iter_type = uc_type_declare(vm, "bpf.map_iter", map_iter_fns, free); |  | ||||||
| 	program_type = uc_type_declare(vm, "bpf.program", prog_fns, uc_bpf_fd_free); |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user
	 John Crispin
					John Crispin