]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: make libbpf a dlopen() dependency 19705/head
authorLuca Boccassi <bluca@debian.org>
Sun, 23 May 2021 12:04:53 +0000 (13:04 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 25 May 2021 11:59:26 +0000 (12:59 +0100)
meson.build
src/core/meson.build
src/core/socket-bind.c
src/shared/bpf-dlopen.c [new file with mode: 0644]
src/shared/bpf-dlopen.h [new file with mode: 0644]
src/shared/bpf-link.c
src/shared/meson.build
src/test/meson.build
src/test/test-dlopen-so.c
test/test-functions
tools/build-bpf-skel.py

index 9d486b9e4e05ec507a69b5e2f441052c2ce5537e..3ddae84c1e498684ae337e21f80dec39b8f95b2f 100644 (file)
@@ -1675,7 +1675,7 @@ install_libsystemd_static = static_library(
                         libxz,
                         libzstd,
                         liblz4,
-                        libbpf,
+                        libdl,
                         libcap,
                         libblkid,
                         libmount,
index e93d17a43a0a16826b8cb5f782dbcedfc59b0b80..69013311eab9195d001980ddf51e2d6f110fd839 100644 (file)
@@ -159,7 +159,7 @@ libcore = static_library(
         include_directories : includes,
         dependencies : [versiondep,
                         threads,
-                        libbpf,
+                        libdl,
                         librt,
                         libseccomp,
                         libpam,
index ed6c36c11f7281a58a6a81ca9a1fac0ba198cc0c..b44e3ae8ab76450bf0aff072eeb47e4b62835cf7 100644 (file)
@@ -9,6 +9,7 @@
 
 #if BPF_FRAMEWORK
 /* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */
+#include "bpf-dlopen.h"
 #include "bpf-link.h"
 #include "bpf/socket_bind/socket-bind.skel.h"
 #include "bpf/socket_bind/socket-bind-api.bpf.h"
@@ -37,7 +38,7 @@ static int update_rules_map(
                         .port_min = item->port_min,
                 };
 
-                if (bpf_map_update_elem(map_fd, &key, &val, BPF_ANY) != 0)
+                if (sym_bpf_map_update_elem(map_fd, &key, &val, BPF_ANY) != 0)
                         return -errno;
         }
 
@@ -71,34 +72,34 @@ static int prepare_socket_bind_bpf(
         if (!obj)
                 return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOMEM), "Failed to open BPF object");
 
-        if (bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
+        if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
                 return log_unit_error_errno(u, errno,
-                                "Failed to resize BPF map '%s': %m", bpf_map__name(obj->maps.sd_bind_allow));
+                                "Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_allow));
 
-        if (bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
+        if (sym_bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
                 return log_unit_error_errno(u, errno,
-                                "Failed to resize BPF map '%s': %m", bpf_map__name(obj->maps.sd_bind_deny));
+                                "Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_deny));
 
         if (socket_bind_bpf__load(obj) != 0)
                 return log_unit_error_errno(u, errno, "Failed to load BPF object");
 
-        allow_map_fd = bpf_map__fd(obj->maps.sd_bind_allow);
+        allow_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_allow);
         assert(allow_map_fd >= 0);
 
         r = update_rules_map(allow_map_fd, allow);
         if (r < 0)
                 return log_unit_error_errno(
                                 u, r, "Failed to put socket bind allow rules into BPF map '%s'",
-                                bpf_map__name(obj->maps.sd_bind_allow));
+                                sym_bpf_map__name(obj->maps.sd_bind_allow));
 
-        deny_map_fd = bpf_map__fd(obj->maps.sd_bind_deny);
+        deny_map_fd = sym_bpf_map__fd(obj->maps.sd_bind_deny);
         assert(deny_map_fd >= 0);
 
         r = update_rules_map(deny_map_fd, deny);
         if (r < 0)
                 return log_unit_error_errno(
                                 u, r, "Failed to put socket bind deny rules into BPF map '%s'",
-                                bpf_map__name(obj->maps.sd_bind_deny));
+                                sym_bpf_map__name(obj->maps.sd_bind_deny));
 
         *ret_obj = TAKE_PTR(obj);
         return 0;
@@ -117,7 +118,13 @@ int socket_bind_supported(void) {
                 return 0;
         }
 
-        if (!bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) {
+        r = dlopen_bpf();
+        if (r < 0) {
+                log_info_errno(r, "Could not load libbpf: %m");
+                return 0;
+        }
+
+        if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) {
                 log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                 "BPF program type cgroup_sock_addr is not supported");
                 return 0;
@@ -178,17 +185,17 @@ static int socket_bind_install_impl(Unit *u) {
                 return log_unit_error_errno(
                                 u, errno, "Failed to open cgroup=%s for reading", cgroup_path);
 
-        ipv4 = bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
-        r = libbpf_get_error(ipv4);
+        ipv4 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
+        r = sym_libbpf_get_error(ipv4);
         if (r != 0)
                 return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program",
-                                bpf_program__name(obj->progs.sd_bind4));
+                                sym_bpf_program__name(obj->progs.sd_bind4));
 
-        ipv6 = bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
-        r = libbpf_get_error(ipv6);
+        ipv6 = sym_bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
+        r = sym_libbpf_get_error(ipv6);
         if (r != 0)
                 return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program",
-                                bpf_program__name(obj->progs.sd_bind6));
+                                sym_bpf_program__name(obj->progs.sd_bind6));
 
         u->ipv4_socket_bind_link = TAKE_PTR(ipv4);
         u->ipv6_socket_bind_link = TAKE_PTR(ipv6);
diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c
new file mode 100644 (file)
index 0000000..64120f1
--- /dev/null
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "dlfcn-util.h"
+#include "bpf-dlopen.h"
+
+#if HAVE_LIBBPF
+static void *bpf_dl = NULL;
+
+struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int);
+long (*sym_libbpf_get_error)(const void *);
+int (*sym_bpf_link__fd)(const struct bpf_link *);
+int (*sym_bpf_link__destroy)(struct bpf_link *);
+int (*sym_bpf_map__fd)(const struct bpf_map *);
+const char* (*sym_bpf_map__name)(const struct bpf_map *);
+int (*sym_bpf_map__resize)(struct bpf_map *, __u32);
+int (*sym_bpf_map_update_elem)(int, const void *, const void *, __u64);
+int (*sym_bpf_object__open_skeleton)(struct bpf_object_skeleton *, const struct bpf_object_open_opts *);
+int (*sym_bpf_object__load_skeleton)(struct bpf_object_skeleton *);
+int (*sym_bpf_object__attach_skeleton)(struct bpf_object_skeleton *);
+void (*sym_bpf_object__detach_skeleton)(struct bpf_object_skeleton *);
+void (*sym_bpf_object__destroy_skeleton)(struct bpf_object_skeleton *);
+bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32);
+const char* (*sym_bpf_program__name)(const struct bpf_program *);
+
+int dlopen_bpf(void) {
+        _cleanup_(dlclosep) void *dl = NULL;
+        int r;
+
+        if (bpf_dl)
+                return 0; /* Already loaded */
+
+        dl = dlopen("libbpf.so.0", RTLD_LAZY);
+        if (!dl)
+                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "libbpf is not installed: %s", dlerror());
+
+        r = dlsym_many_and_warn(
+                        dl,
+                        LOG_ERR,
+                        DLSYM_ARG(bpf_link__destroy),
+                        DLSYM_ARG(bpf_link__fd),
+                        DLSYM_ARG(bpf_map__fd),
+                        DLSYM_ARG(bpf_map__name),
+                        DLSYM_ARG(bpf_map__resize),
+                        DLSYM_ARG(bpf_map_update_elem),
+                        DLSYM_ARG(bpf_object__open_skeleton),
+                        DLSYM_ARG(bpf_object__load_skeleton),
+                        DLSYM_ARG(bpf_object__attach_skeleton),
+                        DLSYM_ARG(bpf_object__detach_skeleton),
+                        DLSYM_ARG(bpf_object__destroy_skeleton),
+                        DLSYM_ARG(bpf_probe_prog_type),
+                        DLSYM_ARG(bpf_program__attach_cgroup),
+                        DLSYM_ARG(bpf_program__name),
+                        DLSYM_ARG(libbpf_get_error),
+                        NULL);
+        if (r < 0)
+                return r;
+
+        /* Note that we never release the reference here, because there's no real reason to, after all this
+         * was traditionally a regular shared library dependency which lives forever too. */
+        bpf_dl = TAKE_PTR(dl);
+
+        return 1;
+}
+
+#else
+
+int dlopen_bpf(void) {
+        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                               "libbpf support is not compiled in.");
+}
+#endif
diff --git a/src/shared/bpf-dlopen.h b/src/shared/bpf-dlopen.h
new file mode 100644 (file)
index 0000000..fbb5531
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#if HAVE_LIBBPF
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+extern struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int);
+extern long (*sym_libbpf_get_error)(const void *);
+extern int (*sym_bpf_link__fd)(const struct bpf_link *);
+extern int (*sym_bpf_link__destroy)(struct bpf_link *);
+extern int (*sym_bpf_map__fd)(const struct bpf_map *);
+extern const char* (*sym_bpf_map__name)(const struct bpf_map *);
+extern int (*sym_bpf_map__resize)(struct bpf_map *, __u32);
+extern int (*sym_bpf_map_update_elem)(int, const void *, const void *, __u64);
+/* The *_skeleton APIs are autogenerated by bpftool, the targets can be found
+ * in ./build/src/core/bpf/socket_bind/socket-bind.skel.h */
+extern int (*sym_bpf_object__open_skeleton)(struct bpf_object_skeleton *, const struct bpf_object_open_opts *);
+extern int (*sym_bpf_object__load_skeleton)(struct bpf_object_skeleton *);
+extern int (*sym_bpf_object__attach_skeleton)(struct bpf_object_skeleton *);
+extern void (*sym_bpf_object__detach_skeleton)(struct bpf_object_skeleton *);
+extern void (*sym_bpf_object__destroy_skeleton)(struct bpf_object_skeleton *);
+extern bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32);
+extern const char* (*sym_bpf_program__name)(const struct bpf_program *);
+
+#endif
+
+int dlopen_bpf(void);
index f718cb7d2d39954f463dbd621a161e24da12d644..405874374c9ca2811de90cca4a3767ba0d8643c6 100644 (file)
@@ -1,18 +1,26 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include "bpf-dlopen.h"
 #include "bpf-link.h"
 #include "serialize.h"
 
 bool can_link_bpf_program(struct bpf_program *prog) {
         _cleanup_(bpf_link_freep) struct bpf_link *link = NULL;
+        int r;
 
         assert(prog);
 
+        r = dlopen_bpf();
+        if (r < 0) {
+                log_debug_errno(r, "Could not load libbpf: %m");
+                return false;
+        }
+
         /* Pass invalid cgroup fd intentionally. */
-        link = bpf_program__attach_cgroup(prog, /*cgroup_fd=*/-1);
+        link = sym_bpf_program__attach_cgroup(prog, /*cgroup_fd=*/-1);
 
         /* EBADF indicates that bpf_link is supported by kernel. */
-        return libbpf_get_error(link) == -EBADF;
+        return sym_libbpf_get_error(link) == -EBADF;
 }
 
 int serialize_bpf_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *link) {
@@ -23,16 +31,19 @@ int serialize_bpf_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *li
         if (!link)
                 return -ENOENT;
 
-        if (libbpf_get_error(link) != 0)
+        if (sym_libbpf_get_error(link) != 0)
                 return -EINVAL;
 
-        fd = bpf_link__fd(link);
+        fd = sym_bpf_link__fd(link);
         return serialize_fd(f, fds, key, fd);
 }
 
 struct bpf_link *bpf_link_free(struct bpf_link *link) {
-        /* bpf_link__destroy handles link == NULL case */
-        (void) bpf_link__destroy(link);
+        /* Avoid a useless dlopen() if link == NULL */
+        if (!link)
+                return NULL;
+
+        (void) sym_bpf_link__destroy(link);
 
         return NULL;
 }
