LCOV - code coverage report
Current view: top level - bpfilter - ctx.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 9.7 % 175 17
Test Date: 2025-03-25 15:17:39 Functions: 9.1 % 22 2

            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 <stdbool.h>
      10              : #include <stdlib.h>
      11              : #include <unistd.h>
      12              : 
      13              : #include "bpfilter/cgen/cgen.h"
      14              : #include "core/chain.h"
      15              : #include "core/dump.h"
      16              : #include "core/front.h"
      17              : #include "core/helper.h"
      18              : #include "core/hook.h"
      19              : #include "core/list.h"
      20              : #include "core/logger.h"
      21              : #include "core/marsh.h"
      22              : #include "core/ns.h"
      23              : 
      24              : #define _cleanup_bf_ctx_ __attribute__((cleanup(_bf_ctx_free)))
      25              : 
      26              : /**
      27              :  * @struct bf_ctx
      28              :  *
      29              :  * bpfilter working context. Only one context is used during the daemon's
      30              :  * lifetime.
      31              :  */
      32              : struct bf_ctx
      33              : {
      34              :     /// Namespaces the daemon was started in.
      35              :     struct bf_ns ns;
      36              : 
      37              :     /// Codegens defined in bpfilter. Defined as an array of lists as some
      38              :     /// hooks can have multiple codegens (e.g. XDP).
      39              :     bf_list cgens[_BF_HOOK_MAX];
      40              : };
      41              : 
      42              : static void _bf_ctx_free(struct bf_ctx **ctx);
      43              : 
      44              : /// Global daemon context. Hidden in this translation unit.
      45              : static struct bf_ctx *_bf_global_ctx = NULL;
      46              : 
      47              : /**
      48              :  * Get the requested BF_HOOK_XDP codegen from the list.
      49              :  *
      50              :  * Use @c opts->ifindex to find the expected codegen and return it.
      51              :  *
      52              :  * @param list List containing all the BF_HOOK_XDP codegens. Can't be NULL.
      53              :  * @param opts Hook options, @c opts->ifindex is used to find the correct
      54              :  *        codegen. Can't be NULL.
      55              :  * @return The request codegen, or NULL if not found.
      56              :  */
      57            0 : static struct bf_cgen *_bf_ctx_get_xdp_cgen(const bf_list *list,
      58              :                                             const struct bf_hook_opts *opts)
      59              : {
      60            0 :     bf_list_foreach (list, cgen_node) {
      61            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
      62              : 
      63            0 :         if (cgen->chain->hook_opts.ifindex == opts->ifindex)
      64              :             return cgen;
      65              :     }
      66              : 
      67              :     return NULL;
      68              : }
      69              : 
      70              : /**
      71              :  * Get the requested BF_HOOK_NF_* codegen from the list.
      72              :  *
      73              :  * There can be only one codegen defined for each BF_HOOK_NF_* hook, so we
      74              :  * return the first of the list, if defined.
      75              :  *
      76              :  * @param list List containing all the BF_HOOK_NF_* codegens. Can't be NULL.
      77              :  * @param opts Unused.
      78              :  * @return The requested codegen, or NULL if not found.
      79              :  */
      80            0 : static struct bf_cgen *_bf_ctx_get_nf_cgen(const bf_list *list,
      81              :                                            const struct bf_hook_opts *opts)
      82              : {
      83              :     UNUSED(opts);
      84              :     struct bf_list_node *node;
      85              : 
      86            0 :     bf_assert(list);
      87              : 
      88            0 :     node = bf_list_get_head(list);
      89            0 :     return node ? bf_list_node_get_data(node) : NULL;
      90              : }
      91              : 
      92            0 : static struct bf_cgen *_bf_ctx_get_cgroup_cgen(const bf_list *list,
      93              :                                                const struct bf_hook_opts *opts)
      94              : {
      95            0 :     bf_list_foreach (list, cgen_node) {
      96            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
      97              : 
      98            0 :         if (bf_streq(cgen->chain->hook_opts.cgroup, opts->cgroup))
      99              :             return cgen;
     100              :     }
     101              : 
     102              :     return NULL;
     103              : }
     104              : 
     105              : static struct bf_cgen *(*_bf_cgen_getters[])(
     106              :     const bf_list *list, const struct bf_hook_opts *opts) = {
     107              :     [BF_HOOK_XDP] = _bf_ctx_get_xdp_cgen,
     108              :     [BF_HOOK_TC_INGRESS] = _bf_ctx_get_xdp_cgen,
     109              :     [BF_HOOK_NF_PRE_ROUTING] = _bf_ctx_get_nf_cgen,
     110              :     [BF_HOOK_NF_LOCAL_IN] = _bf_ctx_get_nf_cgen,
     111              :     [BF_HOOK_CGROUP_INGRESS] = _bf_ctx_get_cgroup_cgen,
     112              :     [BF_HOOK_CGROUP_EGRESS] = _bf_ctx_get_cgroup_cgen,
     113              :     [BF_HOOK_NF_FORWARD] = _bf_ctx_get_nf_cgen,
     114              :     [BF_HOOK_NF_LOCAL_OUT] = _bf_ctx_get_nf_cgen,
     115              :     [BF_HOOK_NF_POST_ROUTING] = _bf_ctx_get_nf_cgen,
     116              :     [BF_HOOK_TC_EGRESS] = _bf_ctx_get_xdp_cgen,
     117              : };
     118              : 
     119              : static_assert(ARRAY_SIZE(_bf_cgen_getters) == _BF_HOOK_MAX,
     120              :               "missing entries in _bf_cgen_getters array");
     121              : 
     122              : /**
     123              :  * Create and initialize a new context.
     124              :  *
     125              :  * On failure, @p ctx is left unchanged.
     126              :  *
     127              :  * @param ctx New context to create. Can't be NULL.
     128              :  * @return 0 on success, negative errno value on failure.
     129              :  */
     130            4 : static int _bf_ctx_new(struct bf_ctx **ctx)
     131              : {
     132            3 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     133              :     int r;
     134              : 
     135            4 :     bf_assert(ctx);
     136              : 
     137            3 :     _ctx = malloc(sizeof(*_ctx));
     138            3 :     if (!_ctx)
     139              :         return -ENOMEM;
     140              : 
     141            3 :     r = bf_ns_init(&_ctx->ns, getpid());
     142            3 :     if (r)
     143            0 :         return bf_err_r(r, "failed to initialise current bf_ns");
     144              : 
     145           33 :     for (int i = 0; i < _BF_HOOK_MAX; ++i)
     146           30 :         _ctx->cgens[i] = bf_cgen_list();
     147              : 
     148            3 :     *ctx = TAKE_PTR(_ctx);
     149              : 
     150            3 :     return 0;
     151              : }
     152              : 
     153              : /**
     154              :  * Allocate a new context and initialise it from serialised data.
     155              :  *
     156              :  * @param ctx On success, points to the newly allocated and initialised
     157              :  *        context. Can't be NULL.
     158              :  * @param marsh Serialised data to use to initialise the context.
     159              :  * @return 0 on success, or negative errno value on failure.
     160              :  */
     161            0 : static int _bf_ctx_new_from_marsh(struct bf_ctx **ctx,
     162              :                                   const struct bf_marsh *marsh)
     163              : {
     164            0 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     165              :     struct bf_marsh *list_elem = NULL;
     166              :     int i = 0;
     167              :     int r;
     168              : 
     169            0 :     bf_assert(ctx && marsh);
     170              : 
     171            0 :     r = _bf_ctx_new(&_ctx);
     172            0 :     if (r < 0)
     173              :         return r;
     174              : 
     175              :     // Unmarsh bf_ctx.cgens
     176            0 :     while ((list_elem = bf_marsh_next_child(marsh, list_elem))) {
     177              :         struct bf_marsh *cgen_elem = NULL;
     178              : 
     179            0 :         while ((cgen_elem = bf_marsh_next_child(list_elem, cgen_elem))) {
     180            0 :             _cleanup_bf_cgen_ struct bf_cgen *cgen = NULL;
     181              : 
     182            0 :             r = bf_cgen_new_from_marsh(&cgen, cgen_elem);
     183            0 :             if (r < 0)
     184              :                 return r;
     185              : 
     186            0 :             r = bf_list_add_tail(&_ctx->cgens[i], cgen);
     187            0 :             if (r < 0)
     188              :                 return r;
     189              : 
     190            0 :             TAKE_PTR(cgen);
     191              :         }
     192              : 
     193            0 :         ++i;
     194              :     }
     195              : 
     196            0 :     *ctx = TAKE_PTR(_ctx);
     197              : 
     198            0 :     return 0;
     199              : }
     200              : 
     201              : /**
     202              :  * Free a context.
     203              :  *
     204              :  * If @p ctx points to a NULL pointer, this function does nothing. Once
     205              :  * the function returns, @p ctx points to a NULL pointer.
     206              :  *
     207              :  * @param ctx Context to free. Can't be NULL.
     208              :  */
     209            9 : static void _bf_ctx_free(struct bf_ctx **ctx)
     210              : {
     211            9 :     bf_assert(ctx);
     212              : 
     213            8 :     if (!*ctx)
     214              :         return;
     215              : 
     216            3 :     bf_ns_clean(&(*ctx)->ns);
     217              : 
     218           33 :     for (int i = 0; i < _BF_HOOK_MAX; ++i)
     219           30 :         bf_list_clean(&(*ctx)->cgens[i]);
     220              : 
     221              :     freep((void *)ctx);
     222              : }
     223              : 
     224              : /**
     225              :  * See @ref bf_ctx_dump for details.
     226              :  */
     227            0 : static void _bf_ctx_dump(const struct bf_ctx *ctx, prefix_t *prefix)
     228              : {
     229            0 :     DUMP(prefix, "struct bf_ctx at %p", ctx);
     230              : 
     231            0 :     bf_dump_prefix_push(prefix);
     232              : 
     233              :     // Namespaces
     234            0 :     DUMP(prefix, "ns: struct bf_ns")
     235            0 :     bf_dump_prefix_push(prefix);
     236              : 
     237            0 :     DUMP(prefix, "net: struct bf_ns_info");
     238            0 :     bf_dump_prefix_push(prefix);
     239            0 :     DUMP(prefix, "fd: %d", ctx->ns.net.fd);
     240            0 :     DUMP(bf_dump_prefix_last(prefix), "inode: %u", ctx->ns.net.inode);
     241            0 :     bf_dump_prefix_pop(prefix);
     242              : 
     243            0 :     DUMP(bf_dump_prefix_last(prefix), "mnt: struct bf_ns_info");
     244            0 :     bf_dump_prefix_push(prefix);
     245            0 :     DUMP(prefix, "fd: %d", ctx->ns.mnt.fd);
     246            0 :     DUMP(bf_dump_prefix_last(prefix), "inode: %u", ctx->ns.mnt.inode);
     247            0 :     bf_dump_prefix_pop(prefix);
     248              : 
     249            0 :     bf_dump_prefix_pop(prefix);
     250              : 
     251              :     // Codegens
     252            0 :     DUMP(bf_dump_prefix_last(prefix), "cgens: bf_list[%d]", _BF_HOOK_MAX);
     253            0 :     bf_dump_prefix_push(prefix);
     254              : 
     255            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     256            0 :         if (i == _BF_HOOK_MAX - 1)
     257            0 :             bf_dump_prefix_last(prefix);
     258              : 
     259            0 :         DUMP(prefix, "bf_list<bf_cgen>[%lu]", bf_list_size(&ctx->cgens[i]));
     260            0 :         bf_dump_prefix_push(prefix);
     261              : 
     262            0 :         bf_list_foreach (&ctx->cgens[i], cgen_node) {
     263            0 :             struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     264              : 
     265            0 :             if (bf_list_is_tail(&ctx->cgens[i], cgen_node))
     266            0 :                 bf_dump_prefix_last(prefix);
     267              : 
     268            0 :             bf_cgen_dump(cgen, prefix);
     269              :         }
     270              : 
     271            0 :         bf_dump_prefix_pop(prefix);
     272              :     }
     273              : 
     274            0 :     bf_dump_prefix_pop(prefix);
     275            0 : }
     276              : 
     277              : /**
     278              :  * Marsh a context.
     279              :  *
     280              :  * If the function succeeds, @p marsh will contain the marshalled context.
     281              :  *
     282              :  * @ref bf_ctx only contain the codegens, so the serialized data can be
     283              :  * flattened to:
     284              :  *   - ctx marsh
     285              :  *     - list marsh
     286              :  *       - cgen marsh
     287              :  *       - ...
     288              :  *     - list marsh
     289              :  *     - ...
     290              :  *
     291              :  * @param ctx Context to marsh.
     292              :  * @param marsh Marsh'd context.
     293              :  * @return 0 on success, negative errno value on failure.
     294              :  */
     295            0 : static int _bf_ctx_marsh(const struct bf_ctx *ctx, struct bf_marsh **marsh)
     296              : {
     297            0 :     _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
     298              :     int r;
     299              : 
     300            0 :     bf_assert(ctx && marsh);
     301              : 
     302            0 :     r = bf_marsh_new(&_marsh, NULL, 0);
     303            0 :     if (r)
     304            0 :         return bf_err_r(r, "failed to create marsh for context");
     305              : 
     306            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     307            0 :         _cleanup_bf_marsh_ struct bf_marsh *child = NULL;
     308              : 
     309            0 :         r = bf_list_marsh(&ctx->cgens[i], &child);
     310            0 :         if (r < 0)
     311              :             return r;
     312              : 
     313            0 :         r = bf_marsh_add_child_obj(&_marsh, child);
     314            0 :         if (r)
     315            0 :             return bf_err_r(r, "failed to append codegen marsh");
     316              :     }
     317              : 
     318            0 :     *marsh = TAKE_PTR(_marsh);
     319              : 
     320            0 :     return 0;
     321              : }
     322              : 
     323              : /**
     324              :  * See @ref bf_ctx_get_cgen for details.
     325              :  */
     326            0 : static struct bf_cgen *_bf_ctx_get_cgen(const struct bf_ctx *ctx,
     327              :                                         enum bf_hook hook,
     328              :                                         const struct bf_hook_opts *opts)
     329              : {
     330            0 :     bf_assert(ctx);
     331              : 
     332            0 :     return _bf_cgen_getters[hook](&ctx->cgens[hook], opts);
     333              : }
     334              : 
     335              : /**
     336              :  * See @ref bf_ctx_get_cgens_for_front for details.
     337              :  */
     338            0 : static int _bf_ctx_get_cgens_for_front(const struct bf_ctx *ctx, bf_list *cgens,
     339              :                                        enum bf_front front)
     340              : {
     341            0 :     _clean_bf_list_ bf_list _cgens = bf_list_default(NULL, bf_cgen_marsh);
     342              :     int r;
     343              : 
     344            0 :     bf_assert(ctx && cgens);
     345              : 
     346            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     347            0 :         bf_list_foreach (&ctx->cgens[i], cgen_node) {
     348            0 :             struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     349              : 
     350            0 :             if (cgen->front != front)
     351            0 :                 continue;
     352              : 
     353            0 :             r = bf_list_add_tail(&_cgens, cgen);
     354            0 :             if (r)
     355            0 :                 return bf_err_r(r, "failed to insert codegen into list");
     356              :         }
     357              :     }
     358              : 
     359            0 :     *cgens = bf_list_move(_cgens);
     360              : 
     361            0 :     return 0;
     362              : }
     363              : 
     364              : /**
     365              :  * See @ref bf_ctx_set_cgen for details.
     366              :  */
     367            0 : static int _bf_ctx_set_cgen(struct bf_ctx *ctx, struct bf_cgen *cgen)
     368              : {
     369            0 :     bf_assert(ctx && cgen);
     370              : 
     371            0 :     if (_bf_ctx_get_cgen(ctx, cgen->chain->hook, &cgen->chain->hook_opts))
     372            0 :         return bf_err_r(-EEXIST, "codegen already exists in context");
     373              : 
     374            0 :     return bf_list_add_tail(&ctx->cgens[cgen->chain->hook], cgen);
     375              : }
     376              : 
     377            0 : int bf_ctx_setup(void)
     378              : {
     379            0 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     380              :     int r;
     381              : 
     382            0 :     bf_assert(!_ctx);
     383              : 
     384            0 :     r = _bf_ctx_new(&_ctx);
     385            0 :     if (r)
     386            0 :         return bf_err_r(r, "failed to create new context");
     387              : 
     388            0 :     _bf_global_ctx = TAKE_PTR(_ctx);
     389              : 
     390            0 :     return 0;
     391              : }
     392              : 
     393            0 : void bf_ctx_teardown(bool clear)
     394              : {
     395            0 :     if (clear) {
     396            0 :         for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     397            0 :             bf_list_foreach (&_bf_global_ctx->cgens[i], cgen_node)
     398            0 :                 bf_cgen_unload(bf_list_node_get_data(cgen_node));
     399              :         }
     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 :     _cleanup_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 :     _cleanup_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 : int bf_ctx_flush(void)
     438              : {
     439            0 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     440              :     int r;
     441              :     int err = 0;
     442              : 
     443            0 :     r = _bf_ctx_new(&_ctx);
     444            0 :     if (r)
     445            0 :         return bf_err_r(r, "failed to create new context");
     446              : 
     447            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     448            0 :         bf_list_foreach (&_bf_global_ctx->cgens[i], cgen_node) {
     449            0 :             struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     450            0 :             r = bf_cgen_unload(cgen);
     451            0 :             if (r) {
     452            0 :                 bf_err("failed to unload a %s program attached to %s",
     453              :                        bf_front_to_str(cgen->front),
     454              :                        bf_hook_to_str(cgen->chain->hook));
     455            0 :                 err = err ?: r;
     456              :             }
     457              :         }
     458              :     }
     459              : 
     460            0 :     _bf_ctx_free(&_bf_global_ctx);
     461              : 
     462            0 :     _bf_global_ctx = TAKE_PTR(_ctx);
     463              : 
     464            0 :     if (err)
     465            0 :         bf_warn("the global context has been partially flushed");
     466              :     else
     467            0 :         bf_info("the global context has been flushed");
     468              : 
     469              :     return err;
     470              : }
     471              : 
     472            0 : bool bf_ctx_is_empty(void)
     473              : {
     474            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     475            0 :         if (!bf_list_is_empty(&_bf_global_ctx->cgens[i]))
     476              :             return false;
     477              :     }
     478              : 
     479              :     return true;
     480              : }
     481              : 
     482            0 : void bf_ctx_dump(prefix_t *prefix)
     483              : {
     484            0 :     _bf_ctx_dump(_bf_global_ctx, prefix);
     485            0 : }
     486              : 
     487            0 : struct bf_cgen *bf_ctx_get_cgen(enum bf_hook hook,
     488              :                                 const struct bf_hook_opts *opts)
     489              : {
     490            0 :     return _bf_ctx_get_cgen(_bf_global_ctx, hook, opts);
     491              : }
     492              : 
     493            0 : int bf_ctx_get_cgens_for_front(bf_list *cgens, enum bf_front front)
     494              : {
     495            0 :     return _bf_ctx_get_cgens_for_front(_bf_global_ctx, cgens, front);
     496              : }
     497              : 
     498            0 : int bf_ctx_set_cgen(struct bf_cgen *cgen)
     499              : {
     500            0 :     return _bf_ctx_set_cgen(_bf_global_ctx, cgen);
     501              : }
     502              : 
     503            0 : struct bf_ns *bf_ctx_get_ns(void)
     504              : {
     505            0 :     return &_bf_global_ctx->ns;
     506              : }
        

Generated by: LCOV version 2.0-1