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