]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bpf-util: rename from bpf-dlopen, unify version-specific symbol handling
authorDaan De Meyer <daan@amutable.com>
Thu, 14 May 2026 17:13:06 +0000 (17:13 +0000)
committerDaan De Meyer <daan@amutable.com>
Mon, 18 May 2026 21:17:38 +0000 (21:17 +0000)
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 <bpf/bpf.h> 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').

21 files changed:
src/bpf/bpf-skel-wrapper.h.in
src/bpf/meson.build
src/core/bpf-bind-iface.c
src/core/bpf-restrict-fs.c
src/core/bpf-restrict-fsaccess.c
src/core/bpf-restrict-ifaces.c
src/core/bpf-socket-bind.c
src/core/exec-invoke.c
src/network/networkd-sysctl.c
src/nsresourced/nsresourced-manager.c
src/nsresourced/userns-restrict.c
src/shared/bpf-compat.h [deleted file]
src/shared/bpf-dlopen.c [deleted file]
src/shared/bpf-link.c
src/shared/bpf-util.c [new file with mode: 0644]
src/shared/bpf-util.h [moved from src/shared/bpf-dlopen.h with 52% similarity]
src/shared/meson.build
src/test/meson.build
src/test/test-bpf-restrict-fsaccess.c
src/test/test-bpf-token.c
src/test/test-dlopen-so.c

index 3dc3cede3e2e14710ecde7b91dd095d22ef26002..d9450a12b0adacd4f07ce4bd093d359c84f100c1 100644 (file)
@@ -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
index 13af4c968e825557e1321e6622a06151fce7de27..ac05eb4b1840fccfcfa685059f8a0cbaf95c8fc6 100644 (file)
@@ -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',
index 74cb2b3d523748e6a434c72716fed55777d94d2e..1ba643a1166b361a500e889ca56fdcec5980b18f 100644 (file)
@@ -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"
 
index d3c6adc14525165cdca74a57169f797396a833ec..d28abfe4dd1632ed178df18391b286db19c25fff 100644 (file)
@@ -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"
 
index be66f7da01c927f16629e5696612769868e9a7fc..e043bf9fd66efdb8503337319cbb40c25e278700 100644 (file)
@@ -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"
 
index 3c73a682a3d387bf731b4f73c8fb7643b987f5f2..e30d15cd256ef9fb456b4efac7f54a98d53a1f2e 100644 (file)
@@ -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"
 
index 87e7d60c055a9dc5911ff0c25f2e95d0670695bf..b4bf37b41e0f444593caf61ddf274feaefbeb848 100644 (file)
@@ -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"
index 260e7a36afb08c3c2ce0d98e0bf4530f3bee051c..b82f7b995b6380c973d80c0100b466c0517c5ffd 100644 (file)
@@ -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"
index 0801ba977a643bb283f39e520002ae9f0628f934..ba90d342c5a8b3bcb807490bff28991356a65a6f 100644 (file)
@@ -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"
index 406db72e7d72a30396c3b407064ecfd0c90acdd4..c2a6cd6eab03580b2f6c4be9a9cbd8741742ca3a 100644 (file)
@@ -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"
index 11bb2e7d8fd36e4d158c8d4316c4e4b5cafe5846..1acc42cd02d72f6cf2365d442025b578eaf38b35 100644 (file)
@@ -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 (file)
index 7f76ee8..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <bpf/libbpf.h>
-
-/* 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 (file)
index ca74682..0000000
+++ /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
index 80a6db47ded2393dccd384fc5a210ba3ef3fa4ed..4ae402625b8e82bfe32c6fd4583b086d54c41cdf 100644 (file)
@@ -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 (file)
index 0000000..03f3281
--- /dev/null
@@ -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
similarity index 52%
rename from src/shared/bpf-dlopen.h
rename to src/shared/bpf-util.h
index 62ea9cf707b8465a49ea8fc4368111a111c6ded7..622e4c2e2831f480cf71265c825f0da983704350 100644 (file)
@@ -8,10 +8,27 @@
 #include <bpf/bpf.h>    /* IWYU pragma: export */
 #include <bpf/libbpf.h> /* 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. */
index 9b539f79839d963846606caf47d602f80afcd039..ce96ce3025cb0105229e8a1551ca2565225566fa 100644 (file)
@@ -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',
index 4871c5517f376c896b1e57a2af5bbbd83a6f52bf..c8279bc7514f05e1bdc0085f345b5d839697d530 100644 (file)
@@ -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',
         },
index e95e706c276cc984598dc9d0bcd078afb34b3e47..0d4e18671aa3e1e24c20af124a4a384952fc26ec 100644 (file)
@@ -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) {
index 7e83a048e52f94790cc3697002c459491b19d918..f991f92601271a7b8e2c2bcf22d69e20a545296b 100644 (file)
@@ -1,26 +1,34 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <bpf/bpf.h>
 #include <fcntl.h>
 
+#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
 }
 
index 7421a77024f1b14545e06b68828ad29cc37a2be6..0eca1356c0313c31e4ccbf6e9e47927ddd655066 100644 (file)
@@ -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"