From 7d822ca8afc52f9dd9e32b83b275bf9f1abaa838 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 14 May 2026 17:13:06 +0000 Subject: [PATCH] bpf-util: rename from bpf-dlopen, unify version-specific symbol handling MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Renames src/shared/bpf-dlopen.{c,h} to src/shared/bpf-util.{c,h} and folds the former src/shared/bpf-compat.h (struct forward decl and compat_bpf_map_create() helper) into the new header. Aligns dlopen_bpf() with the standard wrapper pattern: drops the manual dlopen_safe()/dlsym_many_or_warn()/TAKE_PTR(dl) plumbing and the bespoke 'cached' int in favor of dlopen_many_sym_or_warn() inside a FOREACH_STRING() soname-fallback loop. Unifies declaration of the version-specific symbols (bpf_create_map, bpf_map_create, bpf_object__next_map, bpf_token_create) into a single DISABLE_WARNING_REDUNDANT_DECLS block in the header, and alphabetically merges the DLSYM_PROTOTYPE list. DLSYM_OPTIONAL is used to load each one — call sites already handle NULL (compat_bpf_map_create() and the sym_bpf_object__next_map guard in userns-restrict.c). bpf_token_create additionally defaults to a missing_bpf_token_create() stub returning -ENOSYS, so callers can branch on the errno instead of NULL-checking the pointer. Updates test-bpf-token to match: drops the compile-time LIBBPF_MAJOR_VERSION ≥ 1.5 gate and the direct include in favor of dlopen_bpf() + sym_bpf_token_create(), and treats -ENOSYS as the test-skip path (covering both 'libbpf too old' and 'kernel lacks BPF_TOKEN_CREATE support'). --- src/bpf/bpf-skel-wrapper.h.in | 2 +- src/bpf/meson.build | 2 +- src/core/bpf-bind-iface.c | 2 +- src/core/bpf-restrict-fs.c | 2 +- src/core/bpf-restrict-fsaccess.c | 2 +- src/core/bpf-restrict-ifaces.c | 2 +- src/core/bpf-socket-bind.c | 2 +- src/core/exec-invoke.c | 2 +- src/network/networkd-sysctl.c | 2 +- src/nsresourced/nsresourced-manager.c | 2 +- src/nsresourced/userns-restrict.c | 2 +- src/shared/bpf-compat.h | 49 ------ src/shared/bpf-dlopen.c | 221 ------------------------ src/shared/bpf-link.c | 2 +- src/shared/bpf-util.c | 178 +++++++++++++++++++ src/shared/{bpf-dlopen.h => bpf-util.h} | 39 ++++- src/shared/meson.build | 2 +- src/test/meson.build | 2 +- src/test/test-bpf-restrict-fsaccess.c | 2 +- src/test/test-bpf-token.c | 22 ++- src/test/test-dlopen-so.c | 2 +- 21 files changed, 247 insertions(+), 294 deletions(-) delete mode 100644 src/shared/bpf-compat.h delete mode 100644 src/shared/bpf-dlopen.c create mode 100644 src/shared/bpf-util.c rename src/shared/{bpf-dlopen.h => bpf-util.h} (52%) diff --git a/src/bpf/bpf-skel-wrapper.h.in b/src/bpf/bpf-skel-wrapper.h.in index 3dc3cede3e2..d9450a12b0a 100644 --- a/src/bpf/bpf-skel-wrapper.h.in +++ b/src/bpf/bpf-skel-wrapper.h.in @@ -7,7 +7,7 @@ * fine given that LGPL-2.1-or-later downgrades to GPL if needed. */ -#include "bpf-dlopen.h" /* IWYU pragma: keep */ +#include "bpf-util.h" /* IWYU pragma: keep */ /* libbpf is used via dlopen(), so rename symbols */ #define bpf_object__attach_skeleton sym_bpf_object__attach_skeleton diff --git a/src/bpf/meson.build b/src/bpf/meson.build index 13af4c968e8..ac05eb4b184 100644 --- a/src/bpf/meson.build +++ b/src/bpf/meson.build @@ -394,7 +394,7 @@ foreach program : bpf_programs # Keeping it out of bpf_programs_by_name also keeps it out of the # clang-tidy per-source test loop, which would otherwise fall back # to a BPF compile_commands.json entry (no -Isrc/shared) and fail - # to resolve bpf-dlopen.h. + # to resolve bpf-util.h. configure_file( input : 'bpf-skel-wrapper.h.in', output : name + '-skel.h', diff --git a/src/core/bpf-bind-iface.c b/src/core/bpf-bind-iface.c index 74cb2b3d523..1ba643a1166 100644 --- a/src/core/bpf-bind-iface.c +++ b/src/core/bpf-bind-iface.c @@ -12,7 +12,7 @@ #if BPF_FRAMEWORK /* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */ -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "bind-iface-skel.h" diff --git a/src/core/bpf-restrict-fs.c b/src/core/bpf-restrict-fs.c index d3c6adc1452..d28abfe4dd1 100644 --- a/src/core/bpf-restrict-fs.c +++ b/src/core/bpf-restrict-fs.c @@ -15,7 +15,7 @@ #if BPF_FRAMEWORK /* libbpf, clang and llc compile time dependencies are satisfied */ -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "restrict-fs-skel.h" diff --git a/src/core/bpf-restrict-fsaccess.c b/src/core/bpf-restrict-fsaccess.c index be66f7da01c..e043bf9fd66 100644 --- a/src/core/bpf-restrict-fsaccess.c +++ b/src/core/bpf-restrict-fsaccess.c @@ -37,7 +37,7 @@ const char* const restrict_fsaccess_link_names[_RESTRICT_FILESYSTEM_ACCESS_LINK_ }; #if BPF_FRAMEWORK && HAVE_LSM_INTEGRITY_TYPE -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "restrict-fsaccess-skel.h" diff --git a/src/core/bpf-restrict-ifaces.c b/src/core/bpf-restrict-ifaces.c index 3c73a682a3d..e30d15cd256 100644 --- a/src/core/bpf-restrict-ifaces.c +++ b/src/core/bpf-restrict-ifaces.c @@ -14,7 +14,7 @@ #if BPF_FRAMEWORK /* libbpf, clang and llc compile time dependencies are satisfied */ -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "restrict-ifaces-skel.h" diff --git a/src/core/bpf-socket-bind.c b/src/core/bpf-socket-bind.c index 87e7d60c055..b4bf37b41e0 100644 --- a/src/core/bpf-socket-bind.c +++ b/src/core/bpf-socket-bind.c @@ -9,7 +9,7 @@ #if BPF_FRAMEWORK /* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */ -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "socket-bind-api.bpf.h" #include "socket-bind-skel.h" diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 260e7a36afb..b82f7b995b6 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -21,7 +21,7 @@ #include "ask-password-api.h" #include "barrier.h" #include "bitfield.h" -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-restrict-fs.h" #include "btrfs-util.h" #include "capability-util.h" diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 0801ba977a6..ba90d342c5a 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -5,7 +5,7 @@ #include "sd-messages.h" #include "af-list.h" -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "conf-parser.h" #include "alloc-util.h" #include "cgroup-util.h" diff --git a/src/nsresourced/nsresourced-manager.c b/src/nsresourced/nsresourced-manager.c index 406db72e7d7..c2a6cd6eab0 100644 --- a/src/nsresourced/nsresourced-manager.c +++ b/src/nsresourced/nsresourced-manager.c @@ -5,7 +5,7 @@ #include "sd-daemon.h" -#include "bpf-dlopen.h" +#include "bpf-util.h" #if HAVE_VMLINUX_H #include "bpf-link.h" #include "userns-restrict-skel.h" diff --git a/src/nsresourced/userns-restrict.c b/src/nsresourced/userns-restrict.c index 11bb2e7d8fd..1acc42cd02d 100644 --- a/src/nsresourced/userns-restrict.c +++ b/src/nsresourced/userns-restrict.c @@ -6,7 +6,7 @@ #include "userns-restrict-skel.h" #endif -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "fd-util.h" #include "log.h" diff --git a/src/shared/bpf-compat.h b/src/shared/bpf-compat.h deleted file mode 100644 index 7f76ee87a07..00000000000 --- a/src/shared/bpf-compat.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include - -/* libbpf has been moving quickly. - * They added new symbols in the 0.x versions and shortly after removed - * deprecated symbols in 1.0. - * We only need bpf_map_create and libbpf_probe_bpf_prog_type so we work - * around the incompatibility here by: - * - declaring both symbols, and looking for either depending on the libbpf - * so version we found - * - having helpers that automatically use the appropriate version behind the - * new API for easy cleanup later - * - * The advantage of doing this instead of only looking for the symbols declared at - * compile time is that we can then load either the old or the new symbols at runtime - * regardless of the version we were compiled with */ - -/* declare the struct for libbpf <= 0.6.0 -- it causes no harm on newer versions */ -struct bpf_map_create_opts; - -/* new symbols available from 0.7.0. - * We need the symbols here: - * - after bpf_map_create_opts struct has been defined for older libbpf - * - before the compat static inline helpers that use them. - * When removing this file move these back to bpf-dlopen.h */ -extern int (*sym_bpf_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *); -extern struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map); - -/* compat symbols removed in libbpf 1.0 */ -extern int (*sym_bpf_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags); - -/* helpers to use the available variant behind new API */ -static inline int compat_bpf_map_create( - enum bpf_map_type map_type, - const char *map_name, - __u32 key_size, - __u32 value_size, - __u32 max_entries, - const struct bpf_map_create_opts *opts) { - - if (sym_bpf_map_create) - return sym_bpf_map_create(map_type, map_name, key_size, - value_size, max_entries, opts); - - return sym_bpf_create_map(map_type, key_size, value_size, max_entries, - 0 /* opts->map_flags, but opts is always NULL for us so skip build dependency on the type */); -} diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c deleted file mode 100644 index ca746828233..00000000000 --- a/src/shared/bpf-dlopen.c +++ /dev/null @@ -1,221 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "sd-dlopen.h" - -#include "bpf-dlopen.h" -#include "dlfcn-util.h" -#include "errno-util.h" -#include "initrd-util.h" -#include "log.h" - -#if HAVE_LIBBPF - -/* libbpf changed types of function prototypes around, so we need to disable some type checking for older - * libbpf. We consider everything older than 0.7 too old for accurate type checks. */ -#if defined(__LIBBPF_CURRENT_VERSION_GEQ) -#if __LIBBPF_CURRENT_VERSION_GEQ(0, 7) -#define MODERN_LIBBPF 1 -#endif -#endif -#if !defined(MODERN_LIBBPF) -#define MODERN_LIBBPF 0 -#endif - -static void *bpf_dl = NULL; - -static DLSYM_PROTOTYPE(libbpf_get_error) = NULL; - -DLSYM_PROTOTYPE(bpf_link__destroy) = NULL; -DLSYM_PROTOTYPE(bpf_link__fd) = NULL; -DLSYM_PROTOTYPE(bpf_link__open) = NULL; -DLSYM_PROTOTYPE(bpf_link__pin) = NULL; -DLSYM_PROTOTYPE(bpf_map__fd) = NULL; -DLSYM_PROTOTYPE(bpf_map__name) = NULL; -DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd) = NULL; -DLSYM_PROTOTYPE(bpf_map__set_max_entries) = NULL; -DLSYM_PROTOTYPE(bpf_map__set_pin_path) = NULL; -DLSYM_PROTOTYPE(bpf_map_delete_elem) = NULL; -DLSYM_PROTOTYPE(bpf_map_get_fd_by_id) = NULL; -DLSYM_PROTOTYPE(bpf_map_lookup_elem) = NULL; -DLSYM_PROTOTYPE(bpf_map_update_elem) = NULL; -DLSYM_PROTOTYPE(bpf_obj_get_info_by_fd) = NULL; -DLSYM_PROTOTYPE(bpf_object__attach_skeleton) = NULL; -DLSYM_PROTOTYPE(bpf_object__destroy_skeleton) = NULL; -DLSYM_PROTOTYPE(bpf_object__detach_skeleton) = NULL; -DLSYM_PROTOTYPE(bpf_object__load_skeleton) = NULL; -DLSYM_PROTOTYPE(bpf_object__name) = NULL; -DLSYM_PROTOTYPE(bpf_object__open_skeleton) = NULL; -DLSYM_PROTOTYPE(bpf_object__pin_maps) = NULL; -DLSYM_PROTOTYPE(bpf_program__attach) = NULL; -DLSYM_PROTOTYPE(bpf_program__attach_cgroup) = NULL; -DLSYM_PROTOTYPE(bpf_program__attach_lsm) = NULL; -DLSYM_PROTOTYPE(bpf_program__name) = NULL; -DLSYM_PROTOTYPE(bpf_program__set_autoload) = NULL; -DLSYM_PROTOTYPE(libbpf_set_print) = NULL; -DLSYM_PROTOTYPE(ring_buffer__epoll_fd) = NULL; -DLSYM_PROTOTYPE(ring_buffer__free) = NULL; -DLSYM_PROTOTYPE(ring_buffer__new) = NULL; -DLSYM_PROTOTYPE(ring_buffer__poll) = NULL; - -/* new symbols available from libbpf 0.7.0 */ -int (*sym_bpf_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *); -struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map); - -/* compat symbols removed in libbpf 1.0 */ -int (*sym_bpf_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags); - -_printf_(2,0) -static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_list ap) { -#if !LOG_TRACE - /* libbpf logs a lot of details at its debug level, which we don't need to see. */ - if (level == LIBBPF_DEBUG) - return 0; -#endif - /* All other levels are downgraded to LOG_DEBUG */ - - /* errno is used here, on the assumption that if the log message uses %m, errno will be set to - * something useful. Otherwise, it shouldn't matter, we may pass 0 or some bogus value. */ - return log_internalv(LOG_DEBUG, errno, NULL, 0, NULL, fmt, ap); -} - -int dlopen_bpf(int log_level) { - static int cached = 0; - int r; - - if (cached != 0) - return cached; - - SD_ELF_NOTE_DLOPEN( - "bpf", - "Support firewalling and sandboxing with BPF", - SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, - "libbpf.so.1", "libbpf.so.0"); - - DISABLE_WARNING_DEPRECATED_DECLARATIONS; - - _cleanup_(dlclosep) void *dl = NULL; - r = dlopen_safe("libbpf.so.1", &dl, /* reterr_dlerror= */ NULL); - if (r < 0) { - /* libbpf < 1.0.0 (we rely on 0.1.0+) provide most symbols we care about, but - * unfortunately not all until 0.7.0. See bpf-compat.h for more details. - * Once we consider we can assume 0.7+ is present we can just use the same symbol - * list for both files, and when we assume 1.0+ is present we can remove this dlopen */ - const char *dle = NULL; - r = dlopen_safe("libbpf.so.0", &dl, &dle); - if (r < 0) { - log_full_errno(in_initrd() ? LOG_DEBUG : log_level, r, - "Neither libbpf.so.1 nor libbpf.so.0 are installed, cgroup BPF features disabled: %s", dle ?: STRERROR(r)); - return (cached = -EOPNOTSUPP); /* turn into recognizable error */ - } - - log_debug("Loaded 'libbpf.so.0' via dlopen()"); - - /* symbols deprecated in 1.0 we use as compat */ - r = dlsym_many_or_warn( - dl, LOG_DEBUG, -#if MODERN_LIBBPF - /* Don't exist anymore in new libbpf, hence cannot type check them */ - DLSYM_ARG_FORCE(bpf_create_map) -#else - DLSYM_ARG(bpf_create_map) -#endif - ); - - /* NB: we don't try to load bpf_object__next_map() on old versions */ - } else { - log_debug("Loaded 'libbpf.so.1' via dlopen()"); - - /* symbols available from 0.7.0 */ - r = dlsym_many_or_warn( - dl, LOG_DEBUG, -#if MODERN_LIBBPF - DLSYM_ARG(bpf_map_create), - DLSYM_ARG(bpf_object__next_map) -#else - /* These symbols did not exist in old libbpf, hence we cannot type check them */ - DLSYM_ARG_FORCE(bpf_map_create), - DLSYM_ARG_FORCE(bpf_object__next_map) -#endif - ); - } - if (r < 0) - return cached = log_full_errno(log_level, r, "Failed to load libbpf symbols, cgroup BPF features disabled: %m"); - - r = dlsym_many_or_warn( - dl, LOG_DEBUG, - DLSYM_ARG(bpf_link__destroy), - DLSYM_ARG(bpf_link__fd), - DLSYM_ARG(bpf_link__open), - DLSYM_ARG(bpf_link__pin), - DLSYM_ARG(bpf_map__fd), - DLSYM_ARG(bpf_map__name), - DLSYM_ARG(bpf_map__set_inner_map_fd), - DLSYM_ARG(bpf_map__set_max_entries), - DLSYM_ARG(bpf_map__set_pin_path), - DLSYM_ARG(bpf_map_delete_elem), - DLSYM_ARG(bpf_map_get_fd_by_id), - DLSYM_ARG(bpf_map_lookup_elem), - DLSYM_ARG(bpf_map_update_elem), - DLSYM_ARG(bpf_obj_get_info_by_fd), - DLSYM_ARG(bpf_object__attach_skeleton), - DLSYM_ARG(bpf_object__destroy_skeleton), - DLSYM_ARG(bpf_object__detach_skeleton), - DLSYM_ARG(bpf_object__load_skeleton), - DLSYM_ARG(bpf_object__name), - DLSYM_ARG(bpf_object__open_skeleton), - DLSYM_ARG(bpf_object__pin_maps), -#if MODERN_LIBBPF - DLSYM_ARG(bpf_program__attach), - DLSYM_ARG(bpf_program__attach_cgroup), - DLSYM_ARG(bpf_program__attach_lsm), -#else - /* libbpf added a "const" to function parameters where it should not have, ignore this type incompatibility */ - DLSYM_ARG_FORCE(bpf_program__attach), - DLSYM_ARG_FORCE(bpf_program__attach_cgroup), - DLSYM_ARG_FORCE(bpf_program__attach_lsm), -#endif - DLSYM_ARG(bpf_program__name), - DLSYM_ARG(bpf_program__set_autoload), - DLSYM_ARG(libbpf_get_error), - DLSYM_ARG(libbpf_set_print), - DLSYM_ARG(ring_buffer__epoll_fd), - DLSYM_ARG(ring_buffer__free), - DLSYM_ARG(ring_buffer__new), - DLSYM_ARG(ring_buffer__poll)); - if (r < 0) - return cached = log_full_errno(log_level, r, "Failed to load libbpf symbols, cgroup BPF features disabled: %m"); - - /* We set the print helper unconditionally. Otherwise libbpf will emit not useful log messages. */ - (void) sym_libbpf_set_print(bpf_print_func); - - REENABLE_WARNING; - - bpf_dl = TAKE_PTR(dl); - - return cached = true; -} - -int bpf_get_error_translated(const void *ptr) { - int r; - - r = sym_libbpf_get_error(ptr); - - switch (r) { - case -524: - /* Workaround for kernel bug, BPF returns an internal error instead of translating it, until - * it is fixed: - * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/errno.h?h=v6.9&id=a38297e3fb012ddfa7ce0321a7e5a8daeb1872b6#n27 - */ - return -EOPNOTSUPP; - default: - return r; - } -} - -#else - -int dlopen_bpf(int log_level) { - return log_once_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), - "libbpf support is not compiled in, cgroup BPF features disabled."); -} -#endif diff --git a/src/shared/bpf-link.c b/src/shared/bpf-link.c index 80a6db47ded..4ae402625b8 100644 --- a/src/shared/bpf-link.c +++ b/src/shared/bpf-link.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "bpf-link.h" #include "serialize.h" diff --git a/src/shared/bpf-util.c b/src/shared/bpf-util.c new file mode 100644 index 00000000000..03f3281b075 --- /dev/null +++ b/src/shared/bpf-util.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-dlopen.h" + +#include "bpf-util.h" +#include "dlfcn-util.h" +#include "initrd-util.h" +#include "log.h" +#include "strv.h" + +#if HAVE_LIBBPF + +/* libbpf changed types of function prototypes around, so we need to disable some type checking for older + * libbpf. We consider everything older than 0.7 too old for accurate type checks. */ +#if defined(__LIBBPF_CURRENT_VERSION_GEQ) +#if __LIBBPF_CURRENT_VERSION_GEQ(0, 7) +#define MODERN_LIBBPF 1 +#endif +#endif +#if !defined(MODERN_LIBBPF) +#define MODERN_LIBBPF 0 +#endif + +static DLSYM_PROTOTYPE(libbpf_get_error) = NULL; + +DLSYM_PROTOTYPE(bpf_create_map) = NULL; +DLSYM_PROTOTYPE(bpf_link__destroy) = NULL; +DLSYM_PROTOTYPE(bpf_link__fd) = NULL; +DLSYM_PROTOTYPE(bpf_link__open) = NULL; +DLSYM_PROTOTYPE(bpf_link__pin) = NULL; +DLSYM_PROTOTYPE(bpf_map__fd) = NULL; +DLSYM_PROTOTYPE(bpf_map__name) = NULL; +DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd) = NULL; +DLSYM_PROTOTYPE(bpf_map__set_max_entries) = NULL; +DLSYM_PROTOTYPE(bpf_map__set_pin_path) = NULL; +DLSYM_PROTOTYPE(bpf_map_create) = NULL; +DLSYM_PROTOTYPE(bpf_map_delete_elem) = NULL; +DLSYM_PROTOTYPE(bpf_map_get_fd_by_id) = NULL; +DLSYM_PROTOTYPE(bpf_map_lookup_elem) = NULL; +DLSYM_PROTOTYPE(bpf_map_update_elem) = NULL; +DLSYM_PROTOTYPE(bpf_obj_get_info_by_fd) = NULL; +DLSYM_PROTOTYPE(bpf_object__attach_skeleton) = NULL; +DLSYM_PROTOTYPE(bpf_object__destroy_skeleton) = NULL; +DLSYM_PROTOTYPE(bpf_object__detach_skeleton) = NULL; +DLSYM_PROTOTYPE(bpf_object__load_skeleton) = NULL; +DLSYM_PROTOTYPE(bpf_object__name) = NULL; +DLSYM_PROTOTYPE(bpf_object__next_map) = NULL; +DLSYM_PROTOTYPE(bpf_object__open_skeleton) = NULL; +DLSYM_PROTOTYPE(bpf_object__pin_maps) = NULL; +DLSYM_PROTOTYPE(bpf_program__attach) = NULL; +DLSYM_PROTOTYPE(bpf_program__attach_cgroup) = NULL; +DLSYM_PROTOTYPE(bpf_program__attach_lsm) = NULL; +DLSYM_PROTOTYPE(bpf_program__name) = NULL; +DLSYM_PROTOTYPE(bpf_program__set_autoload) = NULL; +static int missing_bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts) { + return -ENOSYS; +} +DLSYM_PROTOTYPE(bpf_token_create) = missing_bpf_token_create; +DLSYM_PROTOTYPE(libbpf_set_print) = NULL; +DLSYM_PROTOTYPE(ring_buffer__epoll_fd) = NULL; +DLSYM_PROTOTYPE(ring_buffer__free) = NULL; +DLSYM_PROTOTYPE(ring_buffer__new) = NULL; +DLSYM_PROTOTYPE(ring_buffer__poll) = NULL; + +_printf_(2,0) +static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_list ap) { +#if !LOG_TRACE + /* libbpf logs a lot of details at its debug level, which we don't need to see. */ + if (level == LIBBPF_DEBUG) + return 0; +#endif + /* All other levels are downgraded to LOG_DEBUG */ + + /* errno is used here, on the assumption that if the log message uses %m, errno will be set to + * something useful. Otherwise, it shouldn't matter, we may pass 0 or some bogus value. */ + return log_internalv(LOG_DEBUG, errno, NULL, 0, NULL, fmt, ap); +} + +int dlopen_bpf(int log_level) { + static void *bpf_dl = NULL; + int r; + + SD_ELF_NOTE_DLOPEN( + "bpf", + "Support firewalling and sandboxing with BPF", + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libbpf.so.1", "libbpf.so.0"); + + DISABLE_WARNING_DEPRECATED_DECLARATIONS; + + FOREACH_STRING(soname, "libbpf.so.1", "libbpf.so.0") { + r = dlopen_many_sym_or_warn( + &bpf_dl, soname, LOG_DEBUG, + DLSYM_ARG(bpf_link__destroy), + DLSYM_ARG(bpf_link__fd), + DLSYM_ARG(bpf_link__open), + DLSYM_ARG(bpf_link__pin), + DLSYM_ARG(bpf_map__fd), + DLSYM_ARG(bpf_map__name), + DLSYM_ARG(bpf_map__set_inner_map_fd), + DLSYM_ARG(bpf_map__set_max_entries), + DLSYM_ARG(bpf_map__set_pin_path), + DLSYM_ARG(bpf_map_delete_elem), + DLSYM_ARG(bpf_map_get_fd_by_id), + DLSYM_ARG(bpf_map_lookup_elem), + DLSYM_ARG(bpf_map_update_elem), + DLSYM_ARG(bpf_obj_get_info_by_fd), + DLSYM_ARG(bpf_object__attach_skeleton), + DLSYM_ARG(bpf_object__destroy_skeleton), + DLSYM_ARG(bpf_object__detach_skeleton), + DLSYM_ARG(bpf_object__load_skeleton), + DLSYM_ARG(bpf_object__name), + DLSYM_ARG(bpf_object__open_skeleton), + DLSYM_ARG(bpf_object__pin_maps), +#if MODERN_LIBBPF + DLSYM_ARG(bpf_program__attach), + DLSYM_ARG(bpf_program__attach_cgroup), + DLSYM_ARG(bpf_program__attach_lsm), +#else + /* libbpf added a "const" to function parameters where it should not have, ignore this type incompatibility */ + DLSYM_ARG_FORCE(bpf_program__attach), + DLSYM_ARG_FORCE(bpf_program__attach_cgroup), + DLSYM_ARG_FORCE(bpf_program__attach_lsm), +#endif + DLSYM_ARG(bpf_program__name), + DLSYM_ARG(bpf_program__set_autoload), + DLSYM_ARG(libbpf_get_error), + DLSYM_ARG(libbpf_set_print), + DLSYM_ARG(ring_buffer__epoll_fd), + DLSYM_ARG(ring_buffer__free), + DLSYM_ARG(ring_buffer__new), + DLSYM_ARG(ring_buffer__poll)); + if (r >= 0) + break; + } + REENABLE_WARNING; + if (r < 0) + return log_full_errno(in_initrd() ? LOG_DEBUG : log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Neither libbpf.so.1 nor libbpf.so.0 are installed, cgroup BPF features disabled."); + + /* Version-specific symbols: bpf_create_map exists only in libbpf < 1.0; bpf_map_create and + * bpf_object__next_map only in 0.7+. bpf_token_create only in 1.5+. Unresolved prototypes keep + * their initializers (NULL, or a fallback returning -ENOSYS for bpf_token_create). */ + DLSYM_OPTIONAL(bpf_dl, bpf_create_map); + DLSYM_OPTIONAL(bpf_dl, bpf_map_create); + DLSYM_OPTIONAL(bpf_dl, bpf_object__next_map); + DLSYM_OPTIONAL(bpf_dl, bpf_token_create); + + /* We set the print helper unconditionally. Otherwise libbpf will emit not useful log messages. */ + (void) sym_libbpf_set_print(bpf_print_func); + + return 1; +} + +int bpf_get_error_translated(const void *ptr) { + int r; + + r = sym_libbpf_get_error(ptr); + + switch (r) { + case -524: + /* Workaround for kernel bug, BPF returns an internal error instead of translating it, until + * it is fixed: + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/errno.h?h=v6.9&id=a38297e3fb012ddfa7ce0321a7e5a8daeb1872b6#n27 + */ + return -EOPNOTSUPP; + default: + return r; + } +} + +#else + +int dlopen_bpf(int log_level) { + return log_once_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), + "libbpf support is not compiled in, cgroup BPF features disabled."); +} +#endif diff --git a/src/shared/bpf-dlopen.h b/src/shared/bpf-util.h similarity index 52% rename from src/shared/bpf-dlopen.h rename to src/shared/bpf-util.h index 62ea9cf707b..622e4c2e283 100644 --- a/src/shared/bpf-dlopen.h +++ b/src/shared/bpf-util.h @@ -8,10 +8,27 @@ #include /* IWYU pragma: export */ #include /* IWYU pragma: export */ -#include "bpf-compat.h" /* IWYU pragma: export */ #include "dlfcn-util.h" #include "shared-forward.h" +/* Always redeclare these so DLSYM_PROTOTYPE's typeof() resolves regardless of libbpf version; + * suppress the warning when the libbpf headers already declare them. + * - bpf_map_create, bpf_object__next_map: available since libbpf 0.7 + * - bpf_token_create: available since libbpf 1.5 + * - bpf_create_map: removed in libbpf 1.0 + */ +DISABLE_WARNING_REDUNDANT_DECLS; +/* NOLINTBEGIN(readability-redundant-declaration) */ +struct bpf_map_create_opts; +struct bpf_token_create_opts; +extern int bpf_create_map(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags); +extern int bpf_map_create(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *); +extern struct bpf_map* bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *map); +extern int bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts); +/* NOLINTEND(readability-redundant-declaration) */ +REENABLE_WARNING; + +extern DLSYM_PROTOTYPE(bpf_create_map); extern DLSYM_PROTOTYPE(bpf_link__destroy); extern DLSYM_PROTOTYPE(bpf_link__fd); extern DLSYM_PROTOTYPE(bpf_link__open); @@ -21,6 +38,7 @@ extern DLSYM_PROTOTYPE(bpf_map__name); extern DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd); extern DLSYM_PROTOTYPE(bpf_map__set_max_entries); extern DLSYM_PROTOTYPE(bpf_map__set_pin_path); +extern DLSYM_PROTOTYPE(bpf_map_create); extern DLSYM_PROTOTYPE(bpf_map_delete_elem); extern DLSYM_PROTOTYPE(bpf_map_get_fd_by_id); extern DLSYM_PROTOTYPE(bpf_map_lookup_elem); @@ -33,6 +51,7 @@ extern DLSYM_PROTOTYPE(bpf_object__destroy_skeleton); extern DLSYM_PROTOTYPE(bpf_object__detach_skeleton); extern DLSYM_PROTOTYPE(bpf_object__load_skeleton); extern DLSYM_PROTOTYPE(bpf_object__name); +extern DLSYM_PROTOTYPE(bpf_object__next_map); extern DLSYM_PROTOTYPE(bpf_object__open_skeleton); extern DLSYM_PROTOTYPE(bpf_object__pin_maps); extern DLSYM_PROTOTYPE(bpf_program__attach); @@ -40,12 +59,30 @@ extern DLSYM_PROTOTYPE(bpf_program__attach_cgroup); extern DLSYM_PROTOTYPE(bpf_program__attach_lsm); extern DLSYM_PROTOTYPE(bpf_program__name); extern DLSYM_PROTOTYPE(bpf_program__set_autoload); +extern DLSYM_PROTOTYPE(bpf_token_create); extern DLSYM_PROTOTYPE(libbpf_set_print); extern DLSYM_PROTOTYPE(ring_buffer__epoll_fd); extern DLSYM_PROTOTYPE(ring_buffer__free); extern DLSYM_PROTOTYPE(ring_buffer__new); extern DLSYM_PROTOTYPE(ring_buffer__poll); +/* Helper that uses the available variant behind the new API. */ +static inline int compat_bpf_map_create( + enum bpf_map_type map_type, + const char *map_name, + __u32 key_size, + __u32 value_size, + __u32 max_entries, + const struct bpf_map_create_opts *opts) { + + if (sym_bpf_map_create) + return sym_bpf_map_create(map_type, map_name, key_size, + value_size, max_entries, opts); + + return sym_bpf_create_map(map_type, key_size, value_size, max_entries, + 0 /* opts->map_flags, but opts is always NULL for us so skip build dependency on the type */); +} + /* libbpf sometimes returns error codes that make sense only in the kernel, like 524 for EOPNOTSUPP. Use * this helper instead of libbpf_get_error() to ensure some of the known ones are translated into errnos * we understand. */ diff --git a/src/shared/meson.build b/src/shared/meson.build index 9b539f79839..ce96ce3025c 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -19,8 +19,8 @@ shared_sources = files( 'boot-entry.c', 'boot-timestamps.c', 'bootspec.c', - 'bpf-dlopen.c', 'bpf-program.c', + 'bpf-util.c', 'bridge-util.c', 'btrfs-util.c', 'bus-get-properties.c', diff --git a/src/test/meson.build b/src/test/meson.build index 4871c5517f3..c8279bc7514 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -554,7 +554,7 @@ executables += [ }, core_test_template + { 'sources' : files('test-bpf-token.c'), - 'dependencies' : common_test_dependencies + libbpf, + 'dependencies' : common_test_dependencies + libbpf_cflags, 'conditions' : ['BPF_FRAMEWORK'], 'type' : 'manual', }, diff --git a/src/test/test-bpf-restrict-fsaccess.c b/src/test/test-bpf-restrict-fsaccess.c index e95e706c276..0d4e18671aa 100644 --- a/src/test/test-bpf-restrict-fsaccess.c +++ b/src/test/test-bpf-restrict-fsaccess.c @@ -92,7 +92,7 @@ static int do_mprotect_exec(const char *path) { } #if BPF_FRAMEWORK && HAVE_LSM_INTEGRITY_TYPE -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "restrict-fsaccess-skel.h" static struct restrict_fsaccess_bpf *restrict_fsaccess_bpf_free(struct restrict_fsaccess_bpf *obj) { diff --git a/src/test/test-bpf-token.c b/src/test/test-bpf-token.c index 7e83a048e52..f991f926012 100644 --- a/src/test/test-bpf-token.c +++ b/src/test/test-bpf-token.c @@ -1,26 +1,34 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include +#include "bpf-util.h" #include "fd-util.h" #include "main-func.h" -#include "tests.h" /* NOLINT(misc-include-cleaner): this is needed conditionally */ +#include "tests.h" static int run(int argc, char *argv[]) { -#if defined(LIBBPF_MAJOR_VERSION) && (LIBBPF_MAJOR_VERSION > 1 || (LIBBPF_MAJOR_VERSION == 1 && LIBBPF_MINOR_VERSION >= 5)) - _cleanup_close_ int bpffs_fd = open("/sys/fs/bpf", O_RDONLY); +#if HAVE_LIBBPF + int r; + + r = dlopen_bpf(LOG_ERR); + if (r < 0) + return r; + + _cleanup_close_ int bpffs_fd = open("/sys/fs/bpf", O_CLOEXEC|O_RDONLY); if (bpffs_fd < 0) return log_error_errno(errno, "Failed to open '/sys/fs/bpf': %m"); - _cleanup_close_ int token_fd = bpf_token_create(bpffs_fd, /* opts= */ NULL); + _cleanup_close_ int token_fd = sym_bpf_token_create(bpffs_fd, /* opts= */ NULL); + if (token_fd == -ENOSYS) + return log_tests_skipped("bpf_token_create() unavailable (libbpf too old or kernel lacks BPF_TOKEN_CREATE support)"); if (token_fd < 0) - return log_error_errno(errno, "Failed to create bpf token: %m"); + return log_error_errno(token_fd, "Failed to create bpf token: %m"); log_info("Successfully created token fd."); return 0; #else - return log_tests_skipped("libbpf is older than v1.5"); + return log_tests_skipped("BPF framework support is disabled"); #endif } diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c index 7421a77024f..0eca1356c03 100644 --- a/src/test/test-dlopen-so.c +++ b/src/test/test-dlopen-so.c @@ -3,7 +3,7 @@ #include "acl-util.h" #include "apparmor-util.h" #include "blkid-util.h" -#include "bpf-dlopen.h" +#include "bpf-util.h" #include "compress.h" #include "crypto-util.h" #include "cryptsetup-util.h" -- 2.47.3