diff --git a/README b/README index cc0782ecbf1..fa5d3aca1b9 100644 --- a/README +++ b/README @@ -181,7 +181,7 @@ REQUIREMENTS: libcryptsetup (optional), >= 2.3.0 required for signed Verity images support libaudit (optional) libacl (optional) - libbpf >= 0.2.0 (optional) + libbpf >= 0.7.0 (optional) libfdisk >= 2.32 (from util-linux) (optional) libselinux (optional) liblzma (optional) diff --git a/meson.build b/meson.build index 2fb3c11a6c6..75f5f0f70ac 100644 --- a/meson.build +++ b/meson.build @@ -1050,7 +1050,7 @@ want_bpf_framework = get_option('bpf-framework') bpf_compiler = get_option('bpf-compiler') bpf_framework_required = want_bpf_framework == 'true' -libbpf_version_requirement = '>= 0.2.0' +libbpf_version_requirement = '>= 0.1.0' if bpf_compiler == 'gcc' libbpf_version_requirement = '>= 1.0.0' endif diff --git a/mkosi.build b/mkosi.build index 1154e738f4c..07f9bb21a69 100755 --- a/mkosi.build +++ b/mkosi.build @@ -149,7 +149,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then -D gnu-efi=true \ -D kernel-install=true \ -D analyze=true \ - -D bpf-framework=true + -D bpf-framework=auto fi cd "$BUILDDIR" diff --git a/src/core/bpf-lsm.c b/src/core/bpf-lsm.c index f3b93395582..173221b9f17 100644 --- a/src/core/bpf-lsm.c +++ b/src/core/bpf-lsm.c @@ -65,14 +65,14 @@ static int prepare_restrict_fs_bpf(struct restrict_fs_bpf **ret_obj) { return log_error_errno(errno, "bpf-lsm: Failed to open BPF object: %m"); /* TODO Maybe choose a number based on runtime information? */ - r = sym_bpf_map__resize(obj->maps.cgroup_hash, CGROUP_HASH_SIZE_MAX); + r = sym_bpf_map__set_max_entries(obj->maps.cgroup_hash, CGROUP_HASH_SIZE_MAX); assert(r <= 0); if (r < 0) return log_error_errno(r, "bpf-lsm: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.cgroup_hash)); /* Dummy map to satisfy the verifier */ - inner_map_fd = sym_bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), 128, 0); + inner_map_fd = compat_bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(uint32_t), sizeof(uint32_t), 128U, NULL); if (inner_map_fd < 0) return log_error_errno(errno, "bpf-lsm: Failed to create BPF map: %m"); @@ -202,12 +202,13 @@ int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, bool allo return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL), "bpf-lsm: BPF LSM object is not installed, has setup failed?"); - int inner_map_fd = sym_bpf_create_map( + int inner_map_fd = compat_bpf_map_create( BPF_MAP_TYPE_HASH, + NULL, sizeof(uint32_t), sizeof(uint32_t), - 128, /* Should be enough for all filesystem types */ - 0); + 128U, /* Should be enough for all filesystem types */ + NULL); if (inner_map_fd < 0) return log_unit_error_errno(u, errno, "bpf-lsm: Failed to create inner BPF map: %m"); diff --git a/src/core/bpf-socket-bind.c b/src/core/bpf-socket-bind.c index 30e5b2f3927..660ffdb7232 100644 --- a/src/core/bpf-socket-bind.c +++ b/src/core/bpf-socket-bind.c @@ -79,11 +79,11 @@ static int prepare_socket_bind_bpf( if (!obj) return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "bpf-socket-bind: Failed to open BPF object: %m"); - if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0) + if (sym_bpf_map__set_max_entries(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0) return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, "bpf-socket-bind: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_allow)); - if (sym_bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0) + if (sym_bpf_map__set_max_entries(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0) return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, "bpf-socket-bind: Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_bind_deny)); @@ -120,7 +120,7 @@ int bpf_socket_bind_supported(void) { if (!cgroup_bpf_supported()) return false; - if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) { + if (!compat_libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*opts=*/NULL)) { log_debug("bpf-socket-bind: BPF program type cgroup_sock_addr is not supported"); return false; } diff --git a/src/core/bpf/restrict_fs/restrict-fs.bpf.c b/src/core/bpf/restrict_fs/restrict-fs.bpf.c index 99940bedfd8..522a029785d 100644 --- a/src/core/bpf/restrict_fs/restrict-fs.bpf.c +++ b/src/core/bpf/restrict_fs/restrict-fs.bpf.c @@ -28,7 +28,7 @@ struct file { } __attribute__((preserve_access_index)); /* - * max_entries is set from user space with the bpf_map__resize helper. + * max_entries is set from user space with the bpf_map__set_max_entries helper. * */ struct { __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); diff --git a/src/core/bpf/socket_bind/socket-bind.bpf.c b/src/core/bpf/socket_bind/socket-bind.bpf.c index 8004400e6ca..b7972a887a4 100644 --- a/src/core/bpf/socket_bind/socket-bind.bpf.c +++ b/src/core/bpf/socket_bind/socket-bind.bpf.c @@ -18,7 +18,7 @@ #include /* - * max_entries is set from user space with bpf_map__resize helper. + * max_entries is set from user space with bpf_map__set_max_entries helper. */ struct socket_bind_map_t { __uint(type, BPF_MAP_TYPE_ARRAY); diff --git a/src/core/restrict-ifaces.c b/src/core/restrict-ifaces.c index a0ecaff814b..134f70a07b8 100644 --- a/src/core/restrict-ifaces.c +++ b/src/core/restrict-ifaces.c @@ -36,7 +36,7 @@ static int prepare_restrict_ifaces_bpf( if (!obj) return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "restrict-interfaces: Failed to open BPF object: %m"); - r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u)); + r = sym_bpf_map__set_max_entries(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u)); if (r != 0) return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, "restrict-interfaces: Failed to resize BPF map '%s': %m", @@ -83,7 +83,7 @@ int restrict_network_interfaces_supported(void) { if (!cgroup_bpf_supported()) return (supported = false); - if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) { + if (!compat_libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*opts=*/NULL)) { log_debug("restrict-interfaces: BPF program type cgroup_skb is not supported"); return (supported = false); } diff --git a/src/shared/bpf-compat.h b/src/shared/bpf-compat.h new file mode 100644 index 00000000000..04ade82fc1e --- /dev/null +++ b/src/shared/bpf-compat.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/* 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 bool (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *); + +/* 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); +extern bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32); + +/* 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 */); +} + +static inline int compat_libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts) { + if (sym_libbpf_probe_bpf_prog_type) + return sym_libbpf_probe_bpf_prog_type(prog_type, opts); + + return sym_bpf_probe_prog_type(prog_type, 0); +} diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c index d8e778794c2..2556053cbb6 100644 --- a/src/shared/bpf-dlopen.c +++ b/src/shared/bpf-dlopen.c @@ -3,18 +3,17 @@ #include "dlfcn-util.h" #include "bpf-dlopen.h" #include "log.h" +#include "strv.h" #if HAVE_LIBBPF -static void *bpf_dl = NULL; - struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int); struct bpf_link* (*sym_bpf_program__attach_lsm)(struct bpf_program *); 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_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags); -int (*sym_bpf_map__resize)(struct bpf_map *, __u32); +int (*sym_bpf_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *); +int (*sym_bpf_map__set_max_entries)(struct bpf_map *, __u32); int (*sym_bpf_map_update_elem)(int, const void *, const void *, __u64); int (*sym_bpf_map_delete_elem)(int, const void *); int (*sym_bpf_map__set_inner_map_fd)(struct bpf_map *, int); @@ -23,11 +22,15 @@ 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); +bool (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *); const char* (*sym_bpf_program__name)(const struct bpf_program *); libbpf_print_fn_t (*sym_libbpf_set_print)(libbpf_print_fn_t); long (*sym_libbpf_get_error)(const void *); +/* 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); +bool (*sym_bpf_probe_prog_type)(enum bpf_prog_type, __u32); + _printf_(2,0) static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_list ap) { #if !LOG_TRACE @@ -43,16 +46,38 @@ static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_lis } int dlopen_bpf(void) { + void *dl; int r; - r = dlopen_many_sym_or_warn( - &bpf_dl, "libbpf.so.0", LOG_DEBUG, + dl = dlopen("libbpf.so.1", RTLD_LAZY); + if (!dl) { + /* 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 */ + dl = dlopen("libbpf.so.0", RTLD_LAZY); + if (!dl) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "neither libbpf.so.1 nor libbpf.so.0 are installed: %s", dlerror()); + + /* symbols deprecated in 1.0 we use as compat */ + r = dlsym_many_or_warn(dl, LOG_DEBUG, + DLSYM_ARG(bpf_create_map), + DLSYM_ARG(bpf_probe_prog_type)); + } else { + /* symbols available from 0.7.0 */ + r = dlsym_many_or_warn(dl, LOG_DEBUG, + DLSYM_ARG(bpf_map_create), + DLSYM_ARG(libbpf_probe_bpf_prog_type)); + } + + r = dlsym_many_or_warn( + dl, LOG_DEBUG, DLSYM_ARG(bpf_link__destroy), DLSYM_ARG(bpf_link__fd), DLSYM_ARG(bpf_map__fd), DLSYM_ARG(bpf_map__name), - DLSYM_ARG(bpf_create_map), - DLSYM_ARG(bpf_map__resize), + DLSYM_ARG(bpf_map__set_max_entries), DLSYM_ARG(bpf_map_update_elem), DLSYM_ARG(bpf_map_delete_elem), DLSYM_ARG(bpf_map__set_inner_map_fd), @@ -61,7 +86,6 @@ int dlopen_bpf(void) { 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__attach_lsm), DLSYM_ARG(bpf_program__name), diff --git a/src/shared/bpf-dlopen.h b/src/shared/bpf-dlopen.h index f0d40325d90..95951e63e0e 100644 --- a/src/shared/bpf-dlopen.h +++ b/src/shared/bpf-dlopen.h @@ -6,14 +6,15 @@ #include #include +#include "bpf-compat.h" + extern struct bpf_link* (*sym_bpf_program__attach_cgroup)(struct bpf_program *, int); extern struct bpf_link* (*sym_bpf_program__attach_lsm)(struct bpf_program *); 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_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags); -extern int (*sym_bpf_map__resize)(struct bpf_map *, __u32); +extern int (*sym_bpf_map__set_max_entries)(struct bpf_map *, __u32); extern int (*sym_bpf_map_update_elem)(int, const void *, const void *, __u64); extern int (*sym_bpf_map_delete_elem)(int, const void *); extern int (*sym_bpf_map__set_inner_map_fd)(struct bpf_map *, int); @@ -24,7 +25,6 @@ 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 *); extern libbpf_print_fn_t (*sym_libbpf_set_print)(libbpf_print_fn_t); extern long (*sym_libbpf_get_error)(const void *);