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