Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "cgen/handle.h"
7 : :
8 : : #include <assert.h>
9 : : #include <errno.h>
10 : : #include <stdlib.h>
11 : : #include <string.h>
12 : : #include <unistd.h>
13 : :
14 : : #include <bpfilter/bpf.h>
15 : : #include <bpfilter/core/list.h>
16 : : #include <bpfilter/counter.h>
17 : : #include <bpfilter/dump.h>
18 : : #include <bpfilter/helper.h>
19 : : #include <bpfilter/hook.h>
20 : : #include <bpfilter/logger.h>
21 : : #include <bpfilter/pack.h>
22 : :
23 : : #include "cgen/prog/link.h"
24 : : #include "cgen/prog/map.h"
25 : : #include "core/lock.h"
26 : :
27 : : #define _BF_LINK_NAME "bf_link"
28 : :
29 : 6637 : int bf_handle_new(struct bf_handle **handle, const char *prog_name)
30 : : {
31 : 6637 : _free_bf_handle_ struct bf_handle *_handle = NULL;
32 : :
33 : : assert(handle);
34 : : assert(prog_name);
35 : :
36 : 6637 : _handle = calloc(1, sizeof(*_handle));
37 [ + - ]: 6637 : if (!_handle)
38 : : return -ENOMEM;
39 : :
40 : 6637 : (void)snprintf(_handle->prog_name, BPF_OBJ_NAME_LEN, "%s", prog_name);
41 : 6637 : _handle->prog_fd = -1;
42 : 6637 : _handle->sets = bf_list_default(bf_map_free, bf_map_pack);
43 : :
44 : 6637 : *handle = TAKE_PTR(_handle);
45 : :
46 : 6637 : return 0;
47 : : }
48 : :
49 : 5327 : int bf_handle_new_from_pack(struct bf_handle **handle, struct bf_lock *lock,
50 : : bf_rpack_node_t node)
51 : : {
52 : 5327 : _free_bf_handle_ struct bf_handle *_handle = NULL;
53 : 5327 : _cleanup_free_ char *name = NULL;
54 : : bf_rpack_node_t child, array_node;
55 : : int dir_fd;
56 : : int r;
57 : :
58 : : assert(handle);
59 : : assert(lock);
60 : :
61 : 5327 : dir_fd = lock->chain_fd;
62 : :
63 : 5327 : r = bf_rpack_kv_str(node, "prog_name", &name);
64 [ - + ]: 5327 : if (r)
65 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.name");
66 : :
67 : 5327 : r = bf_handle_new(&_handle, name);
68 [ + - ]: 5327 : if (r)
69 : : return r;
70 : :
71 : 5327 : r = bf_bpf_obj_get(_handle->prog_name, dir_fd, &_handle->prog_fd);
72 [ - + ]: 5327 : if (r < 0)
73 [ # # ]: 0 : return bf_err_r(r, "failed to restore bf_handle.prog_fd from pin");
74 : :
75 : 5327 : r = bf_rpack_kv_node(node, "link", &child);
76 [ - + ]: 5327 : if (r)
77 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.link");
78 [ + + ]: 5327 : if (!bf_rpack_is_nil(child)) {
79 : 158 : r = bf_link_new_from_pack(&_handle->link, dir_fd, child);
80 [ - + ]: 158 : if (r)
81 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.link");
82 : : }
83 : :
84 : 5327 : r = bf_rpack_kv_node(node, "cmap", &child);
85 [ - + ]: 5327 : if (r)
86 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.cmap");
87 [ + - ]: 5327 : if (!bf_rpack_is_nil(child)) {
88 : 5327 : r = bf_map_new_from_pack(&_handle->cmap, dir_fd, child);
89 [ - + ]: 5327 : if (r)
90 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.cmap");
91 : : }
92 : :
93 : 5327 : r = bf_rpack_kv_node(node, "pmap", &child);
94 [ - + ]: 5327 : if (r)
95 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.pmap");
96 [ + - ]: 5327 : if (!bf_rpack_is_nil(child)) {
97 : 5327 : r = bf_map_new_from_pack(&_handle->pmap, dir_fd, child);
98 [ - + ]: 5327 : if (r)
99 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.pmap");
100 : : }
101 : :
102 : 5327 : r = bf_rpack_kv_node(node, "lmap", &child);
103 [ - + ]: 5327 : if (r)
104 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.lmap");
105 [ + + ]: 5327 : if (!bf_rpack_is_nil(child)) {
106 : 20 : r = bf_map_new_from_pack(&_handle->lmap, dir_fd, child);
107 [ - + ]: 20 : if (r)
108 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.lmap");
109 : : }
110 : :
111 : 5327 : r = bf_rpack_kv_array(node, "sets", &child);
112 [ - + ]: 5327 : if (r)
113 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_handle.sets");
114 [ + + - + : 12666 : bf_rpack_array_foreach (child, array_node) {
+ + ]
115 : 0 : _free_bf_map_ struct bf_map *map = NULL;
116 : :
117 [ - + ]: 1006 : if (bf_rpack_is_nil(array_node)) {
118 : 0 : r = bf_list_add_tail(&_handle->sets, NULL);
119 [ # # ]: 0 : if (r)
120 : : return r;
121 : : continue;
122 : : }
123 : :
124 [ + - + - : 1006 : r = bf_list_emplace(&_handle->sets, bf_map_new_from_pack, map, dir_fd,
- - - - ]
125 : : array_node);
126 : : if (r)
127 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_map into bf_handle.sets");
128 : : }
129 : :
130 : 5327 : *handle = TAKE_PTR(_handle);
131 : :
132 : 5327 : return 0;
133 : : }
134 : :
135 : 18601 : void bf_handle_free(struct bf_handle **handle)
136 : : {
137 : : assert(handle);
138 : :
139 [ + + ]: 18601 : if (!*handle)
140 : : return;
141 : :
142 : 6637 : closep(&(*handle)->prog_fd);
143 : :
144 : 6637 : bf_link_free(&(*handle)->link);
145 : 6637 : bf_map_free(&(*handle)->cmap);
146 : 6637 : bf_map_free(&(*handle)->pmap);
147 : 6637 : bf_map_free(&(*handle)->lmap);
148 : 6637 : bf_list_clean(&(*handle)->sets);
149 : :
150 : 6637 : free(*handle);
151 : 6637 : *handle = NULL;
152 : : }
153 : :
154 : 1329 : int bf_handle_pack(const struct bf_handle *handle, bf_wpack_t *pack)
155 : : {
156 : : assert(handle);
157 : : assert(pack);
158 : :
159 : 1329 : bf_wpack_kv_str(pack, "prog_name", handle->prog_name);
160 : :
161 [ + + ]: 1329 : if (handle->link) {
162 : 143 : bf_wpack_open_object(pack, "link");
163 : 143 : bf_link_pack(handle->link, pack);
164 : 143 : bf_wpack_close_object(pack);
165 : : } else {
166 : : bf_wpack_kv_nil(pack, "link");
167 : : }
168 : :
169 [ + - ]: 1329 : if (handle->cmap) {
170 : 1329 : bf_wpack_open_object(pack, "cmap");
171 : 1329 : bf_map_pack(handle->cmap, pack);
172 : 1329 : bf_wpack_close_object(pack);
173 : : } else {
174 : : bf_wpack_kv_nil(pack, "cmap");
175 : : }
176 : :
177 [ + - ]: 1329 : if (handle->pmap) {
178 : 1329 : bf_wpack_open_object(pack, "pmap");
179 : 1329 : bf_map_pack(handle->pmap, pack);
180 : 1329 : bf_wpack_close_object(pack);
181 : : } else {
182 : : bf_wpack_kv_nil(pack, "pmap");
183 : : }
184 : :
185 [ + + ]: 1329 : if (handle->lmap) {
186 : 31 : bf_wpack_open_object(pack, "lmap");
187 : 31 : bf_map_pack(handle->lmap, pack);
188 : 31 : bf_wpack_close_object(pack);
189 : : } else {
190 : : bf_wpack_kv_nil(pack, "lmap");
191 : : }
192 : :
193 : 1329 : bf_wpack_kv_list(pack, "sets", &handle->sets);
194 : :
195 [ - + ]: 1329 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
196 : : }
197 : :
198 : 16 : void bf_handle_dump(const struct bf_handle *handle, prefix_t *prefix)
199 : : {
200 : : assert(handle);
201 : : assert(prefix);
202 : :
203 [ - + ]: 16 : DUMP(prefix, "struct bf_handle at %p", handle);
204 : :
205 : 16 : bf_dump_prefix_push(prefix);
206 : :
207 [ - + ]: 16 : DUMP(prefix, "prog_name: %s", handle->prog_name);
208 [ - + ]: 16 : DUMP(prefix, "prog_fd: %d", handle->prog_fd);
209 : :
210 [ - + ]: 16 : if (handle->link) {
211 [ # # ]: 0 : DUMP(prefix, "link: struct bf_link *");
212 : 0 : bf_dump_prefix_push(prefix);
213 : 0 : bf_link_dump(handle->link, bf_dump_prefix_last(prefix));
214 : 0 : bf_dump_prefix_pop(prefix);
215 : : } else {
216 [ - + ]: 16 : DUMP(prefix, "link: struct bf_link * (NULL)");
217 : : }
218 : :
219 [ + - ]: 16 : if (handle->cmap) {
220 [ - + ]: 16 : DUMP(prefix, "cmap: struct bf_map *");
221 : 16 : bf_dump_prefix_push(prefix);
222 : 16 : bf_map_dump(handle->cmap, bf_dump_prefix_last(prefix));
223 : 16 : bf_dump_prefix_pop(prefix);
224 : : } else {
225 [ # # ]: 0 : DUMP(prefix, "cmap: struct bf_map * (NULL)");
226 : : }
227 : :
228 [ + - ]: 16 : if (handle->pmap) {
229 [ - + ]: 16 : DUMP(prefix, "pmap: struct bf_map *");
230 : 16 : bf_dump_prefix_push(prefix);
231 : 16 : bf_map_dump(handle->pmap, bf_dump_prefix_last(prefix));
232 : 16 : bf_dump_prefix_pop(prefix);
233 : : } else {
234 [ # # ]: 0 : DUMP(prefix, "pmap: struct bf_map * (NULL)");
235 : : }
236 : :
237 [ + + ]: 16 : if (handle->lmap) {
238 [ - + ]: 3 : DUMP(prefix, "lmap: struct bf_map *");
239 : 3 : bf_dump_prefix_push(prefix);
240 : 3 : bf_map_dump(handle->lmap, bf_dump_prefix_last(prefix));
241 : 3 : bf_dump_prefix_pop(prefix);
242 : : } else {
243 [ - + ]: 13 : DUMP(prefix, "lmap: struct bf_map * (NULL)");
244 : : }
245 : :
246 [ - + ]: 16 : DUMP(bf_dump_prefix_last(prefix), "sets: bf_list<bf_map>[%lu]",
247 : : bf_list_size(&handle->sets));
248 : 16 : bf_dump_prefix_push(prefix);
249 [ - + - - : 32 : bf_list_foreach (&handle->sets, map_node) {
- + ]
250 : : struct bf_map *map = bf_list_node_get_data(map_node);
251 : :
252 [ # # ]: 0 : if (bf_list_is_tail(&handle->sets, map_node))
253 : 0 : bf_dump_prefix_last(prefix);
254 : :
255 [ # # ]: 0 : if (!map) {
256 [ # # ]: 0 : DUMP(prefix, "struct bf_map * (NULL)");
257 : 0 : continue;
258 : : }
259 : :
260 : 0 : bf_map_dump(map, prefix);
261 : : }
262 : 16 : bf_dump_prefix_pop(prefix);
263 : :
264 : 16 : bf_dump_prefix_pop(prefix);
265 : 16 : }
266 : :
267 : 1305 : int bf_handle_pin(struct bf_handle *handle, struct bf_lock *lock)
268 : : {
269 : : int dir_fd;
270 : : int r;
271 : :
272 : : assert(handle);
273 : : assert(lock);
274 : :
275 : 1305 : dir_fd = lock->chain_fd;
276 : :
277 : 1305 : r = bf_bpf_obj_pin(handle->prog_name, handle->prog_fd, dir_fd);
278 [ + + ]: 1305 : if (r) {
279 [ + - ]: 2 : bf_err_r(r, "failed to pin BPF program");
280 : 2 : goto err_unpin_all;
281 : : }
282 : :
283 [ + - ]: 1303 : if (handle->cmap) {
284 : 1303 : r = bf_map_pin(handle->cmap, dir_fd);
285 [ - + ]: 1303 : if (r) {
286 [ # # ]: 0 : bf_err_r(r, "failed to pin BPF counters map");
287 : 0 : goto err_unpin_all;
288 : : }
289 : : }
290 : :
291 [ + - ]: 1303 : if (handle->pmap) {
292 : 1303 : r = bf_map_pin(handle->pmap, dir_fd);
293 [ - + ]: 1303 : if (r) {
294 [ # # ]: 0 : bf_err_r(r, "failed to pin BPF printer map");
295 : 0 : goto err_unpin_all;
296 : : }
297 : : }
298 : :
299 [ + + ]: 1303 : if (handle->lmap) {
300 : 27 : r = bf_map_pin(handle->lmap, dir_fd);
301 [ - + ]: 27 : if (r) {
302 [ # # ]: 0 : bf_err_r(r, "failed to pin BPF log map");
303 : 0 : goto err_unpin_all;
304 : : }
305 : : }
306 : :
307 [ + + + + : 3206 : bf_list_foreach (&handle->sets, set_node) {
+ + ]
308 : : struct bf_map *map = bf_list_node_get_data(set_node);
309 : :
310 [ - + ]: 300 : if (!map)
311 : 0 : continue;
312 : :
313 : 300 : r = bf_map_pin(map, dir_fd);
314 [ - + ]: 300 : if (r) {
315 [ # # ]: 0 : bf_err_r(r, "failed to pin BPF set map");
316 : 0 : goto err_unpin_all;
317 : : }
318 : : }
319 : :
320 [ + + ]: 1303 : if (handle->link) {
321 : 119 : r = bf_link_pin(handle->link, lock);
322 [ + - ]: 119 : if (r) {
323 [ # # ]: 0 : bf_err_r(r, "failed to pin BPF link");
324 : 0 : goto err_unpin_all;
325 : : }
326 : : }
327 : :
328 : : return 0;
329 : :
330 : 2 : err_unpin_all:
331 : 2 : bf_handle_unpin(handle, lock);
332 : 2 : return r;
333 : : }
334 : :
335 : 1279 : void bf_handle_unpin(struct bf_handle *handle, struct bf_lock *lock)
336 : : {
337 : : int dir_fd;
338 : :
339 : : assert(handle);
340 : : assert(lock);
341 : :
342 : 1279 : dir_fd = lock->chain_fd;
343 : :
344 [ + - ]: 1279 : if (handle->cmap)
345 : 1279 : bf_map_unpin(handle->cmap, dir_fd);
346 [ + - ]: 1279 : if (handle->pmap)
347 : 1279 : bf_map_unpin(handle->pmap, dir_fd);
348 [ + + ]: 1279 : if (handle->lmap)
349 : 16 : bf_map_unpin(handle->lmap, dir_fd);
350 : :
351 [ + + - + : 3002 : bf_list_foreach (&handle->sets, set_node) {
+ + ]
352 : : struct bf_map *map = bf_list_node_get_data(set_node);
353 : :
354 [ - + ]: 222 : if (!map)
355 : 0 : continue;
356 : :
357 : 222 : bf_map_unpin(map, dir_fd);
358 : : }
359 : :
360 [ + + ]: 1279 : if (handle->link)
361 : 122 : bf_link_unpin(handle->link, lock);
362 : :
363 : 1279 : unlinkat(dir_fd, handle->prog_name, 0);
364 : 1279 : }
365 : :
366 : 3479 : int bf_handle_get_counter(const struct bf_handle *handle, uint32_t counter_idx,
367 : : struct bf_counter *counter)
368 : : {
369 : : int r;
370 : :
371 : : assert(handle);
372 : : assert(counter);
373 : :
374 [ - + ]: 3479 : if (!handle->cmap)
375 [ # # ]: 0 : return bf_err_r(-ENOENT, "handle has no counters map");
376 : :
377 : 3479 : r = bf_bpf_map_lookup_elem(handle->cmap->fd, &counter_idx, counter);
378 [ - + ]: 3479 : if (r < 0)
379 [ # # ]: 0 : return bf_err_r(r, "failed to lookup counters map");
380 : :
381 : : return 0;
382 : : }
383 : :
384 : 123 : int bf_handle_attach(struct bf_handle *handle, enum bf_hook hook,
385 : : struct bf_hookopts **hookopts)
386 : : {
387 : : int r;
388 : :
389 : : assert(handle);
390 : : assert(hookopts);
391 : :
392 [ - + ]: 123 : if (handle->link)
393 [ # # ]: 0 : return bf_err_r(-EEXIST, "program is already attached");
394 : :
395 : 123 : r = bf_link_new(&handle->link, _BF_LINK_NAME, hook, hookopts,
396 : : handle->prog_fd);
397 [ + + ]: 123 : if (r)
398 [ + - ]: 4 : return bf_err_r(r, "failed to attach bf_link");
399 : :
400 : : return 0;
401 : : }
402 : :
403 : 1263 : void bf_handle_detach(struct bf_handle *handle)
404 : : {
405 : : assert(handle);
406 : :
407 [ + + ]: 1263 : if (handle->link) {
408 : 110 : (void)bf_bpf_link_detach(handle->link->fd);
409 [ + + ]: 110 : if (handle->link->fd_extra >= 0)
410 : 6 : (void)bf_bpf_link_detach(handle->link->fd_extra);
411 : : }
412 : :
413 : 1263 : bf_link_free(&handle->link);
414 : 1263 : }
415 : :
416 : 1263 : void bf_handle_unload(struct bf_handle *handle)
417 : : {
418 : : assert(handle);
419 : :
420 : 1263 : closep(&handle->prog_fd);
421 : :
422 : : /* Explicitly detach the BPF link before closing it. Relying solely on
423 : : * close() is not sufficient when the link is pinned (the pin holds a
424 : : * kernel reference that keeps the link alive). Using BPF_LINK_DETACH
425 : : * ensures the program is removed from the hook immediately. */
426 : 1263 : bf_handle_detach(handle);
427 : :
428 : 1263 : bf_link_free(&handle->link);
429 : 1263 : bf_map_free(&handle->cmap);
430 : 1263 : bf_map_free(&handle->pmap);
431 : 1263 : bf_map_free(&handle->lmap);
432 : 1263 : bf_list_clean(&handle->sets);
433 : 1263 : }
|