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

Generated by: LCOV version 2.0-1