index 2e7132c40261b2c92f292e71624f1a64ab0a81eb..6ecdf576ab88e7a66c645d872599543cc399cc5f 100644 (file)
@@ -23,6 +23,8 @@ shared_sources = files('''
         boot-timestamps.h
         bootspec.c
         bootspec.h
+        bpf-dlopen.c
+        bpf-dlopen.h
         bpf-program.c
         bpf-program.h
         bridge-util.c
@@ -387,9 +389,9 @@ libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
 libshared_deps = [threads,
                   libacl,
                   libblkid,
-                  libbpf,
                   libcap,
                   libcrypt,
+                  libdl,
                   libgcrypt,
                   libiptc,
                   libkmod,
index 9b11324c7b58e066ca625ac9164866955f191387..e02eaf78ff9e1e6e1644d9ab1673c4729d1139f6 100644 (file)
@@ -616,7 +616,7 @@ tests += [
          [['src/test/test-socket-bind.c'],
           [libcore,
           libshared],
-         [libbpf],
+         [libdl],
          core_includes,
          'BPF_FRAMEWORK'],
 ]
index 8e1ce6a0d00aa627fd5348fe394f3c0cc99c06e3..ea2ef31b1fdd2a28c7f305134ae5571c258fdbd9 100644 (file)
@@ -3,6 +3,7 @@
 #include <dlfcn.h>
 #include <stdlib.h>
 
+#include "bpf-dlopen.h"
 #include "cryptsetup-util.h"
 #include "idn-util.h"
 #include "libfido2-util.h"
@@ -44,6 +45,10 @@ static int run(int argc, char **argv) {
         assert_se(dlopen_libfido2() >= 0);
 #endif
 
+#if HAVE_LIBBPF
+        assert_se(dlopen_bpf() >= 0);
+#endif
+
         return 0;
 }
 
index 64accfc29ea98c47925965396dea4331ae84b1a7..3a33eacfedebc36d6598cf247c1ca6f227de8952 100644 (file)
@@ -964,7 +964,7 @@ install_missing_libraries() {
     local lib path
     # A number of dependencies is now optional via dlopen, so the install
     # script will not pick them up, since it looks at linkage.
-    for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2; do
+    for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2 libbpf; do
         ddebug "Searching for $lib via pkg-config"
         if pkg-config --exists "$lib"; then
                 path="$(pkg-config --variable=libdir "$lib")"
index 5506fdd8750c55f5eaebf0ca05d0ab273ca0de33..b28766fa6480d998993da4a26178c08fa870dfab 100755 (executable)
@@ -4,6 +4,7 @@
 import argparse
 import logging
 import pathlib
+import re
 import subprocess
 import sys
 
@@ -59,7 +60,11 @@ def gen_bpf_skeleton(bpftool_exec, in_file, out_fd):
 
     logging.debug('Generating BPF skeleton:')
     logging.debug('{}'.format(' '.join(bpftool_args)))
-    subprocess.check_call(bpftool_args, stdout=out_fd)
+    skel = subprocess.check_output(bpftool_args, universal_newlines=True)
+    # libbpf is used via dlopen(), so rename symbols as defined
+    # in src/shared/bpf-dlopen.h
+    skel = re.sub(r'(bpf_object__\w+_skeleton)', r'sym_\1', skel)
+    out_fd.write(skel)
 
 
 def bpf_build(args):