LCOV - code coverage report
Current view: top level - bpfilter - ctx.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 15.1 % 218 33
Test Date: 2025-07-03 20:16:54 Functions: 14.8 % 27 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/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              : }
        

Generated by: LCOV version 2.0-1