LCOV - code coverage report
Current view: top level - bpfilter/cgen - cgen.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 23.7 % 236 56
Test Date: 2025-05-09 22:39:08 Functions: 28.6 % 14 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 "bpfilter/cgen/cgen.h"
       7              : 
       8              : #include <errno.h>
       9              : #include <stddef.h>
      10              : #include <stdint.h>
      11              : #include <stdlib.h>
      12              : #include <string.h>
      13              : #include <sys/types.h>
      14              : 
      15              : #include "bpfilter/cgen/dump.h"
      16              : #include "bpfilter/cgen/prog/link.h"
      17              : #include "bpfilter/cgen/program.h"
      18              : #include "bpfilter/ctx.h"
      19              : #include "bpfilter/opts.h"
      20              : #include "core/chain.h"
      21              : #include "core/counter.h"
      22              : #include "core/dump.h"
      23              : #include "core/front.h"
      24              : #include "core/helper.h"
      25              : #include "core/hook.h"
      26              : #include "core/io.h"
      27              : #include "core/list.h"
      28              : #include "core/logger.h"
      29              : #include "core/marsh.h"
      30              : #include "core/ns.h"
      31              : #include "core/rule.h"
      32              : 
      33            0 : static int _bf_cgen_get_chain_pindir_fd(const char *name)
      34              : {
      35            0 :     _cleanup_close_ int bf_fd = -1;
      36            0 :     _cleanup_close_ int chain_fd = -1;
      37              : 
      38            0 :     bf_assert(name);
      39              : 
      40            0 :     bf_fd = bf_ctx_get_pindir_fd();
      41            0 :     if (bf_fd < 0)
      42              :         return bf_fd;
      43              : 
      44            0 :     chain_fd = bf_opendir_at(bf_fd, name, true);
      45            0 :     if (chain_fd < 0)
      46              :         return chain_fd;
      47              : 
      48            0 :     return TAKE_FD(chain_fd);
      49              : }
      50              : 
      51           12 : int bf_cgen_new(struct bf_cgen **cgen, enum bf_front front,
      52              :                 struct bf_chain **chain)
      53              : {
      54           15 :     bf_assert(cgen && chain && *chain);
      55              : 
      56            9 :     *cgen = malloc(sizeof(struct bf_cgen));
      57            9 :     if (!*cgen)
      58              :         return -ENOMEM;
      59              : 
      60            8 :     (*cgen)->front = front;
      61            8 :     (*cgen)->program = NULL;
      62            8 :     (*cgen)->chain = TAKE_PTR(*chain);
      63              : 
      64            8 :     return 0;
      65              : }
      66              : 
      67            3 : int bf_cgen_new_from_marsh(struct bf_cgen **cgen, const struct bf_marsh *marsh)
      68              : {
      69            1 :     _cleanup_bf_cgen_ struct bf_cgen *_cgen = NULL;
      70            1 :     _cleanup_bf_program_ struct bf_program *program = NULL;
      71            1 :     _cleanup_bf_chain_ struct bf_chain *chain = NULL;
      72              :     struct bf_marsh *marsh_elem = NULL;
      73              :     enum bf_front front;
      74              :     int r;
      75              : 
      76            3 :     bf_assert(cgen);
      77            2 :     bf_assert(marsh);
      78              : 
      79            1 :     if (!(marsh_elem = bf_marsh_next_child(marsh, marsh_elem)))
      80              :         return -EINVAL;
      81            1 :     memcpy(&front, marsh_elem->data, sizeof(front));
      82              : 
      83            1 :     if (!(marsh_elem = bf_marsh_next_child(marsh, marsh_elem)))
      84              :         return -EINVAL;
      85              : 
      86            1 :     r = bf_chain_new_from_marsh(&chain, marsh_elem);
      87            1 :     if (r < 0)
      88              :         return r;
      89              : 
      90            1 :     r = bf_cgen_new(&_cgen, front, &chain);
      91            1 :     if (r)
      92            0 :         return bf_err_r(r, "failed to allocate codegen object");
      93              : 
      94            1 :     if (!(marsh_elem = bf_marsh_next_child(marsh, marsh_elem)))
      95              :         return -EINVAL;
      96            1 :     if (!bf_marsh_is_empty(marsh_elem)) {
      97            0 :         _cleanup_close_ int dir_fd = -1;
      98              : 
      99            0 :         if ((dir_fd = _bf_cgen_get_chain_pindir_fd(_cgen->chain->name)) < 0) {
     100            0 :             return bf_err_r(dir_fd,
     101              :                             "failed to open chain pin directory for '%s'",
     102              :                             _cgen->chain->name);
     103              :         }
     104              : 
     105            0 :         r = bf_program_unmarsh(marsh_elem, &_cgen->program, _cgen->chain,
     106              :                                dir_fd);
     107            0 :         if (r < 0)
     108              :             return r;
     109              :     }
     110              : 
     111            1 :     if (bf_marsh_next_child(marsh, marsh_elem))
     112            0 :         bf_warn("codegen marsh has more children than expected");
     113              : 
     114            1 :     *cgen = TAKE_PTR(_cgen);
     115              : 
     116            1 :     return 0;
     117              : }
     118              : 
     119           12 : void bf_cgen_free(struct bf_cgen **cgen)
     120              : {
     121            3 :     _cleanup_close_ int pin_fd = -1;
     122              : 
     123           12 :     bf_assert(cgen);
     124              : 
     125           11 :     if (!*cgen)
     126              :         return;
     127              : 
     128              :     /* Perform a non-recursive removal of the chain's pin directory: if
     129              :      * the chain hasn't been pinned (e.g. due to a failure), the pin directory
     130              :      * will be empty and will be removed. If the chain is valid and pinned, then
     131              :      * the removal of the pin directory will fail, but that's alright. */
     132            8 :     if (bf_opts_persist() && (pin_fd = bf_ctx_get_pindir_fd()) >= 0)
     133            0 :         bf_rmdir_at(pin_fd, (*cgen)->chain->name, false);
     134              : 
     135            8 :     bf_program_free(&(*cgen)->program);
     136            8 :     bf_chain_free(&(*cgen)->chain);
     137              : 
     138            8 :     free(*cgen);
     139            8 :     *cgen = NULL;
     140              : }
     141              : 
     142            3 : int bf_cgen_marsh(const struct bf_cgen *cgen, struct bf_marsh **marsh)
     143              : {
     144            1 :     _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
     145              :     int r;
     146              : 
     147            3 :     bf_assert(cgen);
     148            2 :     bf_assert(marsh);
     149              : 
     150            1 :     r = bf_marsh_new(&_marsh, NULL, 0);
     151            1 :     if (r)
     152              :         return r;
     153              : 
     154            1 :     r = bf_marsh_add_child_raw(&_marsh, &cgen->front, sizeof(cgen->front));
     155            1 :     if (r < 0)
     156            0 :         return bf_err_r(r, "failed to serialize codegen");
     157              : 
     158              :     {
     159              :         // Serialize cgen.chain
     160            1 :         _cleanup_bf_marsh_ struct bf_marsh *chain_elem = NULL;
     161              : 
     162            1 :         r = bf_chain_marsh(cgen->chain, &chain_elem);
     163            1 :         if (r < 0)
     164              :             return r;
     165              : 
     166            1 :         r = bf_marsh_add_child_obj(&_marsh, chain_elem);
     167            1 :         if (r < 0)
     168              :             return r;
     169              :     }
     170              : 
     171              :     {
     172            1 :         _cleanup_bf_marsh_ struct bf_marsh *prog_elem = NULL;
     173              : 
     174            1 :         if (cgen->program) {
     175            0 :             r = bf_program_marsh(cgen->program, &prog_elem);
     176            0 :             if (r < 0)
     177              :                 return r;
     178              :         } else {
     179            1 :             r = bf_marsh_new(&prog_elem, NULL, 0);
     180            1 :             if (r < 0)
     181              :                 return r;
     182              :         }
     183              : 
     184            1 :         r = bf_marsh_add_child_obj(&_marsh, prog_elem);
     185            1 :         if (r)
     186              :             return r;
     187              :     }
     188              : 
     189            1 :     *marsh = TAKE_PTR(_marsh);
     190              : 
     191            1 :     return 0;
     192              : }
     193              : 
     194            0 : void bf_cgen_dump(const struct bf_cgen *cgen, prefix_t *prefix)
     195              : {
     196            0 :     bf_assert(cgen);
     197            0 :     bf_assert(prefix);
     198              : 
     199            0 :     DUMP(prefix, "struct bf_cgen at %p", cgen);
     200              : 
     201            0 :     bf_dump_prefix_push(prefix);
     202            0 :     DUMP(prefix, "front: %s", bf_front_to_str(cgen->front));
     203              : 
     204              :     // Chain
     205            0 :     DUMP(prefix, "chain: struct bf_chain *");
     206            0 :     bf_dump_prefix_push(prefix);
     207            0 :     bf_chain_dump(cgen->chain, bf_dump_prefix_last(prefix));
     208            0 :     bf_dump_prefix_pop(prefix);
     209              : 
     210              :     // Programs
     211            0 :     if (cgen->program) {
     212            0 :         DUMP(bf_dump_prefix_last(prefix), "program: struct bf_program *");
     213            0 :         bf_dump_prefix_push(prefix);
     214            0 :         bf_program_dump(cgen->program, bf_dump_prefix_last(prefix));
     215            0 :         bf_dump_prefix_pop(prefix);
     216              :     } else {
     217            0 :         DUMP(bf_dump_prefix_last(prefix), "program: (struct bf_program *)NULL");
     218              :     }
     219              : 
     220            0 :     bf_dump_prefix_pop(prefix);
     221            0 : }
     222              : 
     223            0 : int bf_cgen_get_counter(const struct bf_cgen *cgen,
     224              :                         enum bf_counter_type counter_idx,
     225              :                         struct bf_counter *counter)
     226              : {
     227            0 :     bf_assert(cgen && counter);
     228              : 
     229              :     /* There are two more counter than rules. The special counters must
     230              :      * be accessed via the specific values, to avoid confusion. */
     231            0 :     enum bf_counter_type rule_count = bf_list_size(&cgen->chain->rules);
     232            0 :     if (counter_idx == BF_COUNTER_POLICY) {
     233              :         counter_idx = rule_count;
     234            0 :     } else if (counter_idx == BF_COUNTER_ERRORS) {
     235            0 :         counter_idx = rule_count + 1;
     236            0 :     } else if (counter_idx < 0 || counter_idx >= rule_count) {
     237              :         return -EINVAL;
     238              :     }
     239              : 
     240            0 :     return bf_program_get_counter(cgen->program, counter_idx, counter);
     241              : }
     242              : 
     243            0 : int bf_cgen_set(struct bf_cgen *cgen, const struct bf_ns *ns,
     244              :                 struct bf_hookopts **hookopts)
     245              : {
     246            0 :     _cleanup_bf_program_ struct bf_program *prog = NULL;
     247            0 :     _cleanup_close_ int pindir_fd = -1;
     248              :     int r;
     249              : 
     250            0 :     bf_assert(cgen);
     251              : 
     252            0 :     if (bf_opts_persist()) {
     253            0 :         pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     254            0 :         if (pindir_fd < 0)
     255              :             return pindir_fd;
     256              :     }
     257              : 
     258            0 :     r = bf_program_new(&prog, cgen->chain);
     259            0 :     if (r < 0)
     260              :         return r;
     261              : 
     262            0 :     r = bf_program_generate(prog);
     263            0 :     if (r < 0)
     264            0 :         return bf_err_r(r, "failed to generate bf_program");
     265              : 
     266            0 :     r = bf_program_load(prog);
     267            0 :     if (r < 0)
     268            0 :         return bf_err_r(r, "failed to load the chain");
     269              : 
     270            0 :     if (hookopts) {
     271            0 :         r = bf_ns_set(ns, bf_ctx_get_ns());
     272            0 :         if (r)
     273            0 :             return bf_err_r(r, "failed to switch to the client's namespaces");
     274              : 
     275            0 :         r = bf_program_attach(prog, hookopts);
     276            0 :         if (r < 0)
     277            0 :             return bf_err_r(r, "failed to load and attach the chain");
     278              : 
     279            0 :         if (bf_ns_set(bf_ctx_get_ns(), ns))
     280            0 :             bf_abort("failed to restore previous namespaces, aborting");
     281              :     }
     282              : 
     283            0 :     if (bf_opts_persist()) {
     284            0 :         r = bf_program_pin(prog, pindir_fd);
     285            0 :         if (r)
     286              :             return r;
     287              :     }
     288              : 
     289            0 :     cgen->program = TAKE_PTR(prog);
     290              : 
     291            0 :     return r;
     292              : }
     293              : 
     294            0 : int bf_cgen_load(struct bf_cgen *cgen)
     295              : {
     296            0 :     _cleanup_bf_program_ struct bf_program *prog = NULL;
     297            0 :     _cleanup_close_ int pindir_fd = -1;
     298              :     int r;
     299              : 
     300            0 :     bf_assert(cgen);
     301              : 
     302            0 :     if (bf_opts_persist()) {
     303            0 :         pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     304            0 :         if (pindir_fd < 0)
     305              :             return pindir_fd;
     306              :     }
     307              : 
     308            0 :     r = bf_program_new(&prog, cgen->chain);
     309            0 :     if (r < 0)
     310              :         return r;
     311              : 
     312            0 :     r = bf_program_generate(prog);
     313            0 :     if (r < 0)
     314            0 :         return bf_err_r(r, "failed to generate bf_program");
     315              : 
     316            0 :     r = bf_program_load(prog);
     317            0 :     if (r < 0)
     318            0 :         return bf_err_r(r, "failed to load the chain");
     319              : 
     320            0 :     if (bf_opts_persist()) {
     321            0 :         r = bf_program_pin(prog, pindir_fd);
     322            0 :         if (r)
     323              :             return r;
     324              :     }
     325              : 
     326            0 :     bf_info("load %s", cgen->chain->name);
     327            0 :     bf_cgen_dump(cgen, EMPTY_PREFIX);
     328              : 
     329            0 :     cgen->program = TAKE_PTR(prog);
     330              : 
     331            0 :     return r;
     332              : }
     333              : 
     334            0 : int bf_cgen_attach(struct bf_cgen *cgen, const struct bf_ns *ns,
     335              :                    struct bf_hookopts **hookopts)
     336              : {
     337            0 :     _cleanup_close_ int pindir_fd = -1;
     338              :     int r;
     339              : 
     340            0 :     bf_assert(cgen && ns && hookopts);
     341              : 
     342            0 :     bf_info("attaching %s to %s", cgen->chain->name,
     343              :             bf_hook_to_str(cgen->chain->hook));
     344            0 :     bf_hookopts_dump(*hookopts, EMPTY_PREFIX);
     345              : 
     346            0 :     if (bf_opts_persist()) {
     347            0 :         pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     348            0 :         if (pindir_fd < 0)
     349              :             return pindir_fd;
     350              :     }
     351              : 
     352            0 :     r = bf_ns_set(ns, bf_ctx_get_ns());
     353            0 :     if (r)
     354            0 :         return bf_err_r(r, "failed to switch to the client's namespaces");
     355              : 
     356            0 :     r = bf_program_attach(cgen->program, hookopts);
     357            0 :     if (r < 0)
     358            0 :         return bf_err_r(r, "failed to attach chain '%s'", cgen->chain->name);
     359              : 
     360            0 :     if (bf_ns_set(bf_ctx_get_ns(), ns))
     361            0 :         bf_abort("failed to restore previous namespaces, aborting");
     362              : 
     363            0 :     if (bf_opts_persist()) {
     364            0 :         r = bf_link_pin(cgen->program->link, pindir_fd);
     365            0 :         if (r) {
     366            0 :             bf_program_detach(cgen->program);
     367            0 :             return r;
     368              :         }
     369              :     }
     370              : 
     371              :     return r;
     372              : }
     373              : 
     374            0 : int bf_cgen_update(struct bf_cgen *cgen, struct bf_chain **new_chain)
     375              : {
     376            0 :     _cleanup_bf_program_ struct bf_program *new_prog = NULL;
     377            0 :     _cleanup_close_ int pindir_fd = -1;
     378              :     struct bf_program *old_prog;
     379              :     int r;
     380              : 
     381            0 :     bf_assert(cgen && new_chain);
     382              : 
     383            0 :     old_prog = cgen->program;
     384              : 
     385            0 :     if (bf_opts_persist()) {
     386            0 :         pindir_fd = _bf_cgen_get_chain_pindir_fd((*new_chain)->name);
     387            0 :         if (pindir_fd < 0)
     388              :             return pindir_fd;
     389              :     }
     390              : 
     391            0 :     r = bf_program_new(&new_prog, *new_chain);
     392            0 :     if (r < 0)
     393            0 :         return bf_err_r(r, "failed to create a new bf_program");
     394              : 
     395            0 :     r = bf_program_generate(new_prog);
     396            0 :     if (r < 0) {
     397            0 :         return bf_err_r(r,
     398              :                         "failed to generate the bytecode for a new bf_program");
     399              :     }
     400              : 
     401            0 :     r = bf_program_load(new_prog);
     402            0 :     if (r)
     403            0 :         return bf_err_r(r, "failed to load new program");
     404              : 
     405            0 :     if (bf_opts_persist())
     406            0 :         bf_program_unpin(old_prog, pindir_fd);
     407              : 
     408            0 :     r = bf_link_update(old_prog->link, cgen->chain->hook,
     409            0 :                        new_prog->runtime.prog_fd);
     410            0 :     if (r) {
     411            0 :         bf_err_r(r, "failed to update bf_link object with new program");
     412            0 :         if (bf_opts_persist() && bf_program_pin(old_prog, pindir_fd) < 0)
     413            0 :             bf_err("failed to repin old program, ignoring");
     414            0 :         return r;
     415              :     }
     416              : 
     417            0 :     if (bf_opts_persist()) {
     418            0 :         r = bf_program_pin(cgen->program, pindir_fd);
     419            0 :         if (r)
     420            0 :             bf_warn_r(r, "failed to pin new prog, ignoring");
     421              :     }
     422              : 
     423            0 :     bf_swap(old_prog->link, new_prog->link);
     424            0 :     bf_swap(cgen->program, new_prog);
     425              : 
     426            0 :     bf_chain_free(&cgen->chain);
     427            0 :     cgen->chain = TAKE_PTR(*new_chain);
     428              : 
     429            0 :     return 0;
     430              : }
     431              : 
     432            0 : void bf_cgen_detach(struct bf_cgen *cgen)
     433              : {
     434            0 :     bf_assert(cgen);
     435              : 
     436            0 :     bf_program_detach(cgen->program);
     437            0 : }
     438              : 
     439            0 : void bf_cgen_unload(struct bf_cgen *cgen)
     440              : {
     441            0 :     _cleanup_close_ int chain_fd = -1;
     442              : 
     443            0 :     bf_assert(cgen);
     444              : 
     445            0 :     chain_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
     446            0 :     if (chain_fd < 0) {
     447            0 :         bf_err_r(chain_fd, "failed to open pin directory for '%s'",
     448              :                  cgen->chain->name);
     449              :         return;
     450              :     }
     451              : 
     452              :     // The chain's pin directory will be removed in bf_cgen_free()
     453            0 :     bf_program_unpin(cgen->program, chain_fd);
     454            0 :     bf_program_unload(cgen->program);
     455              : }
     456              : 
     457            0 : int bf_cgen_get_counters(const struct bf_cgen *cgen, bf_list *counters)
     458              : {
     459            0 :     bf_list _counters = bf_list_default_from(*counters);
     460              :     int r;
     461              : 
     462            0 :     bf_assert(cgen && counters);
     463              : 
     464            0 :     for (ssize_t i = BF_COUNTER_POLICY;
     465            0 :          i < (ssize_t)bf_list_size(&cgen->chain->rules); ++i) {
     466            0 :         _cleanup_bf_counter_ struct bf_counter *counter = NULL;
     467              : 
     468            0 :         r = bf_counter_new(&counter, 0, 0);
     469            0 :         if (r)
     470              :             return r;
     471              : 
     472            0 :         r = bf_cgen_get_counter(cgen, i, counter);
     473            0 :         if (r)
     474              :             return r;
     475              : 
     476            0 :         r = bf_list_add_tail(&_counters, counter);
     477            0 :         if (r)
     478              :             return r;
     479              : 
     480            0 :         TAKE_PTR(counter);
     481              :     }
     482              : 
     483            0 :     *counters = bf_list_move(_counters);
     484              : 
     485            0 :     return 0;
     486              : }
        

Generated by: LCOV version 2.0-1