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