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 "ctx.h"
7 : :
8 : : #include <errno.h>
9 : : #include <fcntl.h>
10 : : #include <stdbool.h>
11 : : #include <stdlib.h>
12 : : #include <unistd.h>
13 : :
14 : : #include <bpfilter/bpf.h>
15 : : #include <bpfilter/btf.h>
16 : : #include <bpfilter/chain.h>
17 : : #include <bpfilter/dump.h>
18 : : #include <bpfilter/front.h>
19 : : #include <bpfilter/helper.h>
20 : : #include <bpfilter/hook.h>
21 : : #include <bpfilter/io.h>
22 : : #include <bpfilter/list.h>
23 : : #include <bpfilter/logger.h>
24 : : #include <bpfilter/ns.h>
25 : : #include <bpfilter/pack.h>
26 : :
27 : : #include "cgen/cgen.h"
28 : : #include "cgen/elfstub.h"
29 : : #include "opts.h"
30 : :
31 : : #define _free_bf_ctx_ __attribute__((cleanup(_bf_ctx_free)))
32 : :
33 : : /**
34 : : * @struct bf_ctx
35 : : *
36 : : * bpfilter working context. Only one context is used during the daemon's
37 : : * lifetime.
38 : : */
39 : : struct bf_ctx
40 : : {
41 : : /// Namespaces the daemon was started in.
42 : : struct bf_ns ns;
43 : :
44 : : /// BPF token file descriptor
45 : : int token_fd;
46 : :
47 : : bf_list cgens;
48 : :
49 : : struct bf_elfstub *stubs[_BF_ELFSTUB_MAX];
50 : : };
51 : :
52 : : static void _bf_ctx_free(struct bf_ctx **ctx);
53 : :
54 : : /// Global daemon context. Hidden in this translation unit.
55 : : static struct bf_ctx *_bf_global_ctx = NULL;
56 : :
57 : 20 : static int _bf_ctx_gen_token(void)
58 : : {
59 : 20 : _cleanup_close_ int mnt_fd = -1;
60 : 20 : _cleanup_close_ int bpffs_fd = -1;
61 : 20 : _cleanup_close_ int token_fd = -1;
62 : :
63 : 20 : mnt_fd = open(bf_opts_bpffs_path(), O_DIRECTORY);
64 [ - + ]: 20 : if (mnt_fd < 0)
65 [ # # ]: 0 : return bf_err_r(errno, "failed to open '%s'", bf_opts_bpffs_path());
66 : :
67 : 20 : bpffs_fd = openat(mnt_fd, ".", 0, O_RDWR);
68 [ - + ]: 20 : if (bpffs_fd < 0)
69 [ # # ]: 0 : return bf_err_r(errno, "failed to get bpffs FD from '%s'",
70 : : bf_opts_bpffs_path());
71 : :
72 : 20 : token_fd = bf_bpf_token_create(bpffs_fd);
73 [ - + ]: 20 : if (token_fd < 0) {
74 [ # # ]: 0 : return bf_err_r(token_fd, "failed to create BPF token for '%s'",
75 : : bf_opts_bpffs_path());
76 : : }
77 : :
78 : 20 : return TAKE_FD(token_fd);
79 : : }
80 : :
81 : : /**
82 : : * Create and initialize a new context.
83 : : *
84 : : * On failure, @p ctx is left unchanged.
85 : : *
86 : : * @param ctx New context to create. Can't be NULL.
87 : : * @return 0 on success, negative errno value on failure.
88 : : */
89 : 20 : static int _bf_ctx_new(struct bf_ctx **ctx)
90 : : {
91 : 20 : _free_bf_ctx_ struct bf_ctx *_ctx = NULL;
92 : : int r;
93 : :
94 : : bf_assert(ctx);
95 : :
96 : 20 : _ctx = calloc(1, sizeof(*_ctx));
97 [ + - ]: 20 : if (!_ctx)
98 : : return -ENOMEM;
99 : :
100 : 20 : r = bf_ns_init(&_ctx->ns, getpid());
101 [ - + ]: 20 : if (r)
102 [ # # ]: 0 : return bf_err_r(r, "failed to initialise current bf_ns");
103 : :
104 : 20 : _ctx->token_fd = -1;
105 [ + - ]: 20 : if (bf_opts_with_bpf_token()) {
106 : 0 : _cleanup_close_ int token_fd = -1;
107 : :
108 : 20 : r = bf_btf_kernel_has_token();
109 [ - + ]: 20 : if (r == -ENOENT) {
110 [ # # ]: 0 : bf_err(
111 : : "--with-bpf-token requested, but this kernel doesn't support BPF token");
112 : 0 : return r;
113 : : }
114 [ - + ]: 20 : if (r)
115 [ # # ]: 0 : return bf_err_r(r, "failed to check for BPF token support");
116 : :
117 : 20 : token_fd = _bf_ctx_gen_token();
118 [ - + ]: 20 : if (token_fd < 0)
119 [ # # ]: 0 : return bf_err_r(token_fd, "failed to generate a BPF token");
120 : :
121 : 20 : _ctx->token_fd = TAKE_FD(token_fd);
122 : : }
123 : :
124 : 20 : _ctx->cgens = bf_list_default(bf_cgen_free, bf_cgen_pack);
125 : :
126 [ + + ]: 100 : for (enum bf_elfstub_id id = 0; id < _BF_ELFSTUB_MAX; ++id) {
127 : 80 : r = bf_elfstub_new(&_ctx->stubs[id], id);
128 [ - + ]: 80 : if (r)
129 [ # # ]: 0 : return bf_err_r(r, "failed to create ELF stub ID %u", id);
130 : : }
131 : :
132 : 20 : *ctx = TAKE_PTR(_ctx);
133 : :
134 : 20 : return 0;
135 : : }
136 : :
137 : : /**
138 : : * @brief Allocate and initialize a new context from serialized data.
139 : : *
140 : : * @param ctx Context object to allocate and initialize from the serialized
141 : : * data. The caller will own the object. On failure, `*ctx` is
142 : : * unchanged. Can't be NULL.
143 : : * @param node Node containing the serialized matcher.
144 : : * @return 0 on success, or a negative errno value on failure.
145 : : */
146 : 2 : static int _bf_ctx_new_from_pack(struct bf_ctx **ctx, bf_rpack_node_t node)
147 : : {
148 : 2 : _free_bf_ctx_ struct bf_ctx *_ctx = NULL;
149 : : bf_rpack_node_t child, array_node;
150 : : int r;
151 : :
152 : : bf_assert(ctx);
153 : :
154 : 2 : r = _bf_ctx_new(&_ctx);
155 [ + - ]: 2 : if (r < 0)
156 : : return r;
157 : :
158 : 2 : r = bf_rpack_kv_array(node, "cgens", &child);
159 [ + - ]: 2 : if (r)
160 : : return r;
161 [ + - - + : 8 : bf_rpack_array_foreach (child, array_node) {
+ + ]
162 : 0 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
163 : :
164 [ + - + - : 2 : r = bf_list_emplace(&_ctx->cgens, bf_cgen_new_from_pack, cgen,
- - - - ]
165 : : array_node);
166 : : if (r)
167 : : return r;
168 : : }
169 : :
170 : 2 : *ctx = TAKE_PTR(_ctx);
171 : :
172 : 2 : return 0;
173 : : }
174 : :
175 : : /**
176 : : * Free a context.
177 : : *
178 : : * If @p ctx points to a NULL pointer, this function does nothing. Once
179 : : * the function returns, @p ctx points to a NULL pointer.
180 : : *
181 : : * @param ctx Context to free. Can't be NULL.
182 : : */
183 : 61 : static void _bf_ctx_free(struct bf_ctx **ctx)
184 : : {
185 : : bf_assert(ctx);
186 : :
187 [ + + ]: 61 : if (!*ctx)
188 : : return;
189 : :
190 : 19 : bf_ns_clean(&(*ctx)->ns);
191 : 19 : closep(&(*ctx)->token_fd);
192 : 19 : bf_list_clean(&(*ctx)->cgens);
193 : :
194 [ + + ]: 95 : for (enum bf_elfstub_id id = 0; id < _BF_ELFSTUB_MAX; ++id)
195 : 76 : bf_elfstub_free(&(*ctx)->stubs[id]);
196 : :
197 : : freep((void *)ctx);
198 : : }
199 : :
200 : : /**
201 : : * See @ref bf_ctx_dump for details.
202 : : */
203 : 20 : static void _bf_ctx_dump(const struct bf_ctx *ctx, prefix_t *prefix)
204 : : {
205 [ + - ]: 20 : DUMP(prefix, "struct bf_ctx at %p", ctx);
206 : :
207 : 20 : bf_dump_prefix_push(prefix);
208 : :
209 : : // Namespaces
210 [ + - ]: 20 : DUMP(prefix, "ns: struct bf_ns")
211 : 20 : bf_dump_prefix_push(prefix);
212 : :
213 [ + - ]: 20 : DUMP(prefix, "net: struct bf_ns_info");
214 : 20 : bf_dump_prefix_push(prefix);
215 [ + - ]: 20 : DUMP(prefix, "fd: %d", ctx->ns.net.fd);
216 [ + - ]: 20 : DUMP(bf_dump_prefix_last(prefix), "inode: %u", ctx->ns.net.inode);
217 : 20 : bf_dump_prefix_pop(prefix);
218 : :
219 [ + - ]: 20 : DUMP(bf_dump_prefix_last(prefix), "mnt: struct bf_ns_info");
220 : 20 : bf_dump_prefix_push(prefix);
221 [ + - ]: 20 : DUMP(prefix, "fd: %d", ctx->ns.mnt.fd);
222 [ + - ]: 20 : DUMP(bf_dump_prefix_last(prefix), "inode: %u", ctx->ns.mnt.inode);
223 : 20 : bf_dump_prefix_pop(prefix);
224 : :
225 : 20 : bf_dump_prefix_pop(prefix);
226 : :
227 [ + - ]: 20 : DUMP(prefix, "token_fd: %d", ctx->token_fd);
228 : :
229 : : // Codegens
230 [ + - ]: 20 : DUMP(bf_dump_prefix_last(prefix), "cgens: bf_list<struct bf_cgen>[%lu]",
231 : : bf_list_size(&ctx->cgens));
232 : 20 : bf_dump_prefix_push(prefix);
233 [ + + - + : 44 : bf_list_foreach (&ctx->cgens, cgen_node) {
+ + ]
234 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
235 : :
236 [ + - ]: 2 : if (bf_list_is_tail(&ctx->cgens, cgen_node))
237 : 2 : bf_dump_prefix_last(prefix);
238 : :
239 : 2 : bf_cgen_dump(cgen, prefix);
240 : : }
241 : 20 : bf_dump_prefix_pop(prefix);
242 : :
243 : 20 : bf_dump_prefix_pop(prefix);
244 : 20 : }
245 : :
246 : : /**
247 : : * @brief Serialize a context.
248 : : *
249 : : * The context is serialized as:
250 : : * @code
251 : : * {
252 : : * "cgens": [
253 : : * { bf_codegen },
254 : : * // ...
255 : : * ]
256 : : * }
257 : : * @endcode
258 : : *
259 : : * @param ctx Context to serialize. Can't be NULL.
260 : : * @param pack `bf_wpack_t` object to serialize the context into. Can't be NULL.
261 : : * @return 0 on success, or a negative error value on failure.
262 : : */
263 : 77 : static int _bf_ctx_pack(const struct bf_ctx *ctx, bf_wpack_t *pack)
264 : : {
265 : : bf_assert(ctx);
266 : : bf_assert(pack);
267 : :
268 : 77 : bf_wpack_kv_list(pack, "cgens", &ctx->cgens);
269 : :
270 [ - + ]: 77 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
271 : : }
272 : :
273 : : /**
274 : : * See @ref bf_ctx_get_cgen for details.
275 : : */
276 : 142 : static struct bf_cgen *_bf_ctx_get_cgen(const struct bf_ctx *ctx,
277 : : const char *name)
278 : : {
279 : : bf_assert(ctx && name);
280 : :
281 [ + + + + : 524 : bf_list_foreach (&ctx->cgens, cgen_node) {
+ + ]
282 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
283 : :
284 [ + + ]: 194 : if (bf_streq(cgen->chain->name, name))
285 : : return cgen;
286 : : }
287 : :
288 : : return NULL;
289 : : }
290 : :
291 : : /**
292 : : * See @ref bf_ctx_get_cgens_for_front for details.
293 : : */
294 : 4 : static int _bf_ctx_get_cgens_for_front(const struct bf_ctx *ctx, bf_list *cgens,
295 : : enum bf_front front)
296 : : {
297 : 4 : _clean_bf_list_ bf_list _cgens =
298 : 4 : bf_list_default(cgens->ops.free, cgens->ops.pack);
299 : : int r;
300 : :
301 : : bf_assert(ctx && cgens);
302 : :
303 [ + - + + : 18 : bf_list_foreach (&ctx->cgens, cgen_node) {
+ + ]
304 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
305 : :
306 [ - + ]: 5 : if (cgen->front != front)
307 : 0 : continue;
308 : :
309 : 5 : r = bf_list_add_tail(&_cgens, cgen);
310 [ - + ]: 5 : if (r)
311 [ # # ]: 0 : return bf_err_r(r, "failed to insert codegen into list");
312 : : }
313 : :
314 : 4 : *cgens = bf_list_move(_cgens);
315 : :
316 : 4 : return 0;
317 : : }
318 : :
319 : : /**
320 : : * See @ref bf_ctx_set_cgen for details.
321 : : */
322 : 55 : static int _bf_ctx_set_cgen(struct bf_ctx *ctx, struct bf_cgen *cgen)
323 : : {
324 : : bf_assert(ctx && cgen);
325 : :
326 [ - + ]: 55 : if (_bf_ctx_get_cgen(ctx, cgen->chain->name))
327 [ # # ]: 0 : return bf_err_r(-EEXIST, "codegen already exists in context");
328 : :
329 : 55 : return bf_list_add_tail(&ctx->cgens, cgen);
330 : : }
331 : :
332 : 30 : static int _bf_ctx_delete_cgen(struct bf_ctx *ctx, struct bf_cgen *cgen,
333 : : bool unload)
334 : : {
335 [ + - + - ]: 66 : bf_list_foreach (&ctx->cgens, cgen_node) {
336 : : struct bf_cgen *_cgen = bf_list_node_get_data(cgen_node);
337 : :
338 [ + + - + ]: 33 : if (_cgen != cgen)
339 : : continue;
340 : :
341 [ + - ]: 30 : if (unload)
342 : 30 : bf_cgen_unload(_cgen);
343 : :
344 : 30 : bf_list_delete(&ctx->cgens, cgen_node);
345 : :
346 : 30 : return 0;
347 : : }
348 : :
349 : : return -ENOENT;
350 : : }
351 : :
352 : 18 : int bf_ctx_setup(void)
353 : : {
354 : 18 : _free_bf_ctx_ struct bf_ctx *_ctx = NULL;
355 : : int r;
356 : :
357 : : bf_assert(!_ctx);
358 : :
359 : 18 : r = _bf_ctx_new(&_ctx);
360 [ - + ]: 18 : if (r)
361 [ # # ]: 0 : return bf_err_r(r, "failed to create new context");
362 : :
363 : 18 : _bf_global_ctx = TAKE_PTR(_ctx);
364 : :
365 : 18 : return 0;
366 : : }
367 : :
368 : 19 : void bf_ctx_teardown(bool clear)
369 : : {
370 [ - + ]: 19 : if (clear) {
371 [ # # # # : 0 : bf_list_foreach (&_bf_global_ctx->cgens, cgen_node)
# # ]
372 : 0 : bf_cgen_unload(bf_list_node_get_data(cgen_node));
373 : : }
374 : :
375 : 19 : _bf_ctx_free(&_bf_global_ctx);
376 : 19 : }
377 : :
378 : 77 : int bf_ctx_save(bf_wpack_t *pack)
379 : : {
380 : : int r;
381 : :
382 : : bf_assert(pack);
383 : :
384 : 77 : r = _bf_ctx_pack(_bf_global_ctx, pack);
385 [ - + ]: 77 : if (r)
386 [ # # ]: 0 : return bf_err_r(r, "failed to serialize context");
387 : :
388 : : return 0;
389 : : }
390 : :
391 : 2 : int bf_ctx_load(bf_rpack_node_t node)
392 : : {
393 : 2 : _free_bf_ctx_ struct bf_ctx *ctx = NULL;
394 : : int r;
395 : :
396 : 2 : r = _bf_ctx_new_from_pack(&ctx, node);
397 [ - + ]: 2 : if (r)
398 [ # # ]: 0 : return bf_err_r(r, "failed to deserialize context");
399 : :
400 : 2 : _bf_global_ctx = TAKE_PTR(ctx);
401 : :
402 : 2 : return 0;
403 : : }
404 : :
405 : 12 : static void _bf_ctx_flush(struct bf_ctx *ctx, enum bf_front front)
406 : : {
407 : : bf_assert(ctx);
408 : :
409 [ + + + + : 36 : bf_list_foreach (&ctx->cgens, cgen_node) {
+ + ]
410 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
411 : :
412 [ - + ]: 6 : if (cgen->front != front)
413 : 0 : continue;
414 : :
415 : 6 : bf_cgen_unload(cgen);
416 : 6 : bf_list_delete(&ctx->cgens, cgen_node);
417 : : }
418 : 12 : }
419 : :
420 : 12 : void bf_ctx_flush(enum bf_front front)
421 : : {
422 : 12 : _bf_ctx_flush(_bf_global_ctx, front);
423 : 12 : }
424 : :
425 : 108 : bool bf_ctx_is_empty(void)
426 : : {
427 : 108 : return bf_list_is_empty(&_bf_global_ctx->cgens);
428 : : }
429 : :
430 : 20 : void bf_ctx_dump(prefix_t *prefix)
431 : : {
432 : 20 : _bf_ctx_dump(_bf_global_ctx, prefix);
433 : 20 : }
434 : :
435 : 87 : struct bf_cgen *bf_ctx_get_cgen(const char *name)
436 : : {
437 : 87 : return _bf_ctx_get_cgen(_bf_global_ctx, name);
438 : : }
439 : :
440 : 4 : int bf_ctx_get_cgens_for_front(bf_list *cgens, enum bf_front front)
441 : : {
442 : 4 : return _bf_ctx_get_cgens_for_front(_bf_global_ctx, cgens, front);
443 : : }
444 : :
445 : 55 : int bf_ctx_set_cgen(struct bf_cgen *cgen)
446 : : {
447 : 55 : return _bf_ctx_set_cgen(_bf_global_ctx, cgen);
448 : : }
449 : :
450 : 30 : int bf_ctx_delete_cgen(struct bf_cgen *cgen, bool unload)
451 : : {
452 : 30 : return _bf_ctx_delete_cgen(_bf_global_ctx, cgen, unload);
453 : : }
454 : :
455 : 46 : struct bf_ns *bf_ctx_get_ns(void)
456 : : {
457 : 46 : return &_bf_global_ctx->ns;
458 : : }
459 : :
460 : 380 : int bf_ctx_token(void)
461 : : {
462 : 380 : return _bf_global_ctx->token_fd;
463 : : }
464 : :
465 : 166 : int bf_ctx_get_pindir_fd(void)
466 : : {
467 : 166 : _cleanup_close_ int bpffs_fd = -1;
468 : 166 : _cleanup_close_ int pindir_fd = -1;
469 : :
470 : 166 : bpffs_fd = bf_opendir(bf_opts_bpffs_path());
471 [ - + ]: 166 : if (bpffs_fd < 0) {
472 [ # # ]: 0 : return bf_err_r(bpffs_fd, "failed to open bpffs at %s",
473 : : bf_opts_bpffs_path());
474 : : }
475 : :
476 : 166 : pindir_fd = bf_opendir_at(bpffs_fd, "bpfilter", true);
477 [ - + ]: 166 : if (pindir_fd < 0) {
478 [ # # ]: 0 : return bf_err_r(pindir_fd, "failed to open pin directory %s/bpfilter",
479 : : bf_opts_bpffs_path());
480 : : }
481 : :
482 : 166 : return TAKE_FD(pindir_fd);
483 : : }
484 : :
485 : 19 : int bf_ctx_rm_pindir(void)
486 : : {
487 : 19 : _cleanup_close_ int bpffs_fd = -1;
488 : : int r;
489 : :
490 : 19 : bpffs_fd = bf_opendir(bf_opts_bpffs_path());
491 [ - + ]: 19 : if (bpffs_fd < 0) {
492 [ # # ]: 0 : return bf_err_r(bpffs_fd, "failed to open bpffs at %s",
493 : : bf_opts_bpffs_path());
494 : : }
495 : :
496 : 19 : r = bf_rmdir_at(bpffs_fd, "bpfilter", false);
497 [ + + - + ]: 19 : if (r < 0 && r != -ENOTEMPTY && r != -ENOENT)
498 [ # # ]: 0 : return bf_err_r(r, "failed to remove bpfilter bpffs directory");
499 : :
500 : : return 0;
501 : : }
502 : :
503 : 144 : const struct bf_elfstub *bf_ctx_get_elfstub(enum bf_elfstub_id id)
504 : : {
505 : 144 : return _bf_global_ctx->stubs[id];
506 : : }
|