]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include "bpf-dlopen.h" | |
4 | #include "dlfcn-util.h" | |
5 | #include "log.h" | |
6 | ||
7 | #if HAVE_LIBBPF | |
8 | ||
9 | /* libbpf changed types of function prototypes around, so we need to disable some type checking for older | |
10 | * libbpf. We consider everything older than 0.7 too old for accurate type checks. */ | |
11 | #if defined(__LIBBPF_CURRENT_VERSION_GEQ) | |
12 | #if __LIBBPF_CURRENT_VERSION_GEQ(0, 7) | |
13 | #define MODERN_LIBBPF 1 | |
14 | #endif | |
15 | #endif | |
16 | #if !defined(MODERN_LIBBPF) | |
17 | #define MODERN_LIBBPF 0 | |
18 | #endif | |
19 | ||
20 | DLSYM_PROTOTYPE(bpf_link__destroy) = NULL; | |
21 | DLSYM_PROTOTYPE(bpf_link__fd) = NULL; | |
22 | DLSYM_PROTOTYPE(bpf_link__open) = NULL; | |
23 | DLSYM_PROTOTYPE(bpf_link__pin) = NULL; | |
24 | DLSYM_PROTOTYPE(bpf_map__fd) = NULL; | |
25 | DLSYM_PROTOTYPE(bpf_map__name) = NULL; | |
26 | DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd) = NULL; | |
27 | DLSYM_PROTOTYPE(bpf_map__set_max_entries) = NULL; | |
28 | DLSYM_PROTOTYPE(bpf_map__set_pin_path) = NULL; | |
29 | DLSYM_PROTOTYPE(bpf_map_delete_elem) = NULL; | |
30 | DLSYM_PROTOTYPE(bpf_map_get_fd_by_id) = NULL; | |
31 | DLSYM_PROTOTYPE(bpf_map_lookup_elem) = NULL; | |
32 | DLSYM_PROTOTYPE(bpf_map_update_elem) = NULL; | |
33 | DLSYM_PROTOTYPE(bpf_object__attach_skeleton) = NULL; | |
34 | DLSYM_PROTOTYPE(bpf_object__destroy_skeleton) = NULL; | |
35 | DLSYM_PROTOTYPE(bpf_object__detach_skeleton) = NULL; | |
36 | DLSYM_PROTOTYPE(bpf_object__load_skeleton) = NULL; | |
37 | DLSYM_PROTOTYPE(bpf_object__name) = NULL; | |
38 | DLSYM_PROTOTYPE(bpf_object__open_skeleton) = NULL; | |
39 | DLSYM_PROTOTYPE(bpf_object__pin_maps) = NULL; | |
40 | DLSYM_PROTOTYPE(bpf_program__attach) = NULL; | |
41 | DLSYM_PROTOTYPE(bpf_program__attach_cgroup) = NULL; | |
42 | DLSYM_PROTOTYPE(bpf_program__attach_lsm) = NULL; | |
43 | DLSYM_PROTOTYPE(bpf_program__name) = NULL; | |
44 | DLSYM_PROTOTYPE(libbpf_get_error) = NULL; | |
45 | DLSYM_PROTOTYPE(libbpf_set_print) = NULL; | |
46 | DLSYM_PROTOTYPE(ring_buffer__epoll_fd) = NULL; | |
47 | DLSYM_PROTOTYPE(ring_buffer__free) = NULL; | |
48 | DLSYM_PROTOTYPE(ring_buffer__new) = NULL; | |
49 | DLSYM_PROTOTYPE(ring_buffer__poll) = NULL; | |
50 | ||
51 | /* new symbols available from libbpf 0.7.0 */ | |
52 | int (*sym_bpf_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *); | |
53 | struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map); | |
54 | ||
55 | /* compat symbols removed in libbpf 1.0 */ | |
56 | int (*sym_bpf_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags); | |
57 | ||
58 | _printf_(2,0) | |
59 | static int bpf_print_func(enum libbpf_print_level level, const char *fmt, va_list ap) { | |
60 | #if !LOG_TRACE | |
61 | /* libbpf logs a lot of details at its debug level, which we don't need to see. */ | |
62 | if (level == LIBBPF_DEBUG) | |
63 | return 0; | |
64 | #endif | |
65 | /* All other levels are downgraded to LOG_DEBUG */ | |
66 | ||
67 | /* errno is used here, on the assumption that if the log message uses %m, errno will be set to | |
68 | * something useful. Otherwise, it shouldn't matter, we may pass 0 or some bogus value. */ | |
69 | return log_internalv(LOG_DEBUG, errno, NULL, 0, NULL, fmt, ap); | |
70 | } | |
71 | ||
72 | int dlopen_bpf_full(int log_level) { | |
73 | static int cached = 0; | |
74 | void *dl; | |
75 | int r; | |
76 | ||
77 | if (cached != 0) | |
78 | return cached; | |
79 | ||
80 | ELF_NOTE_DLOPEN("bpf", | |
81 | "Support firewalling and sandboxing with BPF", | |
82 | ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, | |
83 | "libbpf.so.1", "libbpf.so.0"); | |
84 | ||
85 | DISABLE_WARNING_DEPRECATED_DECLARATIONS; | |
86 | ||
87 | dl = dlopen("libbpf.so.1", RTLD_NOW|RTLD_NODELETE); | |
88 | if (!dl) { | |
89 | /* libbpf < 1.0.0 (we rely on 0.1.0+) provide most symbols we care about, but | |
90 | * unfortunately not all until 0.7.0. See bpf-compat.h for more details. | |
91 | * Once we consider we can assume 0.7+ is present we can just use the same symbol | |
92 | * list for both files, and when we assume 1.0+ is present we can remove this dlopen */ | |
93 | dl = dlopen("libbpf.so.0", RTLD_NOW|RTLD_NODELETE); | |
94 | if (!dl) | |
95 | return cached = log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), | |
96 | "Neither libbpf.so.1 nor libbpf.so.0 are installed, cgroup BPF features disabled: %s", | |
97 | dlerror()); | |
98 | ||
99 | log_debug("Loaded 'libbpf.so.0' via dlopen()"); | |
100 | ||
101 | /* symbols deprecated in 1.0 we use as compat */ | |
102 | r = dlsym_many_or_warn( | |
103 | dl, LOG_DEBUG, | |
104 | #if MODERN_LIBBPF | |
105 | /* Don't exist anymore in new libbpf, hence cannot type check them */ | |
106 | DLSYM_ARG_FORCE(bpf_create_map) | |
107 | #else | |
108 | DLSYM_ARG(bpf_create_map) | |
109 | #endif | |
110 | ); | |
111 | ||
112 | /* NB: we don't try to load bpf_object__next_map() on old versions */ | |
113 | } else { | |
114 | log_debug("Loaded 'libbpf.so.1' via dlopen()"); | |
115 | ||
116 | /* symbols available from 0.7.0 */ | |
117 | r = dlsym_many_or_warn( | |
118 | dl, LOG_DEBUG, | |
119 | #if MODERN_LIBBPF | |
120 | DLSYM_ARG(bpf_map_create), | |
121 | DLSYM_ARG(bpf_object__next_map) | |
122 | #else | |
123 | /* These symbols did not exist in old libbpf, hence we cannot type check them */ | |
124 | DLSYM_ARG_FORCE(bpf_map_create), | |
125 | DLSYM_ARG_FORCE(bpf_object__next_map) | |
126 | #endif | |
127 | ); | |
128 | } | |
129 | if (r < 0) | |
130 | return cached = log_full_errno(log_level, r, "Failed to load libbpf symbols, cgroup BPF features disabled: %m"); | |
131 | ||
132 | r = dlsym_many_or_warn( | |
133 | dl, LOG_DEBUG, | |
134 | DLSYM_ARG(bpf_link__destroy), | |
135 | DLSYM_ARG(bpf_link__fd), | |
136 | DLSYM_ARG(bpf_link__open), | |
137 | DLSYM_ARG(bpf_link__pin), | |
138 | DLSYM_ARG(bpf_map__fd), | |
139 | DLSYM_ARG(bpf_map__name), | |
140 | DLSYM_ARG(bpf_map__set_inner_map_fd), | |
141 | DLSYM_ARG(bpf_map__set_max_entries), | |
142 | DLSYM_ARG(bpf_map__set_pin_path), | |
143 | DLSYM_ARG(bpf_map_delete_elem), | |
144 | DLSYM_ARG(bpf_map_get_fd_by_id), | |
145 | DLSYM_ARG(bpf_map_lookup_elem), | |
146 | DLSYM_ARG(bpf_map_update_elem), | |
147 | DLSYM_ARG(bpf_object__attach_skeleton), | |
148 | DLSYM_ARG(bpf_object__destroy_skeleton), | |
149 | DLSYM_ARG(bpf_object__detach_skeleton), | |
150 | DLSYM_ARG(bpf_object__load_skeleton), | |
151 | DLSYM_ARG(bpf_object__name), | |
152 | DLSYM_ARG(bpf_object__open_skeleton), | |
153 | DLSYM_ARG(bpf_object__pin_maps), | |
154 | #if MODERN_LIBBPF | |
155 | DLSYM_ARG(bpf_program__attach), | |
156 | DLSYM_ARG(bpf_program__attach_cgroup), | |
157 | DLSYM_ARG(bpf_program__attach_lsm), | |
158 | #else | |
159 | /* libbpf added a "const" to function parameters where it should not have, ignore this type incompatibility */ | |
160 | DLSYM_ARG_FORCE(bpf_program__attach), | |
161 | DLSYM_ARG_FORCE(bpf_program__attach_cgroup), | |
162 | DLSYM_ARG_FORCE(bpf_program__attach_lsm), | |
163 | #endif | |
164 | DLSYM_ARG(bpf_program__name), | |
165 | DLSYM_ARG(libbpf_get_error), | |
166 | DLSYM_ARG(libbpf_set_print), | |
167 | DLSYM_ARG(ring_buffer__epoll_fd), | |
168 | DLSYM_ARG(ring_buffer__free), | |
169 | DLSYM_ARG(ring_buffer__new), | |
170 | DLSYM_ARG(ring_buffer__poll)); | |
171 | if (r < 0) | |
172 | return cached = log_full_errno(log_level, r, "Failed to load libbpf symbols, cgroup BPF features disabled: %m"); | |
173 | ||
174 | /* We set the print helper unconditionally. Otherwise libbpf will emit not useful log messages. */ | |
175 | (void) sym_libbpf_set_print(bpf_print_func); | |
176 | ||
177 | REENABLE_WARNING; | |
178 | ||
179 | return cached = true; | |
180 | } | |
181 | ||
182 | int bpf_get_error_translated(const void *ptr) { | |
183 | int r; | |
184 | ||
185 | r = sym_libbpf_get_error(ptr); | |
186 | ||
187 | switch (r) { | |
188 | case -524: | |
189 | /* Workaround for kernel bug, BPF returns an internal error instead of translating it, until | |
190 | * it is fixed: | |
191 | * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/errno.h?h=v6.9&id=a38297e3fb012ddfa7ce0321a7e5a8daeb1872b6#n27 | |
192 | */ | |
193 | return -EOPNOTSUPP; | |
194 | default: | |
195 | return r; | |
196 | } | |
197 | } | |
198 | ||
199 | #else | |
200 | ||
201 | int dlopen_bpf_full(int log_level) { | |
202 | return log_once_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), | |
203 | "libbpf support is not compiled in, cgroup BPF features disabled."); | |
204 | } | |
205 | #endif |