ucode-mod-bpf: add new package for a ucode libbpf binding
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. Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		
							
								
								
									
										40
									
								
								package/utils/ucode-mod-bpf/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								package/utils/ucode-mod-bpf/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					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))
 | 
				
			||||||
							
								
								
									
										814
									
								
								package/utils/ucode-mod-bpf/src/bpf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										814
									
								
								package/utils/ucode-mod-bpf/src/bpf.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,814 @@
 | 
				
			|||||||
 | 
					#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