LCOV - code coverage report
Current view: top level - bpfilter - ctx.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 8.0 % 138 11
Test Date: 2025-02-26 17:59:59 Functions: 10.5 % 19 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              : 
      12              : #include "bpfilter/cgen/cgen.h"
      13              : #include "core/chain.h"
      14              : #include "core/dump.h"
      15              : #include "core/front.h"
      16              : #include "core/helper.h"
      17              : #include "core/hook.h"
      18              : #include "core/list.h"
      19              : #include "core/logger.h"
      20              : #include "core/marsh.h"
      21              : 
      22              : #define _cleanup_bf_ctx_ __attribute__((cleanup(_bf_ctx_free)))
      23              : 
      24              : /**
      25              :  * @struct bf_ctx
      26              :  *
      27              :  * bpfilter working context. Only one context is used during the daemon's
      28              :  * lifetime.
      29              :  */
      30              : struct bf_ctx
      31              : {
      32              :     /// Codegens defined in bpfilter. Defined as an array of lists as some
      33              :     /// hooks can have multiple codegens (e.g. XDP).
      34              :     bf_list cgens[_BF_HOOK_MAX];
      35              : };
      36              : 
      37              : static void _bf_ctx_free(struct bf_ctx **ctx);
      38              : 
      39              : /// Global daemon context. Hidden in this translation unit.
      40              : static struct bf_ctx *_bf_global_ctx = NULL;
      41              : 
      42              : /**
      43              :  * Get the requested BF_HOOK_XDP codegen from the list.
      44              :  *
      45              :  * Use @c opts->ifindex to find the expected codegen and return it.
      46              :  *
      47              :  * @param list List containing all the BF_HOOK_XDP codegens. Can't be NULL.
      48              :  * @param opts Hook options, @c opts->ifindex is used to find the correct
      49              :  *        codegen. Can't be NULL.
      50              :  * @return The request codegen, or NULL if not found.
      51              :  */
      52            0 : static struct bf_cgen *_bf_ctx_get_xdp_cgen(const bf_list *list,
      53              :                                             const struct bf_hook_opts *opts)
      54              : {
      55            0 :     bf_list_foreach (list, cgen_node) {
      56            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
      57              : 
      58            0 :         if (cgen->chain->hook_opts.ifindex == opts->ifindex)
      59              :             return cgen;
      60              :     }
      61              : 
      62              :     return NULL;
      63              : }
      64              : 
      65              : /**
      66              :  * Get the requested BF_HOOK_NF_* codegen from the list.
      67              :  *
      68              :  * There can be only one codegen defined for each BF_HOOK_NF_* hook, so we
      69              :  * return the first of the list, if defined.
      70              :  *
      71              :  * @param list List containing all the BF_HOOK_NF_* codegens. Can't be NULL.
      72              :  * @param opts Unused.
      73              :  * @return The requested codegen, or NULL if not found.
      74              :  */
      75            0 : static struct bf_cgen *_bf_ctx_get_nf_cgen(const bf_list *list,
      76              :                                            const struct bf_hook_opts *opts)
      77              : {
      78              :     UNUSED(opts);
      79              :     struct bf_list_node *node;
      80              : 
      81            0 :     bf_assert(list);
      82              : 
      83            0 :     node = bf_list_get_head(list);
      84            0 :     return node ? bf_list_node_get_data(node) : NULL;
      85              : }
      86              : 
      87            0 : static struct bf_cgen *_bf_ctx_get_cgroup_cgen(const bf_list *list,
      88              :                                                const struct bf_hook_opts *opts)
      89              : {
      90            0 :     bf_list_foreach (list, cgen_node) {
      91            0 :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
      92              : 
      93            0 :         if (bf_streq(cgen->chain->hook_opts.cgroup, opts->cgroup))
      94              :             return cgen;
      95              :     }
      96              : 
      97              :     return NULL;
      98              : }
      99              : 
     100              : static struct bf_cgen *(*_bf_cgen_getters[])(
     101              :     const bf_list *list, const struct bf_hook_opts *opts) = {
     102              :     [BF_HOOK_XDP] = _bf_ctx_get_xdp_cgen,
     103              :     [BF_HOOK_TC_INGRESS] = _bf_ctx_get_xdp_cgen,
     104              :     [BF_HOOK_NF_PRE_ROUTING] = _bf_ctx_get_nf_cgen,
     105              :     [BF_HOOK_NF_LOCAL_IN] = _bf_ctx_get_nf_cgen,
     106              :     [BF_HOOK_CGROUP_INGRESS] = _bf_ctx_get_cgroup_cgen,
     107              :     [BF_HOOK_CGROUP_EGRESS] = _bf_ctx_get_cgroup_cgen,
     108              :     [BF_HOOK_NF_FORWARD] = _bf_ctx_get_nf_cgen,
     109              :     [BF_HOOK_NF_LOCAL_OUT] = _bf_ctx_get_nf_cgen,
     110              :     [BF_HOOK_NF_POST_ROUTING] = _bf_ctx_get_nf_cgen,
     111              :     [BF_HOOK_TC_EGRESS] = _bf_ctx_get_xdp_cgen,
     112              : };
     113              : 
     114              : static_assert(ARRAY_SIZE(_bf_cgen_getters) == _BF_HOOK_MAX,
     115              :               "missing entries in _bf_cgen_getters array");
     116              : 
     117              : /**
     118              :  * Create and initialize a new context.
     119              :  *
     120              :  * On failure, @p ctx is left unchanged.
     121              :  *
     122              :  * @param ctx New context to create. Can't be NULL.
     123              :  * @return 0 on success, negative errno value on failure.
     124              :  */
     125            4 : static int _bf_ctx_new(struct bf_ctx **ctx)
     126              : {
     127            4 :     bf_assert(ctx);
     128              : 
     129            3 :     *ctx = malloc(sizeof(struct bf_ctx));
     130            3 :     if (!*ctx)
     131              :         return -ENOMEM;
     132              : 
     133           33 :     for (int i = 0; i < _BF_HOOK_MAX; ++i)
     134           30 :         (*ctx)->cgens[i] = bf_cgen_list();
     135              : 
     136              :     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 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     151              :     struct bf_marsh *list_elem = NULL;
     152              :     int i = 0;
     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 :     while ((list_elem = bf_marsh_next_child(marsh, list_elem))) {
     163              :         struct bf_marsh *cgen_elem = NULL;
     164              : 
     165            0 :         while ((cgen_elem = bf_marsh_next_child(list_elem, cgen_elem))) {
     166            0 :             _cleanup_bf_cgen_ struct bf_cgen *cgen = NULL;
     167              : 
     168            0 :             r = bf_cgen_new_from_marsh(&cgen, cgen_elem);
     169            0 :             if (r < 0)
     170              :                 return r;
     171              : 
     172            0 :             r = bf_list_add_tail(&_ctx->cgens[i], cgen);
     173            0 :             if (r < 0)
     174              :                 return r;
     175              : 
     176            0 :             TAKE_PTR(cgen);
     177              :         }
     178              : 
     179            0 :         ++i;
     180              :     }
     181              : 
     182            0 :     *ctx = TAKE_PTR(_ctx);
     183              : 
     184            0 :     return 0;
     185              : }
     186              : 
     187              : /**
     188              :  * Free a context.
     189              :  *
     190              :  * If @p ctx points to a NULL pointer, this function does nothing. Once
     191              :  * the function returns, @p ctx points to a NULL pointer.
     192              :  *
     193              :  * @param ctx Context to free. Can't be NULL.
     194              :  */
     195            6 : static void _bf_ctx_free(struct bf_ctx **ctx)
     196              : {
     197            6 :     bf_assert(ctx);
     198              : 
     199            5 :     if (!*ctx)
     200              :         return;
     201              : 
     202           33 :     for (int i = 0; i < _BF_HOOK_MAX; ++i)
     203           30 :         bf_list_clean(&(*ctx)->cgens[i]);
     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              :     // Codegens
     218            0 :     DUMP(bf_dump_prefix_last(prefix), "cgens: bf_list[%d]", _BF_HOOK_MAX);
     219            0 :     bf_dump_prefix_push(prefix);
     220              : 
     221            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     222            0 :         if (i == _BF_HOOK_MAX - 1)
     223            0 :             bf_dump_prefix_last(prefix);
     224              : 
     225            0 :         DUMP(prefix, "bf_list<bf_cgen>[%lu]", bf_list_size(&ctx->cgens[i]));
     226            0 :         bf_dump_prefix_push(prefix);
     227              : 
     228            0 :         bf_list_foreach (&ctx->cgens[i], 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[i], cgen_node))
     232            0 :                 bf_dump_prefix_last(prefix);
     233              : 
     234            0 :             bf_cgen_dump(cgen, prefix);
     235              :         }
     236              : 
     237            0 :         bf_dump_prefix_pop(prefix);
     238              :     }
     239              : 
     240            0 :     bf_dump_prefix_pop(prefix);
     241            0 : }
     242              : 
     243              : /**
     244              :  * Marsh a context.
     245              :  *
     246              :  * If the function succeeds, @p marsh will contain the marshalled context.
     247              :  *
     248              :  * @ref bf_ctx only contain the codegens, so the serialized data can be
     249              :  * flattened to:
     250              :  *   - ctx marsh
     251              :  *     - list marsh
     252              :  *       - cgen marsh
     253              :  *       - ...
     254              :  *     - list marsh
     255              :  *     - ...
     256              :  *
     257              :  * @param ctx Context to marsh.
     258              :  * @param marsh Marsh'd context.
     259              :  * @return 0 on success, negative errno value on failure.
     260              :  */
     261            0 : static int _bf_ctx_marsh(const struct bf_ctx *ctx, struct bf_marsh **marsh)
     262              : {
     263            0 :     _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
     264              :     int r;
     265              : 
     266            0 :     bf_assert(ctx && marsh);
     267              : 
     268            0 :     r = bf_marsh_new(&_marsh, NULL, 0);
     269            0 :     if (r)
     270            0 :         return bf_err_r(r, "failed to create marsh for context");
     271              : 
     272            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     273            0 :         _cleanup_bf_marsh_ struct bf_marsh *child = NULL;
     274              : 
     275            0 :         r = bf_list_marsh(&ctx->cgens[i], &child);
     276            0 :         if (r < 0)
     277              :             return r;
     278              : 
     279            0 :         r = bf_marsh_add_child_obj(&_marsh, child);
     280            0 :         if (r)
     281            0 :             return bf_err_r(r, "failed to append codegen marsh");
     282              :     }
     283              : 
     284            0 :     *marsh = TAKE_PTR(_marsh);
     285              : 
     286            0 :     return 0;
     287              : }
     288              : 
     289              : /**
     290              :  * See @ref bf_ctx_get_cgen for details.
     291              :  */
     292            0 : static struct bf_cgen *_bf_ctx_get_cgen(const struct bf_ctx *ctx,
     293              :                                         enum bf_hook hook,
     294              :                                         const struct bf_hook_opts *opts)
     295              : {
     296            0 :     bf_assert(ctx);
     297              : 
     298            0 :     return _bf_cgen_getters[hook](&ctx->cgens[hook], opts);
     299              : }
     300              : 
     301              : /**
     302              :  * See @ref bf_ctx_set_cgen for details.
     303              :  */
     304            0 : static int _bf_ctx_set_cgen(struct bf_ctx *ctx, struct bf_cgen *cgen)
     305              : {
     306            0 :     bf_assert(ctx && cgen);
     307              : 
     308            0 :     if (_bf_ctx_get_cgen(ctx, cgen->chain->hook, &cgen->chain->hook_opts))
     309            0 :         return bf_err_r(-EEXIST, "codegen already exists in context");
     310              : 
     311            0 :     return bf_list_add_tail(&ctx->cgens[cgen->chain->hook], cgen);
     312              : }
     313              : 
     314            0 : int bf_ctx_setup(void)
     315              : {
     316            0 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     317              :     int r;
     318              : 
     319            0 :     bf_assert(!_ctx);
     320              : 
     321            0 :     r = _bf_ctx_new(&_ctx);
     322            0 :     if (r)
     323            0 :         return bf_err_r(r, "failed to create new context");
     324              : 
     325            0 :     _bf_global_ctx = TAKE_PTR(_ctx);
     326              : 
     327            0 :     return 0;
     328              : }
     329              : 
     330            0 : void bf_ctx_teardown(bool clear)
     331              : {
     332            0 :     if (clear) {
     333            0 :         for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     334            0 :             bf_list_foreach (&_bf_global_ctx->cgens[i], cgen_node)
     335            0 :                 bf_cgen_unload(bf_list_node_get_data(cgen_node));
     336              :         }
     337              :     }
     338              : 
     339            0 :     _bf_ctx_free(&_bf_global_ctx);
     340            0 : }
     341              : 
     342            0 : int bf_ctx_save(struct bf_marsh **marsh)
     343              : {
     344            0 :     _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
     345              :     int r;
     346              : 
     347            0 :     bf_assert(marsh);
     348              : 
     349            0 :     r = _bf_ctx_marsh(_bf_global_ctx, &_marsh);
     350            0 :     if (r)
     351            0 :         return bf_err_r(r, "failed to serialize context");
     352              : 
     353            0 :     *marsh = TAKE_PTR(_marsh);
     354              : 
     355            0 :     return 0;
     356              : }
     357              : 
     358            0 : int bf_ctx_load(const struct bf_marsh *marsh)
     359              : {
     360            0 :     _cleanup_bf_ctx_ struct bf_ctx *ctx = NULL;
     361              :     int r;
     362              : 
     363            0 :     bf_assert(marsh);
     364              : 
     365            0 :     r = _bf_ctx_new_from_marsh(&ctx, marsh);
     366            0 :     if (r)
     367            0 :         return bf_err_r(r, "failed to deserialize context");
     368              : 
     369            0 :     _bf_global_ctx = TAKE_PTR(ctx);
     370              : 
     371            0 :     return 0;
     372              : }
     373              : 
     374            0 : int bf_ctx_flush(void)
     375              : {
     376            0 :     _cleanup_bf_ctx_ struct bf_ctx *_ctx = NULL;
     377              :     int r;
     378              :     int err = 0;
     379              : 
     380            0 :     r = _bf_ctx_new(&_ctx);
     381            0 :     if (r)
     382            0 :         return bf_err_r(r, "failed to create new context");
     383              : 
     384            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     385            0 :         bf_list_foreach (&_bf_global_ctx->cgens[i], cgen_node) {
     386            0 :             struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     387            0 :             r = bf_cgen_unload(cgen);
     388            0 :             if (r) {
     389            0 :                 bf_err("failed to unload a %s program attached to %s",
     390              :                        bf_front_to_str(cgen->front),
     391              :                        bf_hook_to_str(cgen->chain->hook));
     392            0 :                 err = err ?: r;
     393              :             }
     394              :         }
     395              :     }
     396              : 
     397            0 :     _bf_ctx_free(&_bf_global_ctx);
     398              : 
     399            0 :     _bf_global_ctx = TAKE_PTR(_ctx);
     400              : 
     401            0 :     if (err)
     402            0 :         bf_warn("the global context has been partially flushed");
     403              :     else
     404            0 :         bf_info("the global context has been flushed");
     405              : 
     406              :     return err;
     407              : }
     408              : 
     409            0 : bool bf_ctx_is_empty(void)
     410              : {
     411            0 :     for (int i = 0; i < _BF_HOOK_MAX; ++i) {
     412            0 :         if (!bf_list_is_empty(&_bf_global_ctx->cgens[i]))
     413              :             return false;
     414              :     }
     415              : 
     416              :     return true;
     417              : }
     418              : 
     419            0 : void bf_ctx_dump(prefix_t *prefix)
     420              : {
     421            0 :     _bf_ctx_dump(_bf_global_ctx, prefix);
     422            0 : }
     423              : 
     424            0 : struct bf_cgen *bf_ctx_get_cgen(enum bf_hook hook,
     425              :                                 const struct bf_hook_opts *opts)
     426              : {
     427            0 :     return _bf_ctx_get_cgen(_bf_global_ctx, hook, opts);
     428              : }
     429              : 
     430            0 : int bf_ctx_set_cgen(struct bf_cgen *cgen)
     431              : {
     432            0 :     return _bf_ctx_set_cgen(_bf_global_ctx, cgen);
     433              : }
        

Generated by: LCOV version 2.0-1