libbpf: add compat helpers for libbpf down to 0.1.0

- new symbols are available from libbpf 0.6.0 so could be used with
libbpf.so.0, but we're sure the old symbols will be there and this
simplifies code
- detection at runtime should always work, regardless of whether systemd
has been compiled with older or newer libbpf and runs with older or newer
libbpf
This commit is contained in:
Dominique Martinet 2022-09-30 19:25:18 +09:00
parent 5a967e2887
commit 87e462f713
7 changed files with 90 additions and 17 deletions

View file

@ -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.7.0'
libbpf_version_requirement = '>= 0.1.0'
if bpf_compiler == 'gcc'
libbpf_version_requirement = '>= 1.0.0'
endif

View file

@ -72,7 +72,7 @@ static int prepare_restrict_fs_bpf(struct restrict_fs_bpf **ret_obj) {
sym_bpf_map__name(obj->maps.cgroup_hash));
/* Dummy map to satisfy the verifier */
inner_map_fd = sym_bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(uint32_t), sizeof(uint32_t), 128U, NULL);
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,7 +202,7 @@ 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_map_create(
int inner_map_fd = compat_bpf_map_create(
BPF_MAP_TYPE_HASH,
NULL,
sizeof(uint32_t),

View file

@ -120,7 +120,7 @@ int bpf_socket_bind_supported(void) {
if (!cgroup_bpf_supported())
return false;
if (!sym_libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*opts=*/NULL)) {
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;
}

View file

@ -83,7 +83,7 @@ int restrict_network_interfaces_supported(void) {
if (!cgroup_bpf_supported())
return (supported = false);
if (!sym_libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*opts=*/NULL)) {
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);
}

54
src/shared/bpf-compat.h Normal file
View file

@ -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);
}

View file

@ -6,8 +6,6 @@
#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 *);
@ -29,6 +27,10 @@ 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
@ -44,16 +46,37 @@ static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_lis
}
int dlopen_bpf(void) {
void *dl;
int r;
FOREACH_STRING(f, "libbpf.so.1", "libbpf.so.0") {
r = dlopen_many_sym_or_warn(
&bpf_dl, f, 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_map_create),
DLSYM_ARG(bpf_map__set_max_entries),
DLSYM_ARG(bpf_map_update_elem),
DLSYM_ARG(bpf_map_delete_elem),
@ -66,12 +89,8 @@ int dlopen_bpf(void) {
DLSYM_ARG(bpf_program__attach_cgroup),
DLSYM_ARG(bpf_program__attach_lsm),
DLSYM_ARG(bpf_program__name),
DLSYM_ARG(libbpf_probe_bpf_prog_type),
DLSYM_ARG(libbpf_set_print),
DLSYM_ARG(libbpf_get_error));
if (r >= 0)
break;
}
if (r < 0)
return r;

View file

@ -6,13 +6,14 @@
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#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_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *);
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 *);
@ -25,7 +26,6 @@ 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 const char* (*sym_bpf_program__name)(const struct bpf_program *);
extern bool (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *);
extern libbpf_print_fn_t (*sym_libbpf_set_print)(libbpf_print_fn_t);
extern long (*sym_libbpf_get_error)(const void *);