LCOV - code coverage report
Current view: top level - libbpfilter - cli.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 85.1 % 221 188
Test Date: 2026-04-04 09:03:29 Functions: 100.0 % 14 14
Branches: 50.0 % 190 95

             Branch data     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 <stdlib.h>
       7                 :             : #include <string.h>
       8                 :             : #include <unistd.h>
       9                 :             : 
      10                 :             : #include <bpfilter/ctx.h>
      11                 :             : 
      12                 :             : #include "bpfilter/chain.h"
      13                 :             : #include "bpfilter/core/list.h"
      14                 :             : #include "bpfilter/counter.h"
      15                 :             : #include "bpfilter/helper.h"
      16                 :             : #include "bpfilter/hook.h"
      17                 :             : #include "bpfilter/logger.h"
      18                 :             : #include "bpfilter/pack.h"
      19                 :             : #include "bpfilter/set.h"
      20                 :             : #include "cgen/cgen.h"
      21                 :             : #include "cgen/handle.h"
      22                 :             : #include "cgen/prog/link.h"
      23                 :             : #include "cgen/prog/map.h"
      24                 :             : 
      25                 :         143 : static int copy_hookopts(struct bf_hookopts **dest,
      26                 :             :                          const struct bf_hookopts *src)
      27                 :             : {
      28                 :             :     struct bf_hookopts *copy;
      29                 :             : 
      30                 :         143 :     copy = bf_memdup(src, sizeof(*src));
      31         [ -  + ]:         143 :     if (!copy)
      32                 :             :         return -ENOMEM;
      33                 :             : 
      34         [ +  + ]:         143 :     if (src->cgpath) {
      35                 :          51 :         copy->cgpath = strdup(src->cgpath);
      36         [ -  + ]:          51 :         if (!copy->cgpath) {
      37                 :           0 :             free(copy);
      38                 :           0 :             return -ENOMEM;
      39                 :             :         }
      40                 :             :     }
      41                 :             : 
      42                 :         143 :     *dest = copy;
      43                 :             : 
      44                 :         143 :     return 0;
      45                 :             : }
      46                 :             : 
      47                 :           4 : int bf_ruleset_get(bf_list *chains, bf_list *hookopts, bf_list *counters)
      48                 :             : {
      49                 :           4 :     _clean_bf_list_ bf_list cgens = bf_list_default(NULL, NULL);
      50                 :           4 :     _clean_bf_list_ bf_list _chains = bf_list_default_from(*chains);
      51                 :           4 :     _clean_bf_list_ bf_list _hookopts = bf_list_default_from(*hookopts);
      52                 :           4 :     _clean_bf_list_ bf_list _counters = bf_list_default_from(*counters);
      53                 :             :     int r;
      54                 :             : 
      55                 :           4 :     r = bf_ctx_get_cgens(&cgens);
      56         [ -  + ]:           4 :     if (r < 0)
      57         [ #  # ]:           0 :         return bf_err_r(r, "failed to get cgen list");
      58                 :             : 
      59   [ +  -  +  +  :          18 :     bf_list_foreach (&cgens, cgen_node) {
                   +  + ]
      60                 :             :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
      61                 :           5 :         _free_bf_chain_ struct bf_chain *chain = NULL;
      62                 :           5 :         _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
      63                 :           0 :         _free_bf_list_ bf_list *cgen_counters = NULL;
      64                 :             : 
      65                 :           5 :         r = bf_chain_new_from_copy(&chain, cgen->chain);
      66         [ -  + ]:           5 :         if (r)
      67         [ #  # ]:           0 :             return bf_err_r(r, "failed to copy chain");
      68                 :             : 
      69                 :           5 :         r = bf_list_add_tail(&_chains, chain);
      70         [ +  - ]:           5 :         if (r)
      71                 :             :             return r;
      72                 :           5 :         TAKE_PTR(chain);
      73                 :             : 
      74   [ +  +  +  - ]:           5 :         if (cgen->handle->link && cgen->handle->link->hookopts) {
      75                 :           4 :             r = copy_hookopts(&hookopts_copy, cgen->handle->link->hookopts);
      76         [ -  + ]:           4 :             if (r)
      77         [ #  # ]:           0 :                 return bf_err_r(r, "failed to copy hookopts");
      78                 :             :         }
      79                 :           5 :         r = bf_list_add_tail(&_hookopts, hookopts_copy);
      80         [ +  - ]:           5 :         if (r)
      81                 :             :             return r;
      82                 :           5 :         TAKE_PTR(hookopts_copy);
      83                 :             : 
      84                 :           5 :         r = bf_list_new(&cgen_counters,
      85                 :           5 :                         &bf_list_ops_default(bf_counter_free, NULL));
      86         [ +  - ]:           5 :         if (r)
      87                 :             :             return r;
      88                 :             : 
      89                 :           5 :         r = bf_cgen_get_counters(cgen, cgen_counters);
      90         [ +  - ]:           5 :         if (r)
      91                 :             :             return r;
      92                 :             : 
      93                 :           5 :         r = bf_list_add_tail(&_counters, cgen_counters);
      94         [ +  - ]:           5 :         if (r)
      95                 :             :             return r;
      96                 :           5 :         TAKE_PTR(cgen_counters);
      97                 :             :     }
      98                 :             : 
      99                 :           4 :     *chains = bf_list_move(_chains);
     100                 :           4 :     *hookopts = bf_list_move(_hookopts);
     101                 :           4 :     *counters = bf_list_move(_counters);
     102                 :             : 
     103                 :           4 :     return 0;
     104                 :             : }
     105                 :             : 
     106         [ +  + ]:           9 : int bf_ruleset_set(bf_list *chains, bf_list *hookopts)
     107                 :             : {
     108                 :             :     struct bf_list_node *chain_node = bf_list_get_head(chains);
     109                 :             :     struct bf_list_node *hookopts_node = bf_list_get_head(hookopts);
     110                 :             :     int r;
     111                 :             : 
     112         [ +  + ]:           9 :     if (bf_list_size(chains) != bf_list_size(hookopts))
     113                 :             :         return -EINVAL;
     114                 :             : 
     115                 :           8 :     bf_ctx_flush();
     116                 :             : 
     117         [ +  + ]:          18 :     while (chain_node && hookopts_node) {
     118                 :          12 :         _free_bf_cgen_ struct bf_cgen *cgen = NULL;
     119                 :          12 :         _free_bf_chain_ struct bf_chain *chain_copy = NULL;
     120                 :          24 :         _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
     121                 :             :         struct bf_chain *chain = bf_list_node_get_data(chain_node);
     122                 :             :         struct bf_hookopts *node_hookopts =
     123                 :             :             bf_list_node_get_data(hookopts_node);
     124                 :             : 
     125                 :          12 :         r = bf_chain_new_from_copy(&chain_copy, chain);
     126         [ -  + ]:          12 :         if (r)
     127                 :           0 :             goto err_load;
     128                 :             : 
     129         [ +  + ]:          12 :         if (node_hookopts) {
     130                 :          10 :             r = copy_hookopts(&hookopts_copy, node_hookopts);
     131         [ -  + ]:          10 :             if (r)
     132                 :           0 :                 goto err_load;
     133                 :             :         }
     134                 :             : 
     135                 :          12 :         r = bf_cgen_new(&cgen, &chain_copy);
     136         [ -  + ]:          12 :         if (r)
     137                 :           0 :             goto err_load;
     138                 :             : 
     139         [ +  + ]:          14 :         r = bf_cgen_set(cgen, hookopts_copy ? &hookopts_copy : NULL);
     140         [ +  + ]:          12 :         if (r) {
     141         [ +  - ]:           2 :             bf_err_r(r, "failed to set chain '%s'", cgen->chain->name);
     142                 :           2 :             goto err_load;
     143                 :             :         }
     144                 :             : 
     145                 :          10 :         r = bf_ctx_set_cgen(cgen);
     146         [ -  + ]:          10 :         if (r) {
     147                 :           0 :             bf_cgen_unload(cgen);
     148                 :           0 :             goto err_load;
     149                 :             :         }
     150                 :             : 
     151                 :          10 :         TAKE_PTR(cgen);
     152                 :             : 
     153                 :             :         chain_node = bf_list_node_next(chain_node);
     154                 :             :         hookopts_node = bf_list_node_next(hookopts_node);
     155                 :             :     }
     156                 :             : 
     157                 :             :     return 0;
     158                 :             : 
     159                 :             : err_load:
     160                 :           2 :     bf_ctx_flush();
     161                 :           2 :     return r;
     162                 :             : }
     163                 :             : 
     164                 :        1680 : int bf_ruleset_flush(void)
     165                 :             : {
     166                 :        1680 :     bf_ctx_flush();
     167                 :             : 
     168                 :        1680 :     return 0;
     169                 :             : }
     170                 :             : 
     171                 :        1148 : int bf_chain_set(struct bf_chain *chain, struct bf_hookopts *hookopts)
     172                 :             : {
     173                 :             :     struct bf_cgen *old_cgen;
     174                 :        1148 :     _free_bf_cgen_ struct bf_cgen *new_cgen = NULL;
     175                 :        1148 :     _free_bf_chain_ struct bf_chain *chain_copy = NULL;
     176                 :        1148 :     _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
     177                 :             :     int r;
     178                 :             : 
     179                 :             :     assert(chain);
     180                 :             : 
     181                 :        1148 :     r = bf_chain_new_from_copy(&chain_copy, chain);
     182         [ +  - ]:        1148 :     if (r)
     183                 :             :         return r;
     184                 :             : 
     185         [ +  + ]:        1148 :     if (hookopts) {
     186                 :          87 :         r = copy_hookopts(&hookopts_copy, hookopts);
     187         [ +  - ]:          87 :         if (r)
     188                 :             :             return r;
     189                 :             :     }
     190                 :             : 
     191                 :        1148 :     r = bf_cgen_new(&new_cgen, &chain_copy);
     192         [ +  - ]:        1148 :     if (r)
     193                 :             :         return r;
     194                 :             : 
     195                 :        1148 :     old_cgen = bf_ctx_get_cgen(new_cgen->chain->name);
     196         [ +  + ]:        1148 :     if (old_cgen)
     197                 :         233 :         (void)bf_ctx_delete_cgen(old_cgen, true);
     198                 :             : 
     199         [ +  + ]:        2209 :     r = bf_cgen_set(new_cgen, hookopts_copy ? &hookopts_copy : NULL);
     200         [ +  + ]:        1148 :     if (r)
     201                 :             :         return r;
     202                 :             : 
     203                 :        1145 :     r = bf_ctx_set_cgen(new_cgen);
     204         [ -  + ]:        1145 :     if (r) {
     205                 :           0 :         bf_cgen_unload(new_cgen);
     206                 :           0 :         return r;
     207                 :             :     }
     208                 :             : 
     209                 :        1145 :     TAKE_PTR(new_cgen);
     210                 :             : 
     211                 :        1145 :     return 0;
     212                 :             : }
     213                 :             : 
     214                 :        1047 : int bf_chain_get(const char *name, struct bf_chain **chain,
     215                 :             :                  struct bf_hookopts **hookopts, bf_list *counters)
     216                 :             : {
     217                 :        1047 :     _free_bf_chain_ struct bf_chain *_chain = NULL;
     218                 :        1047 :     _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
     219                 :        1047 :     _clean_bf_list_ bf_list _counters = bf_list_default_from(*counters);
     220                 :             :     struct bf_cgen *cgen;
     221                 :             :     int r;
     222                 :             : 
     223                 :             :     assert(name);
     224                 :             :     assert(chain);
     225                 :             :     assert(hookopts);
     226                 :             :     assert(counters);
     227                 :             : 
     228                 :        1047 :     cgen = bf_ctx_get_cgen(name);
     229         [ +  + ]:        1047 :     if (!cgen)
     230         [ +  - ]:           1 :         return bf_err_r(-ENOENT, "chain '%s' not found", name);
     231                 :             : 
     232                 :        1046 :     r = bf_chain_new_from_copy(&_chain, cgen->chain);
     233         [ +  - ]:        1046 :     if (r)
     234                 :             :         return r;
     235                 :             : 
     236   [ +  +  +  - ]:        1046 :     if (cgen->handle->link && cgen->handle->link->hookopts) {
     237                 :          28 :         r = copy_hookopts(&_hookopts, cgen->handle->link->hookopts);
     238         [ +  - ]:          28 :         if (r)
     239                 :             :             return r;
     240                 :             :     }
     241                 :             : 
     242                 :        1046 :     r = bf_cgen_get_counters(cgen, &_counters);
     243         [ -  + ]:        1046 :     if (r)
     244         [ #  # ]:           0 :         return bf_err_r(r, "failed to get counters for '%s'", name);
     245                 :             : 
     246                 :        1046 :     *chain = TAKE_PTR(_chain);
     247                 :        1046 :     *hookopts = TAKE_PTR(_hookopts);
     248                 :        1046 :     *counters = bf_list_move(_counters);
     249                 :             : 
     250                 :        1046 :     return 0;
     251                 :             : }
     252                 :             : 
     253                 :        2681 : int bf_chain_prog_fd(const char *name)
     254                 :             : {
     255                 :             :     struct bf_cgen *cgen;
     256                 :             : 
     257         [ +  + ]:        2681 :     if (!name)
     258                 :             :         return -EINVAL;
     259                 :             : 
     260                 :        2680 :     cgen = bf_ctx_get_cgen(name);
     261         [ -  + ]:        2680 :     if (!cgen)
     262         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
     263                 :             : 
     264         [ -  + ]:        2680 :     if (cgen->handle->prog_fd == -1)
     265         [ #  # ]:           0 :         return bf_err_r(-ENODEV, "chain '%s' has no loaded program", name);
     266                 :             : 
     267                 :        2680 :     return dup(cgen->handle->prog_fd);
     268                 :             : }
     269                 :             : 
     270                 :           1 : int bf_chain_logs_fd(const char *name)
     271                 :             : {
     272                 :             :     struct bf_cgen *cgen;
     273                 :             : 
     274         [ -  + ]:           1 :     if (!name)
     275                 :             :         return -EINVAL;
     276                 :             : 
     277                 :           0 :     cgen = bf_ctx_get_cgen(name);
     278         [ #  # ]:           0 :     if (!cgen)
     279         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
     280                 :             : 
     281         [ #  # ]:           0 :     if (!cgen->handle->lmap)
     282         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "chain '%s' has no logs buffer", name);
     283                 :             : 
     284                 :           0 :     return dup(cgen->handle->lmap->fd);
     285                 :             : }
     286                 :             : 
     287                 :          16 : int bf_chain_load(struct bf_chain *chain)
     288                 :             : {
     289                 :          16 :     _free_bf_cgen_ struct bf_cgen *cgen = NULL;
     290                 :          16 :     _free_bf_chain_ struct bf_chain *chain_copy = NULL;
     291                 :             :     int r;
     292                 :             : 
     293                 :             :     assert(chain);
     294                 :             : 
     295         [ -  + ]:          16 :     if (bf_ctx_get_cgen(chain->name))
     296         [ #  # ]:           0 :         return bf_err_r(-EEXIST, "chain '%s' already exists", chain->name);
     297                 :             : 
     298                 :          16 :     r = bf_chain_new_from_copy(&chain_copy, chain);
     299         [ +  - ]:          16 :     if (r)
     300                 :             :         return r;
     301                 :             : 
     302                 :          16 :     r = bf_cgen_new(&cgen, &chain_copy);
     303         [ +  - ]:          16 :     if (r)
     304                 :             :         return r;
     305                 :             : 
     306                 :          16 :     r = bf_cgen_load(cgen);
     307         [ +  - ]:          16 :     if (r)
     308                 :             :         return r;
     309                 :             : 
     310                 :          16 :     r = bf_ctx_set_cgen(cgen);
     311         [ -  + ]:          16 :     if (r) {
     312                 :           0 :         bf_cgen_unload(cgen);
     313         [ #  # ]:           0 :         return bf_err_r(r, "failed to add cgen to the runtime context");
     314                 :             :     }
     315                 :             : 
     316                 :          16 :     TAKE_PTR(cgen);
     317                 :             : 
     318                 :          16 :     return 0;
     319                 :             : }
     320                 :             : 
     321                 :          15 : int bf_chain_attach(const char *name, const struct bf_hookopts *hookopts)
     322                 :             : {
     323                 :             :     struct bf_cgen *cgen;
     324                 :          15 :     _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
     325                 :             :     int r;
     326                 :             : 
     327                 :             :     assert(name);
     328                 :             :     assert(hookopts);
     329                 :             : 
     330                 :          15 :     cgen = bf_ctx_get_cgen(name);
     331         [ -  + ]:          15 :     if (!cgen)
     332         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "chain '%s' does not exist", name);
     333                 :             : 
     334         [ -  + ]:          15 :     if (cgen->handle->link)
     335         [ #  # ]:           0 :         return bf_err_r(-EBUSY, "chain '%s' is already linked to a hook", name);
     336                 :             : 
     337                 :          15 :     r = bf_hookopts_validate(hookopts, cgen->chain->hook);
     338         [ +  + ]:          15 :     if (r)
     339         [ +  - ]:           1 :         return bf_err_r(r, "failed to validate hook options");
     340                 :             : 
     341                 :          14 :     r = copy_hookopts(&hookopts_copy, hookopts);
     342         [ +  - ]:          14 :     if (r)
     343                 :             :         return r;
     344                 :             : 
     345                 :          14 :     r = bf_cgen_attach(cgen, &hookopts_copy);
     346         [ +  + ]:          14 :     if (r)
     347         [ +  - ]:           2 :         return bf_err_r(r, "failed to attach codegen to hook");
     348                 :             : 
     349                 :             :     return 0;
     350                 :             : }
     351                 :             : 
     352                 :           7 : int bf_chain_update(const struct bf_chain *chain)
     353                 :             : {
     354                 :           7 :     _free_bf_chain_ struct bf_chain *chain_copy = NULL;
     355                 :             :     struct bf_cgen *cgen;
     356                 :             :     int r;
     357                 :             : 
     358                 :             :     assert(chain);
     359                 :             : 
     360                 :           7 :     cgen = bf_ctx_get_cgen(chain->name);
     361         [ +  + ]:           7 :     if (!cgen)
     362                 :             :         return -ENOENT;
     363                 :             : 
     364                 :           6 :     r = bf_chain_new_from_copy(&chain_copy, chain);
     365         [ +  - ]:           6 :     if (r)
     366                 :             :         return r;
     367                 :             : 
     368                 :           6 :     r = bf_cgen_update(cgen, &chain_copy, 0);
     369                 :             :     if (r)
     370                 :             :         return r;
     371                 :             : 
     372                 :             :     return 0;
     373                 :             : }
     374                 :             : 
     375                 :          16 : static int copy_set(struct bf_set **dest, const struct bf_set *src)
     376                 :             : {
     377                 :          16 :     _free_bf_wpack_ bf_wpack_t *wpack = NULL;
     378                 :          16 :     _free_bf_rpack_ bf_rpack_t *rpack = NULL;
     379                 :             :     const void *data;
     380                 :             :     size_t data_len;
     381                 :             :     int r;
     382                 :             : 
     383                 :          16 :     r = bf_wpack_new(&wpack);
     384         [ +  - ]:          16 :     if (r)
     385                 :             :         return r;
     386                 :             : 
     387                 :          16 :     bf_wpack_open_object(wpack, "set");
     388                 :          16 :     r = bf_set_pack(src, wpack);
     389         [ +  - ]:          16 :     if (r)
     390                 :             :         return r;
     391                 :          16 :     bf_wpack_close_object(wpack);
     392                 :             : 
     393                 :          16 :     r = bf_wpack_get_data(wpack, &data, &data_len);
     394         [ +  - ]:          16 :     if (r)
     395                 :             :         return r;
     396                 :             : 
     397                 :          16 :     r = bf_rpack_new(&rpack, data, data_len);
     398         [ +  - ]:          16 :     if (r)
     399                 :             :         return r;
     400                 :             : 
     401                 :             :     bf_rpack_node_t child;
     402                 :          16 :     r = bf_rpack_kv_obj(bf_rpack_root(rpack), "set", &child);
     403         [ +  - ]:          16 :     if (r)
     404                 :             :         return r;
     405                 :             : 
     406                 :          16 :     return bf_set_new_from_pack(dest, child);
     407                 :             : }
     408                 :             : 
     409                 :           8 : int bf_chain_update_set(const char *name, const struct bf_set *to_add,
     410                 :             :                         const struct bf_set *to_remove)
     411                 :             : {
     412                 :           8 :     _free_bf_chain_ struct bf_chain *new_chain = NULL;
     413                 :             :     struct bf_set *dest_set = NULL;
     414                 :             :     struct bf_cgen *cgen;
     415                 :           8 :     _free_bf_set_ struct bf_set *add_copy = NULL;
     416                 :           8 :     _free_bf_set_ struct bf_set *remove_copy = NULL;
     417                 :             :     int r;
     418                 :             : 
     419                 :             :     assert(name);
     420                 :             :     assert(to_add);
     421                 :             :     assert(to_remove);
     422                 :             : 
     423         [ -  + ]:           8 :     if (!bf_streq(to_add->name, to_remove->name))
     424         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "to_add->name must match to_remove->name");
     425                 :             : 
     426                 :           8 :     cgen = bf_ctx_get_cgen(name);
     427         [ -  + ]:           8 :     if (!cgen)
     428         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "chain '%s' does not exist", name);
     429                 :             : 
     430                 :           8 :     r = bf_chain_new_from_copy(&new_chain, cgen->chain);
     431         [ +  - ]:           8 :     if (r)
     432                 :             :         return r;
     433                 :             : 
     434                 :           8 :     dest_set = bf_chain_get_set_by_name(new_chain, to_add->name);
     435         [ -  + ]:           8 :     if (!dest_set)
     436         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "set '%s' does not exist", to_add->name);
     437                 :             : 
     438                 :           8 :     r = copy_set(&add_copy, to_add);
     439         [ +  - ]:           8 :     if (r)
     440                 :             :         return r;
     441                 :             : 
     442                 :           8 :     r = copy_set(&remove_copy, to_remove);
     443         [ +  - ]:           8 :     if (r)
     444                 :             :         return r;
     445                 :             : 
     446                 :           8 :     r = bf_set_add_many(dest_set, &add_copy);
     447         [ -  + ]:           8 :     if (r)
     448         [ #  # ]:           0 :         return bf_err_r(r, "failed to calculate set union");
     449                 :             : 
     450                 :           8 :     r = bf_set_remove_many(dest_set, &remove_copy);
     451         [ -  + ]:           8 :     if (r)
     452         [ #  # ]:           0 :         return bf_err_r(r, "failed to calculate set difference");
     453                 :             : 
     454                 :           8 :     r = bf_cgen_update(cgen, &new_chain,
     455                 :             :                        BF_FLAG(BF_CGEN_UPDATE_PRESERVE_COUNTERS));
     456         [ -  + ]:           8 :     if (r)
     457         [ #  # ]:           0 :         return bf_err_r(r, "failed to update chain with new set data");
     458                 :             : 
     459                 :             :     return 0;
     460                 :             : }
     461                 :             : 
     462                 :          29 : int bf_chain_flush(const char *name)
     463                 :             : {
     464                 :             :     struct bf_cgen *cgen;
     465                 :             : 
     466                 :             :     assert(name);
     467                 :             : 
     468                 :          29 :     cgen = bf_ctx_get_cgen(name);
     469         [ +  - ]:          29 :     if (!cgen)
     470                 :             :         return -ENOENT;
     471                 :             : 
     472                 :          29 :     return bf_ctx_delete_cgen(cgen, true);
     473                 :             : }
        

Generated by: LCOV version 2.0-1