LCOV - code coverage report
Current view: top level - bpfilter - ctx.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 16.5 % 200 33
Test Date: 2025-09-30 16:37:25 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/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            0 : static int _bf_ctx_gen_token(void)
      58              : {
      59            0 :     _cleanup_close_ int mnt_fd = -1;
      60            0 :     _cleanup_close_ int bpffs_fd = -1;
      61            0 :     _cleanup_close_ int token_fd = -1;
      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 :     token_fd = bf_bpf_token_create(bpffs_fd);
      73            0 :     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            0 :     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            5 : static int _bf_ctx_new(struct bf_ctx **ctx)
      90              : {
      91            4 :     _free_bf_ctx_ struct bf_ctx *_ctx = NULL;
      92              :     int r;
      93              : 
      94            5 :     bf_assert(ctx);
      95              : 
      96            4 :     _ctx = calloc(1, sizeof(*_ctx));
      97            4 :     if (!_ctx)
      98              :         return -ENOMEM;
      99              : 
     100            4 :     r = bf_ns_init(&_ctx->ns, getpid());
     101            4 :     if (r)
     102            0 :         return bf_err_r(r, "failed to initialise current bf_ns");
     103              : 
     104            4 :     _ctx->token_fd = -1;
     105            4 :     if (bf_opts_with_bpf_token()) {
     106            0 :         _cleanup_close_ int token_fd = -1;
     107              : 
     108            0 :         r = bf_btf_kernel_has_token();
     109            0 :         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            0 :         if (r)
     115            0 :             return bf_err_r(r, "failed to check for BPF token support");
     116              : 
     117            0 :         token_fd = _bf_ctx_gen_token();
     118            0 :         if (token_fd < 0)
     119            0 :             return bf_err_r(token_fd, "failed to generate a BPF token");
     120              : 
     121            0 :         _ctx->token_fd = TAKE_FD(token_fd);
     122              :     }
     123              : 
     124            4 :     _ctx->cgens = bf_list_default(bf_cgen_free, bf_cgen_pack);
     125              : 
     126           20 :     for (enum bf_elfstub_id id = 0; id < _BF_ELFSTUB_MAX; ++id) {
     127           16 :         r = bf_elfstub_new(&_ctx->stubs[id], id);
     128           16 :         if (r)
     129            0 :             return bf_err_r(r, "failed to create ELF stub ID %u", id);
     130              :     }
     131              : 
     132            4 :     *ctx = TAKE_PTR(_ctx);
     133              : 
     134            4 :     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            0 : static int _bf_ctx_new_from_pack(struct bf_ctx **ctx, bf_rpack_node_t node)
     147              : {
     148            0 :     _free_bf_ctx_ struct bf_ctx *_ctx = NULL;
     149              :     bf_rpack_node_t child, array_node;
     150              :     int r;
     151              : 
     152            0 :     bf_assert(ctx);
     153              : 
     154            0 :     r = _bf_ctx_new(&_ctx);
     155            0 :     if (r < 0)
     156              :         return r;
     157              : 
     158            0 :     r = bf_rpack_kv_array(node, "cgens", &child);
     159            0 :     if (r)
     160              :         return r;
     161            0 :     bf_rpack_array_foreach (child, array_node) {
     162            0 :         _free_bf_cgen_ struct bf_cgen *cgen = NULL;
     163              : 
     164            0 :         r = bf_list_emplace(&_ctx->cgens, bf_cgen_new_from_pack, cgen,
     165              :                             array_node);
     166              :         if (r)
     167              :             return r;
     168              :     }
     169              : 
     170            0 :     *ctx = TAKE_PTR(_ctx);
     171              : 
     172            0 :     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           11 : static void _bf_ctx_free(struct bf_ctx **ctx)
     184              : {
     185           11 :     bf_assert(ctx);
     186              : 
     187           10 :     if (!*ctx)
     188              :         return;
     189              : 
     190            4 :     bf_ns_clean(&(*ctx)->ns);
     191            4 :     closep(&(*ctx)->token_fd);
     192            4 :     bf_list_clean(&(*ctx)->cgens);
     193              : 
     194           20 :     for (enum bf_elfstub_id id = 0; id < _BF_ELFSTUB_MAX; ++id)
     195           16 :         bf_elfstub_free(&(*ctx)->stubs[id]);
     196              : 
     197              :     freep((void *)ctx);
     198              : }
     199              : 
     200              : /**
     201              :  * See @ref bf_ctx_dump for details.
     202              :  */
     203            0 : static void _bf_ctx_dump(const struct bf_ctx *ctx, prefix_t *prefix)
     204              : {
     205            0 :     DUMP(prefix, "struct bf_ctx at %p", ctx);
     206              : 
     207            0 :     bf_dump_prefix_push(prefix);
     208              : 
     209              :     // Namespaces
     210            0 :     DUMP(prefix, "ns: struct bf_ns")
     211            0 :     bf_dump_prefix_push(prefix);
     212              : 
     213            0 :     DUMP(prefix, "net: struct bf_ns_info");
     214            0 :     bf_dump_prefix_push(prefix);
     215            0 :     DUMP(prefix, "fd: %d", ctx->ns.net.fd);
     216            0 :     DUMP(bf_dump_prefix_last(prefix), "inode: %u", ctx->ns.net.inode);
     217            0 :     bf_dump_prefix_pop(prefix);
     218              : 
     219            0 :     DUMP(bf_dump_prefix_last(prefix), "mnt: struct bf_ns_info");
     220            0 :     bf_dump_prefix_push(prefix);
     221            0 :     DUMP(prefix, "fd: %d", ctx->ns.mnt.fd);
     222            0 :     DUMP(bf_dump_prefix_last(prefix), "inode: %u", ctx->ns.mnt.inode);
     223            0 :     bf_dump_prefix_pop(prefix);
     224              : 
     225            0 :     bf_dump_prefix_pop(prefix);
     226              : 
     227            0 :     DUMP(prefix, "token_fd: %d", ctx->token_fd);
     228              : 
     229              :     // Codegens
     230            0 :     DUMP(bf_dump_prefix_last(prefix), "cgens: bf_list<struct bf_cgen>[%lu]",
     231              :          bf_list_size(&ctx->cgens));
     232            0 :     bf_dump_prefix_push(prefix);
     233            0 :     bf_list_foreach (&ctx->cgens, cgen_node) {
     234            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     235              : 
     236            0 :         if (bf_list_is_tail(&ctx->cgens, cgen_node))
     237            0 :             bf_dump_prefix_last(prefix);
     238              : 
     239            0 :         bf_cgen_dump(cgen, prefix);
     240              :     }
     241            0 :     bf_dump_prefix_pop(prefix);
     242              : 
     243            0 :     bf_dump_prefix_pop(prefix);
     244            0 : }
     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            0 : static int _bf_ctx_pack(const struct bf_ctx *ctx, bf_wpack_t *pack)
     264              : {
     265            0 :     bf_assert(ctx);
     266            0 :     bf_assert(pack);
     267              : 
     268            0 :     bf_wpack_kv_list(pack, "cgens", &ctx->cgens);
     269              : 
     270            0 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     271              : }
     272              : 
     273              : /**
     274              :  * See @ref bf_ctx_get_cgen for details.
     275              :  */
     276            6 : static struct bf_cgen *_bf_ctx_get_cgen(const struct bf_ctx *ctx,
     277              :                                         const char *name)
     278              : {
     279            6 :     bf_assert(ctx && name);
     280              : 
     281           16 :     bf_list_foreach (&ctx->cgens, cgen_node) {
     282            6 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     283              : 
     284            6 :         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            0 : static int _bf_ctx_get_cgens_for_front(const struct bf_ctx *ctx, bf_list *cgens,
     295              :                                        enum bf_front front)
     296              : {
     297            0 :     _clean_bf_list_ bf_list _cgens =
     298            0 :         bf_list_default(cgens->ops.free, cgens->ops.pack);
     299              :     int r;
     300              : 
     301            0 :     bf_assert(ctx && cgens);
     302              : 
     303            0 :     bf_list_foreach (&ctx->cgens, cgen_node) {
     304            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     305              : 
     306            0 :         if (cgen->front != front)
     307            0 :             continue;
     308              : 
     309            0 :         r = bf_list_add_tail(&_cgens, cgen);
     310            0 :         if (r)
     311            0 :             return bf_err_r(r, "failed to insert codegen into list");
     312              :     }
     313              : 
     314            0 :     *cgens = bf_list_move(_cgens);
     315              : 
     316            0 :     return 0;
     317              : }
     318              : 
     319              : /**
     320              :  * See @ref bf_ctx_set_cgen for details.
     321              :  */
     322            3 : static int _bf_ctx_set_cgen(struct bf_ctx *ctx, struct bf_cgen *cgen)
     323              : {
     324            3 :     bf_assert(ctx && cgen);
     325              : 
     326            3 :     if (_bf_ctx_get_cgen(ctx, cgen->chain->name))
     327            1 :         return bf_err_r(-EEXIST, "codegen already exists in context");
     328              : 
     329            2 :     return bf_list_add_tail(&ctx->cgens, cgen);
     330              : }
     331              : 
     332            0 : static int _bf_ctx_delete_cgen(struct bf_ctx *ctx, struct bf_cgen *cgen,
     333              :                                bool unload)
     334              : {
     335            0 :     bf_list_foreach (&ctx->cgens, cgen_node) {
     336            0 :         struct bf_cgen *_cgen = bf_list_node_get_data(cgen_node);
     337              : 
     338            0 :         if (_cgen != cgen)
     339              :             continue;
     340              : 
     341            0 :         if (unload)
     342            0 :             bf_cgen_unload(_cgen);
     343              : 
     344            0 :         bf_list_delete(&ctx->cgens, cgen_node);
     345              : 
     346            0 :         return 0;
     347              :     }
     348              : 
     349              :     return -ENOENT;
     350              : }
     351              : 
     352            0 : int bf_ctx_setup(void)
     353              : {
     354            0 :     _free_bf_ctx_ struct bf_ctx *_ctx = NULL;
     355              :     int r;
     356              : 
     357            0 :     bf_assert(!_ctx);
     358              : 
     359            0 :     r = _bf_ctx_new(&_ctx);
     360            0 :     if (r)
     361            0 :         return bf_err_r(r, "failed to create new context");
     362              : 
     363            0 :     _bf_global_ctx = TAKE_PTR(_ctx);
     364              : 
     365            0 :     return 0;
     366              : }
     367              : 
     368            0 : void bf_ctx_teardown(bool clear)
     369              : {
     370            0 :     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            0 :     _bf_ctx_free(&_bf_global_ctx);
     376            0 : }
     377              : 
     378            0 : int bf_ctx_save(bf_wpack_t *pack)
     379              : {
     380              :     int r;
     381              : 
     382            0 :     bf_assert(pack);
     383              : 
     384            0 :     r = _bf_ctx_pack(_bf_global_ctx, pack);
     385            0 :     if (r)
     386            0 :         return bf_err_r(r, "failed to serialize context");
     387              : 
     388              :     return 0;
     389              : }
     390              : 
     391            0 : int bf_ctx_load(bf_rpack_node_t node)
     392              : {
     393            0 :     _free_bf_ctx_ struct bf_ctx *ctx = NULL;
     394              :     int r;
     395              : 
     396            0 :     r = _bf_ctx_new_from_pack(&ctx, node);
     397            0 :     if (r)
     398            0 :         return bf_err_r(r, "failed to deserialize context");
     399              : 
     400            0 :     _bf_global_ctx = TAKE_PTR(ctx);
     401              : 
     402            0 :     return 0;
     403              : }
     404              : 
     405            0 : static void _bf_ctx_flush(struct bf_ctx *ctx, enum bf_front front)
     406              : {
     407            0 :     bf_assert(ctx);
     408              : 
     409            0 :     bf_list_foreach (&ctx->cgens, cgen_node) {
     410            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     411              : 
     412            0 :         if (cgen->front != front)
     413            0 :             continue;
     414              : 
     415            0 :         bf_cgen_unload(cgen);
     416            0 :         bf_list_delete(&ctx->cgens, cgen_node);
     417              :     }
     418            0 : }
     419              : 
     420            0 : void bf_ctx_flush(enum bf_front front)
     421              : {
     422            0 :     _bf_ctx_flush(_bf_global_ctx, front);
     423            0 : }
     424              : 
     425            0 : bool bf_ctx_is_empty(void)
     426              : {
     427            0 :     return bf_list_is_empty(&_bf_global_ctx->cgens);
     428              : }
     429              : 
     430            0 : void bf_ctx_dump(prefix_t *prefix)
     431              : {
     432            0 :     _bf_ctx_dump(_bf_global_ctx, prefix);
     433            0 : }
     434              : 
     435            0 : struct bf_cgen *bf_ctx_get_cgen(const char *name)
     436              : {
     437            0 :     return _bf_ctx_get_cgen(_bf_global_ctx, name);
     438              : }
     439              : 
     440            0 : int bf_ctx_get_cgens_for_front(bf_list *cgens, enum bf_front front)
     441              : {
     442            0 :     return _bf_ctx_get_cgens_for_front(_bf_global_ctx, cgens, front);
     443              : }
     444              : 
     445            0 : int bf_ctx_set_cgen(struct bf_cgen *cgen)
     446              : {
     447            0 :     return _bf_ctx_set_cgen(_bf_global_ctx, cgen);
     448              : }
     449              : 
     450            0 : int bf_ctx_delete_cgen(struct bf_cgen *cgen, bool unload)
     451              : {
     452            0 :     return _bf_ctx_delete_cgen(_bf_global_ctx, cgen, unload);
     453              : }
     454              : 
     455            0 : struct bf_ns *bf_ctx_get_ns(void)
     456              : {
     457            0 :     return &_bf_global_ctx->ns;
     458              : }
     459              : 
     460            0 : int bf_ctx_token(void)
     461              : {
     462            0 :     return _bf_global_ctx->token_fd;
     463              : }
     464              : 
     465            0 : int bf_ctx_get_pindir_fd(void)
     466              : {
     467            0 :     _cleanup_close_ int bpffs_fd = -1;
     468            0 :     _cleanup_close_ int pindir_fd = -1;
     469              : 
     470            0 :     bpffs_fd = bf_opendir(bf_opts_bpffs_path());
     471            0 :     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            0 :     pindir_fd = bf_opendir_at(bpffs_fd, "bpfilter", true);
     477            0 :     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            0 :     return TAKE_FD(pindir_fd);
     483              : }
     484              : 
     485            0 : int bf_ctx_rm_pindir(void)
     486              : {
     487            0 :     _cleanup_close_ int bpffs_fd = -1;
     488              :     int r;
     489              : 
     490            0 :     bpffs_fd = bf_opendir(bf_opts_bpffs_path());
     491            0 :     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            0 :     r = bf_rmdir_at(bpffs_fd, "bpfilter", false);
     497            0 :     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            0 : const struct bf_elfstub *bf_ctx_get_elfstub(enum bf_elfstub_id id)
     504              : {
     505            0 :     return _bf_global_ctx->stubs[id];
     506              : }
        

Generated by: LCOV version 2.0-1