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