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

Generated by: LCOV version 2.0-1