LCOV - code coverage report
Current view: top level - bpfilter - ctx.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 13.3 % 210 28
Test Date: 2025-05-09 22:39:08 Functions: 15.4 % 26 4

            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              : }
        

Generated by: LCOV version 2.0-1